pax_global_header00006660000000000000000000000064136223413360014515gustar00rootroot0000000000000052 comment=af9b08853e4ce88733ec7e358ba6ae7af5c26fad mlt-6.20.0/000077500000000000000000000000001362234133600123765ustar00rootroot00000000000000mlt-6.20.0/.gitignore000066400000000000000000000010141362234133600143620ustar00rootroot00000000000000*.o *.so *.so.* *.dll *.def config.mak config.h config.log .depend *~ *.cxx docs/html/* releases/* patches/* src/melt/melt src/modules/lumas/luma src/modules/lumas/NTSC/*.pgm src/modules/lumas/PAL/*.pgm src/modules/lumas/16_9/*.pgm src/modules/lumas/9_16/*.pgm src/modules/lumas/square/*.pgm src/modules/lumas/.executed src/swig/csharp/src_swig/* src/swig/perl/blib/* *.dylib src/swig/java/src_swig disable-* mlt.config mlt.creator mlt.files mlt.includes mlt-config *.pc packages.dat make.inc src/examples/play *.dot *.rej mlt-6.20.0/.gitlab-ci.yml000066400000000000000000000105571362234133600150420ustar00rootroot00000000000000ubuntu-daily: image: ubuntu:disco script: - sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" - DEBIAN_FRONTEND=noninteractive apt-get -qq update - apt-get -yqq build-dep mlt - ./configure --enable-gpl --enable-gpl3 && make && make install # there's no `check` or `test` `make` target ubuntu-latest: image: ubuntu:cosmic script: - sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" - DEBIAN_FRONTEND=noninteractive apt-get -qq update - apt-get -yqq build-dep mlt - ./configure --enable-gpl --enable-gpl3 && make && make install # there's no `check` or `test` `make` target ubuntu-lts-3: image: ubuntu:bionic script: - sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" - DEBIAN_FRONTEND=noninteractive apt-get -qq update - apt-get -yqq build-dep mlt - ./configure --enable-gpl --enable-gpl3 && make && make install # there's no `check` or `test` `make` target ubuntu-lts-2: image: ubuntu:xenial script: - sed -i '/^#\sdeb-src /s/^#//' "/etc/apt/sources.list" - apt-get -qq update - apt-get -yqq build-dep mlt - ./configure --enable-gpl --enable-gpl3 && make && make install # there's no `check` or `test` `make` target debian-testing: image: debian:buster script: - echo -e 'deb-src http://deb.debian.org/debian buster main\ndeb-src http://deb.debian.org/debian buster-updates main\ndeb-src http://security.debian.org buster/updates main' >> /etc/apt/sources.list - apt-get -qq update - apt-get -yqq build-dep mlt - ./configure --enable-gpl --enable-gpl3 && make && make install # there's no `check` or `test` `make` target debian-stable: image: debian:stretch script: - echo -e 'deb-src http://deb.debian.org/debian stretch main\ndeb-src http://deb.debian.org/debian stretch-updates main\ndeb-src http://security.debian.org stretch/updates main' >> /etc/apt/sources.list - apt-get -qq update - apt-get -yqq build-dep mlt - ./configure --enable-gpl --enable-gpl3 && make && make install # there's no `check` or `test` `make` target fedora-29: image: fedora:29 script: - yum --assumeyes groupinstall "Development Tools" - yum --assumeyes install yasm gavl-devel libsamplerate-devel libxml2-devel ladspa-devel jack-audio-connection-kit-devel sox-devel SDL-devel gtk2-devel qt-devel libexif-devel libtheora-devel libvorbis-devel libvdpau-devel libsoup-devel liboil-devel python-devel alsa-lib pulseaudio-libs-devel gcc-c++ # unclear why `gcc-c++` isn't in `Development Tools` - ./configure --enable-gpl --enable-gpl3 && make && make install fedora-28: image: fedora:28 script: - yum --assumeyes groupinstall "Development Tools" - yum --assumeyes install yasm gavl-devel libsamplerate-devel libxml2-devel ladspa-devel jack-audio-connection-kit-devel sox-devel SDL-devel gtk2-devel qt-devel libexif-devel libtheora-devel libvorbis-devel libvdpau-devel libsoup-devel liboil-devel python-devel alsa-lib pulseaudio-libs-devel gcc-c++ # unclear why `gcc-c++` isn't in `Development Tools` - ./configure --enable-gpl --enable-gpl3 && make && make install fedora-27: image: fedora:27 script: - yum --assumeyes groupinstall "Development Tools" - yum --assumeyes install yasm gavl-devel libsamplerate-devel libxml2-devel ladspa-devel jack-audio-connection-kit-devel sox-devel SDL-devel gtk2-devel qt-devel libexif-devel libtheora-devel libvorbis-devel libvdpau-devel libsoup-devel liboil-devel python-devel alsa-lib pulseaudio-libs-devel gcc-c++ # unclear why `gcc-c++` isn't in `Development Tools` - ./configure --enable-gpl --enable-gpl3 && make && make install fedora-26: image: fedora:26 script: - yum --assumeyes groupinstall "Development Tools" - yum --assumeyes install yasm gavl-devel libsamplerate-devel libxml2-devel ladspa-devel jack-audio-connection-kit-devel sox-devel SDL-devel gtk2-devel qt-devel libexif-devel libtheora-devel libvorbis-devel libvdpau-devel libsoup-devel liboil-devel python-devel alsa-lib pulseaudio-libs-devel gcc-c++ # unclear why `gcc-c++` isn't in `Development Tools` - ./configure --enable-gpl --enable-gpl3 && make && make install mlt-6.20.0/.travis.yml000066400000000000000000000022071362234133600145100ustar00rootroot00000000000000matrix: include: - language: c services: - docker before_install: - docker pull ubuntu:16.04 script: - docker run -v $PWD:/root -w /root ubuntu:16.04 /bin/sh -c "sed -i -e 's/# deb-src/deb-src/' /etc/apt/sources.list && apt-get -qq update && apt-get -y build-dep melt && apt-get -y install qt5-default locales && ./configure --prefix=/usr --enable-gpl --enable-gpl3 --enable-lumas && make -j && make install && cd src/tests && qmake tests.pro && make -j && echo de_DE.UTF-8 UTF-8 >> /etc/locale.gen && locale-gen && for test in \$(find . -type f -name 'test_*' -executable); do \$test; done" - docker build -t mltframework/melt:latest . - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - docker push mltframework/melt - language: python python: 3.7 dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069) before_install: pip install flake8 script: # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics mlt-6.20.0/AUTHORS000066400000000000000000000006461362234133600134540ustar00rootroot00000000000000Charles Yates Dan Dennedy Stephane Fillod (effectv) Marco Gittler (frei0r, oldfilm, qimage/kdenlivetitle) Jean-Baptiste Mardelle (kdenlive, qimage) Zachary Drew (motion_est) Maksym Veremeyenko Brian Matherly Janne Liljeblad Steinar H. Gunderson mlt-6.20.0/CMakeLists.txt000066400000000000000000000024451362234133600151430ustar00rootroot00000000000000project(MLT) set(MLT_VERSION 6.19.0) cmake_minimum_required(VERSION 3.0) find_package(PkgConfig REQUIRED) include(GNUInstallDirs) option(GPL "Enable GPLv2 modules" ON) option(GPL3 "Enable GPLv3 modules" ON) if(WIN32) option(NODEPLOY "Keep bin/ lib/ layout on Windows" ON) endif() if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(AMD64)") set(X86_64 ON) add_compile_definitions(USE_MMX USE_SSE USE_SSE2 ARCH_X86_64) endif() if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") add_compile_options(-ffast-math) elseif(MSVC) add_compile_options(/fp:fast) endif() find_package(Threads REQUIRED) add_subdirectory(src/framework) add_subdirectory(src/melt) add_subdirectory(src/mlt++) #add_subdirectory(src/swig) #file(GLOB modules src/modules/*/) set(modules src/modules/avformat src/modules/core src/modules/decklink src/modules/frei0r src/modules/feeds src/modules/gtk2 src/modules/kdenlive src/modules/motion_est src/modules/normalize src/modules/oldfilm src/modules/opencv src/modules/plus src/modules/plusgpl src/modules/qt src/modules/rubberband src/modules/rtaudio src/modules/sdl2 src/modules/vid.stab src/modules/vmfx src/modules/xine src/modules/xml ) foreach(module ${modules}) add_subdirectory(${module}) endforeach() mlt-6.20.0/COPYING000066400000000000000000000636311362234133600134420ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. 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.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! mlt-6.20.0/ChangeLog000066400000000000000000022747521362234133600141730ustar00rootroot000000000000002020-02-16 Dan Dennedy * Doxyfile, NEWS, configure, docs/melt.1, src/framework/mlt_version.h: version 6.20.0 * .travis.yml, Dockerfile: add Dockerfile and integrate it with Travis CI 2020-02-15 Dan Dennedy * src/modules/core/transition_composite.c, src/modules/core/transition_composite.yml: fix #506 crash when using invert in composite 2020-02-14 Dan Dennedy * presets/consumer/avformat/ALAC, presets/consumer/avformat/FLAC, presets/consumer/avformat/intermediate/ProRes HQ, presets/consumer/avformat/intermediate/ProRes-Kostya: add ALAC, FLAC, and ProRes HQ encode presets 2020-02-09 Brian Matherly * src/modules/avformat/filter_swresample.c, src/modules/core/producer_timewarp.c, src/modules/core/producer_timewarp.yml, src/modules/rubberband/filter_rbpitch.cpp: Add pitch compensation to timewarp producer 2020-02-08 Jean-Baptiste Mardelle * src/modules/opencv/filter_opencv_tracker.cpp, src/modules/opencv/filter_opencv_tracker.yml: opencv.tracker : add CSRT and MOSSE algorithms in version 2 2020-02-07 Maksym Veremeyenko * src/modules/core/composite_line_yuv_sse2_simple.c, src/modules/core/transition_matte.c, src/modules/decklink/common.cpp, src/modules/ndi/factory.c: fix #518 crash in matte transition 2020-02-06 Dan Dennedy * presets/consumer/avformat/intermediate/DNxHR-HQ, src/modules/lumas/create_lumas: add a DNxHR-HQ avformat consumer preset 2020-02-06 Jean-Baptiste Mardelle * src/modules/qt/filter_qtblend.cpp, src/modules/qt/transition_qtblend.cpp: qtblend: adjust aspect ratio on consumer scaling, small optimizations 2020-02-01 Dan Dennedy * src/modules/plus/filter_text.c, src/modules/qt/filter_qtext.cpp: support text keyframes in text filters 2020-01-27 Dan Dennedy * src/modules/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_vismv.c, src/modules/opencv/filter_opencv_tracker.cpp: disable consumer scale in motion_est and opencv * CMakeLists.txt, src/modules/rubberband/CMakeLists.txt: add CMake to rubberband module 2020-01-19 Brian Matherly * src/modules/rubberband/Makefile, src/modules/rubberband/configure, src/modules/rubberband/factory.c, src/modules/rubberband/filter_rbpitch.cpp, src/modules/rubberband/filter_rbpitch.yml, src/modules/rubberband/gpl: Add rubberband module and rbpitch filter. 2020-01-26 Dan Dennedy * src/framework/mlt_properties.c, src/tests/test_properties/test_properties.cpp: fix mlt_properties_set regression in e0304b5 2020-01-25 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/mlt++.vers: add mlt_properties_set_string() and Mlt::Properties::set_string() These new functions bypass any attempt to evaluate an expression. * src/melt/melt.c, src/modules/xml/producer_xml.c: use multiplication when applying scale in melt and xml 2020-01-19 Dan Dennedy * src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h, src/mlt++/mlt++.vers: add scale_width() and scale_height() to Mlt::Profile * src/modules/avformat/filter_avfilter.c, src/modules/frei0r/frei0r_helper.c, src/modules/gtk2/producer_pango.c, src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_wave.c, src/modules/oldfilm/filter_lines.c, src/modules/oldfilm/filter_oldfilm.c, src/modules/oldfilm/filter_vignette.c, src/modules/opengl/filter_movit_blur.cpp, .../opengl/filter_movit_deconvolution_sharpen.cpp, src/modules/opengl/filter_movit_glow.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/plus/filter_charcoal.c, src/modules/plus/filter_spot_remover.c, src/modules/plus/transition_affine.c, src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_lightshow.cpp, src/modules/qt/filter_qtblend.cpp, src/modules/qt/filter_qtext.cpp, src/modules/qt/transition_qtblend.cpp: refactor to mlt_profile_scale_width/height() * src/framework/mlt.vers, src/framework/mlt_profile.c, src/framework/mlt_profile.h: add mlt_profile_scale_width() and mlt_profile_scale_height() 2020-01-18 Dan Dennedy * src/modules/vid.stab/filter_vidstab.cpp, src/modules/videostab/filter_videostab.c, src/modules/videostab/filter_videostab2.c: disable consumer scaling in videostab and vidstab * src/modules/avformat/factory.c, src/modules/avformat/filter_avfilter.c, src/modules/avformat/resolution_scale.yml: support consumer scale in avfilter The configuration may not be comprehensive. Some AVFilters may not be able to support consumer scaling. * src/modules/opengl/filter_movit_blur.cpp, .../opengl/filter_movit_deconvolution_sharpen.cpp, src/modules/opengl/filter_movit_glow.cpp, src/modules/opengl/filter_movit_resize.cpp: support consumer scale in opengl module * src/modules/opengl/filter_movit_rect.yml, src/modules/plus/filter_dynamictext.yml, src/modules/plus/filter_text.yml, src/modules/plus/filter_timer.yml, src/modules/qt/filter_qtblend.yml, src/modules/qt/filter_qtext.yml, src/modules/qt/transition_qtblend.yml: fix the metadata parameter type from geometry to rect * src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c, src/modules/qt/transition_qtblend.cpp: fix consumer scale in affine and qtblend transitions 2020-01-17 Dan Dennedy * src/melt/melt.c, src/modules/xml/producer_xml.c: support consumer scale in melt and xml producer 2020-01-12 Dan Dennedy * src/modules/oldfilm/filter_lines.c, src/modules/oldfilm/filter_oldfilm.c, src/modules/oldfilm/filter_vignette.c: add support for consumer scale to oldfilm module * src/modules/frei0r/factory.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/not_thread_safe.txt, src/modules/frei0r/resolution_scale.yml: add support for consumer scale to frei0r module Most pluings are already resolution independent, but some parameters are relative to resolution. This adds a configurable way to address them. 2020-01-11 Dan Dennedy * src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c: fix sdi consumer crash if driver not loaded * src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_lightshow.cpp, src/modules/qt/filter_qtblend.cpp, src/modules/qt/filter_qtblend.yml, src/modules/qt/filter_qtext.cpp, src/modules/qt/filter_qtext.yml, src/modules/qt/graph.cpp, src/modules/qt/graph.h, src/modules/qt/transition_qtblend.cpp, src/modules/qt/transition_qtblend.yml: add support for consumer scale to qt module * src/modules/plus/filter_charcoal.c, src/modules/plus/filter_charcoal.yml, src/modules/plus/filter_spot_remover.c, src/modules/plus/filter_text.c, src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: add support for consumer scale to plus module * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: add support for consumer scale to pango producer * src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_wave.c: add support for consumer scale to kdenlive module 2020-01-06 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/framework/mlt_tractor.c, src/framework/mlt_transition.c: add resolution scale property to mlt_consumer This also adds consumer_scale to mlt_frame and mlt_frame_resolution_scale() to access it. 2019-12-31 Brian Matherly * src/modules/avformat/common.c, src/modules/avformat/common.h, src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Add "mlt" prefix to get_sws_flags. 2019-12-30 Brian Matherly * src/modules/avformat/common.c, src/modules/avformat/common.h, src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Improve colorspace conversion. Choose sws flags based on what conversion is being done. 2019-12-28 Dan Dennedy * presets/consumer/avformat/XDCAM-HD422, presets/consumer/avformat/dv_ntsc/D10, presets/consumer/avformat/dv_ntsc_wide/D10, presets/consumer/avformat/dv_pal/D10, presets/consumer/avformat/dv_pal_wide/D10: fix some avoptions in XDCAM and D10 presets 2019-12-23 Vincent Pinon * src/melt/CMakeLists.txt, src/modules/sdl2/CMakeLists.txt: Fix CMake SDL2 import Fixes: #509 tested on debian sid, which has latest sdl2-config.cmake but it may be patched on some distributions 2019-12-01 Dan Dennedy * src/modules/vorbis/deprecated, src/modules/vorbis/producer_vorbis.yml: take vorbis module out of deprecation (#505) 2019-11-18 Dan Dennedy * src/modules/core/filter_mask_start.yml, src/modules/vmfx/filter_shape.c, src/modules/vmfx/filter_shape.yml: add reverse property to shape filter * src/modules/plus/filter_affine.yml, src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: add invert_scale property to affine * configure, src/framework/mlt_version.h: set the interim version to 6.19.0 2019-11-11 Dan Dennedy * configure, docs/melt.1, src/framework/mlt_version.h: set version to 6.18.0 2019-11-09 Dan Dennedy * src/modules/core/transition_luma.c, src/modules/core/transition_luma.yml: add alpha_over property to luma transition This fixes a behavior regression in 25cb58c reported by @j-b-m. 2019-10-01 Vincent PINON * CMakeLists.txt, src/framework/CMakeLists.txt, src/framework/mlt-framework.pc.in, src/melt/CMakeLists.txt, src/mlt++/CMakeLists.txt, src/mlt++/mlt++.pc.in, src/modules/avformat/CMakeLists.txt, src/modules/core/CMakeLists.txt, src/modules/decklink/CMakeLists.txt, src/modules/feeds/CMakeLists.txt, src/modules/frei0r/CMakeLists.txt, src/modules/gtk2/CMakeLists.txt, src/modules/kdenlive/CMakeLists.txt, src/modules/motion_est/CMakeLists.txt, src/modules/normalize/CMakeLists.txt, src/modules/oldfilm/CMakeLists.txt, src/modules/opencv/CMakeLists.txt, src/modules/plus/CMakeLists.txt, src/modules/plusgpl/CMakeLists.txt, src/modules/qt/CMakeLists.txt, src/modules/rtaudio/CMakeLists.txt, src/modules/sdl2/CMakeLists.txt, src/modules/vid.stab/CMakeLists.txt, src/modules/vmfx/CMakeLists.txt, src/modules/xine/CMakeLists.txt, src/modules/xml/CMakeLists.txt: Add CMake build system 2019-09-24 Vincent PINON * src/modules/plus/configure, src/modules/qt/configure: Allow using FFTW build with CMake (different pkg-config file name) 2019-10-26 Rafael Sadowski * configure, src/framework/mlt_property.h, src/modules/kino/endian_types.h, src/modules/plusgpl/consumer_cbrts.c, src/modules/videostab/stab/estimate.c: OpenBSD support Tweask to build mlt on OpenBSD 2019-10-11 j-b-m * src/modules/qt/filter_qtblend.cpp, src/modules/qt/filter_qtblend.yml: qtblend filter: don't process alpha if no transparency, add background_color property (#485) * Qtblend: add background_color property to allow flattening alpha channel * minor indentation fixes 2019-10-07 Dan Dennedy * src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/not_thread_safe.txt: use separate frei0r instance per frame thread This fixes parameter animation when using frame threads and improves performance of many plugins. Only time-based effects are not compatible with frame threads now. 2019-10-06 Dan Dennedy * src/modules/kdenlive/filter_freeze.c, src/modules/kdenlive/filter_freeze.yml: fix freeze filter crashes and not working on clip only 2019-09-24 Dan Dennedy * presets/consumer/avformat/intermediate/ProRes, presets/consumer/avformat/intermediate/ProRes-Kostya: add write_colr to prores presets 2019-09-23 Dan Dennedy * presets/consumer/avformat/intermediate/ProRes, presets/consumer/avformat/intermediate/ProRes-Kostya, src/modules/core/consumer_multi.c: change the vendor ID for ProRes 2019-09-22 Dan Dennedy * src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/frei0r_helper.h, src/modules/frei0r/producer_frei0r.c, src/modules/frei0r/transition_frei0r.c: refactor process_frei0r_item() to mlt_position * src/modules/frei0r/factory.c, src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/frei0r_helper.h, src/modules/frei0r/producer_frei0r.c, src/modules/frei0r/transition_frei0r.c: add my copyright to frei0r for 10 years of maintenance * src/modules/frei0r/factory.c, src/modules/frei0r/frei0r_helper.c: cleanup formatting 2019-09-21 Dan Dennedy * src/swig/python/build, src/swig/python/codecs.py, src/swig/python/getimage.py, src/swig/python/play.py, src/swig/python/switcher.py, src/swig/python/test_animation.py, src/swig/python/waveforms.py, src/swig/python/webvfx_generator.py: switch to python3 by default patch by Patrick Matthäi 2019-09-08 Dan Dennedy * src/modules/frei0r/filter_cairoblend_mode.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/frei0r_helper.h, src/modules/frei0r/transition_frei0r.c: fix cairoblend_mode filter breaks transition state 2019-08-31 Dan Dennedy * src/modules/frei0r/filter_cairoblend_mode.c, src/modules/frei0r/filter_cairoblend_mode.yml: add cairoblend_mode filter * src/modules/frei0r/Makefile, src/modules/frei0r/factory.c, src/modules/frei0r/transition_frei0r.c: add cairoblend_mode filter * src/modules/vmfx/filter_mono.c, src/modules/vmfx/filter_mono.yml: Add animation to threshold filter. 2019-08-20 Dan Dennedy * src/modules/vmfx/filter_shape.c, src/modules/vmfx/producer_pgm.c: Refactor pgm producer and shape filter to use mlt_luma_map. * src/modules/core/transition_composite.c, src/modules/core/transition_luma.c: Refactor composite and luma transitions to use mlt_luma_map. * src/modules/lumas/Makefile, src/modules/lumas/configure, src/modules/lumas/create_lumas, src/modules/lumas/luma.c: Refactor lumas/luma.c to use mlt_luma_map. This module is now disabled by default in configure and must be explicitly enabled. It also fixes pgm file output on Windows. * src/framework/Makefile, src/framework/mlt.vers, src/framework/mlt_luma_map.c, src/framework/mlt_luma_map.h: Add mlt_luma_map. Several services across a few modules use this functionality. So, it made sense to centralize it. However, the main motivation for this was to remove the need to generate luma files in the luma module and let these images be generated just-in-time. This removes a large amount of data from install and makes them more flexible to image resolution and aspect ratio. 2019-07-29 Dan Dennedy * src/modules/plus/transition_affine.c, src/modules/vmfx/filter_shape.c: Fix crash combining affine with shape. 2019-07-28 Dan Dennedy * src/mlt++/mlt++.vers, src/modules/vid.stab/filter_vidstab.cpp, src/modules/vid.stab/filter_vidstab.yml: Add analyze property to vidstab filter. * src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h, src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h, src/mlt++/mlt++.vers: Add more default/const ctors and copy operators. * src/framework/mlt_consumer.c, src/framework/mlt_filter.c, src/framework/mlt_service.c, src/framework/mlt_transition.c, src/tests/test_service/test_service.cpp, src/tests/test_service/test_service.pro, src/tests/tests.pro: Fix #454 mlt_service_identify() not reliable. * src/modules/avformat/blacklist.txt, src/modules/avformat/filter_avfilter.c: Fix #345 subtitles not using the source position. * src/modules/avformat/factory.c, src/modules/avformat/filter_avfilter.c: Add position property to avfilter (#345). "frame" (default) is the position of the frame within the composition. This corresponds to the consumer's render position. "filter" is relative to the the filter's in point. "producer" is relative to the producer's in point (producer here could be a playlist or multitrack). "source" is the position from the original producer that generated this frame (i.e. absolute). 2019-07-20 Dan Dennedy * src/modules/gtk2/producer_pango.yml, src/modules/plus/filter_text.yml, src/modules/qt/producer_qtext.yml: Improve docs for text producers and filter. 2019-07-06 Dan Dennedy * src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_vidstab.cpp: Fix vid.stab and deshake default values for locale. These properties were initialized using numeric strings with a decimal, which does not interpret correctly depending on MLT locale. 2019-06-28 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Fix serializing +INVALID.txt as a text producer. https://forum.shotcut.org/t/invalid-error-message-fix-needed/11713/ 2019-06-24 Dan Dennedy * docs/melt.1, docs/melt.txt, src/melt/melt.c: Add -repository option to melt command. Fixes #459 If you are also using both -repository and -query options, repository should come before query. 2019-05-31 Dan Dennedy * src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/producer_frei0r.c: Fix filters on frei0r generators. https://forum.shotcut.org/t/color-bars-not-completely-shown-and- destroyed-by-effects/10999 * src/modules/opengl/Makefile, src/modules/opengl/factory.c, src/modules/opengl/filter_movit_flip.cpp, src/modules/opengl/filter_movit_flip.yml: Add movit.flip filter. 2019-05-20 Dan Dennedy * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_still.c: Fix data races in the sdl(1) module. * src/modules/sdl2/consumer_sdl2.c, src/modules/sdl2/consumer_sdl2_audio.c: Fix more thread data races in sdl2. * src/tests/test_events/test_events.cpp, src/tests/test_events/test_events.pro, src/tests/tests.pro: Add unit tests for Mlt::Event. Currently fails in checkOwner() due to #445. 2019-05-07 Dan Dennedy * configure, src/framework/mlt_version.h: Set the interim version to 6.17.0 * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.16.0 2019-03-25 Brian Matherly * src/framework/mlt.vers, src/framework/mlt_producer.c, src/framework/mlt_producer.h, src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h, src/mlt++/mlt++.vers, src/modules/plus/filter_dynamictext.c: Add get/set creation_date to producer. "creation_date" is a reserved producer property that can hold the creation time of the producer. The creation_time property is always in UTC. 2019-04-07 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version to 6.15.0 2019-03-30 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.14.0 2019-03-16 Vincent Pinon * src/framework/mlt_repository.c, src/modules/qt/common.cpp: Windows: fix melt.exe with nodeploy 2019-03-18 Dan Dennedy * src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h, src/mlt++/mlt++.vers: Add Mlt::Transition::connect(Service&). This is needed for manipulating transitions in a field, which connect to one another, and transitions do not inherit from Producer. 2019-03-09 Brian Matherly * src/tests/test_playlist/test_playlist.cpp, src/tests/test_playlist/test_playlist.pro, src/tests/tests.pro: Add unit tests for playlist 2019-02-24 Brian Matherly * src/framework/mlt.vers, src/framework/mlt_playlist.c, src/framework/mlt_playlist.h, src/mlt++/MltPlaylist.cpp, src/mlt++/MltPlaylist.h, src/mlt++/mlt++.vers: Add reorder function to mlt playlist. 2019-03-07 Dan Dennedy * src/mlt++/MltConsumer.cpp, src/mlt++/MltFilter.cpp, src/mlt++/MltProducer.cpp, src/mlt++/MltTractor.cpp, src/mlt++/MltTransition.cpp: Fix compile errors in mlt++ 2019-02-28 alcinos * src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/MltTractor.cpp, src/mlt++/MltTractor.h, src/mlt++/mlt++.vers: add convenience constructor for Mlt::Tractor * src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h, src/mlt++/mlt++.vers: add convenience constructor for Mlt::Filter * src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h, src/mlt++/mlt++.vers: add convenience constructor for Mlt::Transition * src/mlt++/MltConsumer.cpp, src/mlt++/MltConsumer.h, src/mlt++/mlt++.vers: add convenience constructor for Mlt::Consumer 2019-02-25 alcinos * src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h, src/mlt++/mlt++.vers: add convenience constructor for Mlt::Producer 2019-03-06 Vincent Pinon * src/modules/lumas/Makefile, src/modules/oldfilm/Makefile: Support spaces in prefix 2019-02-19 Vincent Pinon * configure, src/framework/Makefile, src/framework/mlt_factory.c, src/melt/Makefile, src/mlt++/Makefile: Windows: option to keep exe and dll in bin/ directory Then app can run after 'make install' with no extra deployment 2019-02-19 Dan Dennedy * src/framework/Makefile, src/framework/mlt_types.h, src/win32/strptime.c: Fix compile error on strptime() not in MinGW. 2019-02-11 Brian Matherly * src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_dynamictext.yml: Add createdate keyword to dynamictext 2019-02-12 Dan Dennedy * profiles/vertical_hd_30, profiles/vertical_hd_60: Add vertical HD profiles. 2019-02-07 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/consumer_avformat.yml, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Remove old code based on minimum versions. * src/swig/mlt.i, src/swig/python/build: Fix build with Python 3. 2019-02-07 cclauss * src/swig/python/codecs.py, src/swig/python/play.py, src/swig/python/test_animation.py: Use print() function in both Python 2 and Python 3 Legacy __print__ statements are syntax errors in Python 3 but __print()__ function works as expected in both Python 2 and Python 3. 2019-02-02 Dan Dennedy * src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_boxblur.yml: Change boxblur hori/vert minimum to 0. This is a little superficial, but when start/blur=1 and using only hori/ vert to make directional blurs, it allows more expression especially with animation. It is easier to go from no-blur to blur, possibly in only one direction with minimal blur in the other direction. 2019-02-02 Brian Matherly * src/modules/plus/filter_timer.c, src/modules/plus/filter_timer.yml: Add offset parameter to timer filter. 2019-02-01 Dan Dennedy * src/modules/qt/producer_qimage.c, src/modules/qt/qimage_wrapper.cpp: Fix qimage build on < Qt 5.5 2019-01-30 Dan Dennedy * src/modules/qt/producer_qimage.c, src/modules/qt/qimage_wrapper.cpp, src/modules/qt/qimage_wrapper.h: Fix loading image sequence on Windows. 2018-10-22 Vincent PINON * src/framework/mlt_property.c, src/framework/mlt_property.h: Fix MinGW compilation warnings 2019-01-26 Dan Dennedy * src/framework/mlt_frame.c, src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_timer.c: Fix text animation broken with in > 0. 2019-01-24 vvck * src/framework/mlt_slices.c, src/framework/mlt_types.h, src/modules/plusgpl/consumer_cbrts.c: mingw32 Compiler compatibility for mingw32 2019-01-12 Dan Dennedy * docs/melt.1, src/melt/melt.c: Update melt copyrights for 2019. 2019-01-03 Dan Dennedy * src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_timer.c, src/modules/qt/filter_qtext.cpp: Fix qtext glitches with multiple threads. Fixes https://github.com/mltframework/shotcut/issues/686 2019-01-04 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_frame.c, src/framework/mlt_frame.h: Add mlt_frame_get_unique_properties() 2019-01-02 Dan Dennedy * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/consumer_decklink.yml, src/modules/decklink/producer_decklink.cpp, src/modules/decklink/producer_decklink.yml: Update copyrights in decklink module. 2018-12-17 Dan Dennedy * src/modules/vmfx/filter_shape.c, src/modules/vmfx/filter_shape.yml: Add use_mix property to the shape filter. * src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c, src/modules/vmfx/filter_mono.c, src/modules/vmfx/filter_shape.c: Convert shape filter to mlt_animation API. 2018-12-16 Dan Dennedy * src/modules/qt/producer_qimage.c, src/modules/qt/qimage_wrapper.cpp: Fix crash when alpha_size is zero. 2018-12-14 Dan Dennedy * src/modules/core/transition_composite.c, src/modules/core/transition_luma.c, src/modules/vmfx/filter_shape.c: Use mlt_profile_lumas_dir() in services. * src/framework/mlt.vers, src/framework/mlt_profile.c, src/framework/mlt_profile.h: Add mlt_profile_lumas_dir(). 2018-12-13 Dan Dennedy * src/modules/core/filter_mask_apply.c, src/modules/core/filter_mask_apply.yml, src/modules/core/filter_mask_start.c, src/modules/core/filter_mask_start.yml, src/modules/core/producer_colour.yml: Make the mask filters' nested service mutable. 2018-12-12 Dan Dennedy * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_mask_apply.c, src/modules/core/filter_mask_apply.yml, src/modules/core/filter_mask_start.c, src/modules/core/filter_mask_start.yml: Add mask_start and mask_apply filters. These do not yet support multiple filter masks. 2018-12-08 Dan Dennedy * src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_timer.c: Fix animation for dynamictext and timer filters. Regression with conversion to use qtext. 2018-11-30 Dan Dennedy * src/modules/avformat/common.c, src/modules/avformat/common.h, src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_avfilter.c, src/modules/avformat/filter_swresample.c, src/modules/avformat/producer_avformat.c: Refactor to prefix global symbols with "mlt_". * src/modules/avformat/common.c, src/modules/avformat/common.h, src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c: Fix color accuracy of RGB->YUV conversion. Fixes https://github.com/mltframework/shotcut/issues/674 2018-11-02 Brian Matherly * src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_timer.c: Use qtext for dynamictext and timer filters. The qtext filter has better performance than the text producer/affine transition combination. Both filters will fall back to the text_filter if qtext is not available. * src/modules/qt/Makefile, src/modules/qt/factory.c, src/modules/qt/filter_qtext.cpp, src/modules/qt/filter_qtext.yml: Add filter_qtext. This filter provides the same capabilities as filter_text, however, it draws directlly on the image rather than applying an encapsulated transition. * src/modules/plus/filter_text.c, src/modules/plus/filter_timer.c: Improve parallel processing for filter_timer. Copy the argument property and pass it to get_image() so that the mutex in get_image can be moved to allow parallel frame processing. 2018-11-29 Dan Dennedy * src/modules/core/producer_colour.c, src/modules/core/producer_colour.yml: Add mlt_image_format property to color producer. 2018-11-28 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version to 6.13.0 2018-11-26 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.12.0 * Makefile, src/modules/jackrack/consumer_jack.c, src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/plugin.c, src/modules/jackrack/plugin_desc.c, src/modules/motion_est/filter_autotrack_rectangle.c, src/modules/motion_est/filter_motion_est.c: Spelling fixes from make codespell. 2018-11-17 Brian Matherly * src/modules/plus/filter_timer.c, src/modules/plus/filter_timer.yml: Add MM:SS.SS to timer filter. 2018-11-05 Brian Matherly * src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_audiowaveform.yml: Add window parameter to waveform filter. Historical audio samples are stored in a sliding window buffer and copied on to each frame to be draw in get_image(). 2018-11-09 Dan Dennedy * src/modules/avformat/common.c, src/modules/avformat/common.h, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Refactor set_luma_transfer() 2018-11-04 Dan Dennedy * profiles/square_1080p_30, profiles/square_1080p_60: Add square video profiles. 2018-10-29 Dan Dennedy * presets/consumer/avformat/alpha/Ut Video, presets/consumer/avformat/lossless/Ut Video: Add Ut Video encoding presets. 2018-10-24 Dan Dennedy * src/framework/mlt_consumer.c, src/modules/avformat/producer_avformat.c: Fix #370 A/V sync on some files. The call to seek_audio() when the decoder gets ahead was not avtually doing anything. 2018-10-16 Dan Dennedy * src/framework/mlt_factory.c, src/modules/frei0r/factory.c, src/modules/jackrack/plugin_mgr.c, src/modules/opengl/filter_glsl_manager.cpp: Convert macOS RELOCATABLE build to use standard app bundle layout. 2018-10-14 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/factory.c, src/modules/plus/filter_spot_remover.c, src/modules/plus/filter_spot_remover.yml: Add new Spot Remover filter. 2018-10-11 luz.paz * ChangeLog, Doxyfile, NEWS, demo/README, docs/framework.txt, docs/install.txt, docs/melt.txt, docs/mlt++.txt, docs/mlt-xml.txt, src/framework/mlt_animation.c, src/framework/mlt_cache.c, src/framework/mlt_consumer.c, src/framework/mlt_field.c, src/framework/mlt_geometry.c, src/framework/mlt_playlist.c, src/framework/mlt_slices.c, src/framework/mlt_tractor.c, src/framework/mlt_transition.c, src/melt/melt.c, src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avfilter.c, src/modules/avformat/producer_avformat.c, src/modules/core/filter_data_feed.c, src/modules/core/filter_data_show.c, src/modules/core/filter_mono.c, src/modules/core/filter_obscure.c, src/modules/core/filter_panner.c, src/modules/core/filter_rescale.c, src/modules/core/producer_colour.c, src/modules/core/producer_noise.c, src/modules/core/transition_composite.c, src/modules/core/transition_luma.c, src/modules/core/transition_mix.c, src/modules/decklink/producer_decklink.yml, src/modules/kdenlive/producer_framebuffer.c, src/modules/kino/avi.cc, src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c, src/modules/lumas/configure, src/modules/motion_est/filter_motion_est.c, src/modules/motion_est/producer_slowmotion.c, src/modules/normalize/filter_volume.c, src/modules/opengl/filter_movit_opacity.yml, src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_dynamictext.yml, src/modules/plus/filter_timer.yml, src/modules/plus/interp.h, src/modules/plusgpl/cJSON.h, src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/filter_burn.c, src/modules/plusgpl/filter_rotoscoping.c, src/modules/plusgpl/image.c, src/modules/rtaudio/RtAudio.cpp, src/modules/rtaudio/RtAudio.h, src/modules/rtaudio/consumer_rtaudio.cpp, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl2/consumer_sdl2.c, src/modules/sdl2/consumer_sdl2_audio.c, src/modules/videostab/filter_videostab2.c, src/modules/videostab/stabilize.c, src/modules/videostab/transform.c, src/modules/videostab/transform_image.c, src/modules/videostab/transform_image.h, src/modules/vorbis/producer_vorbis.c, src/modules/xml/consumer_xml.yml, src/modules/xml/producer_xml.c: Doxygen and misc. typos fixes Found via `codespell -q 3 -L shotcut,sav,boundry,percentil,readded,uint,ith,sinc,amin,childs` 2018-09-17 Dan Dennedy * src/modules/sdl2/consumer_sdl2.c, src/modules/sdl2/consumer_sdl2_audio.c: Fix audio distortion regression in e86a515 on Windows. 2018-09-14 Dan Dennedy * src/modules/qt/producer_qimage.c, src/modules/qt/qimage_wrapper.cpp, src/modules/qt/qimage_wrapper.h: Fix crash on qimage with alpha channel. This crash occured when the mlt_frame_get_image() request changes both size and format. 2018-09-10 Dan Dennedy * src/modules/sdl2/consumer_sdl2.c, src/modules/sdl2/consumer_sdl2_audio.c: Restart SDL audio in case of timeout. A timeout may occur due to a device or device configuration change, typically initiated by the user. This change tries to detect it and restart SDL audio. Note that on Windows while testing, any attempt to close the SDL audio device or quit the audio subsystem results in deadlock. https://forum.shotcut.org/t/changing-the-bit-depth-and-sample-rate-in- windows-renders-the-player-unresponsive-in-v18-06-02-and-later- v18-03-06-and-earlier-dont-have-this-problem/7185 * src/framework/mlt_field.c, src/framework/mlt_tractor.c: Prevent crash on invalid transition track index values. 2018-08-29 Dan Dennedy * src/framework/mlt_version.h, src/swig/mlt.i: Fix warnings seen compiling ruby binding. * .../avformat/{ => alpha}/Quicktime Animation, presets/consumer/avformat/alpha/vp8, presets/consumer/avformat/alpha/vp9: Add avformat presets for VP8 and VP9 with alpha channel. 2018-08-30 Kyle * src/swig/Makefile, src/swig/mlt.i, src/swig/nodejs/apply-filter.js, src/swig/nodejs/binding.gyp, src/swig/nodejs/build, src/swig/nodejs/list-members.js, src/swig/nodejs/play.js: Add swig support for nodejs 2018-08-27 Dan Dennedy * src/framework/mlt_animation.c, src/tests/test_animation/test_animation.cpp, src/tests/test_properties/test_properties.cpp: Fix TestProperties::StringAnimation() test case. Also, when serializing, add quotes around string values that contain special characters ';' and '=' because animation parsing requires them. 2018-07-22 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/factory.c, src/modules/plus/filter_timer.c, src/modules/plus/filter_timer.yml: Add Timer filter. 2018-07-09 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/factory.c, src/modules/plus/filter_text.c, src/modules/plus/filter_text.yml: Add generic text filter. The text filter can be encapsulated by specialized text filters like dynamictext to add additional functionality. 2018-07-22 Dan Dennedy * src/modules/plus/filter_dynamictext.c, src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: Fix dynamic text aliased since switching to affine transition. See https://forum.shotcut.org/t/can-i-use-size-and-position-filter- without-blurry-effect/5526/21?u=shotcut 2018-07-11 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version to 6.11.0 2018-07-02 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.10.0 2018-06-27 Dan Dennedy * src/modules/oldfilm/filter_vignette.c, src/modules/oldfilm/filter_vignette.yml: Convert oldfilm/vignette filter to mlt_animation. 2018-06-14 Dan Dennedy * presets/consumer/avformat/AAC, presets/consumer/avformat/Flash, presets/consumer/avformat/MP3, presets/consumer/avformat/MPEG-4, presets/consumer/avformat/MPEG-4-ASP, presets/consumer/avformat/Sony-PSP, presets/consumer/avformat/Vorbis, presets/consumer/avformat/WAV, presets/consumer/avformat/WMA, presets/consumer/avformat/XDCAM-HD422, presets/consumer/avformat/atsc_1080i_50/DNxHD, presets/consumer/avformat/atsc_1080i_5994/DNxHD, presets/consumer/avformat/atsc_1080p_2398/DNxHD, presets/consumer/avformat/atsc_1080p_24/DNxHD, presets/consumer/avformat/atsc_1080p_25/DNxHD, presets/consumer/avformat/atsc_1080p_2997/DNxHD, presets/consumer/avformat/atsc_1080p_30/DNxHD, presets/consumer/avformat/atsc_1080p_50/DNxHD, presets/consumer/avformat/atsc_1080p_5994/DNxHD, presets/consumer/avformat/atsc_1080p_60/DNxHD, presets/consumer/avformat/atsc_720p_2398/DNxHD, presets/consumer/avformat/atsc_720p_50/DNxHD, presets/consumer/avformat/atsc_720p_5994/DNxHD, presets/consumer/avformat/atsc_720p_60/DNxHD, presets/consumer/avformat/dv_ntsc/D10, presets/consumer/avformat/dv_ntsc/DV, presets/consumer/avformat/dv_ntsc/DVCPRO50, presets/consumer/avformat/dv_ntsc/DVD, presets/consumer/avformat/dv_ntsc_wide/D10, presets/consumer/avformat/dv_ntsc_wide/DV, presets/consumer/avformat/dv_ntsc_wide/DVCPRO50, presets/consumer/avformat/dv_ntsc_wide/DVD, presets/consumer/avformat/dv_pal/D10, presets/consumer/avformat/dv_pal/DV, presets/consumer/avformat/dv_pal/DVCPRO50, presets/consumer/avformat/dv_pal/DVD, presets/consumer/avformat/dv_pal_wide/D10, presets/consumer/avformat/dv_pal_wide/DV, presets/consumer/avformat/dv_pal_wide/DVCPRO50, presets/consumer/avformat/dv_pal_wide/DVD, presets/consumer/avformat/hdv_1080_25p/HDV, presets/consumer/avformat/hdv_1080_30p/HDV, presets/consumer/avformat/hdv_1080_50i/HDV, presets/consumer/avformat/hdv_1080_60i/HDV, presets/consumer/avformat/hdv_720_25p/HDV, presets/consumer/avformat/hdv_720_30p/HDV, presets/consumer/avformat/hdv_720_50p/HDV, presets/consumer/avformat/hdv_720_60p/HDV: Categorize many of the encoding presets. * presets/consumer/avformat/intermediate/ProRes, presets/consumer/avformat/intermediate/ProRes-Kostya: Hide ProRes in Shotcut and show ProRes-Kostya as ProRes. 2018-06-12 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Fix AV_VERSION_INT not found in libav master. 2018-06-11 Dan Dennedy * src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: Add mlt_animation to affine fix_, scale_, and ox/oy props. * src/framework/mlt.vers, src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/mlt++/MltAnimation.cpp, src/mlt++/MltAnimation.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/mlt++.vers, src/tests/common.pri, src/tests/test_animation/test_animation.cpp, src/tests/test_properties/test_properties.cpp: Add functions to serialize animation with a time format. This also includes functions to clear a property (set it to zero and null values), which is important for reconstructing an animation by API. mlt_animation_serialize_cut_tf() mlt_animation_serialize_tf() mlt_property_clear() mlt_property_get_string_tf() mlt_property_get_string_l_tf() mlt_properties_clear() mlt_properties_get_value_tf() Mlt::Properties::get(int, mlt_time_format) Mlt::Properties::clear() Mlt::Animation::serialize_cut(mlt_time_format, int, int) 2018-06-05 Dan Dennedy * src/modules/plus/interp.h, src/modules/plus/transition_affine.yml: Fix affine output alpha incorrect for over mode. 2018-05-30 Dan Dennedy * src/framework/mlt_properties.c, src/tests/test_properties/test_properties.cpp: Fix #343 handle reserved chars in name for YAML. 2018-05-25 Dan Dennedy * src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/frei0r_helper.h, src/modules/frei0r/producer_frei0r.c, src/modules/frei0r/transition_frei0r.c: Convert frei0r services to property animation APIs. * src/tests/test_animation/test_animation.cpp, src/tests/test_properties/test_properties.cpp: Update unit tests for 5256000. 2018-05-22 Dan Dennedy * src/modules/frei0r/producer_frei0r.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/swfdec/producer_swfdec.c: Refactor to mlt_image_format_size(). 2018-05-11 Dan Dennedy * configure, src/framework/mlt_version.h: Set interm version to 6.9.0 2018-05-10 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.8.0 2018-05-09 Dan Dennedy * src/modules/qt/producer_qimage.c, src/modules/qt/qimage_wrapper.cpp: Fix qimage crash on source amd request format mismatch. 2018-05-07 Dan Dennedy * src/framework/mlt_consumer.c, src/modules/avformat/consumer_avformat.c: Fix mlt_frame_get_audio() may be called before setting private data. This fixes https://github.com/mltframework/shotcut/issues/549 2018-05-01 Dan Dennedy * src/modules/core/filter_region.c, src/modules/plus/filter_affine.c: Fix affine and region filters alter the frame's position. 2018-04-08 Vincent Pinon * src/framework/mlt_frame.h, src/modules/avformat/consumer_avformat.c, src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c: Fix GCC warnings 2018-04-25 Dan Dennedy * src/modules/plus/filter_affine.yml, src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: Convert affine to mlt_rect and animation APIs. 2018-04-16 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/mlt++/MltAnimation.cpp, src/mlt++/MltAnimation.h, src/mlt++/mlt++.vers: Add mlt_animation_key_set_frame() and Animation::key_set_frame(). 2018-04-09 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/mlt++/MltAnimation.cpp, src/mlt++/MltAnimation.h, src/mlt++/mlt++.vers: Add mlt_animation_key_set_type() and Animation::key_set_type(). 2018-03-28 Brian Matherly * src/modules/rtaudio/Makefile, src/modules/rtaudio/RtAudio.cpp, src/modules/rtaudio/consumer_rtaudio.cpp: Improve surround support in RtAudio on Windows. The directsound implementation in RtAudio does not support > 2 channels. But the WASAPI implementation does. This change will try all available drivers to try to sucessfully open the audio device. If none of the drivers work with surround, then it will revert to 2 channels. In the case that the module reverts to 2 channels, the requested number of channels will still be processed, but only the first two channels will be output. These implementation changes were carried over from the SDL2 module to match the behavior of consumer_sdl2_audio. 2018-03-25 Brian Matherly * src/modules/sdl2/Makefile, src/modules/sdl2/common.c, src/modules/sdl2/common.h, src/modules/sdl2/consumer_sdl2.c, src/modules/sdl2/consumer_sdl2_audio.c: Improve surround support in SDL2 on Windows. The directsound implementation in SDL does not support > 2 channels. But the xaudio2 implementation does. This change will try all available drivers to try to sucessfully open the audio device. If none of the drivers work with surround, then it will revert to 2 channels. The user can still request a specific driver using the audio_driver parameter. 2018-03-12 Dan Dennedy * presets/consumer/avformat/GIF, src/modules/avformat/consumer_avformat.c: Fix #284 GIF encoding not working. This also fixes DPX. * src/framework/mlt_frame.c, src/framework/mlt_types.h: Order the channel layouts the same as FFmpeg. * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Fix wrong search flags supplied to av_opt_set(). Not only were the wrong AV_OPT flags supplied as search options but also lack of AV_OPT_SEARCH_CHILDREN was the cause of warnings such as: [libx264 @ 0x7ff55c001c60] [Eval @ 0x7ff561544c60] Undefined constant or missing '(' in 'main' [libx264 @ 0x7ff55c001c60] Unable to parse option value "main" 2018-03-12 Brian Matherly * src/framework/mlt.vers, src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/framework/mlt_tractor.c, src/framework/mlt_transition.c, src/framework/mlt_types.h, src/modules/avformat/Makefile, src/modules/avformat/common.c, src/modules/avformat/common.h, src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/filter_avfilter.c, src/modules/avformat/filter_swresample.c, src/modules/avformat/producer_avformat.c, src/modules/core/loader.ini: Add filter_swresample. (#305) * Add filter_swresample. Performs channel configuration aware channel count conversion. Also performs sample format and frequency conversion. Set this filter as the default channelconvert normalize filter. * Resolve review comments for filter_swresample. * Fix applying channel_layout property. MLT allows specifying channel_layout as a string representing the channel layout. But avcodec expects channel_layout to be specified as an integer which represents a channel position bitmap. This change stops the channel_layout property from being passed to avcodec so that the value calculated by MLT will be used instead. 2018-03-02 Dan Dennedy * src/modules/sdl/consumer_sdl.yml, src/modules/sdl2/consumer_sdl2.yml, src/modules/sdl2/factory.c: Fix sdl2 metadata. 2018-02-16 Dan Dennedy * presets/consumer/avformat/MJPEG, presets/consumer/avformat/atsc_1080i_50/DNxHD, presets/consumer/avformat/atsc_1080i_5994/DNxHD, presets/consumer/avformat/atsc_1080p_2398/DNxHD, presets/consumer/avformat/atsc_1080p_24/DNxHD, presets/consumer/avformat/atsc_1080p_25/DNxHD, presets/consumer/avformat/atsc_1080p_2997/DNxHD, presets/consumer/avformat/atsc_1080p_30/DNxHD, presets/consumer/avformat/atsc_1080p_50/DNxHD, presets/consumer/avformat/atsc_1080p_5994/DNxHD, presets/consumer/avformat/atsc_1080p_60/DNxHD, presets/consumer/avformat/atsc_720p_2398/DNxHD, presets/consumer/avformat/atsc_720p_50/DNxHD, presets/consumer/avformat/atsc_720p_5994/DNxHD, presets/consumer/avformat/atsc_720p_60/DNxHD, presets/consumer/avformat/dv_ntsc/DV, presets/consumer/avformat/dv_ntsc/DVCPRO50, presets/consumer/avformat/dv_ntsc_wide/DV, presets/consumer/avformat/dv_ntsc_wide/DVCPRO50, presets/consumer/avformat/dv_pal/DV, presets/consumer/avformat/dv_pal/DVCPRO50, presets/consumer/avformat/dv_pal_wide/DV, presets/consumer/avformat/dv_pal_wide/DVCPRO50, presets/consumer/avformat/intermediate/MJPEG, presets/consumer/avformat/lossless/FFV1, presets/consumer/avformat/lossless/HuffYUV: Add g(op) and bf(rames) to more encode presets. 2018-02-04 Vincent Pinon * src/framework/mlt_property.c, src/modules/avformat/filter_avdeinterlace.c, src/modules/gtk2/pixops.c, src/modules/linsys/sdi_generator.c, src/modules/opengl/transition_movit_luma.cpp, src/modules/plusgpl/cJSON.c, src/modules/plusgpl/filter_burn.c, src/modules/vmfx/filter_shape.c: Fix trivial GCC warnings 2018-02-05 Brian Matherly * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl.yml, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl2/consumer_sdl2.c, src/modules/sdl2/consumer_sdl2.yml: Fix mulit-channel audio. (#294) * Fix mulit-channel audio. At some framerates, audio with > 6 channels would result in an audio buffer larger than that allocated for SDL. This change will copy from the buffer in pieces if it won't all fit at once. * Unify consumer_play_audio() in sdl consumers. Use the same code for all 3 implementations. Affected features (all have these now): * Handling audio frames larger than the sdl audio buffer * Audio scrubbing * "audio_offset" property * Handling more or fewer channels than requested * Default scrub_audio to 1 for sdl and sdl2 consumers. 2018-02-01 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version to 0.6.7 2018-01-30 johannes * src/framework/mlt_properties.c, src/framework/mlt_property.c, src/framework/mlt_property.h: musl libc support musl has no macro, so it can not be detected straigthforward. HAVE_STRTOD_L tells that strdod_l() is available HAVE_LOCALE_H tells that the header locale.h is available 2018-01-22 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.6.0 2018-01-21 Dan Dennedy * src/modules/jackrack/factory.c, src/modules/jackrack/filter_jack.yml, src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/filter_jackrack.yml: Add jack filter. This differs from "jackrack" such that it does not load a JackRack file and instead takes the name of the JACK client as the argument. This is really only needed if you want to control the name of the client. * src/modules/sdl/consumer_sdl.c, src/modules/sdl2/consumer_sdl2.c: Fix potential sdl/sdl2 deadlock. (#245) If the thread might be waiting on the condition variable, signal it first, and then join. 2018-01-14 Dan Dennedy * src/framework/mlt_factory.c, src/framework/mlt_factory.h, src/modules/sdl/Makefile, src/modules/sdl/configure, src/modules/sdl/factory.c, src/modules/sdl2/Makefile, src/modules/sdl2/configure, src/modules/{sdl => sdl2}/consumer_sdl2.c, src/modules/sdl2/consumer_sdl2.yml, src/modules/sdl2/consumer_sdl2_audio.yml, src/modules/sdl2/factory.c: Add sdl2 module that may coexist with sdl (1.2). Now, it is possible to build both SDL 1.2 and SDL 2 consumers. However, using both in the same process is not supported. Builders can choose to disable one or the other at configure time as they deem fit. It can be desirable to build both to allow an application such as Flowblade that only works with SDL 1.2 while another application such as melt uses SDL 2. 2018-01-08 Dan Dennedy * src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/transition_frei0r.c: Fix memory leak closing frei0r filter or transition (#271). * src/modules/frei0r/factory.c, src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/transition_frei0r.c: Stop using "this" as variable in frei0r module. 2017-12-29 Dan Dennedy * src/framework/mlt_property.c, src/tests/test_properties/test_properties.cpp: More fixes to frames-time conversions. This time with more comprehensive unit tests (high range loops). 2017-12-02 Dan Dennedy * src/modules/qt/Makefile, src/modules/qt/configure: Only use c++11 with Qt 5.7 or higher. 2017-11-30 Dan Dennedy * src/modules/qt/Makefile, src/modules/qt/configure: Use -std=c++11 with clang and Qt 5. 2017-11-13 Dan Dennedy * src/framework/mlt_property.c, src/tests/test_properties/test_properties.cpp: Fix accuracy of frames-time conversions. 2017-10-23 Dan Dennedy * .gitignore, src/modules/avformat/consumer_avformat.c: Fix FFmpeg master removed AVFMT_RAWPICTURE. * src/modules/avformat/Makefile, src/modules/avformat/factory.c, src/modules/avformat/filter_avresample.c, src/modules/core/loader.ini: Remove filter_avresample - dropped by FFmpeg. 2017-10-18 Yuri Valentini * src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.yml: pixbuf producer csv files support 2017-10-08 Dan Dennedy * src/modules/ndi/consumer_ndi.yml, src/modules/ndi/producer_ndi.yml: Convert newlines to linefeeds. * src/modules/ndi/consumer_ndi.yml, src/modules/ndi/producer_ndi.yml: Add Audio tag and version to NDI metadata. 2017-10-04 Stéphane L * src/modules/ndi/consumer_ndi.yml, src/modules/ndi/producer_ndi.yml: Add metadata to NDI module 2017-10-03 Jean-Baptiste Mardelle * src/modules/qt/filter_qtblend.cpp, src/modules/qt/filter_qtblend.yml, src/modules/qt/transition_qtblend.cpp, src/modules/qt/transition_qtblend.yml: Allow centered rotation in qtblend filter/transition 2017-09-17 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Fix auto multi-threading on FFmpeg 3.2+. * 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, src/modules/avformat/producer_avformat.c: Stop using deprectated AVPicture API. 2017-09-10 Dan Dennedy * src/framework/mlt_filter.c, src/framework/mlt_tractor.c, src/modules/core/filter_panner.c, src/modules/core/transition_composite.c, src/modules/core/transition_region.c, src/modules/opengl/transition_movit_luma.cpp, src/modules/vmfx/filter_shape.c: Prevent frame property collisions on _unique_id. 2017-09-03 Dan Dennedy * src/modules/sdl/consumer_sdl2.c, src/modules/sdl/consumer_sdl_audio.c: Fix audio glitches with SDL2. 2017-09-02 Dan Dennedy * src/modules/sdl/consumer_sdl2.c, src/modules/sdl/consumer_sdl_audio.c: Get SDL2 working on Windows. 2017-08-29 Dan Dennedy * src/melt/Makefile, src/melt/melt.c: Fix SDL 2 events integration into melt. * .travis.yml, configure, src/melt/Makefile, src/melt/melt.c, src/modules/decklink/Makefile, src/modules/sdl/Makefile, src/modules/sdl/configure, src/modules/sdl/consumer_sdl2.c, src/modules/sdl/factory.c: Fix #246 and #112 by adding support for SDL 2. 2017-08-04 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.yml, src/modules/qt/producer_qimage.c, src/modules/qt/producer_qimage.yml: Fix regression with image sequence duration. When setting length explicitly - whether to support looping or hold the last image - the chosen length would not work when loaded from XML. Reported by Jean-Baptiste Mardelle. 2017-04-24 Dan Dennedy * src/framework/mlt_frame.c, src/framework/mlt_properties.c, src/framework/mlt_types.h, src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c, src/modules/core/producer_melt.c, src/modules/core/transition_composite.c, src/modules/core/transition_luma.c, src/modules/dv/consumer_libdv.c, src/modules/gtk2/producer_pango.c, src/modules/opengl/transition_movit_luma.cpp, src/modules/plus/consumer_blipflash.c, src/modules/qt/producer_kdenlivetitle.c, src/modules/qt/producer_qtext.cpp, src/modules/vid.stab/filter_vidstab.cpp, src/modules/vmfx/filter_shape.c, src/modules/vmfx/producer_pgm.c, src/modules/vorbis/producer_vorbis.c, src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c, src/win32/win32.c: Fix opening files with extended chars on Windows. FFmpeg can already open files whose names are encoded as UTF-8 on Windows - no need to try to work around it. Instead, use its implementation in libavutil/file_open.c for hints to make an fopen replacement in MLT named mlt_fopen. This fixes Shotuct bug https://github.com/mltframework/shotcut/issues/278 2017-04-14 alcinos * src/framework/mlt.vers, src/framework/mlt_service.c, src/framework/mlt_service.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/mlt++.vers: Add function to disconnect all producers (#226) * Add function to disconnect all producers * Add forgotten versionning * Change logic to avoid consumer disconnection 2017-03-27 Vincent Pinon * .gitignore, Makefile, src/framework/Makefile, src/mlt++/Makefile, src/modules/Makefile, src/modules/avformat/Makefile, src/modules/decklink/Makefile, src/modules/jackrack/Makefile, src/modules/ndi/Makefile, src/modules/rtaudio/Makefile, src/modules/sox/Makefile: Avoid distclean failure if already clean 2017-03-18 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml: Add no_profile property to xml consumer. (#212) 2017-03-13 Dan Dennedy * README, src/framework/mlt.h, src/melt/melt.c: Update web site links. 2017-03-12 Dan Dennedy * src/framework/mlt_properties.c, src/framework/mlt_property.c: Fix #214 setlocale in mlt_properties is slow. On Windows, this change makes mlt_properties_set_lcnumeric() no-op and mlt_property and mlt_properties insenstive to the locale parameters. IOW, it always uses the thread's locale. Mainly, this means that the LC_NUMERIC attribute in MLT XML is not honored on Windows, and the document is instead interpreted in the thread's locale. 2017-02-27 Dan Dennedy * src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: Change affine threads default to 0. This is working well enough in my testing that it can default to using slices. One can set threads=1 to not use slice processing. 2017-02-25 Dan Dennedy * src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: Add mlt_slices and threads property to affine transition. 2017-02-24 Maksym Veremeyenko * src/framework/mlt_consumer.c, src/modules/avformat/filter_avdeinterlace.c, src/modules/core/filter_fieldorder.c, src/modules/core/transition_composite.c, src/modules/decklink/consumer_decklink.cpp: Add more timings monitoring 2017-02-20 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/configure, src/modules/plus/filter_dynamic_loudness.c, src/modules/plus/filter_loudness.c, src/modules/plus/filter_loudness_meter.c: Fix include directives for ebur128 filters. 2017-02-19 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/configure: Use external libebur128 if detected. 2017-02-19 Dan Dennedy * src/mlt++/MltFactory.cpp, src/mlt++/MltFactory.h, src/mlt++/MltRepository.cpp, src/mlt++/MltRepository.h: Fix copyrights in Mlt::Factory and Mlt::Repository. 2017-02-19 Maksym Veremeyenko * src/modules/decklink/producer_decklink.cpp, src/modules/decklink/producer_decklink.yml: Implement 10-bit capturing * src/framework/mlt.vers, src/framework/mlt_frame.c, src/framework/mlt_frame.h: Implement mlt_image_format_planes and mlt_image_format_id, fix frame size calculation for mlt_image_yuv422p16 2017-02-11 Maksym Veremeyenko * src/framework/mlt.vers, src/framework/mlt_log.c, src/framework/mlt_log.h, src/melt/melt.c, src/modules/avformat/producer_avformat.c: Timestamped logging and timings measurment implemented 2017-02-06 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_slices.c, src/framework/mlt_slices.h, src/modules/frei0r/frei0r_helper.c: Replace mlt_slices_count() with mlt_slices_count_normal(). As well as mlt_slices_count_rr() and mlt_slices_count_fifo(). 2017-02-05 Dan Dennedy * src/framework/mlt_slices.c, src/framework/mlt_types.h: Hide mlt_schedule_policy enum. * src/framework/mlt_slices.c, src/framework/mlt_slices.h: Use only MLT_SLICES_COUNT env var and document it. * src/framework/mlt.vers, src/framework/mlt_slices.c, src/framework/mlt_slices.h, src/modules/frei0r/frei0r_helper.c: Make mlt_slices_get_global() private. * src/framework/mlt.vers, src/framework/mlt_slices.c, src/framework/mlt_slices.h, src/modules/decklink/consumer_decklink.cpp, src/modules/frei0r/frei0r_helper.c, src/modules/ndi/consumer_ndi.c, src/modules/ndi/producer_ndi.c: Remove mlt_slices_init_pool(). 2017-02-04 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_slices.c, src/framework/mlt_slices.h, src/framework/mlt_types.h: Add mlt_slices_get_global() and friends. 2017-02-05 Maksym Veremeyenko * src/modules/ndi/consumer_ndi.c, src/modules/ndi/producer_ndi.c: Fix compiler warnings for ndi module 2017-02-05 Brian Matherly * src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/ebur128.h: Update internal libebur128 to version 1.2.0 2017-01-09 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qt/producer_qimage.c: Fix #168 factoring ttl into computed length. * src/modules/gtk2/producer_pixbuf.c, src/modules/qt/producer_qimage.c: Fix #168 image sequence does not set length. 2017-01-06 Jean-Baptiste Mardelle * src/modules/qt/filter_qtblend.cpp, src/modules/qt/transition_qtblend.cpp: Fix qtblend crash (don't rely on producer for scaling) 2017-01-04 Dan Dennedy * src/modules/qt/kdenlivetitle_wrapper.cpp, src/modules/qt/qimage_wrapper.cpp: Fix some compiler warnings. 2017-01-03 Jean-Baptiste Mardelle * src/modules/qt/kdenlivetitle_wrapper.cpp, src/modules/qt/qimage_wrapper.cpp: Revert incorrect fixes for transparency in qimage and kdenlive titler 2016-11-21 Maksym Veremeyenko * src/modules/core/transition_composite.c, src/modules/decklink/consumer_decklink.cpp, src/modules/ndi/consumer_ndi.c, src/modules/ndi/producer_ndi.c: Use sliced threading pool for some modules 2016-12-30 Maksym Veremeyenko * src/framework/mlt.vers, src/framework/mlt_slices.c, src/framework/mlt_slices.h: Implement pool of sliced threading 2016-12-07 Jean-Baptiste Mardelle * src/modules/qt/kdenlivetitle_wrapper.cpp, src/modules/qt/kdenlivetitle_wrapper.h: Fix kdenlive title transparency (revealed in qtblend transition) 2016-11-25 Dan Dennedy * src/framework/mlt_types.h, src/modules/xml/consumer_xml.c, src/win32/win32.c: Change LC_NUMERIC in XML on Windows to use ISO codes. 2016-11-20 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_slices.c, src/framework/mlt_slices.h: Add mlt_slices_count(). 2016-11-19 Dan Dennedy * src/modules/core/transition_mix.c, src/modules/core/transition_mix.yml: Add sum property to mix transition. * configure, src/framework/mlt_version.h: Set interim version to 6.5.0 2016-11-15 Dan Dennedy * Doxyfile, NEWS, configure, docs/melt.1, src/framework/mlt_version.h: Release hotfix version 6.4.1 for #150 2016-11-11 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.4.0 2016-11-09 Dan Dennedy * demo/mlt_bouncy, demo/mlt_bouncy_ball, demo/mlt_composite_transition, demo/mlt_my_name_is, demo/mlt_news, demo/mlt_obscure, demo/mlt_pango_keyframes, demo/mlt_push, demo/mlt_squeeze, demo/mlt_squeeze_box, demo/mlt_ticker, demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark, demo/mlt_voiceover, demo/mlt_watermark: Add sliced_composite=1 to demos. 2016-11-05 Jean-Baptiste Mardelle * src/modules/qt/kdenlivetitle_wrapper.cpp, src/modules/qt/kdenlivetitle_wrapper.h, src/modules/qt/producer_kdenlivetitle.c: kdenlive titler fixes: correctly create alpha mask, cache converted image for improved performance 2016-11-05 Maksym Veremeyenko * src/modules/decklink/consumer_decklink.cpp, src/modules/ndi/ndi.c: Fix sliced segment size 2016-11-01 Maksym Veremeyenko * src/modules/ndi/factory.h, src/modules/ndi/producer_ndi.c: Implement NDI producer 2016-10-31 Maksym Veremeyenko * src/modules/ndi/Makefile, src/modules/ndi/{ndi.c => consumer_ndi.c}, src/modules/ndi/factory.c, src/modules/ndi/factory.h, src/modules/ndi/producer_ndi.c: Split NDI's producer/consumer code 2016-10-23 Maksym Veremeyenko * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Implement wrapping support * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Implement stretch parameter for pango producer * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Implement line spacing of pango * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Implement fontmap reloading 2016-10-22 Maksym Veremeyenko * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Implement oblique font style handling 2016-10-22 Maksym Veremeyenko * src/framework/Makefile, src/framework/mlt.h, src/framework/mlt.vers, src/framework/mlt_slices.c, src/framework/mlt_slices.h, src/modules/core/transition_composite.c, src/modules/core/transition_composite.yml, src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/consumer_decklink.yml, src/modules/ndi/ndi.c: Sliced3 (#136) * Implement sliced processing framework * Implement sliced swab operation for NDI consumer * Implement sliced swab operation for Decklink consumer * Implement sliced compositing in transition_composite 2016-10-13 Dan Dennedy * src/framework/mlt_types.h, src/modules/avformat/filter_avfilter.c, src/win32/win32.c: Fix mingw compile error. _create_locale, _free_locale, and related require msvcr100. Falling back to a temporary locale switch on Windows. * src/framework/mlt_types.h, src/modules/avformat/filter_avfilter.c, src/win32/win32.c: Fix #130 avfilter.lut3d. When MLT switches to the user-defined locale, the locale-sensitive numeric string conversion functions in libavfilter may fail because LUT data files always use periods for the decimal point. 2016-10-10 Maksym Veremeyenko * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: Decklink improvements (#129) * Save frame arrivial time * Use RP188 to get timecode from signal * Dropped frames recovery * Push all available audio samples to decklink * Avoid possible race condition 2016-10-06 Bart Massey * src/modules/jackrack/jack_rack.c, src/modules/jackrack/plugin.c: Fix loading a second LADSPA plugin. When using Flowblade or OpenShot to edit videos, trying to apply a second LADSPA effect from the swh-tools package to an audio stream caused a coredump in MLT's jackrack module. After some debugging, this proved to be lack of a null pointer check in jackrack: the null pointer was unexpected, since it corresponded to the LADSPA plugin having been incorrectly loaded. After fixing MLT jackrack to not dump core and fixing up the logging, the problem with LADSPA loading was traced to unnecessarily using RTLD_GLOBAL in the dlopen() call for the LADSPA plugin in one of two locations where the plugin was opened. This (for some reason I don't understand) prevented the swh_init constructor for the LADSPA plugin from being called at load time, which in turn caused the plugin to fail to be configured properly. With RTLD_GLOBAL removed, MLT worked as expected. Flowblade applied multiple swh-plugins LADSPA effects to an audio track successfully. This patch cleans up the debug logging for MLT jackrack, prevents a coredump in the case that a LADSPA plugin reports no configuration, and removes the offending RTLD_GLOBAL. 2016-10-05 Dan Dennedy * src/melt/Makefile, src/modules/sdl/Makefile, src/modules/sdl/configure: Try sdl-config if no pkg-config for SDL. 2016-10-04 Maksym Veremeyenko * src/modules/ndi/Makefile, src/modules/ndi/configure, src/modules/ndi/ndi.c: Implement NewTek NDI consumer (#128) 2016-10-04 Vincent Pinon * src/melt/Makefile, src/modules/sdl/Makefile, src/modules/sdl/configure: pkg-config instead of sdl-config allows cross-builds (#127) 2016-09-16 Jean-Baptiste Mardelle * src/modules/opencv/Makefile, src/modules/opencv/configure, src/modules/opencv/factory.c, src/modules/opencv/filter_opencv_tracker.cpp, src/modules/opencv/filter_opencv_tracker.yml: New tracking filter based on opencv 2016-08-13 Dan Dennedy * src/framework/mlt_types.h, src/modules/frei0r/factory.c, src/modules/jackrack/plugin_mgr.c: Refactor to MLT_DIRLIST_DELIMITER. 2016-07-27 Jean-Baptiste Mardelle * src/modules/qt/filter_qtblend.yml, src/modules/qt/transition_qtblend.yml: Fix qtblend yml 2016-07-24 Jean-Baptiste Mardelle * src/modules/qt/Makefile, src/modules/qt/filter_qtblend.c, src/modules/qt/filter_qtblend.cpp, src/modules/qt/filter_qtblend.yml, src/modules/qt/transition_qtblend.cpp: Fix qtblend transition ratio issue Rewrite qtblend filter to process directly without transition, fixes memory and performance issues 2016-07-22 Jean-Baptiste Mardelle * src/modules/qt/transition_qtblend.cpp, src/modules/qt/transition_qtblend.yml: Fix qtblend transition frame size handling. Kdenlive issue #365965 Add rotation parameter 2016-07-21 Dan Dennedy * src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h, src/mlt++/mlt++.vers: Add Mlt::Profile::is_valid(). 2016-07-21 Jean-Baptiste Mardelle * src/modules/qt/transition_qtblend.cpp, src/modules/qt/transition_qtblend.yml: qtblend: don't scale top image by default, add fill property like affine * src/modules/qt/kdenlivetitle_wrapper.cpp, src/modules/qt/kdenlivetitle_wrapper.h, src/modules/qt/producer_kdenlivetitle.c: Optimize kdenlivetitler 2016-07-19 Brian Matherly * src/modules/qt/Makefile, src/modules/qt/common.cpp, src/modules/qt/common.h, src/modules/qt/configure, src/modules/qt/factory.c, src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_lightshow.cpp, src/modules/qt/transition_qtblend.cpp: Use QImage::Format_RGBA8888 for common conversion functions. 2016-07-18 Jean-Baptiste Mardelle * src/modules/qt/Makefile, src/modules/qt/configure, src/modules/qt/factory.c, src/modules/qt/filter_qtblend.c, src/modules/qt/filter_qtblend.yml, src/modules/qt/transition_qtblend.cpp, src/modules/qt/transition_qtblend.yml: New qtblend transition and filter, allowing compositing and transform (Qt5 only) 2016-07-14 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qt/producer_qimage.c: Fix crash using wrong size to read lib buffer. This fixes https://github.com/mltframework/shotcut/issues/277 2016-07-09 Dan Dennedy * presets/consumer/avformat/{lossless => intermediate}/MJPEG, presets/consumer/avformat/{lossless => intermediate}/MPEG-2, presets/consumer/avformat/{lossless => intermediate}/MPEG-4, presets/consumer/avformat/{lossless => intermediate}/ProRes, presets/consumer/avformat/{lossless => intermediate}/ProRes-Kostya: Move some encode presets to new "intermediate" folder. Fixes Shotcut bug https://github.com/mltframework/shotcut/issues/272 2016-07-05 Dan Dennedy * COPYING, GPL, src/framework/mlt.h, src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/framework/mlt_cache.c, src/framework/mlt_cache.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_log.c, src/framework/mlt_log.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/mlt_version.c, src/framework/mlt_version.h, src/melt/io.c, src/melt/io.h, src/melt/melt.c, src/mlt++/Mlt.h, src/mlt++/MltAnimation.cpp, src/mlt++/MltAnimation.h, src/mlt++/MltConfig.h, src/mlt++/MltConsumer.cpp, src/mlt++/MltConsumer.h, src/mlt++/MltDeque.cpp, src/mlt++/MltDeque.h, src/mlt++/MltEvent.cpp, src/mlt++/MltEvent.h, src/mlt++/MltFactory.cpp, src/mlt++/MltFactory.h, src/mlt++/MltField.cpp, src/mlt++/MltField.h, src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h, src/mlt++/MltFilteredConsumer.cpp, src/mlt++/MltFilteredConsumer.h, src/mlt++/MltFilteredProducer.cpp, src/mlt++/MltFilteredProducer.h, src/mlt++/MltFrame.cpp, src/mlt++/MltFrame.h, src/mlt++/MltGeometry.cpp, src/mlt++/MltGeometry.h, src/mlt++/MltMultitrack.cpp, src/mlt++/MltMultitrack.h, src/mlt++/MltParser.cpp, src/mlt++/MltParser.h, src/mlt++/MltPlaylist.cpp, src/mlt++/MltPlaylist.h, src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h, src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/MltPushConsumer.cpp, src/mlt++/MltPushConsumer.h, src/mlt++/MltRepository.cpp, src/mlt++/MltRepository.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/MltTokeniser.cpp, src/mlt++/MltTokeniser.h, src/mlt++/MltTractor.cpp, src/mlt++/MltTractor.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h, src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_avdeinterlace.c, src/modules/avformat/filter_avfilter.c, src/modules/avformat/filter_avresample.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c, src/modules/core/composite_line_yuv_sse2_simple.c, src/modules/core/consumer_multi.c, src/modules/core/consumer_null.c, src/modules/core/factory.c, src/modules/core/filter_audiochannels.c, src/modules/core/filter_audioconvert.c, src/modules/core/filter_audiomap.c, src/modules/core/filter_audiowave.c, src/modules/core/filter_brightness.c, src/modules/core/filter_channelcopy.c, src/modules/core/filter_crop.c, src/modules/core/filter_data_feed.c, src/modules/core/filter_data_show.c, src/modules/core/filter_fieldorder.c, src/modules/core/filter_gamma.c, src/modules/core/filter_greyscale.c, src/modules/core/filter_imageconvert.c, src/modules/core/filter_luma.c, src/modules/core/filter_mirror.c, src/modules/core/filter_mono.c, src/modules/core/filter_obscure.c, src/modules/core/filter_panner.c, src/modules/core/filter_region.c, src/modules/core/filter_rescale.c, src/modules/core/filter_resize.c, src/modules/core/filter_transition.c, src/modules/core/filter_watermark.c, src/modules/core/producer_colour.c, src/modules/core/producer_consumer.c, src/modules/core/producer_hold.c, src/modules/core/producer_loader.c, src/modules/core/producer_melt.c, src/modules/core/producer_noise.c, src/modules/core/producer_timewarp.c, src/modules/core/producer_tone.c, src/modules/core/transition_composite.c, src/modules/core/transition_composite.h, src/modules/core/transition_luma.c, src/modules/core/transition_matte.c, src/modules/core/transition_mix.c, src/modules/core/transition_region.c, src/modules/core/transition_region.h, src/modules/decklink/common.cpp, src/modules/decklink/common.h, src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp, src/modules/dv/consumer_libdv.c, src/modules/dv/factory.c, src/modules/dv/producer_libdv.c, 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/producer_frei0r.c, src/modules/frei0r/transition_frei0r.c, src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.c, 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/producer_pixbuf.c, src/modules/gtk2/scale_line_22_yuv_mmx.S, src/modules/jackrack/consumer_jack.c, src/modules/jackrack/factory.c, src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/filter_ladspa.c, src/modules/jackrack/producer_ladspa.c, src/modules/kdenlive/factory.c, src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_freeze.c, src/modules/kdenlive/filter_wave.c, src/modules/kdenlive/producer_framebuffer.c, src/modules/kino/avi.cc, src/modules/kino/avi.h, 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/riff.cc, src/modules/kino/riff.h, src/modules/lumas/luma.c, src/modules/motion_est/arrow_code.c, src/modules/motion_est/arrow_code.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/motion_est/sad_sse.h, src/modules/normalize/factory.c, src/modules/normalize/filter_audiolevel.c, src/modules/normalize/filter_volume.c, src/modules/oldfilm/factory.c, src/modules/oldfilm/filter_dust.c, src/modules/oldfilm/filter_grain.c, src/modules/oldfilm/filter_lines.c, src/modules/oldfilm/filter_oldfilm.c, src/modules/oldfilm/filter_tcolor.c, src/modules/oldfilm/filter_vignette.c, src/modules/opengl/consumer_xgl.c, src/modules/opengl/factory.c, src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h, src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_crop.cpp, .../opengl/filter_movit_deconvolution_sharpen.cpp, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_glow.cpp, .../opengl/filter_movit_lift_gamma_gain.cpp, src/modules/opengl/filter_movit_mirror.cpp, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_rect.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_movit_white_balance.cpp, src/modules/opengl/mlt_flip_effect.h, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h, src/modules/opengl/transition_movit_luma.cpp, src/modules/opengl/transition_movit_mix.cpp, src/modules/opengl/transition_movit_overlay.cpp, src/modules/plus/consumer_blipflash.c, src/modules/plus/factory.c, src/modules/plus/filter_affine.c, src/modules/plus/filter_charcoal.c, src/modules/plus/filter_dance.c, src/modules/plus/filter_dynamic_loudness.c, src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_fft.c, src/modules/plus/filter_invert.c, src/modules/plus/filter_lift_gamma_gain.c, src/modules/plus/filter_loudness.c, src/modules/plus/filter_loudness_meter.c, src/modules/plus/filter_lumakey.c, src/modules/plus/filter_rgblut.c, src/modules/plus/filter_sepia.c, src/modules/plus/producer_blipflash.c, src/modules/plus/producer_count.c, src/modules/plus/transition_affine.c, src/modules/plusgpl/factory.c, src/modules/plusgpl/filter_burn.c, src/modules/plusgpl/filter_lumaliftgaingamma.c, src/modules/plusgpl/filter_rotoscoping.c, src/modules/plusgpl/filter_telecide.c, src/modules/qt/common.cpp, src/modules/qt/common.h, src/modules/qt/consumer_qglsl.cpp, src/modules/qt/factory.c, src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_lightshow.cpp, src/modules/qt/graph.cpp, src/modules/qt/graph.h, src/modules/qt/producer_qimage.c, src/modules/qt/producer_qtext.cpp, src/modules/qt/qimage_wrapper.cpp, src/modules/qt/qimage_wrapper.h, src/modules/resample/factory.c, src/modules/resample/filter_resample.c, src/modules/rtaudio/consumer_rtaudio.cpp, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_osx.h, src/modules/sdl/consumer_sdl_osx.m, 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/sox/factory.c, src/modules/sox/filter_sox.c, src/modules/swfdec/producer_swfdec.c, src/modules/vid.stab/common.c, src/modules/vid.stab/common.h, src/modules/vid.stab/factory.c, src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_vidstab.cpp, src/modules/videostab/factory.c, src/modules/videostab/filter_videostab.c, src/modules/videostab/filter_videostab2.c, src/modules/vmfx/factory.c, src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c, src/modules/vmfx/filter_mono.c, src/modules/vmfx/filter_shape.c, src/modules/vmfx/producer_pgm.c, src/modules/vorbis/factory.c, src/modules/vorbis/producer_vorbis.c, src/modules/xine/attributes.h, 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/xineutils.h, src/modules/xml/common.c, src/modules/xml/common.h, src/modules/xml/consumer_xml.c, src/modules/xml/factory.c, src/modules/xml/producer_xml.c, src/swig/mlt.i, src/swig/python/webvfx_generator.py, src/tests/test_animation/test_animation.cpp, src/tests/test_filter/test_filter.cpp, src/tests/test_frame/test_frame.cpp, src/tests/test_properties/test_properties.cpp, src/tests/test_repository/test_repository.cpp, src/tests/test_tractor/test_tractor.cpp, src/win32/win32.c: Fix #116 Free Software Foundation address. 2016-06-27 Pawel Golinski * src/framework/mlt_properties.c, src/framework/mlt_property.c: Use LC_NUMERIC_MASK instead of LC_NUMERIC in querylocale() Without this, mlt_properties_set_lcnumeric() doesn't work properly on OS X. 2016-06-25 Dan Dennedy * src/modules/core/producer_timewarp.c, src/modules/core/transition_mix.c: Fix compiler warnings in core module. 2016-06-23 Hugo Beauzée-Luyssen * src/mlt++/Makefile, src/mlt++/MltAnimation.h, src/mlt++/{config.h => MltConfig.h}, src/mlt++/MltConsumer.h, src/mlt++/MltDeque.h, src/mlt++/MltEvent.h, src/mlt++/MltFactory.h, src/mlt++/MltField.h, src/mlt++/MltFilter.h, src/mlt++/MltFilteredConsumer.h, src/mlt++/MltFilteredProducer.h, src/mlt++/MltFrame.h, src/mlt++/MltGeometry.h, src/mlt++/MltMultitrack.h, src/mlt++/MltParser.h, src/mlt++/MltPlaylist.h, src/mlt++/MltProducer.h, src/mlt++/MltProfile.h, src/mlt++/MltProperties.h, src/mlt++/MltPushConsumer.h, src/mlt++/MltRepository.h, src/mlt++/MltService.h, src/mlt++/MltTokeniser.h, src/mlt++/MltTractor.h, src/mlt++/MltTransition.h: Rename config.h to MltConfig.h 2016-05-27 Dan Dennedy * src/modules/xml/Makefile, src/modules/xml/common.c, src/modules/xml/common.h, src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Fix relative paths for timewarp producer. 2016-05-11 Brian Matherly * src/modules/plus/filter_loudness_meter.c, src/modules/plus/filter_loudness_meter.yml: Add peak and true peak measurements to loudness meter. 2016-04-28 Brian Matherly * src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/ebur128.h: Add prev_sample_peak() and prev_true_peak() 2016-04-24 Brian Matherly * src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/ebur128.h: Fix typo 2016-05-02 Yuri Valentini * src/modules/core/transition_composite.c, src/modules/core/transition_composite.yml: Add crop_to_fill property to composite transition. 2016-05-02 Dan Dennedy * configure, src/framework/mlt_version.h: Set to interim version 6.3.0. 2016-04-20 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.2.0 2016-04-18 Dan Dennedy * src/framework/metaschema.yaml, src/modules/qt/filter_audiospectrum.yml, src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_audiowaveform.yml, src/modules/qt/filter_lightshow.yml: Fix rect format and defaults in metadata. It is bad to suggest using comma as a separator for rect values because that is the decimal separator for many locales. The canonical format (as set in the mlt_property serializer) is to use spaces as separator. 2016-04-12 Dan Dennedy * src/modules/avformat/factory.c, src/modules/avformat/filter_avfilter.c: Only show & set threads property if the filter supports it. * src/modules/avformat/factory.c, src/modules/avformat/filter_avfilter.c: Add av.threads property to avfilter. 2016-04-06 Brian Matherly * src/modules/avformat/Makefile, src/modules/avformat/blacklist.txt, src/modules/avformat/factory.c: Add blacklist for avformat filters. 2016-03-30 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Fix relative path handling for "plain" webvfx resources. 2016-03-28 Dan Dennedy * src/modules/avformat/configure, src/modules/avformat/factory.c: Fix build against Libav by disabling avfilter. 2016-03-28 Brian Matherly * src/modules/avformat/factory.c, src/modules/avformat/filter_avfilter.c: Change parameter prefixes from .avf to .av. * src/modules/avformat/Makefile, src/modules/avformat/factory.c, src/modules/avformat/filter_avfilter.c: Add filter to provide services from avfilter. 2016-03-26 Dan Dennedy * src/modules/avformat/Makefile, src/modules/avformat/configure, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Add auto-rotate support to avformat producer. 2016-03-12 Dan Dennedy * src/framework/mlt_multitrack.c, src/framework/mlt_service.c: Fix crash on invalid pointer after disconnect and then connect services. 2016-03-11 Brian Matherly * src/modules/jackrack/factory.c, src/modules/jackrack/filter_ladspa.c, 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/producer_ladspa.c: Add status parameters to ladspa producer & filter. 2016-03-08 Dan Dennedy * src/modules/rtaudio/configure, src/modules/rtaudio/consumer_rtaudio.cpp: Fix rtaudio build using internal copy. 2016-03-08 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/factory.c, src/modules/plus/filter_dynamic_loudness.c, src/modules/plus/filter_dynamic_loudness.yml, src/modules/plus/filter_loudness_meter.c, src/modules/plus/filter_loudness_meter.yml: Add dynamic_loudness and loudness_meter filters. * src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/ebur128.h: Multiple changes to add configurable history and window size. These changes have been submitted, but not applied to the official repo. https://github.com/jiixyj/libebur128/pull/49 2016-03-07 Dan Dennedy * src/modules/rtaudio/RtAudio.cpp, src/modules/rtaudio/RtAudio.h, src/modules/rtaudio/RtError.h: Upgrade bundled RtAudio to v4.1.2. * src/modules/rtaudio/Makefile, src/modules/rtaudio/configure, src/modules/rtaudio/consumer_rtaudio.cpp: Support building rtaudio against external versions. 2016-03-02 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/ebur128.h, src/modules/plus/ebur128/{ => queue/sys}/queue.h: Update internal libebur128 to version 1.1.0 2016-02-29 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version to 6.1.0 2016-02-17 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 6.0.0 2016-02-11 Dan Dennedy * src/framework/mlt_frame.h, src/framework/mlt_service.c, src/framework/mlt_service.h, src/framework/mlt_tractor.c, src/modules/xine/filter_deinterlace.c: Fix YADIF in some multitrack scenarios. 2016-01-26 Maksym Veremeyenko * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Implement text cropping and fitting to specified width 2016-01-18 Dan Dennedy * src/framework/mlt_tractor.c, src/framework/mlt_types.h, src/modules/core/filter_audioconvert.c, src/modules/core/filter_brightness.c, src/modules/frei0r/factory.c, src/modules/motion_est/arrow_code.c, src/modules/motion_est/filter_autotrack_rectangle.c, src/modules/motion_est/filter_motion_est.c, src/modules/oldfilm/filter_grain.c, src/modules/oldfilm/filter_tcolor.c, src/modules/oldfilm/filter_vignette.c, src/modules/plus/transition_affine.c, src/modules/plusgpl/filter_rotoscoping.c, src/modules/videostab/transform_image.c: Define MIN, MAX, CLAMP in mlt_types.h. Consolidate all of the definitions of these macros and make them more convenient. 2016-01-13 Dan Dennedy * src/framework/mlt_multitrack.c, src/framework/mlt_transition.c: Fix possible array index crashes in multitrack and transition. Reported by JBM. 2016-01-11 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_factory.c, src/framework/mlt_pool.c, src/framework/mlt_properties.c, src/framework/mlt_repository.c, src/framework/mlt_types.h, src/melt/io.c, src/melt/melt.c, src/mlt++/config.h, src/modules/decklink/common.cpp, src/modules/decklink/common.h, src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp, src/modules/frei0r/factory.c, src/modules/gtk2/consumer_gtk2.c, src/modules/jackrack/consumer_jack.c, src/modules/jackrack/jack_rack.c, src/modules/jackrack/plugin_mgr.c, src/modules/opengl/factory.c, src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_movit_convert.cpp, src/modules/plusgpl/consumer_cbrts.c, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c, src/modules/videostab/stab/estimate.c, src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Switch to _WIN32 define for Windows. Based on de facto info collected at http://predef.sourceforge.net/ * configure, src/framework/mlt_factory.c, src/framework/mlt_properties.c, src/framework/mlt_property.c, src/framework/mlt_property.h, src/melt/melt.c, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/configure, src/modules/decklink/common.cpp, src/modules/decklink/common.h, src/modules/frei0r/factory.c, src/modules/jackrack/plugin.c, src/modules/jackrack/plugin_mgr.c, src/modules/opengl/factory.c, src/modules/opengl/filter_glsl_manager.cpp, src/modules/plusgpl/utils.c, src/modules/plusgpl/utils.h, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_osx.h, src/modules/sdl/consumer_sdl_still.c, src/modules/videostab/stab/estimate.c, src/tests/test_filter/test_filter.cpp, src/tests/test_properties/test_properties.cpp: Switch to __APPLE__ define for OS X. Based on de facto info collected at http://predef.sourceforge.net/ 2016-01-06 Dan Dennedy * src/framework/mlt_filter.c, src/modules/xml/producer_xml.c: Fix saving and loading filters attached to tractor. 2016-01-05 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/modules/xml/consumer_xml.c, src/win32/win32.c: Fix possible non-UTF-8 in XML on Windows (Shotcut-173). 2015-12-22 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/xml/consumer_xml.c: Fix some UTF-8 strings mangled by xml consumer. (Shotuct-163) This moves filter_restricted() into the avformat producer since it was its meta tag values that were not known to be UTF-8 being passed into MLT. 2015-11-30 Brian Matherly * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/producer_timewarp.c, src/modules/core/producer_timewarp.yml: Add timewarp producer. 2015-11-29 Brian Matherly * src/framework/mlt.vers, src/framework/mlt_factory.c, src/framework/mlt_factory.h: Add mlt_factory_repository() to mlt_factory. 2015-11-20 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/modules/decklink/consumer_decklink.cpp: Add drop_count property to mlt_consumer and verbose log it. Apps can use this to detect and display the integrity of capture or realtime playout. 2015-11-03 Dan Dennedy * presets/consumer/avformat/stills/BMP, presets/consumer/avformat/stills/DPX, presets/consumer/avformat/stills/JPEG, presets/consumer/avformat/stills/PNG, presets/consumer/avformat/stills/PPM, presets/consumer/avformat/stills/TGA, presets/consumer/avformat/stills/TIFF: Set GOP size to 1 and B frames to 0 for all stills presets. 2015-10-19 Brian Matherly * src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiospectrum.yml: Add "mirror" and "reverse" parameters. 2015-10-14 Brian Matherly * src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_dynamictext.yml: Add style property to dynamictext filter. 2015-10-11 Brian Matherly * src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiospectrum.yml, src/modules/qt/graph.cpp, src/modules/qt/graph.h: Add bar graph. 2015-10-11 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Fix YCbCr colorspace conversion wrong dest coefficients. 2015-10-07 Brian Matherly * src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiospectrum.yml, src/modules/qt/graph.cpp, src/modules/qt/graph.h: Implement tension and fill parameters. * src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/graph.cpp, src/modules/qt/graph.h: Improve spectrum line graph. 2015-09-30 Brian Matherly * src/modules/qt/Makefile, src/modules/qt/common.cpp, src/modules/qt/common.h, src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_lightshow.cpp, src/modules/qt/graph.cpp, src/modules/qt/graph.h: Move redundant code into shared files for reuse. 2015-09-26 Dan Dennedy * src/modules/qt/producer_qimage.c, src/modules/qt/qimage_wrapper.cpp, src/modules/qt/qimage_wrapper.h: Make qimage reject animated images. This will allow loader to fallback to using avformat, which can already handle animated images. 2015-09-22 Bertrand Nouvel * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Add seek_threshold property to avformat producer. 2015-09-21 Dan Dennedy * src/tests/test_tractor/test_tractor.cpp, src/tests/test_tractor/test_tractor.pro, src/tests/tests.pro: Add unit tests for tractor/multitrack/field. 2015-09-17 Brian Matherly * src/modules/qt/Makefile, src/modules/qt/factory.c, src/modules/qt/filter_audiospectrum.cpp, src/modules/qt/filter_audiospectrum.yml: Add audiospectrum filter. 2015-09-15 Dan Dennedy * presets/consumer/avformat/dv_ntsc/D10, presets/consumer/avformat/dv_ntsc_wide/D10, presets/consumer/avformat/dv_pal/D10, presets/consumer/avformat/dv_pal_wide/D10, presets/consumer/avformat/lossless/H.264, presets/consumer/avformat/lossless/MPEG-2, presets/consumer/avformat/lossless/MPEG-4, presets/consumer/avformat/lossless/ProRes, presets/consumer/avformat/lossless/ProRes-Kostya: Change GOP size 0 to 1 in presets. As a matter of proactive correctness. 2015-09-13 Dan Dennedy * presets/consumer/avformat/dv_ntsc/D10, presets/consumer/avformat/dv_ntsc_wide/D10, presets/consumer/avformat/dv_pal/D10, presets/consumer/avformat/dv_pal_wide/D10: Set 0 B-frames for all D10 presets. 2015-09-11 Jean-Baptiste Mardelle * src/modules/motion_est/filter_autotrack_rectangle.c, src/modules/vmfx/filter_shape.c: Use mlt_filter_get_length2 to get filter length 2015-09-05 Dan Dennedy * 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, src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c: Fix avformat build against FFmpeg and Libav master. This drops support for FFmpeg v1.0; requires at least v1.1. Still works with Libav v9. CPU flags are no longer required/used by libswscale. They are detected at runtime automatically. 2015-08-30 Dan Dennedy * src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/consumer_cbrts.yml: Change the smpte2022 property prefix to udp. 2015-08-29 Dan Dennedy * src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/consumer_cbrts.yml: Add smpte2022.interface property to cbrts. * src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/consumer_cbrts.yml: Change the default UDP buffer size. 2015-08-23 Dan Dennedy * src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/consumer_cbrts.yml: Add RTP header to cbrts UDP output. This can be disabled by settings smpte2022.rtp=0. 2015-08-22 Dan Dennedy * src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/consumer_cbrts.yml: Remove remux thread and add smpte2022.buffer property. The remux thread was not necessary and adds additional complexity. Now, the buffer between avformat and the socket is configurable and self-adjusts. There was also some refactoring of types used for times and muxrate. 2015-08-21 Dan Dennedy * src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/consumer_cbrts.yml: Add rate-controlled network delivery to cbrts. This adds a strict network output thread based on a high resolution timer with realtime process priority. 2015-08-15 Dan Dennedy * src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/consumer_cbrts.yml: Add UDP output to cbrts. This is currently fixed to 7 TSPs, and there is not yet any sending rate control. 2015-07-30 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version to 0.9.9. 2015-07-29 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 0.9.8. 2015-06-10 Dan Dennedy * src/swig/python/test_animation.py, src/tests/test_animation/test_animation.cpp, src/tests/tests.pro: Add tests for new Mlt::Animation API. * src/mlt++/MltAnimation.cpp, src/mlt++/MltAnimation.h, src/mlt++/mlt++.vers: Add Mlt::Animation::key_count() and key_get(). Also adds Mlt::Animation::key_get_frame() and Mlt::Animation::key_get_type(), which are useful for scripting/managed languages that cannot pass arguments by reference. * src/framework/mlt.vers, src/framework/mlt_animation.c, src/framework/mlt_animation.h: Add mlt_animation_key_count() and mlt_animation_key_get(). 2015-06-09 Dan Dennedy * src/mlt++/Makefile, src/mlt++/Mlt.h, src/mlt++/MltAnimation.cpp, src/mlt++/MltAnimation.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/mlt++.vers, src/swig/mlt.i: Add Mlt::Animation C++ API. 2015-07-27 Dan Dennedy * src/modules/frei0r/factory.c, src/modules/frei0r/not_thread_safe.txt: Make frei0r thread-safe check plugin version. Now, the frei0r MLT services will compare the plugin version to the version in the data file to see if they have been fixed yet. Also, update the data file to include versions that have the latest frei0r change to remove needless, non-parallel-safe state from frei0r.hpp. 2015-06-25 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_transition.c, src/framework/mlt_transition.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h, src/mlt++/mlt++.vers: Add Transition::set_tracks(). As well as mlt_transition_set_tracks(). 2015-06-24 Dan Dennedy * src/modules/jackrack/factory.c, src/modules/jackrack/filter_ladspa.c: Add property animation to ladspa filter. 2015-06-20 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h, src/framework/mlt_service.c, src/framework/mlt_service.h, src/framework/mlt_tractor.c, src/framework/mlt_tractor.h, src/mlt++/MltMultitrack.cpp, src/mlt++/MltMultitrack.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/MltTractor.cpp, src/mlt++/MltTractor.h, src/mlt++/mlt++.vers: Add Mlt::Tractor::insert_track(). As well as Mlt::Multitrack::insert(), Mlt::Service::insert_producer(), mlt_tractor_insert_track(), mlt_multitrack_insert(), and mlt_service_insert_producer(). 2015-06-19 Dan Dennedy * src/modules/avformat/consumer_avformat.yml, src/modules/avformat/factory.c: Fix valid YAML for avformat parameter default string. 2015-06-08 Maksym Veremeyenko * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_audiomap.c, src/modules/core/filter_audiomap.yml: Implement audiomap filter. 2015-06-14 Dan Dennedy * src/modules/decklink/darwin/DeckLinkAPI.h, src/modules/decklink/linux/DeckLinkAPI.h, src/modules/decklink/win/DeckLinkAPI_h.h: Add 2K DCI and 4K modes to DeckLink headers. 2015-06-08 Maksym Veremeyenko * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Implement text rotation in pango producer 2015-05-25 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/oldfilm/filter_lines.c, src/modules/plus/transition_affine.c, src/modules/plusgpl/filter_telecide.c, src/modules/sox/filter_sox.c: Fix clang warnings about abs functions. * src/modules/frei0r/producer_frei0r.c, src/modules/plus/filter_dance.c: Remove some more unusued variables. * src/framework/mlt.vers, src/framework/mlt_multitrack.c, src/framework/mlt_multitrack.h, src/framework/mlt_service.c, src/framework/mlt_service.h, src/framework/mlt_tractor.c, src/framework/mlt_tractor.h, src/mlt++/MltMultitrack.cpp, src/mlt++/MltMultitrack.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/MltTractor.cpp, src/mlt++/MltTractor.h, src/mlt++/mlt++.vers: Add Mlt::Tractor::remove_track(). As well as Mlt::Multitrack::disconnect, Mlt::Service::disconnect_producer(), mlt_tractor_remove_track(), mlt_multitrack_disconnect(), and mlt_service_disconnect_producer(). 2015-05-22 Dan Dennedy * src/framework/mlt_profile.c, src/modules/core/filter_resize.c: Fix heap corruption on auto-profile if yuv420p. mlt_image_none asks the producer for a MLT image format closest to the source video. If that yields yuv420p, which is very common, then filter_resize does not know how to pad that format. But, filter_resize would try anyways, get bpp=1 from mlt_image_format_size(), and not allocate enough memory for the new image. 2015-05-16 Dan Dennedy * src/modules/decklink/Makefile, src/modules/decklink/common.cpp: Fix crash listing DeckLink devices on MinGW-x64. It is incorrect to free() a BSTR; must use SysFreeString() instead. * src/modules/motion_est/sad_sse.h, src/modules/xine/vf_yadif_template.h, src/modules/xine/yadif.c: Fix assembly for MinGW-x64. 2015-05-03 Bernhard Frauendienst * src/swig/csharp/build, src/swig/java/build, src/swig/lua/build, src/swig/php/build, src/swig/python/build, src/swig/tcl/build: Allow build system to specify the name of g++ This is already possible in most places, but missing for most of the bindings (except for perl and ruby) 2015-04-19 Brian Matherly * src/modules/opengl/filter_movit_rect.cpp, src/modules/opengl/filter_movit_rect.yml: Add distort property to rect filter. 2015-04-18 Brian Matherly * src/modules/qt/Makefile, src/modules/qt/configure, src/modules/qt/factory.c, src/modules/qt/filter_lightshow.cpp, src/modules/qt/filter_lightshow.yml: Add lightshow filter. 2015-04-16 Brian Matherly * src/modules/plus/filter_dance.c, src/modules/plus/filter_dance.yml: Cleanup dance filter. 2015-04-13 Brian Matherly * src/modules/plus/filter_dance.c, src/modules/plus/filter_dance.yml: Set default zoom to 0. 2015-04-07 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/configure, src/modules/plus/factory.c, src/modules/plus/filter_dance.c, src/modules/plus/filter_dance.yml, src/modules/plus/filter_fft.c, src/modules/plus/filter_fft.yml: Add fft and dance filters. 2015-03-19 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version to 0.9.7. 2015-03-13 Brian Matherly * src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_audiowaveform.yml: Multiple improvements. * Don't mirror the gradient. * Fix waveform when horizontal resolution exceeds the number of audio samples. * Only use a single color by default. 2015-03-12 Brian Matherly * src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_audiowaveform.yml: Add gradient orientation parameter. * src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_audiowaveform.yml: Add fill parameter. 2015-03-04 Brian Matherly * src/framework/metaschema.yaml, src/modules/qt/Makefile, src/modules/qt/common.cpp, src/modules/qt/common.h, src/modules/qt/factory.c, src/modules/qt/filter_audiowaveform.cpp, src/modules/qt/filter_audiowaveform.yml: Add audiowaveform filter. 2015-03-01 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 0.9.6. 2015-02-25 Brian Matherly * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Add mute_on_pause property. Default behavior is to mute the audio if a frame is requested twice. This matches the previous behavior. 2015-02-18 Dan Dennedy * src/framework/mlt_consumer.c, src/modules/sdl/consumer_sdl.c: Fix deadlock with sdl_preview seeking/playing near end. Found while investigating https://bugs.kde.org/show_bug.cgi?id=343326 2015-02-17 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version to 0.9.5. 2015-02-15 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 0.9.4. 2015-02-14 Dan Dennedy * src/framework/mlt_factory.c, src/framework/mlt_types.h, src/modules/opengl/filter_glsl_manager.cpp: Some fixes for building on Windows. Noticed while working on 64-bit build, but they do not appear to be 64- toolchain (4.8.2). 2015-02-08 Brian Matherly * src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h, src/mlt++/mlt++.vers, src/tests/test_filter/test_filter.cpp, src/tests/test_filter/test_filter.pro, src/tests/tests.pro: Add Mlt::Filter::process(Frame&) and an appropriate test. 2015-02-08 Dan Dennedy * src/mlt++/Mlt.h, src/mlt++/MltConsumer.cpp, src/mlt++/MltConsumer.h, src/mlt++/MltDeque.cpp, src/mlt++/MltDeque.h, src/mlt++/MltEvent.cpp, src/mlt++/MltEvent.h, src/mlt++/MltFactory.cpp, src/mlt++/MltFactory.h, src/mlt++/MltField.cpp, src/mlt++/MltField.h, src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h, src/mlt++/MltFilteredConsumer.cpp, src/mlt++/MltFilteredConsumer.h, src/mlt++/MltFilteredProducer.cpp, src/mlt++/MltFilteredProducer.h, src/mlt++/MltFrame.cpp, src/mlt++/MltFrame.h, src/mlt++/MltGeometry.cpp, src/mlt++/MltGeometry.h, src/mlt++/MltMultitrack.cpp, src/mlt++/MltMultitrack.h, src/mlt++/MltParser.cpp, src/mlt++/MltParser.h, src/mlt++/MltPlaylist.cpp, src/mlt++/MltPlaylist.h, src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/MltPushConsumer.cpp, src/mlt++/MltPushConsumer.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/MltTokeniser.cpp, src/mlt++/MltTokeniser.h, src/mlt++/MltTractor.cpp, src/mlt++/MltTractor.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h, src/mlt++/config.h, src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c, src/modules/vmfx/filter_mono.c, src/modules/vmfx/filter_shape.c, src/swig/mlt.i: Transfer copyright of mlt++ and swig/mlt.i to Meltytech, LLC. Also, update Charles' email address. 2015-02-03 Maksym Veremeyenko * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/core/filter_crop.c, src/modules/core/filter_rescale.c: Replace some mlt_frame_get_alpha_mask with mlt_frame_get_alpha. * src/framework/mlt_tractor.c, src/modules/core/filter_resize.c, src/modules/core/transition_composite.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c: Use mlt_frame_get_alpha() instead of mlt_frame_get_alpha_mask(). * src/framework/mlt.vers, src/framework/mlt_frame.c, src/framework/mlt_frame.h: implement mlt_frame_get_alpha 2015-02-02 Maksym Veremeyenko * src/modules/gtk2/factory.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: implement caching images * src/modules/gtk2/factory.c, src/modules/gtk2/producer_pixbuf.yml: introduce environment variable MLT_PIXBUF_PRODUCER_CACHE to increase/override number of cached converted images 2015-02-02 Brian Matherly * src/mlt++/MltFrame.cpp, src/mlt++/MltFrame.h, src/mlt++/mlt++.vers, src/tests/test_frame/test_frame.cpp: Add default constructor, const copy constructor and copy operator=. The default constructor is useful to define frame objects that will be assigned at a later time. In Mlt++, some objects have a default constructor that initializes the underlying MLT type while others create an invalid object. The Frame copy constructor creates an invalid object so that empty frames can be predeclared without the expense of initializing an empty frame only to destroy it all when it is actually assigned to a frame. The const copy constructor duplicates the non-const constructor, but is useful so that frames can be initialized from tempory variables. The copy operator= is necessary so that frames can be stored in containers. Additionally, with this change, Mlt::Frame implements "the big three" which is a good practice. 2015-01-31 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qt/qimage_wrapper.cpp: Remove unusused variable image_key. 2015-01-29 Dan Dennedy * src/mlt++/MltTractor.cpp, src/mlt++/MltTractor.h, src/mlt++/mlt++.vers: Add Mlt::Tractor constructor that applies a Mlt::Profile. 2015-01-15 Dan Dennedy * presets/consumer/avformat/x265-medium, presets/consumer/avformat/x265-medium-pass1: Add a couple of x265 presets. 2015-01-04 Kevin Deldycke * profiles/qhd_1440p_2398, profiles/qhd_1440p_24, profiles/qhd_1440p_25, profiles/qhd_1440p_2997, profiles/qhd_1440p_30, profiles/qhd_1440p_50, profiles/qhd_1440p_5994, profiles/qhd_1440p_60, profiles/uhd_2160p_2398, profiles/uhd_2160p_24, profiles/uhd_2160p_25, profiles/uhd_2160p_2997, profiles/uhd_2160p_30, profiles/uhd_2160p_50, profiles/uhd_2160p_5994, profiles/uhd_2160p_60: Add 4K and 2.5K profiles. 2014-12-19 Brian Matherly * src/modules/plus/producer_count.c, src/modules/plus/producer_count.yml: Add non-drop-frame support. Also, use mlt_properties_get_time() instead of calculating the timecode manually. 2014-12-16 Brian Matherly * src/modules/qt/Makefile, src/modules/qt/configure, src/modules/qt/factory.c: Allow building qt module without OpenGL. 2014-12-16 Brian Matherly * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/producer_tone.c, src/modules/core/producer_tone.yml: Add new audio tone producer. 2014-12-12 Brian Matherly * src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml: Add support for smpte_ndf to consumer_xml. * src/framework/mlt_property.c, src/framework/mlt_types.h, src/mlt++/MltProducer.h, src/mlt++/MltProperties.h, src/modules/core/filter_data_show.c, src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_dynamictext.yml, src/modules/xml/consumer_xml.c, src/tests/test_properties/test_properties.cpp: Add support for non-drop-frame timecode. 2014-12-08 Dan Dennedy * src/framework/mlt_tractor.c, src/modules/avformat/producer_avformat.c, src/modules/core/producer_consumer.c: Fix audio and huge memory leak using consumer producer. (SF-221) This would occur when the output frame rate is less than the project frame rate. The problem was that the avformat producer would not produce enough samples to fulfill the consumer producer's request. Then, the consumer producer would skip frames to reduce frame rate. That resulted in some audio being skipped and choppy output. This also caused a massive audio underflow in the avformat consumer. Next, while the avformat consumer is trying maintain audio/video sync, it would accumulate video frames in its queue causing the memory utilization to rise quickly. 2014-12-05 Brian Matherly * src/modules/core/filter_watermark.c, src/modules/plus/filter_dynamictext.c: Second Try: Fix filters with encapsulated transitions corrupting frame position. Filters that use an encapsulated transition were modifying the frame position. Other filters that rely on the frame position would get an incorrect value. Test case: melt -producer frei0r.test_pat_B -filter wave -filter watermark:"+hi.txt" in=200 out=400 At frame 200 the wave would "jump" because the wave phase is calculated from frame position which gets modified by the watermark filter. 2014-12-02 Brian Matherly * src/modules/core/filter_watermark.c, src/modules/plus/filter_dynamictext.c: Fix filters with encapsulated transitions corrupting frame position. Filters that use an encapsulated transition were modifying the frame position. Other filters that rely on the frame position would get an incorrect value. Test case: melt -producer frei0r.test_pat_B -filter wave -filter watermark:"+hi.txt" in=200 out=400 At frame 200 the wave would "jump" because the wave phase is calculated from frame position which gets modified by the watermark filter. 2014-11-24 Maksym Veremeyenko * src/framework/mlt.vers, src/framework/mlt_pool.c, src/framework/mlt_pool.h: Implement mlt_pool_stat. 2014-11-15 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml: Add an option to not output the root attribute. 2014-11-04 Dan Dennedy * src/modules/oldfilm/filter_dust.yml, src/modules/oldfilm/filter_grain.yml, src/modules/oldfilm/filter_oldfilm.yml, src/swig/ruby/metadata.rb: Changes to YAML and metadata.rb to make ruby 1.9.3 happy. The Ruby YAML parser does not like percent sign in double-quotes - probably trying to expand it or something. 2014-11-03 Dan Dennedy * README, docs/framework.txt, docs/melt.1, docs/melt.txt, docs/mlt-xml.txt, src/framework/mlt.h, src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/framework/mlt_cache.c, src/framework/mlt_cache.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_log.c, src/framework/mlt_log.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/mlt_version.c, src/framework/mlt_version.h, src/melt/io.c, src/melt/io.h, src/melt/melt.c, src/modules/avformat/consumer_avformat.c, src/modules/avformat/consumer_avformat.yml, src/modules/avformat/factory.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_avdeinterlace.c, src/modules/avformat/filter_avresample.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml, src/modules/avformat/vdpau.c, src/modules/core/composite_line_yuv_sse2_simple.c, src/modules/core/consumer_multi.c, src/modules/core/consumer_multi.yml, src/modules/core/consumer_null.c, src/modules/core/factory.c, src/modules/core/filter_audiochannels.c, src/modules/core/filter_audioconvert.c, src/modules/core/filter_audiowave.c, src/modules/core/filter_audiowave.yml, src/modules/core/filter_brightness.c, src/modules/core/filter_brightness.yml, src/modules/core/filter_channelcopy.c, src/modules/core/filter_channelcopy.yml, src/modules/core/filter_crop.c, src/modules/core/filter_crop.yml, src/modules/core/filter_data_feed.c, src/modules/core/filter_data_show.c, src/modules/core/filter_data_show.yml, src/modules/core/filter_fieldorder.c, src/modules/core/filter_fieldorder.yml, src/modules/core/filter_gamma.c, src/modules/core/filter_gamma.yml, src/modules/core/filter_greyscale.c, src/modules/core/filter_greyscale.yml, src/modules/core/filter_imageconvert.c, src/modules/core/filter_luma.c, src/modules/core/filter_luma.yml, src/modules/core/filter_mirror.c, src/modules/core/filter_mirror.yml, src/modules/core/filter_mono.c, src/modules/core/filter_mono.yml, src/modules/core/filter_obscure.c, src/modules/core/filter_obscure.yml, src/modules/core/filter_panner.c, src/modules/core/filter_panner.yml, src/modules/core/filter_region.c, src/modules/core/filter_region.yml, src/modules/core/filter_rescale.c, src/modules/core/filter_rescale.yml, src/modules/core/filter_resize.c, src/modules/core/filter_resize.yml, src/modules/core/filter_transition.c, src/modules/core/filter_transition.yml, src/modules/core/filter_watermark.c, src/modules/core/filter_watermark.yml, src/modules/core/producer_colour.c, src/modules/core/producer_colour.yml, src/modules/core/producer_consumer.c, src/modules/core/producer_consumer.yml, src/modules/core/producer_hold.c, src/modules/core/producer_hold.yml, src/modules/core/producer_loader.c, src/modules/core/producer_loader.yml, src/modules/core/producer_melt.c, src/modules/core/producer_melt.yml, src/modules/core/producer_melt_file.yml, src/modules/core/producer_noise.c, src/modules/core/producer_noise.yml, src/modules/core/transition_composite.c, src/modules/core/transition_composite.h, src/modules/core/transition_composite.yml, src/modules/core/transition_luma.c, src/modules/core/transition_luma.yml, src/modules/core/transition_matte.c, src/modules/core/transition_matte.yml, src/modules/core/transition_mix.c, src/modules/core/transition_mix.yml, src/modules/core/transition_region.c, src/modules/core/transition_region.h, src/modules/core/transition_region.yml, src/modules/dv/consumer_libdv.c, src/modules/dv/consumer_libdv.yml, src/modules/dv/factory.c, src/modules/dv/producer_libdv.c, src/modules/dv/producer_libdv.yml, src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/consumer_gtk2_preview.yml, src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.c, src/modules/gtk2/filter_rescale.yml, src/modules/gtk2/pixops.c, src/modules/gtk2/pixops.h, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml, src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.yml, src/modules/gtk2/scale_line_22_yuv_mmx.S, src/modules/jackrack/consumer_jack.c, src/modules/jackrack/consumer_jack.yml, src/modules/jackrack/factory.c, src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/filter_jackrack.yml, src/modules/jackrack/filter_ladspa.c, src/modules/jackrack/filter_ladspa.yml, 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/producer_ladspa.c, src/modules/jackrack/producer_ladspa.yml, src/modules/kino/factory.c, src/modules/kino/kino_wrapper.cc, src/modules/kino/kino_wrapper.h, src/modules/kino/producer_kino.c, src/modules/lumas/luma.c, src/modules/normalize/factory.c, src/modules/normalize/filter_volume.c, src/modules/normalize/filter_volume.yml, src/modules/plus/factory.c, src/modules/plus/filter_affine.c, src/modules/plus/filter_affine.yml, src/modules/plus/filter_charcoal.c, src/modules/plus/filter_charcoal.yml, src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_dynamictext.yml, src/modules/plus/filter_invert.c, src/modules/plus/filter_invert.yml, src/modules/plus/filter_sepia.c, src/modules/plus/filter_sepia.yml, src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml, src/modules/resample/factory.c, src/modules/resample/filter_resample.c, src/modules/resample/filter_resample.yml, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl.yml, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_audio.yml, src/modules/sdl/consumer_sdl_osx.h, src/modules/sdl/consumer_sdl_osx.m, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_preview.yml, src/modules/sdl/consumer_sdl_still.c, src/modules/sdl/consumer_sdl_still.yml, src/modules/sdl/factory.c, src/modules/sdl/producer_sdl_image.c, src/modules/sdl/producer_sdl_image.yml, src/modules/sox/factory.c, src/modules/sox/filter_sox.c, src/modules/sox/filter_sox.yml, src/modules/sox/filter_sox_effect.yml, src/modules/vorbis/factory.c, src/modules/vorbis/producer_vorbis.c, src/modules/vorbis/producer_vorbis.yml, src/modules/xine/factory.c, src/modules/xine/filter_deinterlace.c, src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml, src/modules/xml/factory.c, src/modules/xml/producer_xml-nogl.yml, src/modules/xml/producer_xml-string.yml, src/modules/xml/producer_xml.c, src/modules/xml/producer_xml.yml, src/win32/win32.c: Transfer Ushodaya copyrights to Meltytech, LLC. On September 30, 2014, Ushodaya Enterprises Limited assigned copyright to Daniel Dennedy, acting on behalf of Meltytech, LLC. 2014-11-02 Jean-Baptiste Mardelle * src/modules/gtk2/configure, src/modules/qt/configure: print message when libexif is missing on configure 2014-10-30 Dan Dennedy * src/modules/plus/filter_dynamictext.c, src/modules/plus/filter_dynamictext.yml: Add localtime keyword to dynamictext filter. 2014-10-20 Janne Liljeblad * src/modules/plus/Makefile, src/modules/plus/factory.c, src/modules/plus/filter_lumakey.c, src/modules/plus/filter_lumakey.yml: Add Luma Key filter 2014-10-04 Dan Dennedy * src/modules/avformat/Makefile, src/modules/avformat/producer_avformat.c: Revert 279ebd to prevent integer overflow for duration calculation. The integer math could easily overflow with large number for profile- auto-profile. To address the Maksym's concern, rounding was added. 2014-10-02 Dan Dennedy * src/framework/mlt_factory.c, src/framework/mlt_factory.h: Ignore MLT_REPOSITORY env var on Windows and RELOCATABLE Darwin builds. 2014-10-01 Markus Elfring * coccinelle/delete_if4.cocci, src/framework/mlt_pool.c: Remove an unnecessary null check in mlt_pool. (SF-216) 2014-09-27 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Use av_frame_alloc() instead of deprecated avcodec_alloc_frame(). * src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/filter_avresample.c, src/modules/avformat/producer_avformat.c: Drop support for FFmpeg < v1.0 and Libav < v9. 2014-09-22 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Fix a couple of compiler warnings in avformat unrelated to API. 2014-09-22 Jean-Baptiste Mardelle * src/modules/qt/kdenlivetitle_wrapper.cpp, src/modules/qt/producer_kdenlivetitle.c: Fix initialization of aspect ratio and meta.media.width/height 2014-09-21 Dan Dennedy * presets/consumer/avformat/MJPEG, presets/consumer/avformat/lossless/MJPEG: threads=1 is no longer needed for MJPEG. * src/mlt++/Mlt.h, src/mlt++/MltConsumer.h, src/mlt++/MltDeque.h, src/mlt++/MltEvent.h, src/mlt++/MltFactory.h, src/mlt++/MltField.h, src/mlt++/MltFilter.h, src/mlt++/MltFilteredConsumer.h, src/mlt++/MltFilteredProducer.h, src/mlt++/MltFrame.h, src/mlt++/MltGeometry.h, src/mlt++/MltMultitrack.h, src/mlt++/MltParser.h, src/mlt++/MltPlaylist.h, src/mlt++/MltProducer.h, src/mlt++/MltProfile.h, src/mlt++/MltProperties.h, src/mlt++/MltRepository.h, src/mlt++/MltService.h, src/mlt++/MltTokeniser.h, src/mlt++/MltTractor.h, src/mlt++/MltTransition.h, src/mlt++/config.h: Fix reserved identifiers for include guards in mlt++. (SF-215) * src/framework/mlt.h, src/framework/mlt_cache.h, src/framework/mlt_consumer.h, src/framework/mlt_deque.h, src/framework/mlt_events.h, src/framework/mlt_factory.h, src/framework/mlt_field.h, src/framework/mlt_filter.h, src/framework/mlt_frame.h, src/framework/mlt_geometry.h, src/framework/mlt_multitrack.h, src/framework/mlt_parser.h, src/framework/mlt_playlist.h, src/framework/mlt_pool.h, src/framework/mlt_producer.h, src/framework/mlt_profile.h, src/framework/mlt_properties.h, src/framework/mlt_property.h, src/framework/mlt_repository.h, src/framework/mlt_service.h, src/framework/mlt_tokeniser.h, src/framework/mlt_tractor.h, src/framework/mlt_transition.h, src/framework/mlt_types.h, src/framework/mlt_version.h: Fix reserved identifiers for include guards in framework. (SF-215) 2014-09-20 Dan Dennedy * Doxyfile, src/framework/mlt_factory.c, src/framework/mlt_factory.h, src/framework/mlt_profile.h: Add a dedicated page to doxygen docs for environment variables. * src/framework/mlt_factory.c, src/framework/mlt_properties.c, src/framework/mlt_repository.c: Refactor handling MLT_PRESETS_PATH environment var. 2014-09-20 Markus Elfring * src/modules/kino/filehandler.cc, src/modules/qt/qimage_wrapper.cpp: Bug #MLT-216: Removal of unnecessary checks before a few calls of the C++ delete operator The C++ delete operator can also handle passed null pointers on its own without unexpected side effects. http://www.parashift.com/c++-faq/delete-handles-null.html It is therefore not needed to keep additional safety checks directly before such function calls. * src/framework/mlt_animation.c, src/framework/mlt_consumer.c, src/framework/mlt_geometry.c, src/framework/mlt_profile.c, src/framework/mlt_properties.c, src/framework/mlt_property.c, src/melt/melt.c, src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c, src/modules/core/consumer_multi.c, src/modules/gtk2/producer_pixbuf.c, src/modules/linsys/consumer_SDIstream.c, src/modules/plus/transition_affine.c, src/modules/plusgpl/cJSON.c, src/modules/videostab/filter_videostab.c, src/modules/videostab/filter_videostab2.c, src/modules/videostab/stabilize.c, src/modules/videostab/tlist.c, src/modules/vorbis/producer_vorbis.c, src/modules/xml/producer_xml.c: Bug #MLT-216: Deletion of unnecessary checks before specific function calls A couple of functions perform input parameter validation before their implementations will try further actions with side effects. Some calling functions perform similar safety checks. Functions which release a system resource are occasionally documented in the way that they tolerate the passing of a null pointer for example. There is not a need because of this fact that a function caller repeats a corresponding check. A previously added semantic patch approach suggested the changes that are shown here. 2014-09-08 Dan Dennedy * src/framework/mlt_playlist.c, src/framework/mlt_producer.c: Fix LC_NUMERIC for a couple of nested services (shotcut-36) 2014-09-07 Dan Dennedy * src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h, src/mlt++/mlt++.vers: Add Mlt::Profile::set_display_aspect(). 2014-09-03 Brian Matherly * src/modules/core/filter_brightness.yml, src/modules/core/filter_watermark.yml, src/modules/core/producer_loader.yml, src/modules/core/transition_composite.yml, src/modules/normalize/filter_volume.yml, src/modules/plus/filter_rgblut.c, src/modules/plus/filter_rgblut.yml: Don't use "unset" for parameters that are not set by default. Exclude the "default" tag altogether instead. 2014-09-02 Brian Matherly * src/modules/normalize/filter_volume.c, src/modules/normalize/filter_volume.yml: Change level parameter to be in units of dB. 2014-09-01 Dan Dennedy * src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.yml: Add MLT_AVFORMAT_PRODUCER_CACHE environment variable. 2014-08-27 Dan Dennedy * src/framework/mlt_factory.c, src/framework/mlt_factory.h: Improve dox for mlt_factory_producer(). 2014-08-25 Andreas Cadhalpun * src/modules/avformat/Makefile, src/modules/avformat/configure: Use pkg-config to determine FFmpeg linker flags. Use 'pkg-config --libs' instead of 'pkg-config --libs-only-L'. 2014-08-24 Dan Dennedy * src/modules/core/filter_brightness.c, src/modules/core/filter_brightness.yml: Add keyframable alpha property to brightness filter. 2014-08-19 Dan Dennedy * presets/consumer/avformat/WMA, presets/consumer/avformat/WMV: Add WMV and WMA encode presets for Windows Media Player. * src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: Add halign and valign properties to affine. 2014-08-17 Dan Dennedy * src/modules/plus/transition_affine.c, src/modules/plus/transition_affine.yml: Add fill property to affine. * src/modules/plus/filter_affine.yml, src/modules/plus/transition_affine.yml: Document the affine filter and transition. 2014-07-24 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Add support for color_trc handling to avformat module. * src/framework/mlt_consumer.c, src/framework/mlt_frame.h, src/framework/mlt_tractor.c, src/framework/mlt_transition.c, src/modules/core/consumer_multi.c: Add support for color_trc (transfer characteristic) to framework. Frames and consumers can now have a color_trc property. Setting color_trc on the consumer propogates to a consumer_color_trc property on the frame. 2014-07-20 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Cleanup full range YUV handling. In FFmpeg v2.3, calling sws_setColorspaceDetails() after sws_getContext() for YUV conversions works as desired. Now, we can also get rid of the swscale deprecated pixel format warnings. Based on patches by Loic Guibert * configure, src/framework/mlt_version.h: Set interim version to 0.9.3. 2014-06-29 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 0.9.2. 2014-06-27 Maksym Veremeyenko * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/transition_matte.c, src/modules/core/transition_matte.yml: Add matte transition. 2014-06-02 Dan Dennedy * src/modules/videostab/filter_videostab.yml, src/modules/videostab/filter_videostab2.yml: Document the videostab module as deprecated. This does not remove from the build process because we want to continue to support legacy projects especially Kdenlive. However, now documentation lets people know to use vidstab instead. * src/modules/Makefile, src/modules/configure, src/modules/dv/consumer_libdv.yml, src/modules/dv/deprecated, src/modules/dv/producer_libdv.yml, src/modules/kino/deprecated, src/modules/vorbis/deprecated, src/modules/vorbis/producer_vorbis.yml: Deprecate the dv, kino, and vorbis modules. They are removed from the normal configure & make build process, but they are still easy to build manually if needed, e.g.: make -C src/ * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/producer_ppm.c: Remove the ppm pipe producer. No one uses this, and it is problematic to support. It was mainly used in very early development before avformat module was any good and smilutils was still maintained and in use. * src/modules/core/filter_transition.yml, src/modules/core/producer_hold.yml, src/modules/core/producer_melt_file.yml: Finish documenting transition filter and hold and melt file producers. * src/modules/core/consumer_null.c, src/modules/core/filter_mirror.c, src/modules/core/producer_consumer.c, src/modules/core/producer_hold.c, src/modules/core/producer_noise.c, src/modules/core/producer_ppm.c, src/modules/core/transition_luma.c, src/modules/core/transition_mix.c: Remove usage of "this" as a variable name in core module. 2014-05-24 Dan Dennedy * src/modules/gtk2/producer_pango.c, src/modules/qt/producer_kdenlivetitle.c, src/modules/qt/producer_qtext.cpp: Fix file names with ext. chars on Windows for text producers. * src/modules/core/transition_composite.c, src/modules/core/transition_luma.c: Fix file names with ext. chars on Windows for luma files. 2014-05-18 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Fix file names with ext. chars on Windows for xml module. * src/framework/mlt_properties.c, src/mlt++/MltProperties.cpp: Fix file names with exts chars on Windows for mlt_properties. * src/framework/Makefile, src/framework/mlt.vers, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_util.c, src/framework/mlt_util.h, src/modules/avformat/producer_avformat.c: Move from_utf8 function into mlt_properties; remove mlt_util. * src/framework/Makefile, src/framework/mlt.vers, src/framework/mlt_util.c, src/framework/mlt_util.h, src/win32/win32.c: Add mlt_util API with mlt_util_from_utf8(). This will be used for converting a text encoding from a standard MLT internal string encoding of UTF-8 into other encodings - on Windows only initially. 2014-05-07 Dan Dennedy * src/modules/plusgpl/Makefile, src/modules/plusgpl/consumer_cbrts.c: Fix cbrts build on Windows. 2014-05-06 Dan Dennedy * src/modules/plusgpl/Makefile, src/modules/plusgpl/consumer_cbrts.c, src/modules/plusgpl/consumer_cbrts.yml, src/modules/plusgpl/factory.c: Add cbrts consumer. See http://www.mltframework.org/bin/view/MLT/ConsumerCbrtsMore 2014-04-26 Janne Liljeblad * src/modules/core/filter_channelcopy.c, src/modules/core/filter_data_feed.c, src/modules/core/filter_data_show.c: Clean up channel_copy, data_feed and data_show 2014-04-16 Dan Dennedy * src/modules/kdenlive/filter_boxblur.yml, src/modules/kdenlive/filter_wave.yml: Improve service metadata/documentation for boxblur filter. 2014-04-15 Janne Liljeblad * src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_wave.c: Add animated property wave to filter wave, minor boxblur cleanups * src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_boxblur.yml: Add animated property blur to boxblur * src/modules/core/filter_panner.c, src/modules/core/filter_panner.yml: Add animated property split to panner 2014-04-08 Dan Dennedy * src/modules/core/filter_brightness.yml, src/modules/normalize/filter_volume.yml, src/modules/plusgpl/filter_lumaliftgaingamma.yml: Fix YAML validation errors: double => float. 2014-04-08 Janne Liljeblad * src/modules/normalize/filter_volume.c, src/modules/normalize/filter_volume.yml: Add animated property level to filter volume 2014-04-08 Steinar H. Gunderson * src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: Convert on CPU if we are asked to finalize an empty Movit chain. This fixes an issue with interlaced content and no resize or other normalizers in actual use. 2014-04-05 Dan Dennedy * src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_crop.cpp, .../opengl/filter_movit_deconvolution_sharpen.cpp, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_glow.cpp, src/modules/opengl/filter_movit_lift_gamma_gain.cpp, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_movit_white_balance.cpp, src/modules/opengl/transition_movit_luma.cpp, src/modules/opengl/transition_movit_mix.cpp: Hide the movit.parms properties from serialization. These properties were adding unncessary noise in the XML output. 2014-04-04 Dan Dennedy * src/framework/mlt_frame.c, src/modules/core/producer_colour.c, src/modules/gtk2/producer_pixbuf.c, src/modules/opengl/filter_movit_crop.cpp, src/modules/qt/qimage_wrapper.cpp: Fix crashing when using opengl services with wrapper producers. Steinar reported crashing in Kdenlive when using the framebuffer producer due to movit.crop changing the format to mlt_image_none. Removing that required making other components handle requests with format = mlt_image_none. 2014-04-01 Dan Dennedy * src/modules/qt/Makefile, src/modules/qt/common.cpp, src/modules/qt/common.h, src/modules/qt/consumer_qglsl.cpp, src/modules/qt/kdenlivetitle_wrapper.cpp, src/modules/qt/producer_qtext.cpp, src/modules/qt/qimage_wrapper.cpp, src/modules/qt/transition_vqm.cpp: Refactor QApplication creation and fix lifetime of its args. 2014-03-27 Brian Matherly * src/modules/gtk2/Makefile, src/modules/gtk2/configure, src/modules/gtk2/factory.c, src/modules/gtk2/producer_pixbuf.c, src/modules/kino/Makefile, src/modules/kino/avi.cc, src/modules/kino/configure, src/modules/kino/filehandler.cc, src/modules/kino/kino_wrapper.cc, src/modules/kino/riff.cc, src/modules/opengl/Makefile, src/modules/qt/Makefile, src/modules/qt/configure, src/modules/qt/qimage_wrapper.h: Avoid unnecessary compilation when running "./configure; make; make install" multiple times. 2014-03-25 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/factory.c, src/modules/plus/filter_lift_gamma_gain.c, src/modules/plus/filter_lift_gamma_gain.yml: Add lift_gamma_gain filter. This filter is equivalent to the movit.lift_gamma_gain filter but does not depend on opengl/movit. 2014-03-25 Dan Dennedy * src/modules/opengl/Makefile, src/modules/opengl/factory.c, src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/transition_movit_luma.cpp, src/modules/opengl/transition_movit_luma.yml: Add movit.luma transition. This can be improved by adding an invert parameter to the Movit effect and by supplying full 16-bit PGM to the Movit input, but it is a start. 2014-03-24 Dan Dennedy * src/modules/core/transition_composite.c, src/modules/core/transition_luma.c, src/modules/dv/consumer_libdv.c, src/modules/vmfx/producer_pgm.c, src/modules/vorbis/producer_vorbis.c: Fix reading binary files on Windows. 2014-03-22 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_playlist.c, src/framework/mlt_playlist.h, src/mlt++/MltPlaylist.cpp, src/mlt++/MltPlaylist.h, src/mlt++/mlt++.vers: Add mlt_playlist_mix_in() and mlt_playlist_mix_out(). These are new alternatives to mlt_playlist_mix() that Shotcut is using. 2014-03-17 Janne Liljeblad * src/modules/core/filter_brightness.c, src/modules/core/filter_brightness.yml: Add animated level parameter to brightness 2014-02-26 Dan Dennedy * src/framework/mlt_properties.c, src/framework/mlt_property.c, src/framework/mlt_property.h: Implement LC_NUMERIC handling for non-glibc and non-Darwin OS. 2014-02-24 Dan Dennedy * presets/filter/movit.opacity/fade_in, presets/filter/movit.opacity/fade_in_out, presets/filter/movit.opacity/fade_out: Add some movit.opacity presets for fading. * src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_opacity.yml: Add alpha property to movit.opacity filter. 2014-02-24 Janne Liljeblad * src/modules/plusgpl/Makefile, src/modules/plusgpl/factory.c, src/modules/plusgpl/filter_lumaliftgaingamma.c, src/modules/plusgpl/filter_lumaliftgaingamma.yml: Add lumaliftgaingamma filter to plusgpl 2014-02-19 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Fix YUV to RGB conversion when profile colorspace not 601. The non-default coefficients for RGB cause incorrect conversion. Reported by Claus R. F. Overbeck on the kdenlive-devel mailing list. 2014-02-17 Dan Dennedy * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: Fix compiler warnings due to non-virtual destructors. 2014-02-16 Dan Dennedy * src/modules/plusgpl/Makefile, src/modules/{rotoscoping => plusgpl}/cJSON.c, src/modules/{rotoscoping => plusgpl}/cJSON.h, src/modules/plusgpl/factory.c, .../{rotoscoping => plusgpl}/filter_rotoscoping.c, .../filter_rotoscoping.yml, src/modules/rotoscoping/Makefile, src/modules/rotoscoping/factory.c, src/modules/rotoscoping/gpl: Move rotoscoping filter into plusgpl module. * src/modules/effectv/Makefile, src/modules/effectv/factory.c, src/modules/effectv/gpl, src/modules/plusgpl/Makefile, src/modules/plusgpl/factory.c, src/modules/{effectv => plusgpl}/filter_burn.c, .../{effectv => plusgpl}/filter_burningtv.yml, src/modules/{effectv => plusgpl}/image.c, src/modules/{effectv => plusgpl}/utils.c, src/modules/{effectv => plusgpl}/utils.h: Move burningtv into plusgpl module. * src/modules/{dgraft => plusgpl}/Makefile, src/modules/{dgraft => plusgpl}/factory.c, src/modules/{dgraft => plusgpl}/filter_telecide.c, src/modules/{dgraft => plusgpl}/gpl: Start new plusgpl module from dgraft. 2014-02-13 Maksym Veremeyenko * src/modules/decklink/common.cpp, src/modules/decklink/common.h, src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: implement SSE optimized swab function 2014-02-12 Dan Dennedy * demo/mlt_swf_variables, demo/txtField.swf: Remove Flash binary. 2014-02-12 Steinar H. Gunderson * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h, src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_crop.cpp, .../opengl/filter_movit_deconvolution_sharpen.cpp, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_glow.cpp, .../opengl/filter_movit_lift_gamma_gain.cpp, src/modules/opengl/filter_movit_mirror.cpp, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_rect.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_movit_white_balance.cpp, src/modules/opengl/mlt_flip_effect.h, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h, src/modules/opengl/optional_effect.h, src/modules/opengl/transition_movit_mix.cpp, src/modules/opengl/transition_movit_overlay.cpp: Adjust for Movit moving into namespace movit. 2014-02-11 Janne Liljeblad * src/modules/plus/filter_rgblut.c, src/modules/plus/filter_rgblut.yml: Change this to frame or filter as appropriate 2014-02-10 Janne Liljeblad * src/modules/plus/Makefile, src/modules/plus/factory.c, src/modules/plus/filter_rgblut.c, src/modules/plus/filter_rgblut.yml: Add rgblut filter 2014-02-05 Brian Matherly * src/modules/plus/filter_loudness.c, src/modules/plus/filter_loudness.yml: Make program property mutable 2014-02-05 Dan Dennedy * src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/queue.h: Fix build on Windows due to missing queue macros. 2014-02-04 Brian Matherly * src/modules/plus/Makefile, src/modules/plus/ebur128/COPYING, src/modules/plus/ebur128/ebur128.c, src/modules/plus/ebur128/ebur128.h, src/modules/plus/factory.c, src/modules/plus/filter_loudness.c, src/modules/plus/filter_loudness.yml: Add new audio loudness filter based on EBU R128 2014-01-28 Steinar H. Gunderson * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h: Remove the FBO freelist. FBOs are cheap to construct and delete (they carry almost no state), so it is less complex just to do it on the fly. It also gives less leakage, as we use new contexts all the time. * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: Take MltInput out of the EffectChain. Having the MltInput be an Input which forwards down to the real implementation has been a source of multiple headaches, and now lastly, when finalize() disappeared, source of a broken build. We still need the unified set_pixel_pointer() etc., but the class is now simply a holder of the Input*, not a forwarder as viewed from the EffectChain. 2014-01-29 Brian Matherly * src/modules/vid.stab/common.c, src/modules/vid.stab/common.h, src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_vidstab.cpp, src/modules/vid.stab/filter_vidstab.yml: Save vidstab results to file. Rather than save vidstab results (which can get quite large) in the properties, save them in a separate file. Also redirect vid.stab log messages through the MLT logging system (sort of). 2014-01-28 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml, src/modules/xml/producer_xml.c, src/modules/xml/producer_xml.yml: Add xml_retain property support to xml module. This is used to serialize and deserialize extra services that are not part of the lastmost service's graph. This is useful, for example, to save and load a media bin as a playlist in addition to the main multitrack graph. Or, it can be used for compound documents. 2014-01-21 Steinar H. Gunderson * src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: Call invalidate_pixel_data() after frame rendering. This helps the input return its values back to the ResourcePool, which means we won't be allocating ever more textures as we get more clips on the timeline. 2014-01-17 Steinar H. Gunderson * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: Use the new ResourcePool Movit functionality. 2014-01-25 Steinar H. Gunderson * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h, src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_crop.cpp, .../opengl/filter_movit_deconvolution_sharpen.cpp, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_glow.cpp, src/modules/opengl/filter_movit_lift_gamma_gain.cpp, src/modules/opengl/filter_movit_mirror.cpp, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_rect.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_movit_white_balance.cpp: Propertly refcount the GlslManager. Makes sure it is not deleted before all the associated services connected to it are. 2014-01-20 Dan Dennedy * presets/consumer/avformat/dv_ntsc/DV, presets/consumer/avformat/dv_ntsc/DVCPRO50, presets/consumer/avformat/dv_ntsc_wide/DV, presets/consumer/avformat/dv_ntsc_wide/DVCPRO50, presets/consumer/avformat/dv_pal/DV, presets/consumer/avformat/dv_pal/DVCPRO50, presets/consumer/avformat/dv_pal_wide/DV, presets/consumer/avformat/dv_pal_wide/DVCPRO50: Add default format and extension to all DV presets. * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Fix a few problems with YCbCr colorspace conversion. In avformat producer on libav (and FFmpeg < v2.1) conversion from RGB to YCbCr would not use the destination colorspace because sws_getColorspaceDetails() fails. Switch to calling only sws_setColorspaceDetails(). In full luma yuvj420p->mlt_image_yuv420p conversion, the luma range was always scaled down to MPEG range. The swscale implementation does not let one override the range as the conversion routines are initialized at the time a swscale context is allocated and initialized. Any changes in sws_setColorspaceChanges() are mute. In RGB->YCbCr conversion, the existing (source) colorspace was used instead of the profile colorspace. Also, we need to set the new colorspace as a property of the frame. 2014-01-19 Brian Matherly * src/modules/vid.stab/common.c, src/modules/vid.stab/common.h, src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_deshake.yml, src/modules/vid.stab/filter_vidstab.cpp, src/modules/vid.stab/filter_vidstab.yml: Updates to vid.stab module. * Correct some metadata * Remove "reset" property by making deshake properties mutable. * Implement "reload" for vidstab for reloading results. * Misc. changes for MLT consistency. 2014-01-17 Brian Matherly * src/framework/mlt_frame.h, src/modules/core/filter_imageconvert.c, src/modules/vid.stab/Makefile, src/modules/vid.stab/common.c, src/modules/vid.stab/common.h, src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_vidstab.cpp: Add support for more image formats to vid.stab 2014-01-16 Steinar H. Gunderson * src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_deconvolution_sharpen.cpp: Let Movit effects supply their own fingerprint. This allows effects to signal that some sort of change means the chain needs to be regenerated. In particular, this unbreaks changing the matrix_size parameter of DeconvolutionSharpenEffect; if you change it, the entire chain will now be regenerated, instead of getting an assertion failure. * src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_crop.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp: Stop special-casing the disable parameter for setting. There are more parameters then just 'disable' that should be set before chain finalization; in particular, DeconvolutionSharpenEffect compiles the matrix size into the shader. Instead, just set all the parameters once right after the chain has been built, which includes the disable parameter. 2014-01-15 Brian Matherly * src/framework/mlt_animation.c, src/tests/test_properties/test_properties.cpp: Fix animation serialization when length is not specified 2014-01-11 Steinar H. Gunderson * src/modules/opengl/Makefile, src/modules/opengl/fbo_input.cpp, src/modules/opengl/fbo_input.h, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h: Remove now unused FBOInput. 2014-01-01 Steinar H. Gunderson * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h, src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_crop.cpp, .../opengl/filter_movit_deconvolution_sharpen.cpp, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_glow.cpp, .../opengl/filter_movit_lift_gamma_gain.cpp, src/modules/opengl/filter_movit_mirror.cpp, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_movit_white_balance.cpp, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h, src/modules/opengl/optional_effect.h, src/modules/opengl/transition_movit_mix.cpp, src/modules/opengl/transition_movit_overlay.cpp: Change how the Movit chain is built. * Build the chain in GlslManager. This allows us to get rid of effects that don't actually do anything (like all the normalizers in the common case); in Movit, they tend to burn a lot of memory bandwidth. We solve this by a new OptionalEffect template, that can rewrite itself out of the graph if it sees it is a no-op. We need to recreate the chain from scratch if this status should change (e.g. the input resolution changed from one frame to the next, and we thus suddenly need resizing after all), so we keep a "fingerprint" string that contains all the unique IDs of the services in use, as well as their disabled status, and compare against this frame. Building the chain in one piece also opens up for transitions to be more efficient; they are now built as part of one big Movit chain, instead of bouncing to an 8-bit sRGB buffer and back. * Change the mlt_glsl type. Now, the mlt_glsl image type has a defined value, which is the mlt_service pointer. Each filter is responsible for storing this input service. This, together with the mlt_frame, enables us to actually build the Movit chain based on the MLT relations, instead of just relying in the order in which they are called and assuming everything has a single input. As a special case, the value (mlt_service) -1 (which should never be a valid pointer) means that we read the information from an input rather than an effect. In this case, we take a copy of the pixel data we get in (since it will soon be garbage collected), store it in an MltInput and then store that MltInput for later use. This could probably be further simplified in the future to get completely rid of MltInput and just use the regular FlatInput/YCbCrInput instead. This also requires us to change so that the chain is built and finalized at the _end_ of the conversion steps (where it's logically needed), instead of at the beginning as before. The beginning (conversion from * -> mlt_glsl) now only stores the input as described below. * Change Effect and EffectChain storage. This changes the storage of Movit stuff as follows: - The EffectChain (along with some associated information to be able to more easily locate the services and Effect pointers; together, called a GlslChain) is now stored on the output service, not on the input producer. This allows us to have multiple EffectChains floating around. - The Effect pointers no longer live permanently on the MLT graph, since each MLT service can have more than one Effect. Instead, they live temporarily on the frame (because the frame is not shared between threads, giving us a poor man's version of thread-local storage), until they reach the point where we decide if we need to rebuild the EffectChain or not. At this point, they are either made part of the chain (and owned by it), or disposed as unneeded. - The MltInput also lives on the frame. (If we have multiple inputs, we also have multiple frames.) As mentioned above, its use is signaled by an mlt_service of -1. * Change how Movit parameter setting works. Services no longer set parameters directly on the Movit filters, since they cannot know before the graph construction time whether the correct destination is the newly created Effect, or a similar one in the EffectChain. Instead, they set special properties (movit.parms..[]), and then the convert filter uses these to set Movit parameters on the right Effects. 2014-01-13 Steinar H. Gunderson * src/modules/opengl/Makefile, ...nvolution_sharpen.cpp => filter_movit_deconvolution_sharpen.cpp}, ...nvolution_sharpen.yml => filter_movit_deconvolution_sharpen.yml}, ...{filter_lift_gamma_gain.cpp => filter_movit_lift_gamma_gain.cpp}, ...{filter_lift_gamma_gain.yml => filter_movit_lift_gamma_gain.yml}, .../{filter_white_balance.cpp => filter_movit_white_balance.cpp}, .../{filter_white_balance.yml => filter_movit_white_balance.yml}: Rename some Movit filter filenames, for consistency. 2014-01-12 Steinar H. Gunderson * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h: Add back automatic cleanup of OpenGL fences. Instead of having the client do deletion of fences, work around the problem with missing contexts by adding them to a list in the GlslManager, which then it garbage-collected before creating more fences. 2014-01-08 Steinar H. Gunderson * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h: Replace glFinish with OpenGL fences. The glFinish after rendering to a texture serves two purposes: First, and maybe most importantly, it makes sure that if we send the texture ID to another thread and try to draw it there, it is actually valid in that context. (If not, the command to allocate it could still be stuck in the queue, or the command to draw the quad to the screen could be queued before the command to actually render the image to the texture.) Second, it makes sure we don't overwhelm the GPU with rendering commands, especially in the readahead thread. GPUs have a long pipeline, and our commands buffers are typically very short (we render only one or a few quads per frame), which means that we could queue so much rendering that we couldn't actually get to display the frames, or do compositing and other normal UI tasks. (GPUs are not all that good at scheduling.) However, glFinish() also has an unwanted side effect: Since the CPU waits for the GPU to finish, it means it cannot do anything useful in that period; in particular, it cannot start decoding input video for the next frame, which is very frequently a win. Thus, we replace glFinish() with fences: One that we store on the frame and that the client can wait for, and one that we wait for ourselves before we render the next frame. The first fulfills purpose #1 above (although a client that doesn't render in a different thread can just ignore it), while the second fulfills purpose #2. #2 does reduce the possible pipelining somewhat (compared to not having any fence at all), but it seems that the actual performance lost is very small in practice. In any case, this is markedly faster than glFinish -- on my Intel HD 3000, it increases GPU utilization from ~40% to over 80% in a typical transition. Note that this is an API change; a client that wants to send the OpenGL texture number on to a different thread for display, will now need to wait for the fence before it can actually draw using it. * src/modules/avformat/producer_avformat.c, src/modules/opengl/filter_movit_convert.cpp: Make the Movit converter use the correct color primaries. We need to distinguish between the YUV primaries and the color space; for instance, my camera outputs Rec. 601/525 YUV but uses Rec. 709 color primaries. Also fix so that we read the correct full_luma flag, not just check force_full_luma. (Again, my camera outputs this.) Movit doesn't support all the exotic color spaces ffmpeg/libav does, but this should cover most of the common ones. 2014-01-11 Brian Matherly * src/modules/vid.stab/common.h, src/modules/vid.stab/factory.c, src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_vidstab.cpp: File header consistency * src/modules/vid.stab/Makefile, src/modules/vid.stab/common.h, src/modules/vid.stab/factory.c, src/modules/vid.stab/filter_detect.cpp, src/modules/vid.stab/filter_detect.yml, src/modules/vid.stab/filter_transform.cpp, src/modules/vid.stab/filter_transform.yml, src/modules/vid.stab/filter_vidstab.cpp, src/modules/vid.stab/filter_vidstab.yml: Merge filter_detect and filter_transform into filter_vidstab 2014-01-09 Jakub Ksiezniak * src/modules/vid.stab/Makefile, src/modules/vid.stab/common.h, src/modules/vid.stab/configure, src/modules/vid.stab/factory.c, src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_deshake.yml, src/modules/vid.stab/filter_detect.cpp, src/modules/vid.stab/filter_detect.yml, src/modules/vid.stab/filter_transform.cpp, src/modules/vid.stab/filter_transform.yml, src/modules/vid.stab/filter_vidstab.cpp, src/modules/vid.stab/filter_vidstab.yml: Added a fourth filter, that combines both detect and transform passes. * Increased a default smoothing factor, according to the original vid.stab default settings. * Added a deshake data clear when seeking is performed. * Added a version check in configure script. 2014-01-07 Jakub Ksiezniak * src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_deshake.yml, src/modules/vid.stab/filter_detect.cpp, src/modules/vid.stab/filter_detect.yml, src/modules/vid.stab/filter_transform.cpp, src/modules/vid.stab/filter_transform.yml: Update module to work with the latest vid.stab version 0.98. * Added a new property "zoomspeed" for adaptive zooming feature. * Removed a no longer used "sharpen" property. 2013-12-31 Jakub Ksiezniak * src/modules/vid.stab/Makefile, src/modules/vid.stab/common.h, src/modules/vid.stab/configure, src/modules/vid.stab/factory.c, src/modules/vid.stab/filter_deshake.cpp, src/modules/vid.stab/filter_deshake.yml, src/modules/vid.stab/filter_detect.cpp, src/modules/vid.stab/filter_detect.yml, src/modules/vid.stab/filter_transform.cpp, src/modules/vid.stab/filter_transform.yml: Created a new module to support vid.stab library. 2014-01-05 Steinar H. Gunderson * src/modules/opengl/filter_deconvolution_sharpen.cpp, src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_glsl_manager.h, src/modules/opengl/filter_lift_gamma_gain.cpp, src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_crop.cpp, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_glow.cpp, src/modules/opengl/filter_movit_mirror.cpp, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_white_balance.cpp: Change get_effect/add_effect to take in mlt_service. We want to be able to set effects on transitions as well, but in MLT, transitions are not filters, only services. 2014-01-03 Steinar H. Gunderson * src/modules/opengl/filter_deconvolution_sharpen.cpp, src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/{glsl_manager.h => filter_glsl_manager.h}, src/modules/opengl/filter_lift_gamma_gain.cpp, src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_crop.cpp, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_glow.cpp, src/modules/opengl/filter_movit_mirror.cpp, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_rect.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_white_balance.cpp, src/modules/opengl/transition_movit_mix.cpp, src/modules/opengl/transition_movit_overlay.cpp: Rename glsl_manager.h to filter_glsl_manager.h, to be consistent with the .cpp file. 2014-01-04 Dan Dennedy * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h, src/mlt++/MltPlaylist.cpp, src/mlt++/MltPlaylist.h: Change param name "length" to "out" in mlt_playlist_insert_blank. 2014-01-01 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: Add consumer-thread-create and consumer-thread-join events. If an app listens to these, it can override the implementation of thread creation and joining. Otherwise, if no listeners, it falls back to pthread_create() and pthread_join() as usual. At this time, only the base mlt_consumer uses this for real_time=1 or -1 only. * src/framework/mlt_events.c, src/framework/mlt_events.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h: Let mlt_events_fire() return the number of listeners. Callers can determine if there is a listener that overrides some behavior. 2013-12-31 Dan Dennedy * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/glsl_manager.h: Prevent sharing FBOs between contexts. Based on patch by Steinar Gunderson. 2013-12-30 Dan Dennedy * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/glsl_manager.h, src/modules/opengl/transition_movit_mix.cpp, src/modules/opengl/transition_movit_overlay.cpp: Refactor movit.convert, movit.mix, and movit.overlay. To use new methods on GlslManager: render_frame_texture() and render_frame_rgba(). The latter routine was changed to use GL_BGRA in glReadPixels() to improve performance on more OpenGL implementation (per Steinar Gunderson's recommendation). * Makefile, src/examples/Makefile, src/melt/Makefile, src/modules/avformat/Makefile, src/modules/frei0r/Makefile, src/modules/gtk2/Makefile, src/modules/jackrack/Makefile, src/modules/opengl/Makefile, src/modules/resample/Makefile, src/modules/rtaudio/Makefile, src/modules/sdl/Makefile, src/modules/vorbis/Makefile, src/modules/xml/Makefile, src/swig/Makefile: Convert backtick to $(shell) in Makefiles. * src/modules/gtk2/Makefile, src/modules/gtk2/producer_pango.c: Fix build on Freetype 2.5. Reported by Patrick Matthhai/Debian. 2013-12-23 Dan Dennedy * src/framework/mlt_transition.c, src/framework/mlt_transition.h: Add disable property to mlt_transition. * src/framework/mlt_filter.h, src/framework/mlt_service.h: Move "disable" property doc from service to filter. 2013-12-20 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qt/producer_qimage.c, src/modules/qt/qimage_wrapper.cpp: Fix concurrency bug in image producers. Reported by Michael Marina. 2013-12-02 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/mlt++.vers: Add mlt_properties_frames_to_time() and mlt_properties_time_to_frames(). Handy conversion functions for apps. 2013-12-17 Brian Matherly * docs/install.txt, src/modules/qt/Makefile, src/modules/qt/configure, src/modules/qt/factory.c: Finalize qimage->qt rename * src/modules/{qimage => qt}/Makefile, src/modules/{qimage => qt}/configure, src/modules/{qimage => qt}/consumer_qglsl.cpp, src/modules/{qimage => qt}/factory.c, src/modules/{qimage => qt}/gpl, src/modules/{qimage => qt}/kdenlivetitle_wrapper.cpp, src/modules/{qimage => qt}/kdenlivetitle_wrapper.h, src/modules/{qimage => qt}/producer_kdenlivetitle.c, src/modules/{qimage => qt}/producer_kdenlivetitle.yml, src/modules/{qimage => qt}/producer_qimage.c, src/modules/{qimage => qt}/producer_qimage.yml, src/modules/{qimage => qt}/producer_qtext.cpp, src/modules/{qimage => qt}/producer_qtext.yml, src/modules/{qimage => qt}/qimage_wrapper.cpp, src/modules/{qimage => qt}/qimage_wrapper.h, src/modules/{qimage => qt}/transition_vqm.cpp, src/modules/{qimage => qt}/transition_vqm.yml: Rename 'qimage' module to 'qt' 2013-12-04 Brian Matherly * src/modules/avsync/Makefile, src/modules/avsync/factory.c, src/modules/gtk2/Makefile, src/modules/gtk2/factory.c, src/modules/plus/Makefile, src/modules/{avsync => plus}/consumer_blipflash.c, .../{avsync => plus}/consumer_blipflash.yml, src/modules/plus/factory.c, src/modules/{gtk2 => plus}/filter_dynamictext.c, src/modules/{gtk2 => plus}/filter_dynamictext.yml, src/modules/{avsync => plus}/producer_blipflash.c, .../{avsync => plus}/producer_blipflash.yml, src/modules/{gtk2 => plus}/producer_count.c, src/modules/{gtk2 => plus}/producer_count.yml: Move blipflash, dynamictext and count into plus module 2013-11-28 Brian Matherly * src/modules/qimage/Makefile, src/modules/qimage/factory.c, src/modules/qimage/producer_qtext.cpp, src/modules/qimage/producer_qtext.yml: Initial implementation of producer_qtext 2013-12-03 Dan Dennedy * presets/consumer/avformat/lossless/FFV1, presets/consumer/avformat/lossless/HuffYUV: Fix matroska avformat presets. 2013-11-07 Dan Dennedy * src/modules/videostab/filter_videostab2.c, src/modules/videostab/filter_videostab2.yml: Add a refresh property to videostab2 filter. 2013-11-05 Dan Dennedy * src/modules/sox/filter_sox.c, src/modules/sox/filter_sox.yml: Add analysis_level property to sox filter. For normalization analysis when not use_peak, sets the target amplitude. 2013-10-27 Dan Dennedy * src/modules/videostab/filter_videostab2.c, src/modules/videostab/transform_image.c: Fix videostab2 interpolation. This filter uses RGB mode, for which bicubic is broken. vid.stab still to this day uses bilinear with packed pixel formats. In order to fix bilinear, needed to remove extra calls to floor function. 2013-10-16 Dan Dennedy * src/framework/metaschema.yaml, src/modules/core/filter_channelcopy.yml: Add 'boolean' and 'argument' to service metadata schema. 2013-09-17 Dan Dennedy * src/mlt++/MltTokeniser.cpp, src/mlt++/MltTokeniser.h: Fix compile warnings on string literal for default parameter. 2013-09-12 Dan Dennedy * src/modules/xml/factory.c, src/modules/xml/producer_xml-nogl.yml, src/modules/xml/producer_xml-string.yml, src/modules/xml/producer_xml.c: Add producer xml-nogl to disable auto-qglsl creation. Sometimes you want to load MLT XML while ignoring the presence of OpenGL filters and transitions. 2013-08-24 Dan Dennedy * src/modules/qimage/configure, src/modules/qimage/consumer_qglsl.cpp: Fix qglsl on Qt 5 for OS X. * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/glsl_manager.h: Add "close glsl" event to glsl.manager service. Qt 5 apps (and possibly others) must use this because the OpenGL context for rendering needs to be created and destroyed on the thread on which it is actually used. This should be fired on the glsl.manager filter instance inside of a consumer-thread-stopped mlt_event listener. * src/modules/qimage/Makefile, src/modules/qimage/consumer_qglsl.cpp: Fix qglsl consumer for Qt 5. 2013-08-18 Dan Dennedy * presets/consumer/avformat/MPEG-4, presets/consumer/avformat/MPEG-4-ASP, presets/consumer/avformat/x264-medium, presets/consumer/avformat/x264-medium-baseline, presets/consumer/avformat/x264-medium-main, presets/consumer/avformat/x264-medium-pass1: Add faststart muxing flag to MP4 presets. 2013-08-07 Dan Dennedy * src/modules/qimage/Makefile, src/modules/qimage/configure, src/modules/qimage/consumer_qglsl.cpp, src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/transition_vqm.cpp: Add support for Qt 5, drop support for Qt 3 and KDE 3. * src/framework/mlt_frame.c, src/modules/core/consumer_multi.c: Move the aspect ratio for multi consumer from mlt_frame. 2013-06-21 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: Add consumer-stopping event fired before joining threads. 2013-06-19 Dan Dennedy * configure, src/framework/mlt_version.h: Set to interim version 0.9.1. 2013-06-04 Dan Dennedy * src/swig/ruby/build, src/swig/ruby/metadata.rb: Add exception handling around YAML parsing in metadata.rb. 2013-06-02 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 0.9.0. * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/glsl_manager.h: Fix tiny memory leak in GlslManager (coverity-1026795). * src/framework/mlt.h, src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/framework/mlt_geometry.c, src/framework/mlt_geometry.h: Indicate mlt_geometry is deprecated in API docs. 2013-06-01 Dan Dennedy * src/modules/opengl/filter_movit_rect.cpp, src/modules/opengl/filter_movit_rect.yml, src/modules/opengl/filter_movit_resize.cpp: Convert movit.rect to mlt_rect. 2013-05-31 Dan Dennedy * .../opengl/filter_deconvolution_sharpen.cpp, .../opengl/filter_deconvolution_sharpen.yml, src/modules/opengl/filter_lift_gamma_gain.cpp, src/modules/opengl/filter_lift_gamma_gain.yml, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_diffusion.yml, src/modules/opengl/filter_movit_glow.cpp, src/modules/opengl/filter_movit_glow.yml, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_opacity.yml, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_saturation.yml, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_movit_vignette.yml, src/modules/opengl/filter_white_balance.cpp, src/modules/opengl/filter_white_balance.yml, src/modules/opengl/transition_movit_mix.cpp, src/modules/opengl/transition_movit_mix.yml: Add property animation to the other movit services. * presets/filter/movit.blur/blur_in, presets/filter/movit.blur/blur_in_out, presets/filter/movit.blur/blur_out: Add animated movit.blur presets. * src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_blur.yml: Add property animation to movit.blur filter. * src/framework/mlt_properties.c, src/framework/mlt_property.c: Compile fixes for Windows after merging animation branch. * src/framework/mlt_properties.c, src/mlt++/mlt++.vers: Compile fixes for Linux after merging animation branch. 2013-05-30 Dan Dennedy * src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/framework/mlt_types.h: Various documentation and copyright fixes and updates. * src/framework/mlt_animation.c, src/framework/mlt_animation.h: Document the new mlt_animation API. * src/framework/mlt_properties.c, src/framework/mlt_property.c, src/framework/mlt_types.h: Document the property animation, rect, and color additions. * src/framework/mlt_animation.h, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/framework/mlt_types.h: Add mlt_properties_get_animation(); it might come in handy. * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/mlt++.vers, src/tests/test_properties/test_properties.cpp: Add mlt_properties_set_color(). 2013-05-29 Dan Dennedy * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/mlt++/MltProperties.cpp, src/tests/test_properties/test_properties.cpp: Reorder some property anim parameters for consistency. * src/framework/Makefile, src/framework/mlt_animation.c, src/framework/mlt_profile.c, src/framework/mlt_property.c, src/mlt++/MltProperties.h, src/tests/test_properties/test_properties.cpp: Make animation length optional. It is only really needed when using negative time values. With some fixes for parsing negatives in time code/clock values. 2013-05-27 Dan Dennedy * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_types.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/tests/test_properties/test_properties.cpp: Add mlt_color and mlt_properties_get_color(). * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/tests/test_properties/test_properties.cpp: Add mlt_properties_anim_set/get() for string. * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/tests/test_properties/test_properties.cpp: Add mlt_properties_anim_set/get_double(). * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/tests/test_properties/test_properties.cpp: Rename functions with _pos to anim_. * src/framework/mlt_animation.c, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/tests/test_properties/test_properties.cpp: Add mlt_properties_set/get_rect_pos for rect animation. 2013-05-26 Dan Dennedy * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/framework/mlt_types.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/tests/test_properties/test_properties.cpp: Add mlt_rect and mlt_properties_set/get_rect. 2013-05-22 Dan Dennedy * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/tests/test_properties/test_properties.cpp: Add mlt_properties_set/get_int_pos and Properties::set/get_int. 2013-05-19 Dan Dennedy * src/framework/mlt_property.c, src/tests/test_properties/test_properties.cpp: Interpret % after numeric string. * src/framework/mlt_property.c, src/framework/mlt_property.h, src/framework/mlt_types.h, src/tests/test_properties/test_properties.cpp: Add mlt_property_set_double_pos() and mlt_property_set_int_pos(). 2013-05-18 Dan Dennedy * src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/framework/mlt_types.h, src/tests/test_properties/test_properties.cpp: Add Catmull-Rom spline smooth animation interpolation. 2013-05-17 Dan Dennedy * src/framework/mlt_property.c, src/framework/mlt_property.h, src/tests/test_properties/test_properties.cpp: Add mlt_property_get_double_pos() and mlt_property_get_int_pos(). 2013-05-16 Dan Dennedy * src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/framework/mlt_property.c, src/tests/test_properties/test_properties.cpp: Add support for discrete animation including strings. * src/framework/Makefile, src/framework/mlt_animation.c, src/framework/mlt_animation.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/tests/test_properties/test_properties.cpp: Add mlt_animation and mlt_property_interpolate(). mlt_property_interpolate() only works on a scalar double property at this time. 2013-05-14 Maksym Veremeyenko * src/framework/mlt_consumer.c, src/framework/mlt_frame.c, src/framework/mlt_types.h, src/modules/avformat/producer_avformat.c, src/modules/avsync/consumer_blipflash.c, src/modules/core/filter_luma.c, src/modules/dgraft/filter_telecide.c, src/modules/gtk2/producer_count.c, src/modules/kdenlive/producer_framebuffer.c, src/modules/xine/filter_deinterlace.c: make mlt_position type double 2013-05-22 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Add exit_on_disconnect property to avformat producer. 2013-05-21 Dan Dennedy * src/modules/frei0r/Makefile, src/modules/frei0r/factory.c, src/modules/frei0r/{param_name_map.yml => param_name_map.yaml}: Rename frei0r param_name_map filename and install it. * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Add reconnect property to avformat producer. 2013-05-07 Dan Dennedy * src/modules/core/producer_melt.c, src/modules/rtaudio/RtAudio.cpp, src/modules/xine/deinterlace.c, src/modules/xml/producer_xml.c: Fix some compile warnings raised by clang. * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: Make a bunch of fields of mlt_consumer_s truly private. 2013-05-03 Dan Dennedy * src/framework/mlt_properties.c, src/tests/test_properties/test_properties.cpp: Add more properties unit tests. 2013-05-02 Dan Dennedy * src/tests/test_properties/test_properties.cpp, src/tests/test_properties/test_properties.pro: Add more unit tests for mlt_property. 2013-04-30 Dan Dennedy * src/tests/common.pri, src/tests/test_properties/test_properties.cpp, src/tests/test_properties/test_properties.pro, src/tests/test_repository/test_repository.cpp, src/tests/test_repository/test_repository.pro, src/tests/tests.pro: Add the start of a unit test suite using QtTest. * src/tests/Makefile, src/tests/README, src/tests/charlie.c, src/tests/dan.c, src/tests/dissolve.c, src/tests/hello.c, src/tests/io.c, src/tests/io.h, src/tests/luma.c, src/tests/pango.c, src/tests/pixbuf.c, src/tests/test.png: Remove old files in src/tests to make way for new ones. 2013-04-27 Dan Dennedy * src/framework/mlt_consumer.c, src/modules/sdl/consumer_sdl_preview.c: Fix race condition in mlt_consumer_stop(). 2013-04-25 Dan Dennedy * configure, src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c: Make mlt_consumer_purge() more thorough. (SF-187) This is applied to SDL consumers only at the moment since that is what most applications are using. Needs to be extended to other consumers. 2013-04-12 Dan Dennedy * configure, src/framework/mlt_version.h: Set interim version 0.8.9. 2013-04-07 Brian Matherly * src/modules/gtk2/producer_count.yml, src/modules/oldfilm/filter_dust.yml, src/modules/oldfilm/filter_grain.yml, src/modules/oldfilm/filter_oldfilm.yml: Misc YAML fixes 2013-04-06 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_service.c, src/framework/mlt_service.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/mlt++.vers: Add mlt_service_move_filter() and Mlt::Service::move_filter(). 2013-04-04 Brian Matherly * src/modules/gtk2/Makefile, src/modules/gtk2/factory.c, src/modules/gtk2/producer_count.c, src/modules/gtk2/producer_count.yml: Add new count producer 2013-03-21 Dan Dennedy * src/modules/avformat/factory.c, src/modules/avformat/filter_avresample.c: Restore avresample filter when build against FFmpeg. This provides a LGPL audio resampler, but it is using deprecated APIs. Support for swresample or libav avresampler is another project. 2013-03-20 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Add support for libavformat and libavcodec major version 55. 2013-03-18 Dan Dennedy * src/modules/avformat/Makefile, src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_avdeinterlace.c, src/modules/avformat/filter_avresample.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c: Drop support for FFmpeg v0.5 and v0.6 and require swscale. This is cleanup work to make way for more version handling to handle current git master of libav that removes deprecated APIs. 2013-03-23 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/core/consumer_multi.c, src/modules/core/producer_loader.c, src/modules/opengl/filter_movit_convert.cpp: Make the arg to avcolor_space filter a pointer. Instead of passing an int cast as pointer. 2013-03-21 Dan Dennedy * src/modules/frei0r/factory.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/param_name_map.yml: Add a frei0r param name mapping system. For apps that set frei0r parameters by their name, this provides backwards compatibility when a frei0r parameter name changes. 2013-03-17 Dan Dennedy * src/modules/frei0r/factory.c, src/modules/frei0r/frei0r_helper.c: Switch to indexed propery names for frei0r params. Properties supplied by frei0r param name are supported for backwards compatibility. 2013-03-13 Dan Dennedy * src/modules/core/loader.dict, src/modules/core/producer_loader.c: Tell loader how to use WebVfx. With my latest (merged) patches to WebVfx, one can load plain old HTML and QML files. They do not need webvfx script to initialize rendering - with some caveats about the meaning of document-loaded. However, without this loader change, plain resources need to prefaced with "plain:". The loader producer can now do that and instead "webvfx:" is needed to tell webvfx to wait for script in the content to call WebVfx.renderReady(true) (unless you use "webvfx:plain:..."). Also, WebVfx can also now load HTML over HTTP. This is especially handy if your content is updating itself with data from a web service. Otherwise, you will run into cross-site-scripting errors. 2013-03-07 Dan Dennedy * src/modules/opengl/filter_movit_crop.cpp, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/transition_movit_mix.cpp, src/modules/opengl/transition_movit_overlay.cpp: Add locking to opengl services for thread protection. * src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/glsl_manager.h: Make opengl filters support attach, detach, and disable. 2013-03-03 Dan Dennedy * src/framework/mlt.vers, src/framework/mlt_service.c, src/framework/mlt_service.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/mlt++.vers: Add mlt_service_filter_count and Mlt::Service::filter_count. 2013-02-25 Brian Matherly * src/modules/avsync/Makefile, src/modules/avsync/consumer_blipflash.c, src/modules/avsync/consumer_blipflash.yml, src/modules/avsync/factory.c, src/modules/avsync/producer_blipflash.c, src/modules/avsync/producer_blipflash.yml: Add new avsync module 2013-02-24 Dan Dennedy * src/framework/mlt_frame.c, src/modules/core/consumer_multi.c: Let qglsl multi consumer work with more consumers. Works with sdl and decklink consumers. * src/framework/mlt_consumer.c, src/modules/core/producer_loader.c: Fix crash on missing NULL at end of mlt_events_fire(). 2013-02-23 Dan Dennedy * src/melt/melt.c, src/modules/opengl/mlt_movit_input.cpp, src/modules/qimage/consumer_qglsl.cpp: Fix OpenGL context cleanup on Windows. Would crash at end of melt with qglsl. * src/modules/opengl/filter_glsl_manager.cpp, src/modules/qimage/consumer_qglsl.cpp: Some minor logging cleanup. 2013-02-20 Dan Dennedy * src/modules/opengl/consumer_xgl.c, src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_movit_convert.cpp: Cleanup some logging from work in opengl branch. * src/melt/melt.c, src/modules/xml/producer_xml.c: Let melt and xml producer use qglsl consumer (opengl branch). * src/modules/qimage/Makefile, src/modules/qimage/configure, src/modules/qimage/consumer_qglsl.cpp, src/modules/qimage/factory.c: Add qglsl multi consumer (opengl branch). * src/framework/mlt_profile.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/core/consumer_multi.c, src/modules/core/filter_crop.c, src/modules/core/filter_fieldorder.c, src/modules/core/filter_imageconvert.c, src/modules/core/loader.ini, src/modules/core/producer_loader.c: Let loader producer use new GLSL normalizing filters (opengl branch). * src/modules/opengl/Makefile, src/modules/opengl/configure, src/modules/opengl/consumer_xgl.c, src/modules/opengl/factory.c, src/modules/opengl/fbo_input.cpp, src/modules/opengl/fbo_input.h, .../opengl/filter_deconvolution_sharpen.cpp, .../opengl/filter_deconvolution_sharpen.yml, src/modules/opengl/filter_glsl_manager.cpp, src/modules/opengl/filter_lift_gamma_gain.cpp, src/modules/opengl/filter_lift_gamma_gain.yml, src/modules/opengl/filter_movit_blur.cpp, src/modules/opengl/filter_movit_blur.yml, src/modules/opengl/filter_movit_convert.cpp, src/modules/opengl/filter_movit_crop.cpp, src/modules/opengl/filter_movit_diffusion.cpp, src/modules/opengl/filter_movit_diffusion.yml, src/modules/opengl/filter_movit_glow.cpp, src/modules/opengl/filter_movit_glow.yml, src/modules/opengl/filter_movit_mirror.cpp, src/modules/opengl/filter_movit_mirror.yml, src/modules/opengl/filter_movit_opacity.cpp, src/modules/opengl/filter_movit_opacity.yml, src/modules/opengl/filter_movit_rect.cpp, src/modules/opengl/filter_movit_rect.yml, src/modules/opengl/filter_movit_resample.cpp, src/modules/opengl/filter_movit_resize.cpp, src/modules/opengl/filter_movit_saturation.cpp, src/modules/opengl/filter_movit_saturation.yml, src/modules/opengl/filter_movit_vignette.cpp, src/modules/opengl/filter_movit_vignette.yml, src/modules/opengl/filter_white_balance.cpp, src/modules/opengl/filter_white_balance.yml, src/modules/opengl/glsl_manager.h, src/modules/opengl/mlt_flip_effect.h, src/modules/opengl/mlt_movit_input.cpp, src/modules/opengl/mlt_movit_input.h, src/modules/opengl/transition_movit_mix.cpp, src/modules/opengl/transition_movit_mix.yml, src/modules/opengl/transition_movit_overlay.cpp, src/modules/opengl/transition_movit_overlay.yml: Add the new opengl module (opengl branch). * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: Add consumer-thread-started and -stopped events (opengl branch). * src/framework/mlt_frame.c, src/framework/mlt_types.h: Add mlt_image_glsl and _glsl_texture (opengl branch). * src/mlt++/MltDeque.cpp, src/mlt++/MltDeque.h, src/mlt++/mlt++.vers: Add Mlt::Deque::peek() (opengl branch). 2013-02-15 Brian Matherly * src/modules/jackrack/Makefile, src/modules/jackrack/factory.c, src/modules/jackrack/plugin_desc.c, src/modules/jackrack/plugin_desc.h, src/modules/jackrack/plugin_mgr.c, src/modules/jackrack/process.c, src/modules/jackrack/producer_ladspa.c, src/modules/jackrack/producer_ladspa.yml: Add ladspa producer 2013-02-13 Cristian Morales Vega * src/framework/Makefile, src/mlt++/Makefile: Fix OSX buld which broke when adding Linux symbols versioning 2013-02-10 Dan Dennedy * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_still.c: Remove local references to SDL_Surface. (SF-186) Why bother when SDL_GetVideoSurface() is available? 2013-02-07 Cristian Morales Vega * src/framework/Makefile, src/framework/mlt.vers, src/mlt++/Makefile, src/mlt++/config.h, src/mlt++/mlt++.vers: Use symbol versioning 2013-02-08 Cristian Morales Vega * Makefile, configure, src/melt/Makefile: Make the versioning opt-in 2013-02-07 Cristian Morales Vega * Makefile, configure, profiles/Makefile, src/framework/Makefile, src/framework/mlt_factory.c, src/melt/Makefile, src/melt/configure, src/modules/Makefile, src/modules/avformat/Makefile, src/modules/core/Makefile, src/modules/decklink/Makefile, src/modules/dgraft/Makefile, src/modules/dv/Makefile, src/modules/effectv/Makefile, src/modules/feeds/Makefile, src/modules/frei0r/Makefile, src/modules/gtk2/Makefile, src/modules/jackrack/Makefile, src/modules/kdenlive/Makefile, src/modules/kino/Makefile, src/modules/linsys/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/rotoscoping/Makefile, src/modules/rtaudio/Makefile, src/modules/sdl/Makefile, src/modules/sox/Makefile, src/modules/swfdec/Makefile, src/modules/videostab/Makefile, src/modules/vmfx/Makefile, src/modules/vorbis/Makefile, src/modules/xine/Makefile, src/modules/xml/Makefile: Version modules and data directories, and melt Allow the "extras" of binary incompatible versions of MLT to be installed simultaneously. I don't like the idea of versioning the melt binary. But kdenlive is the main user of MLT and it expects the same formats support from both the libmltX it is linked to, and the melt binary it uses to do the actual work. 2013-01-20 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h, src/melt/melt.c: Set version to 0.8.8. 2012-12-31 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Fix build against FFmepg 0.5 and 0.6. 2012-12-26 Niv Sardi * src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.yml: pixbuf producer: loop option to loop sequence selectively 2012-12-22 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_frame.c, src/framework/mlt_types.h, src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c, src/modules/core/filter_audiochannels.c, src/modules/core/filter_audioconvert.c, src/modules/core/filter_channelcopy.c, src/modules/core/filter_mono.c: Add mlt_audio_u8 (sourceforce-182). It should support planar libavutil AV_SAMPLE_FMT_U8P, but it is untested due to lacking a sample. 2012-12-12 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/resample/filter_resample.c: Fix possible divide by zero exceptions. 2012-11-27 Dan Dennedy * src/modules/avformat/factory.c, src/modules/avformat/filter_avresample.c, src/modules/avformat/producer_avformat.c: Fix decoding audio with planar formats. * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: Fix mlt_profile to DeckLink DisplayMode matching. 2012-11-17 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: Fix crash on invalid image sequence. * configure, src/framework/mlt_version.h: set to interim version 0.8.7 * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/videostab/stab/klt/error.c: Remove exit()s that cause unexpected app failures. An app can register a mlt_log callback, trap errors, and do something more graceful than abort as perhaps some of these are not really as fatal as they claim to be (a different patch can change the levels as needed). 2012-11-14 Dan Dennedy * Doxyfile, NEWS, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 0.8.6 2012-11-13 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h, src/modules/avformat/configure: Set version to 0.8.4 2012-11-11 Dan Dennedy * src/modules/core/factory.c, src/modules/normalize/Makefile, src/modules/normalize/factory.c, src/modules/normalize/filter_audiolevel.c, src/modules/normalize/filter_audiolevel.yml: Add audiolevel filter. * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml, src/modules/core/filter_resize.c: More fixes for force_full_luma (kdenlive-2799). This change lets the image converter downstream of the avformat producer perform utilize the range as-needed. Then, when the rescale filter sees that the force_full_range is set on the frame but has not yet been applied, forces a conversion to RGB to enforce it. In addition, the recently added force_full_luma property on the avformat producer is removed because it is redundant with AVOption color_range=2. * presets/consumer/avformat/Sony-PSP, presets/consumer/avformat/atsc_1080i_50/DNxHD, presets/consumer/avformat/atsc_1080i_5994/DNxHD, presets/consumer/avformat/atsc_1080p_2398/DNxHD, presets/consumer/avformat/atsc_1080p_24/DNxHD, presets/consumer/avformat/atsc_1080p_25/DNxHD, presets/consumer/avformat/atsc_1080p_2997/DNxHD, presets/consumer/avformat/atsc_1080p_30/DNxHD, presets/consumer/avformat/atsc_1080p_50/DNxHD, presets/consumer/avformat/atsc_1080p_5994/DNxHD, presets/consumer/avformat/atsc_1080p_60/DNxHD, presets/consumer/avformat/atsc_720p_2398/DNxHD, presets/consumer/avformat/atsc_720p_50/DNxHD, presets/consumer/avformat/atsc_720p_5994/DNxHD, presets/consumer/avformat/atsc_720p_60/DNxHD, presets/consumer/avformat/dv_ntsc/D10, presets/consumer/avformat/dv_ntsc/DV, presets/consumer/avformat/dv_ntsc/DVCPRO50, presets/consumer/avformat/dv_ntsc_wide/D10, presets/consumer/avformat/dv_ntsc_wide/DV, presets/consumer/avformat/dv_ntsc_wide/DVCPRO50, presets/consumer/avformat/dv_pal/D10, presets/consumer/avformat/dv_pal/DV, presets/consumer/avformat/dv_pal/DVCPRO50, presets/consumer/avformat/dv_pal_wide/D10, presets/consumer/avformat/dv_pal_wide/DV, presets/consumer/avformat/dv_pal_wide/DVCPRO50, presets/consumer/avformat/hdv_1080_25p/HDV, presets/consumer/avformat/hdv_1080_30p/HDV, presets/consumer/avformat/hdv_1080_50i/HDV, presets/consumer/avformat/hdv_1080_60i/HDV, presets/consumer/avformat/hdv_720_25p/HDV, presets/consumer/avformat/hdv_720_30p/HDV, presets/consumer/avformat/hdv_720_50p/HDV, presets/consumer/avformat/hdv_720_60p/HDV, presets/consumer/avformat/lossless/FFV1, presets/consumer/avformat/lossless/H.264, presets/consumer/avformat/lossless/MPEG-4: Add more descriptions to encoding presets. 2012-11-05 Dan Dennedy * src/modules/qimage/producer_qimage.c, src/modules/qimage/producer_qimage.yml: qimage: let begin property be passed as a query string parameter * src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.yml: pixbuf: support alt. query syntax begin:value for melt 2012-11-04 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/gtk2/producer_pixbuf.yml: pixbuf: let begin property be passed as a query string parameter 2012-10-23 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Fix force_full_luma (kdenlive-2799). 2012-10-19 Dan Dennedy * src/framework/mlt_geometry.c, src/modules/videostab/filter_videostab.c, src/modules/videostab/filter_videostab2.c: Let vector property of videostab(2) be read directly as mlt_geometry. 2012-10-19 Jean-Baptiste Mardelle * 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: Fix loading of extra image formats using Kdelibs (xcf, ...) 2012-10-09 Dan Dennedy * src/swig/Makefile, src/swig/csharp/build, src/swig/java/build, src/swig/lua/build, src/swig/perl/Makefile.PL, src/swig/perl/build, src/swig/php/build, src/swig/python/build, src/swig/ruby/build, src/swig/tcl/build: Build the SWIG bindings with the CXXFLAGS (3554425) Based on patch by Cristian Morales Vega * src/modules/core/producer_colour.c, src/modules/core/producer_noise.c, src/modules/frei0r/producer_frei0r.c: fix aspect ratio of generators when set via consumer property 2012-09-23 Dan Dennedy * presets/consumer/avformat/MJPEG, presets/consumer/avformat/lossless/FFV1, presets/consumer/avformat/lossless/MJPEG: indicate in some presets codecs which do not support multithread 2012-09-16 Dan Dennedy * src/framework/Makefile, src/framework/configure, src/framework/mlt_property.h: cleanup sys/param.h include on FreeBSD Assisted by Albert Villa who says it is safe to assume sys/param.h is available, which is needed for FreeBSD version check on whether to include xlocale.h in mlt_property.h. 2012-09-13 Dan Dennedy * src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c: sdl_audio and sdl_preview also do not care about field order Also, have sdl_preview pass top_field_first to its children. * src/framework/mlt_consumer.h, src/modules/core/filter_fieldorder.c, src/modules/sdl/consumer_sdl.c: add ability to ignore field order as used by sdl consumer 2012-09-09 Dan Dennedy * src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h: add Mlt::Profile::colorspace() 2012-09-08 Dan Dennedy * configure, src/framework/configure, src/modules/avformat/configure, src/modules/frei0r/configure: allow env CC to override hard-coded gcc in configure scripts patch by Alberto Villa * src/modules/avformat/producer_avformat.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c, src/modules/qimage/qimage_wrapper.cpp, src/modules/swfdec/producer_swfdec.c, src/modules/vorbis/producer_vorbis.c: change producers to use mlt_frame_original_position() * src/framework/mlt_frame.c, src/framework/mlt_frame.h: add mlt_frame_original_position() 2012-09-03 Dan Dennedy * src/mlt++/MltService.cpp, src/mlt++/MltService.h: add Mlt::Service::set_profile() * src/framework/mlt_service.c, src/framework/mlt_service.h: add mlt_service_set_profile() * src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h: add Mlt::Profile::is_explicit() 2012-08-31 Dan Dennedy * src/swig/mlt.i, src/swig/ruby/playlist.rb: extend Ruby API with PlaylistNextListner and show how to use it * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: add playlist-next event to mlt_playlist 2012-08-30 Dan Dennedy * configure, src/framework/mlt_version.h: set interim version to 0.8.3 2012-08-28 Dan Dennedy * ChangeLog, .../consumer/avformat/{MPEG-4 ASP => MPEG-4-ASP}, presets/consumer/avformat/webm: add acodec to webm preset and rename MPEG-4 ASP preset * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: set version to 0.8.2 2012-08-26 Dan Dennedy * presets/consumer/avformat/stills/BMP, presets/consumer/avformat/stills/DPX, presets/consumer/avformat/stills/JPEG, presets/consumer/avformat/stills/PNG, presets/consumer/avformat/stills/PPM, presets/consumer/avformat/stills/TGA, presets/consumer/avformat/stills/TIFF: add meta.preset.extension to image sequence presets * presets/consumer/avformat/AAC, presets/consumer/avformat/Flash, presets/consumer/avformat/MJPEG, presets/consumer/avformat/MP3, presets/consumer/avformat/MPEG-2, presets/consumer/avformat/MPEG-4, presets/consumer/avformat/MPEG-4 ASP, presets/consumer/avformat/Sony-PSP, presets/consumer/avformat/Vorbis, presets/consumer/avformat/WAV, presets/consumer/avformat/XDCAM-HD422, presets/consumer/avformat/atsc_1080i_50/DNxHD, presets/consumer/avformat/atsc_1080i_5994/DNxHD, presets/consumer/avformat/atsc_1080p_2398/DNxHD, presets/consumer/avformat/atsc_1080p_24/DNxHD, presets/consumer/avformat/atsc_1080p_25/DNxHD, presets/consumer/avformat/atsc_1080p_2997/DNxHD, presets/consumer/avformat/atsc_1080p_30/DNxHD, presets/consumer/avformat/atsc_1080p_50/DNxHD, presets/consumer/avformat/atsc_1080p_5994/DNxHD, presets/consumer/avformat/atsc_1080p_60/DNxHD, presets/consumer/avformat/atsc_720p_2398/DNxHD, presets/consumer/avformat/atsc_720p_50/DNxHD, presets/consumer/avformat/atsc_720p_5994/DNxHD, presets/consumer/avformat/atsc_720p_60/DNxHD, presets/consumer/avformat/dv_ntsc/D10, presets/consumer/avformat/dv_ntsc/DVD, presets/consumer/avformat/dv_ntsc_wide/D10, presets/consumer/avformat/dv_ntsc_wide/DVD, presets/consumer/avformat/dv_pal/D10, presets/consumer/avformat/dv_pal/DVD, presets/consumer/avformat/dv_pal_wide/D10, presets/consumer/avformat/dv_pal_wide/DVD, presets/consumer/avformat/hdv_1080_25p/HDV, presets/consumer/avformat/hdv_1080_30p/HDV, presets/consumer/avformat/hdv_1080_50i/HDV, presets/consumer/avformat/hdv_1080_60i/HDV, presets/consumer/avformat/hdv_720_25p/HDV, presets/consumer/avformat/hdv_720_30p/HDV, presets/consumer/avformat/hdv_720_50p/HDV, presets/consumer/avformat/hdv_720_60p/HDV, presets/consumer/avformat/lossless/FFV1, presets/consumer/avformat/lossless/H.264, presets/consumer/avformat/lossless/HuffYUV, presets/consumer/avformat/lossless/MJPEG, presets/consumer/avformat/lossless/MPEG-2, presets/consumer/avformat/lossless/MPEG-4, presets/consumer/avformat/lossless/ProRes, presets/consumer/avformat/webm, presets/consumer/avformat/x264-medium, presets/consumer/avformat/x264-medium-baseline, presets/consumer/avformat/x264-medium-main, presets/consumer/avformat/x264-medium-pass1: add preset metadata such as alternate name, filename extension, note. * presets/consumer/avformat/Sony-PSP, presets/consumer/avformat/webm, presets/consumer/avformat/x264-medium-baseline, presets/consumer/avformat/x264-medium-main: change profile to vprofile in presets * presets/consumer/avformat/Vorbis, presets/consumer/avformat/lossless/ProRes: add vorbis and prores encode presets 2012-08-25 Dan Dennedy * presets/consumer/avformat/AAC, presets/consumer/avformat/Flash, presets/consumer/avformat/MJPEG, presets/consumer/avformat/MP3, presets/consumer/avformat/MPEG-2, presets/consumer/avformat/MPEG-4, presets/consumer/avformat/MPEG-4 ASP, presets/consumer/avformat/WAV, presets/consumer/avformat/hdv_1080_25p/HDV, presets/consumer/avformat/hdv_1080_30p/HDV, presets/consumer/avformat/hdv_1080_50i/HDV, presets/consumer/avformat/hdv_1080_60i/HDV, presets/consumer/avformat/hdv_720_25p/HDV, presets/consumer/avformat/hdv_720_30p/HDV, presets/consumer/avformat/hdv_720_50p/HDV, presets/consumer/avformat/hdv_720_60p/HDV, presets/consumer/avformat/lossless/FFV1, presets/consumer/avformat/lossless/H.264, presets/consumer/avformat/lossless/HuffYUV, presets/consumer/avformat/lossless/MJPEG, presets/consumer/avformat/lossless/MPEG-2, presets/consumer/avformat/lossless/MPEG-4, presets/consumer/avformat/stills/BMP, presets/consumer/avformat/stills/DPX, presets/consumer/avformat/stills/JPEG, presets/consumer/avformat/stills/PNG, presets/consumer/avformat/stills/PPM, presets/consumer/avformat/stills/TGA, presets/consumer/avformat/stills/TIFF, presets/consumer/avformat/x264-medium-pass1: add a bunch of new encoding presets 2012-08-24 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: the recent A/V sync overhaul needed some additional work 2012-08-11 Dan Dennedy * src/melt/io.c, src/melt/melt.c: fix melt progress display on Windows 2012-08-01 Mikko Rapeli * src/modules/videostab/filter_videostab.c, src/modules/videostab/filter_videostab2.c: videostab/filter_videostab*.c: check return value from mlt_filter_new() Fixes Coverity CID 709365 and 709366: Dereference null return value (NULL_RETURNS) Function "mlt_filter_new" returns null (checked 50 out of 52 times). [show details] Assigning: "parent" = null return value from "mlt_filter_new". 201 mlt_filter parent = mlt_filter_new(); Dereferencing a null pointer "parent". 202 parent->child = self; * src/modules/videostab/filter_videostab.c, src/modules/videostab/filter_videostab2.c: videostab/filter_videostab*.c: check for null Fixes Coverity CID 709404: Dereference before null check (REVERSE_INULL) Dereferencing pointer "g". [show details] 85 if ( !mlt_geometry_parse( g, vectors, length, -1, -1 ) ) ... Dereferencing "g" before a null check. 104 if ( g ) mlt_geometry_close( g ); 2012-08-04 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: add image cache size property to avformat producer 2012-08-03 Marco Gittler * src/modules/oldfilm/filter_lines.c, src/modules/oldfilm/filter_lines.yml: fix width output of filter in xml 2012-07-25 Mikko Rapeli * src/framework/mlt_field.c, src/framework/mlt_frame.c, src/framework/mlt_multitrack.c, src/framework/mlt_playlist.c, src/framework/mlt_properties.c, src/framework/mlt_repository.c, src/framework/mlt_service.c, src/framework/mlt_tractor.c, src/modules/core/filter_audioconvert.c, src/modules/core/filter_crop.c, src/modules/core/filter_imageconvert.c, src/modules/core/filter_panner.c, src/modules/core/filter_resize.c, src/modules/core/producer_ppm.c, src/modules/core/transition_composite.c, src/modules/core/transition_mix.c, src/modules/dv/producer_libdv.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/kino/producer_kino.c, src/modules/linsys/consumer_SDIstream.c, src/modules/normalize/filter_volume.c, src/modules/qimage/producer_kdenlivetitle.c, src/modules/qimage/producer_qimage.c, src/modules/rtaudio/RtAudio.cpp, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c, src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Fix calloc() parameter ordering First parameter to calloc() is the count and second the amount of bytes for each item. Likely this has no run time effect since the resulting buffer size is the same. 2012-07-23 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp: fix crash when switching image formats with alpha This happens when switching from image format with distinct alpha channel (yuv422) to one with embedded alpha channel (rgb24a). Reported-by: j-b-m 2012-07-22 Dan Dennedy * src/modules/videostab/stabilize.c, src/modules/videostab/stabilize.h: remove unused function (coverity-709390) * src/mlt++/MltService.cpp, src/mlt++/MltService.h: add Service::get_profile() returns mlt_profile * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: fix memory leak (coverity-709375) 2012-07-20 Dan Dennedy * AUTHORS, src/modules/core/Makefile, src/modules/core/composite_line_yuv_sse2_simple.c, src/modules/core/transition_composite.c: improve compatibility to compile composite sse2 (macports-35243) 2012-07-12 Dan Dennedy * src/modules/core/producer_loader.c, src/modules/xml/producer_xml.c: accept file:// prefix on MLT XML file 2012-06-23 Dan Dennedy * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h, src/mlt++/MltPlaylist.cpp, src/mlt++/MltPlaylist.h, src/modules/core/producer_melt.c, src/modules/xml/producer_xml.c: add support for time string to playlist blanks * src/modules/jackrack/consumer_jack.c, src/modules/jackrack/consumer_jack.yml, src/modules/rtaudio/consumer_rtaudio.cpp, src/modules/rtaudio/consumer_rtaudio.yml, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_audio.yml: add support for audio scrubbing to audio-only consumers 2012-06-19 Dan Dennedy * src/framework/mlt_producer.c, src/framework/mlt_producer.h, src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h: add mlt_producer_seek_time and mlt_producer_frame_time * src/mlt++/MltFilteredConsumer.cpp, src/mlt++/MltFilteredConsumer.h, src/mlt++/MltFilteredProducer.cpp, src/mlt++/MltFilteredProducer.h, src/mlt++/MltPushConsumer.cpp, src/mlt++/MltPushConsumer.h: add const-ness to some strings in specialized service classes 2012-06-18 Dan Dennedy * src/modules/rotoscoping/filter_rotoscoping.c, src/modules/vmfx/filter_shape.c, src/modules/xine/vf_yadif_template.h, src/modules/xine/yadif.c: fix clang errors 2012-06-16 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: overhaul a/v sync and seeking in avformat producer The new_seek property changed to use_pts. This consolidates old seek and new seek code, improves a/v sync for more files, and improves seek performance for AVCHD in general (including libav). 2012-06-04 Dan Dennedy * NEWS, configure, src/framework/mlt_version.h: set interim version to 0.8.1 2012-06-01 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h, src/modules/avformat/configure: set version to 0.8.0 2012-05-29 Dan Dennedy * src/framework/mlt_cache.c, src/framework/mlt_cache.h: add mlt_cache_put_frame and mlt_cache_get_frame * src/modules/gtk2/Makefile, src/modules/gtk2/configure, src/modules/gtk2/scale_line_22_yuv_mmx.S, src/modules/jackrack/Makefile, src/modules/jackrack/configure: fix cross-compiling gtk2 and jackrack modules for windows 2012-05-26 Dan Dennedy * configure, src/mlt++/configure, src/modules/qimage/configure, src/modules/swfdec/Makefile, src/modules/videostab/stab/estimate.c: add configure options and fixes for cross-compiling 2012-05-19 Dan Dennedy * src/modules/decklink/Makefile, src/modules/decklink/common.cpp, src/modules/decklink/common.h, src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/darwin/DeckLinkAPI.h, .../decklink/darwin/DeckLinkAPIDispatch.cpp, src/modules/decklink/{ => linux}/DeckLinkAPI.h, .../decklink/{ => linux}/DeckLinkAPIDispatch.cpp, src/modules/decklink/{ => linux}/LinuxCOM.h, src/modules/decklink/producer_decklink.cpp, src/modules/decklink/{ => win}/DeckLinkAPI_h.h, src/modules/decklink/{ => win}/DeckLinkAPI_i.cpp: fix decklink build for OS X 2012-04-18 Dan Dennedy * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: fix decklink build on Windows 2012-04-11 Dan Dennedy * src/modules/plus/filter_affine.c, src/modules/plus/interp.h, src/modules/plus/transition_affine.c: fix distortion handling alpha channel in affine transition Reported-by: j-b-m 2012-04-10 Dan Dennedy * demo/mlt_ticker, src/modules/plus/filter_affine.c: fix background alpha channel of affine filter broke when black producer was changed to opaque like other colors 2012-04-07 Dan Dennedy * src/modules/plus/transition_affine.c, src/modules/qimage/qimage_wrapper.cpp: fix regressions during refactorization 2012-03-31 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml: add time_format property to xml consumer Now you can save the in, out, and length properties as timecode or clock values. Default unit it still in frame count. * src/framework/mlt_producer.c, src/framework/mlt_producer.h, src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h: add mlt_producer_get_length_time() More functions that return time strings will be added later. * configure, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h, src/framework/mlt_types.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h: add support for timecode and clock time strings to the framework 2012-03-27 Dan Dennedy * GPLv3, configure, src/modules/qimage/Makefile, src/modules/qimage/configure, src/modules/qimage/factory.c: require configure --enable-gpl3 for GPLv3 services (currently only vqm) 2012-03-25 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: fix resource leak regression in image producers 2012-03-19 Dan Dennedy * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: enumerate DeckLink devices when list_devices property is set 2012-03-19 Maksym Veremeyenko * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: Initialize all decklink interface pointers and reset them upon release. Also, add a couple of missing releases. 2012-03-18 Brian Matherly * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Fix incorrect precompiler conditionals for libav/ffmpeg versions. Needed to support ffmpeg 0.9 and 0.10 releases. 2012-03-14 Dan Dennedy * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/consumer_decklink.yml, src/modules/decklink/producer_decklink.cpp, src/modules/decklink/producer_decklink.yml: enumerate available devices in decklink module 2012-03-11 Brian Matherly * src/modules/gtk2/producer_pixbuf.yml, src/modules/qimage/producer_qimage.yml: Fix broken pixbuf and qimage producer metadata. 2012-03-07 Dan Dennedy * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: indicate image producers seekable 2012-03-06 Dan Dennedy * src/modules/gtk2/producer_pixbuf.yml, src/modules/qimage/producer_qimage.yml: update service metadata for pixbuf and qimage 2012-03-05 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: allow %u in image sequence pattern containing begin value * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: add image sequences where scanf format contains begin value For example, if an image sequence begins with the file foo1234.png, you can use the resource string "foo%1234d.png" to load it. 2012-03-04 Dan Dennedy * src/modules/decklink/producer_decklink.cpp, src/modules/qimage/qimage_wrapper.cpp: remove a couple more remnants of legacy real_width and _height * src/modules/avformat/producer_avformat.c, src/modules/dv/producer_libdv.c: remove deprecated source_fps property * src/framework/mlt_tractor.c, src/modules/avformat/producer_avformat.c, src/modules/core/consumer_multi.c, src/modules/core/filter_crop.c, src/modules/core/filter_rescale.c, src/modules/core/filter_resize.c, src/modules/core/producer_colour.c, src/modules/core/producer_consumer.c, src/modules/core/producer_loader.c, src/modules/core/transition_composite.c, src/modules/dv/producer_libdv.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/kdenlive/producer_framebuffer.c, src/modules/plus/transition_affine.c, src/modules/sdl/producer_sdl_image.c, src/modules/swfdec/producer_swfdec.c, src/modules/videostab/filter_videostab2.c, src/modules/vmfx/producer_pgm.c: replace legacy real_width and _height with meta.media.width and .height This takes advantage of mlt_producer copying all meta properties from producer to frame so we do not have to remember to do it everywhere it is needed. 2012-02-29 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_frame.c, src/framework/mlt_profile.c, src/framework/mlt_tractor.c, src/framework/mlt_transition.c, src/modules/core/filter_crop.c, src/modules/core/filter_watermark.c, src/modules/kdenlive/filter_freeze.c, src/modules/kdenlive/producer_framebuffer.c, src/modules/oldfilm/filter_dust.c, src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c: remove consumer_aspect_ratio property - use profile instead * src/framework/mlt_tractor.c, src/modules/core/filter_watermark.c, src/modules/core/transition_composite.c, src/modules/kdenlive/filter_freeze.c: remove output_ratio property - use profile instead * src/modules/core/filter_crop.c, src/modules/core/filter_obscure.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/plus/filter_affine.c, src/modules/plus/transition_affine.c: remove usage of normalised_width and _height properties from services * src/framework/mlt_frame.c, src/framework/mlt_tractor.c: remove normalised_width and _height properties from framework 2012-03-04 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp: fix regression with adding image conversion to image producers 2012-03-02 Dan Dennedy * src/modules/qimage/producer_qimage.c, src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h: convert to and cache requested format in qimage 2012-03-01 Dan Dennedy * src/modules/qimage/producer_qimage.c, src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h: split refresh_qimage() into refresh_qiamge() and refresh_image() 2012-02-27 Dan Dennedy * src/modules/core/transition_composite.yml, src/modules/gtk2/producer_pixbuf.yml: couple of small service metadata fixes 2012-02-22 Dan Dennedy * src/modules/decklink/producer_decklink.cpp, src/modules/decklink/producer_decklink.yml: fix regression when using producer 'consumer' with decklink This feature now requires one to set the preview property on this producer to support special preview mode when the speed is 0. 2012-02-20 Dan Dennedy * src/framework/mlt_frame.c, src/modules/avformat/filter_avcolour_space.c, src/modules/core/filter_crop.c, src/modules/core/filter_resize.c, src/modules/core/transition_region.c: let mlt_frame_set_alpha clear the get_alpha_mask function pointer 2012-02-19 Dan Dennedy * configure, src/framework/mlt_version.h: set interim version 0.7.9 2012-02-16 Dan Dennedy * src/modules/core/transition_composite.c, src/modules/core/transition_composite.h: make composite_line_yuv() available to other services 2012-02-16 Maksym Veremeyenko * src/modules/core/composite_line_yuv_sse2_simple.c, src/modules/core/transition_composite.c: use sse2 instruction for line compositing 2012-02-13 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h, src/melt/melt.c: set version to 0.7.8 2012-02-12 Dan Dennedy * Makefile, src/modules/core/loader.dict, src/modules/sdl/producer_sdl_image.yml: deprecate sdl_image * src/mlt++/MltFrame.cpp, src/mlt++/MltFrame.h: make Frame::get_position() retrun type consistent 2012-02-12 Simon A. Eugster * src/mlt++/MltFrame.cpp, src/mlt++/MltFrame.h: Add get_position to Mlt::Frame 2012-02-08 Dan Dennedy * src/modules/qimage/factory.c, src/modules/qimage/transition_vqm.cpp, src/modules/qimage/transition_vqm.yml: add rendering to vqm and yaml service metadata 2012-02-06 Dan Dennedy * src/modules/qimage/Makefile, src/modules/qimage/factory.c, src/modules/qimage/transition_vqm.cpp: add vqm transition 2012-02-05 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: fix color problem with libav (3483629) 2012-02-04 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: fix AVOption processing on ffmpeg 0.8 2012-01-30 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: fix AVOption processing on libav 0.7.3 Patch for consumer by j-b-m and extended to producer by me. 2012-01-28 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avresample.c, src/modules/avformat/producer_avformat.c: fix SAMPLE_FMT support for v0.6 and less of libav/ffmpeg * src/modules/avformat/filter_avresample.c, src/modules/avformat/producer_avformat.c: convert all SAMPLE_FMT_16 to AV_SAMPLE_FMT_16 2012-01-25 Dan Dennedy * src/modules/core/filter_audiochannels.c, src/modules/core/filter_channelcopy.c, src/modules/core/filter_mono.c: add s32le and f32le format to core audio filters * src/framework/mlt_types.h, src/modules/core/filter_audioconvert.c: add support for converting between all audio sample formats 2012-01-21 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c: remove global avformat mutex and add a local one to the producer for open/close coherency * src/framework/mlt_producer.h, src/framework/mlt_service.h: update doc on service-change and producer-changed events 2012-01-17 Dan Dennedy * src/modules/gtk2/producer_pango.yml, src/modules/gtk2/producer_pixbuf.yml: document force_aspect_ratio on pango and pixbuf producers 2012-01-15 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: enable codec- and format-specific options for v0.7 releases of ffmpeg (but not libav, which uses v53 of libavformat and libavcodec in its 0.7 releases) 2012-01-14 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c: drop deprecated APIs of libavformat/codec v53 2012-01-02 gmarco * src/modules/videostab/filter_videostab.c, src/modules/videostab/stab/resample.c, src/modules/videostab/stab/resample.h, src/modules/videostab/stab/utils.c, src/modules/videostab/stab/utils.h: do not use lanc_kernels as global var. moved to filter struct 2011-12-16 gmarco * src/modules/videostab/filter_videostab2.c, src/modules/videostab/stabilize.c, src/modules/videostab/stabilize.h, src/modules/videostab/transform_image.c, src/modules/videostab/transform_image.h: use calloc insteadt of malloc/memset use struct for instance data small cleanup use PIX(n) dont use instable yuv420 use stabilize on grayimage (converted from yuv422) 2011-11-21 Marco Gittler * src/modules/videostab/stabilize.c, src/modules/videostab/transform_image.c: sse2 updates 2011-12-21 Dan Dennedy * configure, src/mlt++/configure: add configure support for GNU Hurd Patches provided by Patrick Matthäi. * src/modules/rtaudio/RtAudio.cpp, src/modules/rtaudio/configure: only build rtaudio for Linux, Windows, or OS X * src/framework/Makefile, src/framework/configure, src/framework/mlt_property.h: add support for xlocale.h on FreeBSD with assistance from Gleb Smirnoff 2011-12-16 gmarco * src/modules/videostab/filter_videostab2.c, src/modules/videostab/stabilize.c, src/modules/videostab/stabilize.h, src/modules/videostab/transform_image.c, src/modules/videostab/transform_image.h: use calloc insteadt of malloc/memset use struct for instance data small cleanup use PIX(n) dont use instable yuv420 use stabilize on grayimage (converted from yuv422) 2011-11-21 Marco Gittler * src/modules/videostab/stabilize.c, src/modules/videostab/transform_image.c: sse2 updates 2011-12-10 Dan Dennedy * src/modules/core/producer_colour.c, src/modules/core/producer_noise.c, src/modules/dv/producer_libdv.c, src/modules/frei0r/producer_frei0r.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c, src/modules/sdl/producer_sdl_image.c: add mlt_image_none support to producers * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: add consumer properties mlt_image_format and mlt_audio_format 2011-12-09 Dan Dennedy * src/modules/core/filter_fieldorder.c, src/modules/core/filter_fieldorder.yml: add meta.swap_fields to the fieldorder filter 2011-12-08 Dan Dennedy * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_fieldorder.c, src/modules/core/filter_fieldorder.yml, src/modules/core/filter_resize.c, src/modules/core/filter_resize.yml, src/modules/core/loader.ini: refactor field order correction into new filter 2011-12-05 Dan Dennedy * src/modules/rtaudio/RtAudio.cpp, src/modules/rtaudio/RtAudio.h, src/modules/rtaudio/consumer_rtaudio.cpp: improve selecting rtaudio device by name 2011-11-28 Dan Dennedy * src/modules/rtaudio/Makefile, src/modules/rtaudio/RtAudio.cpp, src/modules/rtaudio/RtAudio.h, src/modules/rtaudio/RtError.h, src/modules/rtaudio/consumer_rtaudio.cpp: add rtaudio consumer 2011-11-27 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c: fix VDPAU state issues Patch by Christophe Thommeret 2011-11-04 Dan Dennedy * src/framework/mlt_factory.c, src/modules/frei0r/factory.c, src/modules/jackrack/plugin_mgr.c: fix frei0r and ladspa loading for relocatable builds 2011-11-19 Dan Dennedy * src/melt/melt.c, src/modules/core/consumer_multi.c: change property 'consumer' to 'mlt_service' consistent with xml * src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/modules/core/consumer_multi.c: enhance mlt_frame_clone with a deep/shallow parameter 2011-11-10 Dan Dennedy * src/framework/mlt_frame.c, src/framework/mlt_frame.h: add mlt_frame_clone() 2011-11-05 Dan Dennedy * src/modules/core/Makefile, src/modules/core/consumer_multi.c, src/modules/core/consumer_multi.yml, src/modules/core/factory.c: add multi consumer (non-functional) 2011-11-12 Dan Dennedy * profiles/sdi_486i_5994, profiles/sdi_486p_2398, src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c, src/modules/linsys/sdi_generator.h: improve support for 486 line NTSC in linsys sdi consumer 2011-11-03 Dan Dennedy * configure, src/framework/mlt_version.h: set interim version 0.7.7 * src/modules/avformat/vdpau.c, src/modules/videostab/stab/estimate.c: build fixes for FreeBSD patches by Alberto Villa 2011-10-31 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: set version to 0.7.6 2011-10-30 Dan Dennedy * src/modules/videostab/filter_videostab.c, src/modules/videostab/filter_videostab2.c: Request progressive scan images when stabilizing video. * presets/filter/brightness/from_black, presets/filter/brightness/to_black, presets/filter/volume/fade_in, presets/filter/volume/fade_out: add fade in/out presets When using to_black and fade_out, currently you need to adjust in and out properties as needed because we cannot yet use negative values to mean "from end." 2011-09-20 Marco Gittler * src/modules/videostab/filter_videostab2.c, src/modules/videostab/transform_image.c, src/modules/videostab/transform_image.h: set transform properties from mlt 2011-09-19 Marco Gittler * src/modules/videostab/filter_videostab2.c, src/modules/videostab/transform_image.c: use interpolation settings 2011-09-11 Marco Gittler * src/modules/videostab/stabilize.c, src/modules/videostab/transform_image.c: move printf -> mlt_log* * src/modules/videostab/filter_videostab2.c, src/modules/videostab/stabilize.c: set all paramters * src/modules/videostab/filter_videostab2.c, src/modules/videostab/stabilize.c, src/modules/videostab/stabilize.h, src/modules/videostab/transform_image.c, src/modules/videostab/transform_image.h: avoid unreadable code like (*format==mlt_image_rgb24?0:1) use mlt_types and names for it 2011-08-19 Marco Gittler * src/modules/videostab/Makefile, src/modules/videostab/factory.c, src/modules/videostab/filter_videostab2.c, src/modules/videostab/stabilize.c, src/modules/videostab/stabilize.h, src/modules/videostab/tlist.c, src/modules/videostab/tlist.h, src/modules/videostab/transform.c, src/modules/videostab/transform.h, src/modules/videostab/transform_image.c, src/modules/videostab/transform_image.h: added vstab from http://public.hronopik.de/vid.stab/features.php?lang=en handles yuv, has zoom (to avoid the interpolated borders), and seems to be stabilize a bit better 2011-10-30 Dan Dennedy * src/modules/core/producer_consumer.c, src/modules/core/producer_consumer.yml: add autoprofile property to consumer producer 2011-10-16 Brian Matherly * demo/mlt_voiceover, demo/pango.mlt, src/modules/core/data_fx.properties, src/modules/feeds/NTSC/data_fx.properties, src/modules/feeds/NTSC/etv.properties, src/modules/feeds/PAL/data_fx.properties, src/modules/feeds/PAL/etv.properties, src/modules/gtk2/filter_dynamictext.c, src/modules/gtk2/filter_dynamictext.yml, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml, src/tests/pango.c: Add "family" and "style" properties to pango producer. Deprecate "font" property. 2011-10-11 Brian Matherly * src/modules/decklink/producer_decklink.yml, src/modules/sox/filter_sox.yml: yml validation fixes * src/modules/gtk2/filter_dynamictext.c, src/modules/gtk2/filter_dynamictext.yml, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Add outline to pango and dynamic text services. Add pad and align to dynamic text. 2011-10-02 Brian Matherly * README, docs/TODO, docs/policies.txt, docs/services.txt: Remove docs/TODO, docs/policies.txt, docs/services/txt. All that information is now available on the web site. 2011-10-01 Dan Dennedy * src/melt/melt.c, src/modules/core/producer_melt.c, src/modules/xml/mlt-xml.dtd, src/modules/xml/producer_xml.c: Add consumer element to xml producer. 2011-09-25 Brian Matherly * src/modules/gtk2/filter_dynamictext.yml, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pango.yml: Add support for HTML style color parameter to producer_pango. producer_color and producer_pango now work exactly the same WRT color parameters. 2011-09-25 Dan Dennedy * src/modules/frei0r/factory.c, src/modules/jackrack/plugin_mgr.c: Load frei0r and ladspa plugins relative to exe on win32 * src/framework/Makefile, src/framework/mlt_factory.c, src/framework/mlt_profile.c: Fix build on win32 * src/framework/mlt_profile.c, src/framework/mlt_properties.c, src/framework/mlt_repository.c: Fix and cleanup profile and preset dirs. * src/framework/Makefile, src/framework/mlt_factory.c, src/framework/mlt_profile.c: Fix loading profile from datadir. * src/modules/frei0r/factory.c, src/modules/jackrack/plugin_mgr.c: Add relative frei0r and LADSPA dirs for relocatable. 2011-09-23 Dan Dennedy * src/framework/mlt_profile.c, src/framework/mlt_repository.c: Make profiles relative to MLT_DATA instead of $prefix/share/mlt 2011-09-25 Dan Dennedy * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: Fix building decklink on win32 2011-09-20 Dan Dennedy * presets/consumer/avformat/XDCAM-HD422, presets/consumer/avformat/atsc_1080i_50/DNxHD, presets/consumer/avformat/atsc_1080i_5994/DNxHD, presets/consumer/avformat/atsc_1080p_2398/DNxHD, presets/consumer/avformat/atsc_1080p_24/DNxHD, presets/consumer/avformat/atsc_1080p_25/DNxHD, presets/consumer/avformat/atsc_1080p_2997/DNxHD, presets/consumer/avformat/atsc_1080p_30/DNxHD, presets/consumer/avformat/atsc_1080p_50/DNxHD, presets/consumer/avformat/atsc_1080p_5994/DNxHD, presets/consumer/avformat/atsc_1080p_60/DNxHD, presets/consumer/avformat/atsc_720p_2398/DNxHD, presets/consumer/avformat/atsc_720p_50/DNxHD, presets/consumer/avformat/atsc_720p_5994/DNxHD, presets/consumer/avformat/atsc_720p_60/DNxHD, presets/consumer/avformat/dv_ntsc/D10, presets/consumer/avformat/dv_ntsc/DVD, presets/consumer/avformat/dv_ntsc_wide/D10, presets/consumer/avformat/dv_ntsc_wide/DVD, presets/consumer/avformat/dv_pal/D10, presets/consumer/avformat/dv_pal/DVD, presets/consumer/avformat/dv_pal_wide/D10, presets/consumer/avformat/dv_pal_wide/DVD, presets/consumer/avformat/webm: Fix video bitrate option in presets. * src/modules/avformat/consumer_avformat.c, src/modules/avformat/consumer_avformat.yml: Support streamtype-specific AVOptions (-vb) 2011-08-28 Brian Matherly * src/modules/gtk2/Makefile, src/modules/gtk2/factory.c, src/modules/gtk2/filter_dynamictext.c, src/modules/gtk2/filter_dynamictext.yml: Add filter_dynamictext. 2011-09-11 Dan Dennedy * src/modules/sox/Makefile, src/modules/sox/factory.c, src/modules/sox/filter_sox.yml, src/modules/sox/filter_sox_effect.yml: Document normalise and analysis for sox. This change separates the general sox metadata from effect instance metadata. 2011-09-09 Dan Dennedy * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/{melt => core}/producer_melt.c, src/modules/{melt => core}/producer_melt.yml, src/modules/{melt => core}/producer_melt_file.yml, src/modules/melt/Makefile, src/modules/melt/factory.c: Move melt producer to core module. * presets/consumer/avformat/atsc_1080i_50/DNxHD, presets/consumer/avformat/atsc_1080i_5994/DNxHD, presets/consumer/avformat/atsc_1080p_2398/DNxHD, presets/consumer/avformat/atsc_1080p_24/DNxHD, presets/consumer/avformat/atsc_1080p_25/DNxHD, presets/consumer/avformat/atsc_1080p_2997/DNxHD, presets/consumer/avformat/atsc_1080p_30/DNxHD, presets/consumer/avformat/atsc_1080p_50/DNxHD, presets/consumer/avformat/atsc_1080p_5994/DNxHD, presets/consumer/avformat/atsc_1080p_60/DNxHD, presets/consumer/avformat/atsc_720p_2398/DNxHD, presets/consumer/avformat/atsc_720p_50/DNxHD, presets/consumer/avformat/atsc_720p_5994/DNxHD, presets/consumer/avformat/atsc_720p_60/DNxHD: Add a bunch of DNxHD encode presets. 2011-09-04 Dan Dennedy * src/modules/melt/producer_melt.c, src/modules/xml/producer_xml.c: Change previous mods to use mlt_multitrack_count() * src/modules/melt/producer_melt.c, src/modules/xml/producer_xml.c: Fix XML and melt producers producer_avformat cache size. Use track-count + 2 in case a track is using a mixer between playlist items. * src/framework/mlt_service.c, src/framework/mlt_service.h: Add mlt_service_cache_get_size() * src/framework/mlt_cache.c, src/framework/mlt_cache.h: Add mlt_cache_get_size() 2011-09-03 Dan Dennedy * src/modules/decklink/producer_decklink.cpp, src/modules/decklink/producer_decklink.yml: Add vanc property to decklink producer. This captures vertical ancillary data during the vertical blanking, which often contains metadata with timecode. This can be used to produce D10/IMX50 captures with VBI. 2011-09-03 j-b-m * src/modules/core/transition_region.c, src/modules/core/transition_region.yml: Add filter_only to region transition. YAML patch by Dan Dennedy 2011-09-01 Dan Dennedy * presets/consumer/avformat/dv_ntsc/D10, presets/consumer/avformat/dv_ntsc_wide/D10, presets/consumer/avformat/dv_pal/D10, presets/consumer/avformat/dv_pal_wide/D10: Add SMPTE 356M (aka D-10 and IMX50) encoode presets. * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/framework/mlt_tractor.c, src/framework/mlt_transition.c, src/modules/core/filter_resize.c: Add consumer property top_field_first. 2011-08-31 Dan Dennedy * src/modules/sox/factory.c, src/modules/sox/filter_sox.c: Use the sox version in metadata and serialization. * src/modules/xml/consumer_xml.c, src/modules/xml/mlt-xml.dtd: Add MLT version to serialized XML. * src/modules/frei0r/factory.c, src/modules/motion_est/filter_autotrack_rectangle.c: Convert some printfs to fprintf(stderr) or mlt_log. 2011-08-16 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml: Add no_meta property to xml consumer. Applications that use the consumer for its project file might want to reduce xml bloat by setting this. * src/modules/jackrack/consumer_jack.c, src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/process.c: Fix segfault on concurrent calls to jack_activate(). 2011-08-15 Dan Dennedy * src/modules/videostab/filter_videostab.c, src/modules/videostab/filter_videostab.yml: Add vectors property to videostab. Change videostab to save to and load from a property instead of file. It uses mlt_geometry for the (de)serialization of the vectors, Also, remove seeking on the producer and require a two pass mode of operation. Finally, make it parallel-safe. * src/framework/mlt_geometry.c, src/framework/mlt_geometry.h, src/mlt++/MltGeometry.cpp, src/mlt++/MltGeometry.h, src/modules/core/transition_composite.c, src/modules/gtk2/producer_pango.c, src/modules/motion_est/filter_autotrack_rectangle.c: Add mlt_geometry_interpolate. This removes re-interpolation on each call to mlt_geometry_insert() to make bulk invocations of that call faster. This also makes mlt_geometry_parse() faster. Also, this includes a fix to mlt_geometry_serialise() for a buffer overflow memory corruption. * src/modules/xml/Makefile, src/modules/xml/consumer_xml.c, src/modules/xml/consumer_xml.yml: Add 'all' property to xml consumer. Makes the consumer process all frames before serializing to XML. 2011-08-13 Dan Dennedy * src/framework/mlt_filter.c, src/framework/mlt_filter.h, src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h: Add mlt_filter_get_length2. 2011-08-12 Dan Dennedy * src/modules/videostab/filter_videostab.c, src/modules/videostab/gpl: Fix for GPL license. 2011-08-12 Marco Gittler * src/modules/videostab/factory.c, src/modules/videostab/filter_videostab.yml: added yml metadata 2011-08-11 Marco Gittler * src/modules/videostab/factory.c, src/modules/videostab/filter_videostab.c: compile fix * src/modules/videostab/Makefile, src/modules/videostab/factory.c, src/modules/videostab/filter_videostab.c, src/modules/videostab/stab/estimate.c, src/modules/videostab/stab/estimate.h, src/modules/videostab/stab/klt/base.h, src/modules/videostab/stab/klt/convolve.c, src/modules/videostab/stab/klt/convolve.h, src/modules/videostab/stab/klt/error.c, src/modules/videostab/stab/klt/error.h, src/modules/videostab/stab/klt/klt.c, src/modules/videostab/stab/klt/klt.h, src/modules/videostab/stab/klt/klt_util.c, src/modules/videostab/stab/klt/klt_util.h, src/modules/videostab/stab/klt/pyramid.c, src/modules/videostab/stab/klt/pyramid.h, .../videostab/stab/klt/selectGoodFeatures.c, src/modules/videostab/stab/klt/trackFeatures.c, src/modules/videostab/stab/main.c, src/modules/videostab/stab/resample.c, src/modules/videostab/stab/resample.h, src/modules/videostab/stab/utils.c, src/modules/videostab/stab/utils.h, src/modules/videostab/stab/vector.c, src/modules/videostab/stab/vector.h: first version of video stabilization from http://vstab.sourceforge.net/ 2011-08-03 Dan Dennedy * src/modules/jackrack/Makefile, src/modules/jackrack/configure, src/modules/jackrack/consumer_jack.c, src/modules/jackrack/factory.c, src/modules/jackrack/gpl: Make jack consumer LGPLv2.1 license. This change allows the module to be built in either GPL or LGPL mode where GPL mode also adds the jackrack and ladspa filters. * src/modules/jackrack/Makefile, src/modules/jackrack/consumer_jack.yml: Add service metadata for jack consumer. * src/modules/jackrack/Makefile, src/modules/jackrack/consumer_jack.c, src/modules/jackrack/factory.c: Add audio-only JACK consumer. Fires consumer-frame-show for video frames. 2011-07-24 Brian Matherly * Makefile, src/modules/avformat/consumer_avformat.yml, src/modules/avformat/producer_avformat.yml, src/modules/core/factory.c, src/modules/core/filter_channelcopy.yml, src/modules/core/filter_gamma.yml, src/modules/core/filter_luma.yml, src/modules/core/filter_region.yml, src/modules/core/filter_rescale.yml, src/modules/core/filter_resize.yml, src/modules/core/producer_loader.yml, src/modules/core/transition_composite.yml, src/modules/core/transition_luma.yml, src/modules/dv/producer_libdv.yml, src/modules/gtk2/factory.c, src/modules/gtk2/filter_rescale.yml, src/modules/gtk2/producer_pango.yml, src/modules/kdenlive/filter_boxblur.yml, src/modules/resample/Makefile, src/modules/resample/factory.c, src/modules/resample/filter_resample.yml: Add service metadata for composite transition, all normalizing filters (resample, resize, rescale), and the loader producer. Add yml validation rule to Makefile. All yml files pass validation against metaschema.yaml. 2011-07-22 Dan Dennedy * src/modules/core/filter_mirror.yml, src/modules/core/filter_mono.yml, src/modules/core/filter_obscure.yml, src/modules/core/filter_region.yml, src/modules/core/transition_luma.yml, src/modules/core/transition_mix.yml, src/modules/core/transition_region.yml, src/modules/dv/consumer_libdv.yml, src/modules/normalize/filter_volume.yml, src/modules/sdl/consumer_sdl.yml, src/modules/xml/consumer_xml.yml: Cleanup Brian's service metadata contribution. * src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c: Make ffmpeg v53-specific code more readable and searchable. * configure, src/framework/mlt_version.h: set interim version 0.7.5 * src/framework/mlt_property.c, src/framework/mlt_property.h: Fix build on Debian kfreebsd. * src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c: Make ffmpeg v53-specific code more readable and searchable. * configure, src/framework/mlt_version.h: set interim version 0.7.5 * src/framework/mlt_property.c, src/framework/mlt_property.h: Fix build on Debian kfreebsd. 2011-07-19 Brian Matherly * src/modules/core/filter_mirror.yml, src/modules/core/filter_mono.yml, src/modules/core/filter_obscure.yml, src/modules/core/filter_region.yml, src/modules/core/transition_luma.yml, src/modules/core/transition_mix.yml, src/modules/core/transition_region.yml, src/modules/dv/consumer_libdv.yml, src/modules/jackrack/filter_ladspa.yml, src/modules/normalize/filter_volume.yml, src/modules/sdl/consumer_sdl.yml, src/modules/xml/consumer_xml.yml: Transcribe service metadata from services.txt to corresponding yml files. 2011-07-17 Brian Matherly * src/modules/avformat/consumer_avformat.yml, src/modules/avformat/producer_avformat.yml, src/modules/core/filter_data_show.yml, src/modules/core/filter_watermark.yml, src/modules/core/producer_noise.yml, src/modules/decklink/producer_decklink.yml, src/modules/dv/producer_libdv.yml, src/modules/gtk2/producer_pango.yml, src/modules/gtk2/producer_pixbuf.yml, src/modules/vorbis/producer_vorbis.yml, src/modules/xml/producer_xml.yml: Remove from all existing yml: in, out, length, resource, and aspect_ratio. 2011-07-16 Dan Dennedy * Doxyfile, configure, docs/melt.1, docs/melt.txt, src/framework/mlt_version.h: Set version to 0.7.4 * src/modules/jackrack/factory.c, src/modules/jackrack/plugin_desc.c, src/modules/jackrack/plugin_desc.h: Properly represent LADSPA plugin author. 2011-07-11 Dan Dennedy * src/framework/mlt_properties.c, src/framework/mlt_property.c, src/framework/mlt_property.h: Fix build on new locale stuff when not Linux or OS X. 2011-07-10 Dan Dennedy * src/framework/mlt_properties.c, src/framework/mlt_property.c: Fix build for querylocale() on OS X. * presets/consumer/avformat/webm, presets/consumer/avformat/webm-pass1, presets/consumer/avformat/webm-pass2: Fix webm preset. * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/modules/sdl/consumer_sdl_preview.c: Make maximum consecutive-dropped frames configurable. Defaults to old value of 5 that seems more preferable for video editing. 2011-07-09 Dan Dennedy * src/modules/feeds/NTSC/data_fx.properties, src/modules/feeds/NTSC/etv.properties, src/modules/feeds/NTSC/obscure.properties, src/modules/feeds/PAL/border.properties, src/modules/feeds/PAL/data_fx.properties, src/modules/feeds/PAL/etv.properties: Convert , to / delimiter in data_show templates. * src/framework/metaschema.yaml, src/framework/mlt_properties.c: Add LC_NUMERIC handling to YAML Tiny parser. * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h: Add mlt_properties_get_lcnumeric and Properties::get_lcnumeric * src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h: Add Properties::set_lcnumeric(). * src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_property.c, src/framework/mlt_property.h: Add mlt_properties_set_lcnumeric, mlt_property_get_double_l, and mlt_property_get_string_l. Locale-specific variants of key properties functions. 2011-07-07 Dan Dennedy * src/modules/core/filter_data_show.yml, src/modules/core/filter_gamma.yml, src/modules/core/filter_greyscale.yml, src/modules/core/filter_luma.yml, src/modules/core/filter_watermark.yml, src/modules/core/producer_colour.yml, src/modules/core/producer_noise.yml, src/modules/dv/producer_libdv.yml, src/modules/gtk2/producer_pango.yml, src/modules/gtk2/producer_pixbuf.yml, src/modules/vorbis/producer_vorbis.yml, src/modules/xml/producer_xml.yml: Convert services.txt to metadata YAML (WIP). Patch by Brian Matherly. 2011-07-06 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/mlt-xml.dtd, src/modules/xml/producer_xml.c: Add LC_NUMERIC to MLT XML. This does not permit one to mix locales within a MLT process! In other words, you can not load a document in a locale using one decimal separator and then filter with a double property using a different separator. * src/modules/oldfilm/filter_vignette.c, src/modules/plus/filter_charcoal.c: Fix some default numeric property values in some locales. Strings with a period for decimal separator do not convert correctly in locales that use comma for the decimal separator. * demo/README, demo/demo, demo/mlt_bouncy, demo/mlt_bouncy_ball, demo/mlt_composite_transition, demo/mlt_my_name_is, demo/mlt_news, demo/mlt_obscure, demo/mlt_push, demo/mlt_slideshow2, demo/mlt_slideshow_black, demo/mlt_squeeze, demo/mlt_squeeze_box, demo/mlt_swf_variables, demo/mlt_ticker, demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark, demo/mlt_voiceover, demo/mlt_watermark, demo/pango.mlt, docs/framework.txt, docs/services.txt, src/framework/mlt_geometry.c, src/modules/core/filter_obscure.c, src/modules/core/transition_composite.c, src/modules/core/transition_region.c, src/modules/motion_est/Makefile, src/modules/motion_est/filter_autotrack_rectangle.c, src/modules/plus/transition_affine.c, src/tests/hello.c: Use '/' for coordinate delimiter instead of period. Period is a decimal separator in some locales. * demo/consumers.ini, demo/demo.ini: Fix usage of cut in demo script for some platforms (OSX). 2011-07-05 Dan Dennedy * presets/consumer/avformat/webm, presets/consumer/avformat/webm-pass1, presets/consumer/avformat/webm-pass2: Add some WebM presets. 2011-07-04 Dan Dennedy * presets/consumer/avformat/x264-medium-baseline, presets/consumer/avformat/x264-medium-main: Fix profile-based x264 presets for FFmpeg v0.8+. * presets/consumer/avformat/x264-medium, presets/consumer/avformat/x264-medium-baseline, presets/consumer/avformat/x264-medium-main, presets/consumer/avformat/x264-medium-pass1: Add some x264-medium presets. * src/modules/avformat/Makefile, src/modules/avformat/configure: Dropping support for --avformat-svn. Subversion is no longer used, FFmpeg and libav have forked, and now there are the build scripts. * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml, src/modules/melt/producer_melt.c: Support standard query syntax on avformat URL. 2011-07-02 Dan Dennedy * src/modules/decklink/DeckLinkAPI_h.h, src/modules/decklink/DeckLinkAPI_i.cpp, src/modules/decklink/Makefile, src/modules/decklink/configure, src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/producer_decklink.cpp: Add Windows support for DeckLink. 2011-06-22 Dan Dennedy * presets/consumer/avformat/dv_ntsc/DV, presets/consumer/avformat/dv_ntsc/DVCPRO50, presets/consumer/avformat/dv_ntsc_wide/DV, presets/consumer/avformat/dv_ntsc_wide/DVCPRO50, presets/consumer/avformat/dv_pal/DV, presets/consumer/avformat/dv_pal/DVCPRO50, presets/consumer/avformat/dv_pal_wide/DV, presets/consumer/avformat/dv_pal_wide/DVCPRO50: Add DV and DVCPRO50 encode presets. 2011-06-17 Dan Dennedy * src/framework/Makefile, src/framework/mlt_types.h, src/melt/Makefile, src/modules/avformat/Makefile, src/modules/gtk2/Makefile, src/modules/sdl/Makefile, src/win32/fnmatch.c: Cleanup Win32 build. 2011-06-15 Dan Dennedy * src/framework/mlt_transition.c, src/modules/core/transition_composite.c: Fix regression in field rendering luma transition. Due to refactoring composite and luma into mlt_transition_get_progress_delta(). 2011-06-13 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/producer_avformat.yml: Add video_delay to avformat producer. 2011-06-11 Dan Dennedy * src/modules/decklink/producer_decklink.cpp, src/modules/decklink/producer_decklink.yml: Add prefill property to decklink producer. 2011-06-06 Dan Dennedy * src/framework/mlt_consumer.c, src/modules/avformat/consumer_avformat.yml: Change consumer 'profile' property to 'mlt_profile' libavcodec uses the profile property for aac and libx264. 2011-06-05 Dan Dennedy * src/modules/avformat/consumer_avformat.yml, src/modules/avformat/producer_avformat.yml: Change URI to URL in avformat yaml. * src/melt/melt.c, src/modules/avformat/consumer_avformat.c: Send melt -query and -help to stdout. Nice for use with pager or grep, awk, etc. 2011-06-03 Dan Dennedy * src/modules/decklink/consumer_decklink.cpp, src/modules/decklink/consumer_decklink.yml: Enable external keyer on decklink consumer. Patch supplied by Maksym Veremeyenko. 2011-05-30 Dan Dennedy * src/modules/linsys/Makefile, src/modules/linsys/consumer_sdi.yml, src/modules/linsys/factory.c: Add service metadata to linsys module (WIP). * src/modules/xml/Makefile, src/modules/xml/consumer_xml.yml, src/modules/xml/factory.c, src/modules/xml/producer_xml-string.yml, src/modules/xml/producer_xml.yml: Add service metadata to xml module (WIP). * src/modules/vorbis/Makefile, src/modules/vorbis/factory.c, src/modules/vorbis/producer_vorbis.yml: Add service metadata to vorbis module (WIP). * src/modules/core/filter_mono.yml, src/modules/vmfx/Makefile, src/modules/vmfx/factory.c, src/modules/vmfx/filter_chroma.yml, src/modules/vmfx/filter_chroma_hold.yml, src/modules/vmfx/filter_mono.yml, src/modules/vmfx/filter_shape.yml, src/modules/vmfx/producer_pgm.yml: Add service metadata to vmfx module (WIP). * src/modules/jackrack/plugin.c, src/modules/jackrack/plugin_mgr.c, src/modules/jackrack/plugin_settings.c: Remove aborts in jackrack module. 2011-05-29 Dan Dennedy * src/modules/swfdec/Makefile, src/modules/swfdec/producer_swfdec.c, src/modules/swfdec/producer_swfdec.yml: Add service metadata to swfdec module (WIP). * src/modules/qimage/producer_qimage.yml, src/modules/sdl/Makefile, src/modules/sdl/consumer_sdl.yml, src/modules/sdl/consumer_sdl_audio.yml, src/modules/sdl/consumer_sdl_preview.yml, src/modules/sdl/consumer_sdl_still.yml, src/modules/sdl/factory.c, src/modules/sdl/producer_sdl_image.yml: Add service metadata for SDL module (WIP). * src/modules/plus/transition_affine.yml, src/modules/qimage/Makefile, src/modules/qimage/factory.c, src/modules/qimage/producer_kdenlivetitle.yml, src/modules/qimage/producer_qimage.yml: Add service metadata for qimage module (WIP). * src/modules/plus/Makefile, src/modules/plus/factory.c, src/modules/plus/filter_affine.yml, src/modules/plus/filter_charcoal.yml, src/modules/plus/filter_invert.yml, src/modules/plus/filter_sepia.yml, src/modules/plus/transition_affine.yml: Add service metadata to plus module (WIP). * src/modules/normalize/Makefile, src/modules/normalize/factory.c, src/modules/normalize/filter_volume.yml: Add service metadata to normalize module (WIP). * src/modules/motion_est/Makefile, src/modules/motion_est/factory.c, src/modules/motion_est/filter_autotrack_rectangle.yml, src/modules/motion_est/filter_motion_est.yml, src/modules/motion_est/filter_vismv.yml, src/modules/motion_est/producer_slowmotion.yml: Add service metadata to motion_est module (WIP). * src/modules/melt/Makefile, src/modules/melt/factory.c, src/modules/melt/producer_melt.yml, src/modules/melt/producer_melt_file.yml: Add service metadata for melt module (WIP). * src/modules/kdenlive/Makefile, src/modules/kdenlive/factory.c, src/modules/kdenlive/filter_boxblur.yml, src/modules/kdenlive/filter_freeze.yml, src/modules/kdenlive/filter_wave.yml, src/modules/kdenlive/producer_framebuffer.yml: Add service metadata for kdenlive module (WIP). * src/modules/gtk2/Makefile, src/modules/gtk2/consumer_gtk2_preview.yml, src/modules/gtk2/factory.c, src/modules/gtk2/producer_pango.yml, src/modules/gtk2/producer_pixbuf.yml: Add service metadata for gtk2 module (WIP). * src/modules/effectv/Makefile, src/modules/effectv/factory.c, src/modules/effectv/filter_burningtv.yml: Add service metadata for effectv module (WIP). * src/modules/dv/Makefile, src/modules/dv/consumer_libdv.yml, src/modules/dv/factory.c, src/modules/dv/producer_libdv.yml: Add service metdata for dv module (WIP). * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_audiowave.yml, src/modules/core/filter_brightness.yml, src/modules/core/filter_channelcopy.yml, src/modules/core/filter_crop.yml, src/modules/core/filter_data_show.yml, src/modules/core/filter_gamma.yml, src/modules/core/filter_greyscale.yml, src/modules/core/filter_luma.yml, src/modules/core/filter_mirror.yml, src/modules/core/filter_mono.yml, src/modules/core/filter_obscure.yml, src/modules/core/filter_region.yml, src/modules/core/filter_transition.yml, src/modules/core/filter_watermark.yml, src/modules/core/producer_colour.yml, src/modules/core/producer_consumer.yml, src/modules/core/producer_hold.yml, src/modules/core/producer_noise.yml, src/modules/core/transition_composite.yml, src/modules/core/transition_luma.yml, src/modules/core/transition_mix.yml, src/modules/core/transition_region.yml: Add service metadata for core module (WIP). 2011-05-28 Dan Dennedy * src/swig/python/getimage.py, src/swig/python/waveforms.py: Convert Python examples to new frame method. * src/framework/mlt_profile.c, src/framework/mlt_profile.h, src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h: Add mlt_profile_from_producer(). This new function contains the auto-profile feature. Plus setters for Mlt::Profile. 2011-05-22 Dan Dennedy * src/modules/jackrack/Makefile, src/modules/jackrack/blacklist.txt, src/modules/jackrack/plugin_mgr.c, src/modules/jackrack/plugin_mgr.h: Add blacklist for ladspa filters. Initially includes dssi-vst since that is unstable on AV Linux 5. 2011-05-17 Dan Dennedy * src/modules/jackrack/factory.c, src/modules/jackrack/jack_rack.c: Let all instances of ladspa share single plugin_mgr. 2011-05-15 Dan Dennedy * docs/melt.1, src/melt/melt.c: Document -jack option. * src/melt/melt.c, src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/process.c: Add first draft of JACK transport sync. 2011-05-14 Dan Dennedy * configure, src/mlt++/configure: Fix build on Debian GNU/kFreeBSD. 2011-05-12 Dan Dennedy * Makefile, presets/consumer/avformat/dv_ntsc/DVD, presets/consumer/avformat/dv_ntsc_wide/DVD, presets/consumer/avformat/dv_pal/DVD, presets/consumer/avformat/dv_pal_wide/DVD, setenv, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_repository.c, src/framework/mlt_repository.h, src/melt/melt.c, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/MltRepository.cpp, src/mlt++/MltRepository.h, src/swig/mlt.i: Presets! Put property setters in a file and apply them to a service using properties=filename. Alternatively, apply a supplied preset using properties=preset. For example, melt ... -consumer avformat:my.vob properties=DVD. * src/modules/jackrack/jack_rack.c, src/modules/jackrack/plugin.c, src/modules/jackrack/plugin_mgr.c, src/modules/jackrack/process.c: Convert jackrack printfs to mlt_log (3301094). 2011-05-10 Dan Dennedy * src/modules/jackrack/Makefile, src/modules/jackrack/factory.c, src/modules/jackrack/filter_jackrack.yml, src/modules/jackrack/filter_ladspa.yml: Add service metadata for jackrack, ladspa, and ladspa.id. * src/modules/jackrack/filter_ladspa.c, src/modules/jackrack/plugin_mgr.c: Add support for ladspa.id variants. This is much more convenient to use without having to compose the JACK Rack XML. Also, we will be able to add future support for property animation (automation), which JACK Rack lacks. However, it does still support loading and processing JACK Rack files. 2011-05-08 Dan Dennedy * src/modules/sox/Makefile, src/modules/sox/factory.c, src/modules/sox/filter_sox.c, src/modules/sox/filter_sox.yml: Add support for sox.effect variants. The legacy forms of 'sox:"effect options"' and 'sox effect="name options"' still work. The new forms allows them all to be enumerated by Mlt apps, e.g.: melt -query filter. Also, this registers metadata for both the generic 'sox' filter and all of the new 'sox.effect' ones including their usage help! * src/modules/avformat/filter_avresample.c, src/modules/core/loader.ini: Fix inadvertent reording of resample filters. And add debug log to avresample. 2011-05-06 Dan Dennedy * src/framework/mlt_tractor.c, src/modules/avformat/producer_avformat.c, src/modules/dv/producer_libdv.c, src/modules/vorbis/producer_vorbis.c: Make the frame audio properties consistent. * src/framework/mlt_frame.c, src/framework/mlt_types.h: Add mlt_audio_s32le and mlt_audio_f32le audio formats. * src/modules/avformat/filter_avresample.c, src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_audiochannels.c, src/modules/core/loader.ini, src/modules/resample/filter_resample.c: Add audiochannels normalization filter. Refactors code from the resamplers into a new filter to be more manageable. Eventually, we can add options on what to do when adding/removing channels. 2011-05-03 Dan Dennedy * configure, src/framework/mlt_transition.c: Ensure transition B frames get some consumer properties. Also, ensure both A and B frames have sane scaling and aspect ratio values. This addresses an issue where composite and region were not getting the correct deinterlace method impacting performance. In addition, it factors out some common code (best practice) from various transitions moving it into the framework. 2011-05-01 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 0.7.2 * src/modules/avformat/filter_avresample.c, src/modules/resample/filter_resample.c: Finish work to normalize channel count. Also, refactor the audio resamplers to use mlt_audio_format_size() and mlt_frame_set_audio(). Currently, there are no controls over which channels to drop or duplicate. * src/framework/mlt_tractor.c, src/modules/core/producer_consumer.c, src/modules/core/producer_noise.c, src/modules/core/producer_ppm.c: Refactor to mlt_frame_set_audio(). * src/modules/feeds/NTSC/data_fx.properties, src/modules/feeds/PAL/data_fx.properties: Fix alpha on color of some data-feed properties. * src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c: Add support for new codec- and muxer-specific AVOptions. 2011-04-22 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Use new avio functions in avformat module. 2011-04-21 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Fix regression initializing coefficients. * src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c: Fix avformat compile warns on const and 64-bit string formatting. * src/modules/core/producer_hold.c, src/modules/jackrack/filter_jackrack.c: Fix couple compile warns. * src/framework/mlt_property.c, src/framework/mlt_types.h, src/modules/linsys/sdi_generator.c: Fix some compile warnings about string-formatting 64bit. * src/modules/avformat/audioconvert.h, src/modules/avformat/producer_avformat.c: Drop private audioconvert.h for public samplefmt.h. 2011-04-20 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c: More libavcodec v53 changes required. * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Fix avformat build with libavcodec v53. 2011-04-09 Dan Dennedy * src/framework/mlt_profile.c, src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h, src/swig/mlt.i: Add Mlt::Profile.list(). 2011-04-07 Dan Dennedy * src/modules/avformat/consumer_avformat.yml, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.yml: Add avformat consumer metadata. Improve avformat producer metadata. Significantly extend each with AVOptions. 2011-04-04 Dan Dennedy * demo/README, demo/demo.ini, demo/mlt_pango_keyframes, demo/pango_keyframes.mpl: Add mlt_pango_keyframes demo. 2011-04-03 Dan Dennedy * docs/melt.1, docs/melt.txt, src/melt/melt.c: Add -query formats and codecs to melt. * docs/melt.1, docs/melt.txt, src/melt/melt.c: Add -query profile to melt. * src/framework/mlt_profile.c, src/framework/mlt_profile.h: Add mlt_profile_list(). 2011-03-31 Dan Dennedy * src/modules/decklink/consumer_decklink.yml, src/modules/decklink/producer_decklink.yml: Add metadata for decklink consumer. * src/modules/decklink/Makefile, src/modules/decklink/producer_decklink.cpp, src/modules/decklink/producer_decklink.yml: Add decklink producer. 2011-03-27 Dan Dennedy * configure, src/modules/core/transition_composite.c: Fix regression in region filter (3251260). * Doxyfile, configure, src/framework/mlt_version.h: Set version to 0.7.0 * src/modules/avformat/consumer_avformat.c, src/modules/sox/filter_sox.c, src/modules/xml/producer_xml.c: Use mlt_properties_get_value where possible. * src/framework/mlt_playlist.c, src/framework/mlt_producer.c, src/framework/mlt_properties.c, src/framework/mlt_properties.h, src/framework/mlt_tractor.c, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h: Add mlt_properties_lock and _unlock. Fixes some concurrency safetiness problems. 2011-03-24 Dan Dennedy * src/modules/decklink/configure, src/modules/kino/configure, src/modules/linsys/configure: Enable linsys by default on Linux. Disable linsys and decklink by default on OS X and Windows. 2011-03-23 Dan Dennedy * src/framework/mlt_consumer.c, src/modules/decklink/consumer_decklink.cpp: Fix a couple null pointer bugs. 2011-03-20 Dan Dennedy * src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/plugin.h, src/modules/jackrack/process.c: Fix build of jackrack module on mingw. 2011-03-19 Dan Dennedy * profiles/atsc_1080p_50, profiles/atsc_1080p_5994, profiles/atsc_1080p_60: Add high frame rate 1080p profiles. 2011-03-09 Dan Dennedy * src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/frei0r_helper.h, src/modules/frei0r/producer_frei0r.c, src/modules/frei0r/transition_frei0r.c: Refactor frei0r and fix time parameter. Refactored to use mlt_filter_get_position and mlt_transition_get_position. frei0r's time parameter is seconds, but we were passing frame count. * src/modules/core/transition_region.c, src/modules/plus/transition_affine.c: Refactor to mlt_transition_get_position() * src/framework/mlt_transition.c, src/framework/mlt_transition.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h: Add mlt_transition_get_position() * src/modules/core/filter_luma.c, src/modules/core/filter_watermark.c, src/modules/dgraft/filter_telecide.c, src/modules/kdenlive/filter_freeze.c, src/modules/motion_est/filter_autotrack_rectangle.c, src/modules/motion_est/filter_crop_detect.c, src/modules/oldfilm/filter_vignette.c, src/modules/plus/filter_affine.c, src/modules/vmfx/filter_shape.c: Refactor to mlt_filter_get_position(). 2011-03-08 Dan Dennedy * src/framework/mlt_filter.c, src/framework/mlt_transition.c: Use the producer when filter/transition always active. * src/framework/mlt_filter.c, src/framework/mlt_filter.h, src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h: Add mlt_filter_get_position(). 2011-03-07 Dan Dennedy * src/modules/core/transition_composite.c, src/modules/core/transition_luma.c: Refactor to mlt_transition_get_progress_delta(). * src/framework/mlt_transition.c, src/framework/mlt_transition.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h: Add mlt_transition_get_progress_delta(). * src/modules/core/transition_luma.c, src/modules/core/transition_mix.c: Refactor to mlt_transition_get_progress(). * src/framework/mlt_transition.c, src/framework/mlt_transition.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h: Add mlt_transition_get_progress(). * src/modules/core/filter_brightness.c, src/modules/core/filter_obscure.c, src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_wave.c, src/modules/normalize/filter_volume.c, src/modules/oldfilm/filter_dust.c, src/modules/oldfilm/filter_grain.c, src/modules/oldfilm/filter_lines.c, src/modules/oldfilm/filter_oldfilm.c: Refactor to mlt_filter_get_progress(). * src/framework/mlt_filter.c, src/framework/mlt_filter.h, src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h: Add mlt_filter_get_progress(). 2011-03-10 Dan Dennedy * src/modules/swfdec/Makefile, src/modules/swfdec/configure: Add build support for swfdec 0.7. And prioritize newer versions over older ones. 2011-03-06 Dan Dennedy * src/modules/core/transition_composite.c, src/modules/plus/transition_affine.c: Refactor to use mlt_transition_get_length(). * src/framework/mlt_transition.c, src/framework/mlt_transition.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h: Add mlt_transition_get_length(). * src/framework/mlt_filter.c, src/framework/mlt_filter.h, src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h: Add mlt_filter_get_length(). * src/modules/core/filter_audioconvert.c, src/modules/core/filter_mono.c, src/modules/core/producer_consumer.c: Refactor to mlt_audio_format_size(). * src/framework/mlt_frame.c, src/framework/mlt_frame.h: Add mlt_audio_format_size(). * src/modules/core/producer_noise.c, src/modules/normalize/filter_volume.c: Remove unused variables. * src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/core/filter_crop.c, src/modules/core/filter_resize.c, src/modules/core/producer_colour.c, src/modules/gtk2/filter_rescale.c, src/modules/kdenlive/filter_freeze.c, src/modules/kdenlive/producer_framebuffer.c: Refactor to use mlt_image_format_size(). * src/framework/mlt_frame.c, src/framework/mlt_frame.h: Add mlt_image_format_size() * src/framework/mlt_tractor.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/core/filter_audiowave.c, src/modules/core/filter_crop.c, src/modules/core/filter_imageconvert.c, src/modules/core/filter_luma.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_consumer.c, src/modules/core/producer_hold.c, src/modules/core/producer_noise.c, src/modules/core/producer_ppm.c, src/modules/core/transition_composite.c, src/modules/core/transition_region.c, src/modules/dgraft/filter_telecide.c, src/modules/dv/producer_libdv.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/producer_frei0r.c, src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/kdenlive/filter_freeze.c, src/modules/kdenlive/filter_wave.c, src/modules/kdenlive/producer_framebuffer.c, src/modules/motion_est/producer_slowmotion.c, src/modules/plus/filter_affine.c, src/modules/plus/filter_charcoal.c, src/modules/qimage/producer_kdenlivetitle.c, src/modules/qimage/producer_qimage.c, src/modules/sdl/producer_sdl_image.c, src/modules/swfdec/producer_swfdec.c, src/modules/vmfx/producer_pgm.c, src/modules/xine/filter_deinterlace.c: Refactor to use mlt_frame_set_image/_alpha. * src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/mlt++/MltFrame.cpp, src/mlt++/MltFrame.h: Add mlt_frame_set_image and mlt_frame_set_alpha. * src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c: Alias bicubic for hyper in pango and pixbuf. 2011-03-02 Dan Dennedy * src/framework/mlt_frame.c, src/framework/mlt_frame.h: Add mlt_frame_unique_properties(). * 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/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c: Rename 'this' in avformat module. 2011-03-01 Till Theato * src/modules/rotoscoping/filter_rotoscoping.c, src/modules/rotoscoping/filter_rotoscoping.yml: rotoscoping: remove parameter precision. Its influence on speed was very minimal while it caused some crashes. Also update YAML filter description. 2011-02-28 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_deque.c, src/framework/mlt_events.c, src/framework/mlt_factory.c, src/framework/mlt_field.c, src/framework/mlt_filter.c, src/framework/mlt_frame.c, src/framework/mlt_geometry.c, src/framework/mlt_multitrack.c, src/framework/mlt_parser.c, src/framework/mlt_playlist.c, src/framework/mlt_pool.c, src/framework/mlt_producer.c, src/framework/mlt_profile.c, src/framework/mlt_properties.c, src/framework/mlt_property.c, src/framework/mlt_property.h, src/framework/mlt_repository.c, src/framework/mlt_service.c, src/framework/mlt_tokeniser.h, src/framework/mlt_tractor.c, src/framework/mlt_transition.c: Rename this to self in the framework. This makes doxygen output better match the headers, and it improves life within a code-parsing IDE like Qt Creator. 2011-02-27 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Add support for FFmpeg AVMetadata API. 2011-02-20 Dan Dennedy * src/modules/frei0r/Makefile, src/modules/frei0r/factory.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/not_thread_safe.txt: Mark some frei0r plugins as not thread safe. 2011-02-19 Dan Dennedy * docs/install.txt, docs/mlt-xml.txt, docs/services.txt, setenv: Remove info about mainconcept and bluefish services. * src/framework/mlt_producer.c, src/framework/mlt_producer.h, src/modules/core/producer_consumer.c, src/modules/core/producer_hold.c, src/modules/core/producer_noise.c, src/modules/frei0r/factory.c, src/modules/motion_est/producer_slowmotion.c: Add profile parameter to mlt_producer_new. 2011-02-19 j-b-m * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp: Store exif orientation. Patch attached internally stores the exif orientation so that it can be accessible to the framework and apps using it. Useful it in Kdenlive to correctly rotate images when creating proxy images. 2011-02-13 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/modules/sdl/consumer_sdl_preview.c: Fix deadlocks in sdl_preview with parallel-consumer. 2011-01-27 Till Theato * src/modules/rotoscoping/Makefile, src/modules/rotoscoping/factory.c, src/modules/rotoscoping/filter_rotoscoping.c, src/modules/rotoscoping/filter_rotoscoping.yml: Rotoscoping: Set default mode to alpha and add YAML filter description 2010-11-23 Dan Dennedy * src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/frei0r_helper.h, src/modules/frei0r/producer_frei0r.c, src/modules/frei0r/transition_frei0r.c: Reduce service lock contention in frei0r module. 2010-10-04 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/framework/mlt_frame.h: Use a single queue for parallel workers. This is a major change from the previous model of moving work items (frames) from one queue to another. This new model improves the behavior of realtime mode and performance overall. In the new model, a single queue is used along with an is_processed flag on the frame. Also, there is an index into the queue (process_head) that indicates from which point should a worker consider fetching the next unprocessed frame. There are situations in realtime mode where the processing of a frame takes longer than the queue (or from head to its fetch index). Over extended periods of this heavy processing, the video frame in the consumer may never be updated (rendered=1)! To remedy this, the consumer detects this and automatically moves the process_head towards the tail, but even this may not be good enough. The only real remedy is to increase buffers and suffer with poor latency. If lower latency is preferred, then it may be better to not use realtime mode and permit audio discontinuity. * src/framework/mlt_deque.c, src/framework/mlt_deque.h: Add mlt_deque_peek() with index. 2010-06-14 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_frame.c, src/framework/mlt_tractor.c, src/modules/core/filter_imageconvert.c, src/modules/sdl/consumer_sdl.c: Fix image format consistency and conversion. 2010-06-11 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_tractor.c: Remove the tractor service locking. This completely inhibited parallelism, but removing it also exposes more race conditions that require resolution. 2010-03-04 Dan Dennedy * src/modules/motion_est/filter_autotrack_rectangle.c, src/modules/motion_est/filter_crop_detect.c, src/modules/motion_est/filter_motion_est.c, src/modules/normalize/filter_volume.c, src/modules/oldfilm/filter_dust.c, src/modules/oldfilm/filter_lines.c, src/modules/plus/filter_affine.c, src/modules/plus/transition_affine.c, src/modules/qimage/producer_kdenlivetitle.c, src/modules/qimage/producer_qimage.c, src/modules/sox/filter_sox.c, src/modules/vorbis/producer_vorbis.c: Add service locks for parallelism. * src/modules/avformat/filter_avresample.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/core/filter_data_show.c, src/modules/core/filter_luma.c, src/modules/core/filter_watermark.c, src/modules/core/producer_colour.c, src/modules/core/transition_composite.c, src/modules/core/transition_luma.c, src/modules/core/transition_region.c, src/modules/effectv/filter_burn.c, src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/producer_frei0r.c, src/modules/frei0r/transition_frei0r.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/kdenlive/filter_freeze.c, src/modules/kdenlive/producer_framebuffer.c, src/modules/resample/filter_resample.c: Add service locks for parallelism. RGB filters and transitions from frei0r and burningtv are still not safe enough. * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: Add parallelism to mlt_consumer. To use set real_time greater than 1 for frame-dropping or less than -1 for no frame-dropping. It works better with a liberal buffer size. You can still set prefill less than buffer size, but it must be at least the same number as real_time, preferably a little higher to help with frame ordering. 2010-02-20 Dan Dennedy * src/framework/mlt_deque.c, src/framework/mlt_deque.h: Add mlt_deque_insert(). 2010-02-16 Dan Dennedy * src/framework/mlt_consumer.c, src/framework/mlt_consumer.h: Qualify queue, mutex, and cond vars with frame_queue_. 2011-01-23 Dan Dennedy * configure, src/framework/mlt_version.h: Move to an interim version number. 2011-01-17 Dan Dennedy * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_still.c: SDL tweaks for Windows discovered when embedded. * src/framework/Makefile, src/mlt++/Makefile, src/mlt++/config.h: On Windows install .def and version-less DLLs to let apps build against us. * src/framework/mlt_factory.c, src/modules/avformat/configure, src/modules/frei0r/factory.c, src/modules/jackrack/plugin_mgr.c: On Windows locate plugins and data by directory relative to current directory. lib\mlt lib\frei0r-1 lib\ladspa share\mlt share\ffmpeg 2010-12-31 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Cleanup libxml changes for MinGW. * src/modules/jackrack/configure, src/modules/jackrack/jack_rack.c: Fix JackRack build on MinGW. * 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: Fix qimage build for MinGW. 2010-12-30 Dan Dennedy * src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: Fix libxml2 build on MinGW. * src/modules/gtk2/Makefile, src/modules/gtk2/consumer_gtk2.c, src/modules/gtk2/producer_pixbuf.c: Fix gtk2 build on mingw. 2010-12-15 Dan Dennedy * src/melt/Makefile, src/melt/io.c, src/melt/melt.c, src/modules/sdl/consumer_sdl.c: Fix SDL and keyboard input on Win32. 2010-12-03 Dan Dennedy * configure, src/framework/Makefile, src/melt/Makefile, src/melt/io.c, src/mlt++/Makefile, src/mlt++/MltFactory.cpp, src/mlt++/MltFactory.h, src/mlt++/config.h, src/mlt++/configure, src/modules/avformat/Makefile, src/modules/core/Makefile, src/modules/core/producer_loader.c, src/modules/kino/configure, src/modules/motion_est/Makefile, src/modules/sdl/Makefile, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c, src/win32/fnmatch.c, src/win32/fnmatch.h, src/win32/win32.c: Initial port to Windows using MinGW. Much of the credit goes to Michael Zenov. 2011-01-23 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: Set version to 0.6.2. 2011-01-16 Till Theato * src/modules/rotoscoping/Makefile, src/modules/rotoscoping/cJSON.c, src/modules/rotoscoping/cJSON.h, src/modules/rotoscoping/filter_rotoscoping.c: Rotoscoping: Add support for simple keyframes - current limits: - number of points has to be equal for all keyframes - points have to be in "correct" order (1. point in 1. kf will be moved to 1. point in 2. kf, ...) - the parameter "polygon" is now formated using json: - no keyframes: polygon="[[x,y], [x,y], ...]" - keyframes: polygon= '{ "framepos1" : [[x,y], [x,y], ...], "framepos2" : [[x,y], [x,y], ...], ...}' 2011-01-15 Till Theato * src/modules/rotoscoping/Makefile, src/modules/rotoscoping/factory.c, src/modules/rotoscoping/filter_rotoscoping.c: Add rotoscoping filter (WIP): It hides everything not in the polygon defined by the vertices given through the "polygon" parameter 2011-01-11 Dan Dennedy * configure, src/mlt++/configure, src/modules/avformat/configure, src/modules/kino/endian_types.h, src/modules/kino/riff.cc, src/modules/qimage/configure, src/modules/sox/configure: Enable build on NetBSD (3090684) * src/modules/kino/Makefile, src/modules/qimage/Makefile: Use CXX rather than CC for linking C++ (3090682) * src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c: Fix undefined bahavior in SDL module (3066195). The standard says the post-increment can have effect at any point between the previous and the next sequence point (or something similar), so the behavior of "this->refresh_count = this->refresh_count ++" is undefined. Patch by Cristian Morales Vega 2011-01-10 Dan Dennedy * src/modules/plus/interp.h, src/modules/plus/transition_affine.c: Add geometry opacity interpretation to affine. Also, fixes interpolation method selection and removes a redundant bounds test. 2011-01-10 j-b-m * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: Add force_aspect_ratio to image producers. 2011-01-10 Dan Dennedy * configure, src/framework/mlt_version.h: Move to an interim version. 2011-01-01 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt_version.h: set version to 0.6.0 * src/modules/feeds/NTSC/etv.properties, src/modules/feeds/PAL/etv.properties: Make etv data feeds same and scalable between NTSC and PAL. * demo/mlt_attributes, src/modules/feeds/NTSC/data_fx.properties, src/modules/feeds/NTSC/etv.properties: Make feeds consistent between NTSC and PAL. Fix mlt_attributes demo. * demo/README, demo/mlt_slideshow, demo/mlt_slideshow_black: Convert "Scotland" in demos to "photos" * demo/svg.mlt, src/modules/xml/producer_xml.c: Fix parsing mixed XML documents and svg.mlt example. 2010-12-27 Dan Dennedy * src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h: Revert Producer::set_speed and add Producer::pause. The new Producer::pause contains the wait for consumer-sdl-paused. 2010-12-22 Dan Dennedy * src/framework/mlt_frame.c, src/framework/mlt_frame.h: Add mlt_frame_write_ppm to visualize debugging. 2010-12-19 Dan Dennedy * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c: Increase the speed of switching between sdl_still and sdl. Based on patch from Jonathan Thomas. It does this by not calling the SDL_InitSubSystem( SDL_INIT_AUDIO ) and SDL_QuitSubSystem( SDL_INIT_AUDIO ) methods every time it switches, but rather when the SDL Preview consumer is started and stopped. 2010-12-16 Dan Dennedy * src/framework/Makefile, src/framework/mlt.h, src/framework/mlt_version.c, src/framework/mlt_version.h, src/swig/mlt.i: Add mlt_version API. Contributed by Jonathan Thomas. 2010-12-15 Dan Dennedy * src/mlt++/MltProducer.cpp, src/modules/sdl/consumer_sdl_preview.c: Synchronize Producer.set_speed(0) with sdl_preview. This also helps prevent deadlock while waiting for consumer-sdl-paused event. Not 100% yet, but 100% requires script (swig) apps to handle the event asynchronously via an event listener, which is not available yet for most - only ruby. Furthermore, they would really like to be able to pass opaque data to the asynchronous handler, which is not yet available in the framework. A good example here is pausing playback prior to seeking to a specific frame. The app should be able to make a consumer-paused event handler to which it can pass the new position, so it can properly seek after the pause has officially occurred. Without the ability to pass opaque data, it must save the new position as an instance variable to use within the handler - once it has support for event listeners that is. 2010-12-09 Dan Dennedy * src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h: Added Mlt::Properties::wait_for(string). * src/framework/mlt_log.c, src/melt/melt.c, src/modules/avformat/consumer_avformat.c: Add consumer-fatal-error event to avformat consumer. This addresses Kdenlive bug 1894. When the avformat consumer has a fatal error, it will fire an event. Melt intercepts the event and exits with failure. 2010-12-08 Dan Dennedy * configure, src/framework/mlt_consumer.c, src/framework/mlt_consumer.h, src/framework/mlt_types.h, src/melt/melt.c, src/mlt++/MltConsumer.cpp, src/mlt++/MltConsumer.h: Add mlt_consumer_position (Mlt::Consumer::position). 2010-11-30 Dan Dennedy * demo/mlt_swf_variables, demo/txtField.swf: Add example of using SWF with variables. 2010-11-07 Dan Dennedy * src/modules/core/loader.dict, src/modules/swfdec/Makefile, src/modules/swfdec/producer_swfdec.c: Add swfdec producer. No audio or variables/parameters yet. 2010-11-06 Dan Dennedy * src/modules/decklink/DeckLinkAPI.h, src/modules/decklink/DeckLinkAPIDispatch.cpp, src/modules/decklink/LinuxCOM.h, src/modules/decklink/Makefile, src/modules/decklink/consumer_decklink.cpp: Add Blackmagic Design DeckLink consumer. 2010-10-20 Dan Dennedy * src/framework/mlt_tokeniser.c, src/modules/frei0r/frei0r_helper.c: Add support for frei0r string parameter. 2010-10-17 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/producer_kdenlivetitle.c: Fix serializing xmldata in kdenlivetitle (kdenlive-1841). Patch below fixes an issue with the kdenlivetitle producer. Basically, the problem was that when loading a kdenlivetitle from a file, all the properties were serialized and passed to the xml consumer. The problem became more obvious with the "embeded" images in titles, which then caused images to be embedded inside the kdenlive project file, causing problems like reported in this issue: http://kdenlive.org/mantis/view.php?id=1841 With the patch, titles loaded from a file will not copy the xmldata. 2010-10-17 Dan Dennedy * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c: Fix including SDL headers (3087522). 2010-10-13 Dan Dennedy * src/melt/melt.c, src/modules/avformat/producer_avformat.c: Add colorspace to auto-profile. * src/modules/xml/consumer_xml.c, src/modules/xml/producer_xml.c: (De)serialize colorspace in profile. * src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c: Fix version support for AVCodec:colorspace. 2010-10-07 Dan Dennedy * src/melt/melt.c, src/modules/core/producer_consumer.c, src/modules/core/producer_loader.c, src/modules/melt/producer_melt.c, src/modules/xml/producer_xml.c: Move logic for when to auto-insert consumer producer. Move it into the loader producer so apps other than melt can use it too. To use it, an app must set the profile to explicit. * src/framework/mlt_profile.c, src/framework/mlt_profile.h: Add mlt_profile_clone(). 2010-08-28 Dan Dennedy * src/melt/melt.c, src/modules/melt/producer_melt.c: Add an automatic profile feature to melt. Here are the main use cases this feature provides: - Given a regular (non-mlt-xml) media file, melt reads the media attributes and generates an equivalent MLT profile. This makes it easier to transcode without changing or specifying resolution, aspect, and framerate. - Given a MLT XML file containing a profile attribute or element, melt loads the specified profile. A composition typically contains profile- without you having to remember. - Given a MLT XML containing a profile but also specifying a -profile option, melt automatically uses the 'consumer' producer with the requested profiles. This is similar to the above case, but when explicitly choosing a profile different than the composition one should use the consumer producer. This just makes melt smarter and more automatic. * src/modules/xml/consumer_xml.c, src/modules/xml/mlt-xml.dtd, src/modules/xml/producer_xml.c: Add (de)serialization of profile to XML. In addition to the 'profile' element, one can also set the 'profile' attribute of the root element to a named profile. 2010-10-04 Dan Dennedy * src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c: Add support for short-hand vpre to avformat consumer. For example, when vcodec=libx264, you can use vpre=medium as shorthand for $prefix/share/ffmpeg/libx264-medium.ffpreset. * src/modules/avformat/audioconvert.h, src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c, src/modules/avformat/factory.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_avdeinterlace.c, src/modules/avformat/filter_avresample.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c: FFmpeg build improvements (3078007). Handle --avformat-svn-version=0.5. Fix building without swscale. Fix compiling new colorspace stuff against FFmpeg <= v0.5. FFmpeg libs are increasing; only support contemporary header layout. 2010-09-28 Dan Dennedy * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c: Fix race conditions in SDL (kdenlive-1711). Contributed patch by 'jem' - thanks! 2010-09-26 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Set default colorspace (from profile) on frames. Also, allow affirmatively setting luma to _not_ full range (force_full_luma=0). * profiles/atsc_1080i_50, profiles/atsc_1080i_5994, profiles/atsc_1080i_60, profiles/atsc_1080p_2398, profiles/atsc_1080p_24, profiles/atsc_1080p_25, profiles/atsc_1080p_2997, profiles/atsc_1080p_30, profiles/atsc_720p_2398, profiles/atsc_720p_24, profiles/atsc_720p_25, profiles/atsc_720p_2997, profiles/atsc_720p_30, profiles/atsc_720p_50, profiles/atsc_720p_5994, profiles/atsc_720p_60, profiles/cif_15, 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_25p, profiles/hdv_1080_30p, profiles/hdv_1080_50i, profiles/hdv_1080_60i, profiles/hdv_720_25p, profiles/hdv_720_30p, profiles/hdv_720_50p, profiles/hdv_720_60p, profiles/qcif_15, profiles/qcif_ntsc, profiles/qcif_pal, profiles/quarter_15, profiles/quarter_ntsc, profiles/quarter_ntsc_wide, profiles/quarter_pal, profiles/quarter_pal_wide, profiles/sdi_486i_5994, 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: Add colorspace to all profile presets. * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Rename variables and properties around luma range for clarity. Frame property "force_full_luma" controls this and can be set via producer property "set.force_full_luma." However, it is not really ready for use until libswscale can respect its full_range parameter in a RGB to YUV conversion. 2010-09-13 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Rename yuv_std to colorspace. 2010-08-24 Dan Dennedy * src/framework/mlt_profile.h, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Add input YUV colorspace (601 vs 709) handling. Still need to work on the output side including normalization and setting the encoder. * src/framework/mlt_frame.c, src/framework/mlt_types.h, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c, src/modules/sdl/producer_sdl_image.c: Revert new image types. I think we can just use frame properties. * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/producer_avformat.c: Get initial skipping of luma scaling to work. When the avformat producer property skip_luma_scale is set to 1, then we do not scale the luma on the first YCbCr to RGB conversion. This is only done once because swscale always downscales luma when converting RGB to YCbCr, and we need to keep the conversions symmetrical to prevent luma contraction (loss of contrast). 2010-08-23 Dan Dennedy * src/framework/mlt_frame.c, src/framework/mlt_profile.h, src/framework/mlt_types.h, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c, src/modules/sdl/producer_sdl_image.c: Improve colorspace handling (work in progress) Trying to add support for non-scaling luma between YCbCr and RGB conversions as well as support for ITU Rec. 709 luma conversion for HD formats. 2010-09-13 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.5.10. 2010-09-12 Dan Dennedy * configure, src/modules/core/producer_loader.c: Enable filter avcolor_space on OS X. It works now! * Doxyfile, NEWS, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.5.8. * src/modules/avformat/filter_avcolour_space.c, src/modules/core/filter_imageconvert.c: Enhance image conversion logging. 2010-09-10 Dan Dennedy * src/modules/core/filter_crop.c, src/modules/core/filter_resize.c: Validate alpha channel size before cropping and padding it. Eventually, I need to add mlt_frame_get_alpha() that returns a size and mlt_frame_set_alpha() encapsulates handling of the alpha channel. 2010-09-07 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c: Revert 3a419b4 (Use caching for swscale contexts). This was just making it too unstable (bug 3060324). 2010-09-05 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/core/filter_imageconvert.c: Apply alpha on frame to rgba image (kdenlive-1786). 2010-08-22 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c: Use caching for swscale contexts. 2010-08-21 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c: Enable swscale CPU flags. For FFmpeg builds that use runtime CPU detection. This should make things faster and it seems to be same quality as C routines. * demo/mlt_slideshow2, src/modules/core/filter_luma.c: Enhance luma filter to work with animated filters. Previously, in a slideshow the luma filter would apply the dissolve or wipe repeatedly over a slide. For example, with a slide duration of 75 frames and a luma period of 25 (expressed as 24), the wipe occurs 3 times. However, since the slides were static, you did not notice it until the transition at the beginning of a new slide - when you do want to see it. However, upon adding an affine filter to animate a smooth pan/zoom, you do notice the extra repetitions - the slides appear to blend with one another when they are not transitioning. This change fixes that with new properties 'cycle' and 'duration'. Cycle is basically a replacement for 'period' that fixes the semantics to properly represent a duration. Where you would previously express, for example, period=24, you now say cycle=25. The 'duration' property prevents the repeating and expresses that the transition should only occur within the first N frames of the cycle. See demo/mlt_slideshow2 for an example of using it in conjunction with the affine filter! 2010-08-20 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp: Fix distorted frame in slideshow transitions. Applies to the .all.ext slideshow approach. May also apply to image sequences with mixed resolutions. 2010-08-19 Dan Dennedy * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_audiowave.c: Add audiowave filter. This replaces the video with the audio waveform. Currently, it only works on producers that also provide video. 2010-08-18 Dan Dennedy * src/framework/mlt_frame.c, src/swig/mlt.i, src/swig/python/waveforms.py: Fix waveform generation. It was not obtaining a valid fps. Also, changed rendering to something more expected - negative as negative and channels stacked. Also, add a Python binding to this call to return 8-bit grayscale image as a Python string. Finally, add a Python example. 2010-08-16 Dan Dennedy * src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_panner.c: Add a panning filter. This does a simple left/right balance when channel=-1 (default). When channel >= 0, you can adjust an individual channel's left/right position. Whereas the simple balance will not cause one channel to appear in another channel, the individual channel does. The start/end properties are floats in the range [-1.0, 1.0]. A start property alone makes it constant over the duration of the filter. There is some handling for more than 2 channels by providing front/rear fade and ganging (balance front and rear together or fade left and right together). 2010-08-15 Dan Dennedy * src/modules/core/factory.c, src/modules/core/filter_channelcopy.c: Add filter channelswap. It is a permutation of channelcopy that can be used from channelcopy as well by setting swap=1. 2010-08-14 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c: Improve quality of libswscale conversions and scaling. 2010-08-12 Dan Dennedy * src/framework/mlt_frame.h, src/modules/core/filter_imageconvert.c, src/modules/core/producer_colour.c, src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c: Cleanup existing native color space conversions. This change clarifies that the existing conversions are according to the ITU 601 standard and scaled to and from full gamut RGB. Also, adjust 2 coefficients according to Charles Poynton's matrices. This does not yet attempt to make any substantial improvements. Finally, it replaces the verbose logic and redundancy in the image conversion routine with a concise function dispatch table. 2010-08-08 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp: Make libexif include compatible with more systems/versions. 2010-08-07 Dan Dennedy * src/modules/core/transition_luma.c, src/modules/frei0r/transition_frei0r.c, src/modules/plus/transition_affine.c: Fix scaling method on B frames of some transitions. 2010-08-04 Dan Dennedy * src/modules/avformat/consumer_avformat.c, src/modules/dv/consumer_libdv.c, src/modules/linsys/consumer_SDIstream.c, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_still.c: Move firing consumer-frame-show to after done with image. 2010-07-29 j-b-m * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp: Cleanup & fix memleak modified: gtk2/producer_pixbuf.c modified: qimage/qimage_wrapper.cpp 2010-07-28 j-b-m * src/modules/gtk2/Makefile, src/modules/gtk2/configure, src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/Makefile, src/modules/qimage/configure, src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/readexif.h: Use libexif to read exif orientation in images modified: src/modules/gtk2/Makefile modified: src/modules/gtk2/configure modified: src/modules/gtk2/producer_pixbuf.c modified: src/modules/qimage/Makefile modified: src/modules/qimage/configure modified: src/modules/qimage/qimage_wrapper.cpp deleted: src/modules/qimage/readexif.h 2010-07-27 j-b-m * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/readexif.h: Read EXIF info inside MLT, based on jpegexiforient modified: src/modules/gtk2/producer_pixbuf.c modified: src/modules/qimage/qimage_wrapper.cpp new file: src/modules/qimage/readexif.h 2010-07-20 j-b-m * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp: Fix exif rotation angle modified: src/modules/gtk2/producer_pixbuf.c modified: src/modules/qimage/qimage_wrapper.cpp 2010-07-14 Dan Dennedy * configure, src/modules/avformat/producer_avformat.c: Fix crash when repeating frames after failure to decode video. 2010-06-20 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.5.6. * src/modules/avformat/Makefile, src/modules/avformat/configure, src/modules/avformat/factory.c: Fixup local ffmpeg build. Set PIC compiler flag, make libavdevice optional, and set recommended version to 0.6 branch. 2010-06-15 Dan Dennedy * src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/plugin_desc.h: Fix a few compiler warnings in jackrack. 2010-06-02 Dan Dennedy * src/modules/plus/filter_affine.c, src/modules/plus/interp.h, src/modules/plus/transition_affine.c: Revise affine to use interpolation and sub-pixel positioning. 2010-05-18 Dan Dennedy * src/modules/sdl/consumer_sdl_osx.h, src/modules/sdl/consumer_sdl_osx.m: Fix leaking OS X Cocoa objects in SDL consumers. * src/modules/sdl/Makefile, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_still.c: Fix leaking OS X Cocoa objects in SDL consumers. 2010-05-07 Marco Gittler * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/producer_kdenlivetitle.c: interlaced titles 2010-05-02 Dan Dennedy * src/swig/csharp/build, src/swig/java/build, src/swig/lua/build, src/swig/perl/Makefile.PL, src/swig/perl/build, src/swig/php/build, src/swig/python/build, src/swig/ruby/build, src/swig/tcl/build: Fix missing PIC flags for bindings (2931009) Also, use g++ for linking bindings because some systems (OS X) do not otherwise know to link with libstdc++. 2010-04-25 Dan Dennedy * configure, src/modules/core/filter_resize.c: Fix bad stride in yuv422 due to non-even width requests. 2010-04-19 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.5.4. * src/framework/mlt_frame.c, src/modules/avformat/producer_avformat.c: Improve error handling on video decode failure (kdenlive-1553). 2010-04-18 Dan Dennedy * configure, src/modules/avformat/filter_avcolour_space.c: Only use newish version of libswcale. Some early revisions of 0.7.1 would cause garbage on last column of image with non-even width. 2010-04-08 Dan Dennedy * src/swig/configure, src/swig/csharp/build, src/swig/csharp/play.cs, src/swig/csharp/play.sh: Add C# bindings. Thank you to Steeve Descarpentries for the initial contribution. 2010-04-07 Dan Dennedy * src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c, src/modules/linsys/sdi_generator.h: Add automatic driver configuration to sdi consumer. This uses the MLT profile to determine the configuration values: video buffer size, audio buffer size, video frame mode (resolution, frame rate), video data mode (8 bit or v210), number of audio channels, audio sampling rate, audio sample size. It does _not_ set the clock source or the number of buffers for audio and video. 2010-03-10 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.5.2. * src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c, src/modules/linsys/sdi_generator.h: Improve performance of sdi consumer (patch from BCE). consumer_SDIstream.c - convertYCBCRtoRGB: different calculation sdi_generator.h - SDIAUDIO transmitter event definitions sdi_generator.c - pack changed to pack8 instead of packv210 - Transmitter events are checked only once a frame - create_HD_SDI_Line and create_SD_SDI_Line do not calculate the current position in the video_buffer for each sample. Now it is done once a line. * src/mlt++/Makefile, src/mlt++/MltFilteredProducer.cpp, src/mlt++/MltFilteredProducer.h: Fix MltFilteredProducer not building. 2010-02-28 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_swscale.c, src/modules/core/producer_loader.c: Add resolution as init arg to libswscale filters. * src/framework/mlt_frame.c, src/modules/effectv/filter_burn.c: Fix a couple of compile warnings. 2010-02-25 Dan Dennedy * src/modules/avformat/filter_avcolour_space.c, src/modules/core/producer_loader.c: Make FFmpeg the primary image converter if available. Except on OS X. 2010-02-24 Dan Dennedy * src/modules/xine/Makefile, src/modules/xine/yadif.c: Fix build on --disable-sse(2) or non-sse(2) architectures. 2010-02-22 Dan Dennedy * configure, src/modules/sdl/consumer_sdl_preview.c: Fix video glitches when switching still and normal sdl consumers. 2010-02-15 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.5.0. * NEWS, src/modules/avformat/configure: Add v0.5.0 release notes. 2010-02-11 Dan Dennedy * src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c, src/modules/linsys/sdi_generator.h: Add HD-SDI support to Linsys SDI module. This has only been tested with the VidPort. At this time, you must run the linsys sdiaudiocfg and sdivideocfg utilities to configure your card. In time, we hope to remove this step. * src/modules/linsys/Makefile, src/modules/linsys/configure: Add --linsys-with-jpeg configure option. 2010-02-10 Dan Dennedy * profiles/atsc_1080i_50, profiles/atsc_1080i_5994, profiles/atsc_1080i_60, profiles/atsc_1080p_2398, profiles/atsc_1080p_24, profiles/atsc_1080p_25, profiles/atsc_1080p_2997, profiles/atsc_1080p_30, profiles/atsc_720p_2398, profiles/atsc_720p_24, profiles/atsc_720p_25, profiles/atsc_720p_2997, profiles/atsc_720p_30, profiles/atsc_720p_50, profiles/atsc_720p_5994, profiles/atsc_720p_60, profiles/hdv_1080_25p, profiles/hdv_1080_30p, profiles/hdv_1080_50i, profiles/hdv_1080_60i, profiles/hdv_720_25p, profiles/hdv_720_30p, profiles/hdv_720_50p, profiles/hdv_720_60p, profiles/qcif_15, profiles/quarter_15, profiles/quarter_ntsc, profiles/sdi_486i_5994, src/framework/mlt_profile.c: Revise Hz->fps in profiles and add more ATSC profiles. 2010-02-04 Dan Dennedy * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/consumer_sdl_preview.c: Default SDL to use the onefield deinterlace filter. The previous default in the deinterlace filter was linearblend. The new default is yadif. However, onefield is faster is gives cleaner results than linearblend where preserving most resolution is not a factor. Since most usess of SDL are applications with preview windows smaller than actual resolution, it makes sense to use onefield by default in the SDL consumers. * src/modules/xine/Makefile, src/modules/xine/deinterlace.h, src/modules/xine/filter_deinterlace.c, src/modules/xine/vf_yadif_template.h, src/modules/xine/yadif.c, src/modules/xine/yadif.h: Add YADIF methods in deinterlace filter. 2010-02-03 Dan Dennedy * src/framework/mlt_frame.h, src/framework/mlt_service.c, src/framework/mlt_service.h: Hide need_previous_next property from serialization. 2010-02-02 Dan Dennedy * src/framework/mlt_filter.h, src/framework/mlt_frame.h, src/framework/mlt_service.c, src/framework/mlt_service.h: Add fetching previous and next frames in producers. This is only enabled when the property need-previous-next is set true on the producer. This also adds firing a service-changed event on the filter when it gets attached so the filter can set this property on the producer to which it is attached. These frame references are set as "previous frame" and "next frame" properties on the current frame. It is also important to note that these frames do not have ANY filters applied to them, which is important for YADIF and telecide filters, which process before all other filters. 2010-01-21 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c: Let environment variable MLT_NO_VDPAU=1 disable VDPAU. 2010-01-19 Dan Dennedy * src/modules/avformat/configure, src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c: Add support for libavdevice (v4l/v4l2). Thanks to hints from Volodymyr M. Lisivka. LD_PRELOAD=/usr/lib/libv4l/v4l2convert.so is required to get some formats to work. * src/framework/mlt_frame.c, src/modules/core/filter_rescale.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: Return and handle errors on failure to produce image (kdenlive-1312). 2010-01-16 Dan Dennedy * Doxyfile, src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/framework/mlt_service.h: Add mlt_frame doxygen docs. 2010-01-10 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/xine/filter_deinterlace.c: Fix setting progressive property on repeated frames (kdenlive-1335). 2010-01-06 Marco Gittler * src/modules/oldfilm/filter_oldfilm.c, src/modules/oldfilm/filter_oldfilm.yml: user array with 100 values yml file updated 2009-12-16 Dan Dennedy * src/framework/mlt_service.c, src/framework/mlt_service.h, src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: Add mlt_service_cache_purge and remove purge in mlt_service_close. The avformat producer holds references to cache items within frame objects. This means mlt_service_close can not purge the cache because frames may be closed after the producer. 2009-12-14 Dan Dennedy * configure, src/framework/mlt.h: Bump to unreleased version. * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c: Revert a bunch of changes made to SDL for VDPAU. This set of changes conflicted with Kdenlive, which requires two consumers that need to release SDL on stop. Now, VDPAU support does not need SDL as it gets its own X11 Display pointer. * src/modules/avformat/Makefile, src/modules/avformat/configure, src/modules/avformat/vdpau.c: Make VDPAU independent of SDL X11 Display. This prevents VDPAU from crashing on calls to SDL_Quit() and allows it to be used with non-SDL consumers! (Still requires an X11 session.) 2009-12-13 Dan Dennedy * src/modules/avformat/factory.c, src/modules/avformat/producer_avformat.c: Add producer variant avformat-novalidate. The purpose of this is to increase the speed of loading playlists with known good files. Use with care. This assumes a few properties have been set, in particular "length." This was only tested thus far by modifying the output of consumer xml to change mlt_service from "avformat" to "avformat-novalidate". 2009-12-12 Dan Dennedy * src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c: Add cache support to avformat producer. This also includes a change to make VDPAU work on some versions of FFmpeg beyond Sept 15, 2009 (do not know exactly when). * src/framework/mlt_service.c, src/framework/mlt_service.h: Add mlt_service_cache_set_size() to limit the cache size. * src/framework/mlt_cache.c, src/framework/mlt_cache.h: Add mlt_cache_set_size() to limit the amount of caching. 2009-11-28 Dan Dennedy * src/modules/avformat/Makefile, src/modules/avformat/configure, src/modules/avformat/producer_avformat.c, src/modules/avformat/vdpau.c: Add support for decoding H.264 with VDPAU. This applies to all H.264 at the moment unless novdpau=1 is set on the producer. Also, this can only handle up to about 10 - 15 clips using VDPAU in the project at the moment until the avformat producer is changed to use mlt_cache. * src/framework/mlt_consumer.c, src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c: Make the SDL consumer cooperate with VDPAU. This moves the SDL_Quit calls from the consumer_stop to the consumer_close functions. Also, it exports the X11 Display pointer to the mlt_environment and the global SDL mutex to the consumer class. 2009-12-08 Dan Dennedy * Doxyfile, NEWS, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.4.10 and update release notes. * configure, src/framework/mlt.h, src/modules/avformat/producer_avformat.c: Fix underlinking libm by removing math function. 2009-12-07 Dan Dennedy * Doxyfile, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.4.8. * src/modules/core/loader.ini, src/modules/core/producer_loader.c: Fix some cases image and audio formats not converting (kdenlive-1259). 2009-11-29 Marco Gittler * src/modules/oldfilm/filter_vignette.c, src/modules/oldfilm/filter_vignette.yml: use float for vignette effect fixed also bug in wrong y center * src/modules/oldfilm/filter_vignette.c, src/modules/oldfilm/filter_vignette.yml: use extra paramters for vignette settings 2009-10-10 Dan Dennedy * configure, src/framework/mlt.h: Bump the version to 0.4.7. * src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c: Convert some printfs to mlt_log. * src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c: Cleanup unused parameters. * src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/sdi_generator.c: Add support for >2 audio channels to Linsys SDI consumer. This does not yet have any remapping support. 2009-10-07 Dan Dennedy * AUTHORS, Doxyfile, NEWS, configure, docs/melt.1, src/framework/mlt.h: Set version to 0.4.6 and update release notes. * src/modules/avformat/Makefile, src/modules/avformat/configure: Add configure option --avformat-svn-version. Also update recommended version to Sept 15, 2009 and other cleanup. 2009-10-04 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/producer_kdenlivetitle.c: Fix behaviour of title clips when in and out points are given modified: src/modules/qimage/kdenlivetitle_wrapper.cpp modified: src/modules/qimage/producer_kdenlivetitle.c 2009-09-20 Dan Dennedy * src/mlt++/MltField.cpp, src/mlt++/MltField.h, src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/MltRepository.cpp, src/mlt++/MltRepository.h, src/mlt++/MltTractor.cpp, src/mlt++/MltTractor.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h, src/swig/mlt.i: Update bindings. 2009-09-19 Dan Dennedy * src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h: Add Properties constructor from opaque pointer. 2009-09-15 Dan Dennedy * src/swig/Makefile, src/swig/java/build, src/swig/lua/build, src/swig/perl/build, src/swig/php/build, src/swig/python/build, src/swig/ruby/build, src/swig/tcl/build: Fix distclean make target under swig and cleanup object files. 2009-09-13 Dan Dennedy * src/modules/core/factory.c, src/modules/core/producer_loader.c, src/modules/kdenlive/producer_framebuffer.c: Invert position of normalization filters with framebuffer producer. Previously, the framebuffer producer loaded a normalized producer, which can give undesired results with things like crop and really any time you want to filter the unpadded images of the speed-altered video. Now, the framebuffer uses the new "abnormal" producer to load the clip without normalization filters and sets appropriate frame properties to allow the normalizing filters attached to the framebuffer to act appropriately. This new abnormal filter is simply an alias to the existing loader filter, which uses the name by which it is invoked to toggle the behaviour of whether to attach normalizing filters. 2009-09-10 Dan Dennedy * Makefile, src/swig/Makefile, src/swig/configure: Improve swig build with help from Michael Forney. 2009-08-26 Dan Dennedy * src/modules/sdl/Makefile, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/factory.c: Add audio-only SDL consumer (for Kdenlive on OS X). * src/modules/sdl/Makefile, src/modules/sdl/consumer_sdl_audio.c, src/modules/sdl/factory.c: Add audio-only SDL consumer (for Kdenlive on OS X). 2009-08-19 Dan Dennedy * src/modules/linsys/Makefile, src/modules/linsys/configure, src/modules/linsys/consumer_SDIstream.c, src/modules/linsys/factory.c, src/modules/linsys/gpl, src/modules/linsys/sdi_generator.c: Add Linsys SDI consumer from B.C.E. 2009-08-03 Dan Dennedy * src/framework/Makefile, 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/mlt++/MltFrame.cpp, src/mlt++/MltFrame.h, src/mlt++/configure, src/modules/avformat/consumer_avformat.c, src/modules/avformat/filter_avresample.c, src/modules/avformat/producer_avformat.c, src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_audioconvert.c, src/modules/core/filter_channelcopy.c, src/modules/core/filter_mono.c, src/modules/core/filter_transition.c, src/modules/core/loader.ini, src/modules/core/producer_consumer.c, src/modules/core/transition_mix.c, src/modules/dv/consumer_libdv.c, src/modules/dv/producer_libdv.c, src/modules/jackrack/filter_jackrack.c, src/modules/jackrack/filter_ladspa.c, src/modules/normalize/filter_volume.c, src/modules/resample/filter_resample.c, src/modules/sdl/consumer_sdl.c, src/modules/sox/filter_sox.c, src/modules/vorbis/producer_vorbis.c: Refactor audio conversion and mixing. * src/modules/sdl/consumer_sdl.c, src/modules/sdl/consumer_sdl_preview.c, src/modules/sdl/consumer_sdl_still.c: Fix some SDL concurrency issues I am seeing in Kdenlive on my quad core. 2009-08-02 j-b-m * src/modules/qimage/configure, src/modules/qimage/kdenlivetitle_wrapper.cpp: Add support for svg items in titles modified: configure modified: kdenlivetitle_wrapper.cpp * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: Fix errors in caching + mem leaks, fix resize issue modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h modified: producer_kdenlivetitle.c 2009-08-01 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: Rewrote caching, similar to qimage producer modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h modified: producer_kdenlivetitle.c 2009-07-31 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: Use QImage instead of QPixmap, add myself in copyright modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h modified: producer_kdenlivetitle.c * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h: don't use cache, just normal properties to store scene modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: Rescale title when they are played with a different profile modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h modified: producer_kdenlivetitle.c 2009-07-30 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/producer_kdenlivetitle.c: Fix image size, fix utf-8 characters in titles modified: kdenlivetitle_wrapper.cpp modified: producer_kdenlivetitle.c 2009-07-29 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: Fix use of several title producers in one instance of Kdenlive modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h modified: producer_kdenlivetitle.c 2009-07-27 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: Cleanup + fix crashes when used in Kdenlive modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h modified: producer_kdenlivetitle.c 2009-07-24 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/producer_kdenlivetitle.c: Fix mem leak modified: src/modules/qimage/kdenlivetitle_wrapper.cpp modified: src/modules/qimage/producer_kdenlivetitle.c 2009-07-26 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h: Cleanup & fix crash modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h: Cleanup & fix crash modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h 2009-07-24 j-b-m * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/producer_kdenlivetitle.c: Fix mem leak modified: src/modules/qimage/kdenlivetitle_wrapper.cpp modified: src/modules/qimage/producer_kdenlivetitle.c * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: Fix crash + position in time modified: src/modules/qimage/kdenlivetitle_wrapper.cpp modified: src/modules/qimage/kdenlivetitle_wrapper.h modified: src/modules/qimage/producer_kdenlivetitle.c * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: * Fix memleaks * Cleanup * Reload xml when setting "reload_xml" property modified: src/modules/qimage/kdenlivetitle_wrapper.cpp modified: src/modules/qimage/kdenlivetitle_wrapper.h modified: src/modules/qimage/producer_kdenlivetitle.c * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: Add feature to dynamically replace text in a block modified: kdenlivetitle_wrapper.cpp modified: kdenlivetitle_wrapper.h modified: producer_kdenlivetitle.c 2009-07-24 Marco Gittler * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: kdenlivetitle: reindent code / readded qimage_producer * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h: reindent c++ * src/modules/qimage/factory.c, src/modules/qimage/qimage_wrapper.cpp: readded deleted qimage producer in factory 2009-07-19 Marco Gittler * src/modules/qimage/configure, src/modules/qimage/producer_kdenlivetitle.c: kdenlivetitle: added QtXml during configure, add rescource to producer 2009-07-18 Marco Gittler * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: working color conversion 2009-07-15 Marco Gittler * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: memcpy works now * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: memhandling changed 2009-07-14 Dan Dennedy * src/modules/avformat/Makefile, src/modules/avformat/configure, src/modules/core/Makefile, src/modules/dgraft/Makefile, src/modules/dv/Makefile, src/modules/effectv/Makefile, src/modules/frei0r/Makefile, src/modules/gtk2/Makefile, src/modules/jackrack/Makefile, src/modules/kdenlive/Makefile, src/modules/kino/Makefile, src/modules/melt/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/vmfx/Makefile, src/modules/vorbis/Makefile, src/modules/xine/Makefile, src/modules/xml/Makefile: Fix build on OS X and possibly others. Gives higher priority to local lib and include dirs than system or SDL-based lib and include dirs. Also, moves previous -lm fix to from general build to --avformat-svn and --avformat-static builds. 2009-07-14 Marco Gittler * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/producer_kdenlivetitle.c: cleanup * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: test alpha channel 2009-07-11 Marco Gittler * src/modules/core/loader.dict, src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h: kdenlivetitle_wrapper: interpolate from start-> end, added title to dict 2009-07-10 Marco Gittler * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: kdenlivetitle_wrapper: load kdenlive titles * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h: kdenlivetitle_wrapper: use QApplication, else QGrahicsScene ist not working * src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: kdenlivetitle_wrapper.{cpp,h}, producer_kdenlivetitle.c: LGPL header and byte move for right RGBA values * src/modules/qimage/Makefile, src/modules/qimage/factory.c, src/modules/qimage/kdenlivetitle_wrapper.cpp, src/modules/qimage/kdenlivetitle_wrapper.h, src/modules/qimage/producer_kdenlivetitle.c: first work on kdenlive title producer should later read the xml-file from kdenlive and let the title have scroll and zoom 2009-07-03 Dan Dennedy * configure, docs/policies.txt, src/framework/mlt.h, src/framework/mlt_frame.c, src/framework/mlt_frame.h, src/framework/mlt_log.c, src/modules/avformat/filter_avcolour_space.c, src/modules/avformat/filter_avdeinterlace.c, src/modules/avformat/filter_swscale.c, src/modules/avformat/producer_avformat.c, src/modules/core/Makefile, src/modules/core/factory.c, src/modules/core/filter_brightness.c, src/modules/core/filter_crop.c, src/modules/core/filter_gamma.c, src/modules/core/filter_greyscale.c, src/modules/core/filter_imageconvert.c, src/modules/core/filter_luma.c, src/modules/core/filter_mirror.c, src/modules/core/filter_obscure.c, src/modules/core/filter_rescale.c, src/modules/core/filter_resize.c, src/modules/core/filter_watermark.c, src/modules/core/loader.ini, src/modules/core/producer_colour.c, src/modules/core/producer_consumer.c, src/modules/core/producer_ppm.c, src/modules/core/transition_luma.c, src/modules/effectv/filter_burn.c, src/modules/frei0r/filter_frei0r.c, src/modules/frei0r/frei0r_helper.c, src/modules/frei0r/frei0r_helper.h, src/modules/frei0r/producer_frei0r.c, src/modules/frei0r/transition_frei0r.c, src/modules/gtk2/filter_rescale.c, src/modules/gtk2/producer_pango.c, src/modules/gtk2/producer_pixbuf.c, src/modules/kdenlive/filter_boxblur.c, src/modules/kdenlive/filter_wave.c, src/modules/kdenlive/producer_framebuffer.c, src/modules/motion_est/filter_crop_detect.c, src/modules/motion_est/filter_motion_est.c, src/modules/motion_est/filter_vismv.c, src/modules/motion_est/producer_slowmotion.c, src/modules/oldfilm/filter_dust.c, src/modules/oldfilm/filter_grain.c, src/modules/oldfilm/filter_lines.c, src/modules/oldfilm/filter_oldfilm.c, src/modules/oldfilm/filter_tcolor.c, src/modules/oldfilm/filter_vignette.c, src/modules/plus/filter_affine.c, src/modules/plus/filter_charcoal.c, src/modules/plus/filter_invert.c, src/modules/plus/filter_sepia.c, src/modules/plus/transition_affine.c, 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_still.c, src/modules/sdl/producer_sdl_image.c, src/modules/vmfx/filter_chroma.c, src/modules/vmfx/filter_chroma_hold.c, src/modules/vmfx/filter_mono.c, src/modules/vmfx/filter_shape.c, src/modules/xine/filter_deinterlace.c: Massive refactoring of image conversion. This drops all image color space and pixel format conversions from the mlt_frame class. Instead, it adds a convert_image virtual function to the mlt_frame class that is called within mlt_frame_get_image(). The newly added imageconvert filter sets that virtual function and contains the various conversion routines. The loader producer automatically attaches this filter to the producer it creates. 2009-06-30 Dan Dennedy * Doxyfile, NEWS, configure, docs/melt.1, src/framework/mlt.h: Set to v0.4.4 and update release notes. 2009-06-23 Dan Dennedy * src/swig/configure, src/swig/lua/build, src/swig/lua/play.lua: Add SWIG Lua bindings. 2009-06-22 Dan Dennedy * configure, src/framework/Makefile, src/melt/configure, src/swig/Makefile, src/swig/configure, src/swig/java/build, src/swig/perl/build, src/swig/php/build, src/swig/python/build, src/swig/ruby/build, src/swig/tcl/build: Further integrate swig into build system. This is not enabled by default. It adds configure options --enable-swig and --swig-languages. * src/melt/Makefile, src/melt/configure, src/melt/melt.c: Add configure option --rename-melt. * src/modules/frei0r/Makefile, src/modules/frei0r/blacklist.txt, src/modules/frei0r/factory.c: Add blacklist to frei0r module. This is for Kdenlive bugs 913 and 917. It is populated with only facedetect for now. 2009-06-21 Dan Dennedy * src/modules/avformat/Makefile.orig, src/modules/avformat/Makefile.rej: Remove these bogus make files in avformat. * src/modules/avformat/Makefile.orig, src/modules/avformat/Makefile.rej, src/modules/avformat/consumer_avformat.c: Fix avformat consumer crashing on pcm_s16le. 2009-06-16 Dan Dennedy * src/modules/avformat/filter_avresample.c, src/modules/avformat/producer_avformat.c: Migrate to FFmpeg av_audio_resample_init. * src/melt/Makefile, src/melt/melt.c, src/modules/avformat/producer_avformat.c: Fix (kdenlive-824) >2 channels not downmixed. 2009-06-15 Dan Dennedy * configure, profiles/Makefile, src/framework/Makefile, src/melt/Makefile, src/modules/avformat/Makefile, src/modules/core/Makefile, src/modules/feeds/Makefile, src/modules/lumas/Makefile, src/modules/oldfilm/Makefile, src/modules/xml/Makefile: Add datadir and mandir options to configure. * src/modules/kino/avi.cc, src/modules/kino/filehandler.cc, src/modules/kino/kino_wrapper.cc: Apply patch from Debian to fix compilation of kino module. 2009-06-10 Dan Dennedy * docs/melt.1, docs/policies.txt, src/melt/melt.c: Add man page for melt. Not yet installed. 2009-06-03 Dan Dennedy * configure, src/framework/mlt.h: Set to interim version 0.4.3 2009-05-30 Dan Dennedy * Doxyfile, NEWS, configure, src/framework/mlt.h: Bump versions and update release notes. 2009-05-29 Dan Dennedy * src/modules/oldfilm/filter_tcolor.yml, src/modules/oldfilm/filter_vignette.yml: Fix YAML validation errors and spelling of Vignette. 2009-05-26 Dan Dennedy * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/qimage_wrapper.cpp, src/modules/qimage/qimage_wrapper.h: Fix image sequences sometimes not advancing. * src/modules/gtk2/producer_pixbuf.c, src/modules/qimage/producer_qimage.c: Change the ttl default value for image sequences. When using printf-style image sequences only, the default ttl is now 1. 2009-05-20 Dan Dennedy * configure, src/framework/mlt.h: Bump to an interim version. 2009-05-17 Dan Dennedy * Makefile, NEWS: Add v0.4.0 release notes. * Doxyfile, configure, src/framework/mlt.h: Bump version to 0.4.0 2009-05-13 Dan Dennedy * profiles/atsc_1080i_50, profiles/atsc_1080i_60, profiles/atsc_1080p_2398, profiles/atsc_1080p_24, profiles/atsc_1080p_25, profiles/atsc_1080p_2997, profiles/atsc_1080p_30, profiles/atsc_720p_30, profiles/dv_ntsc, profiles/dv_ntsc_wide, profiles/dv_pal, profiles/dv_pal_wide, profiles/hdv_1080_25p, profiles/hdv_1080_30p, profiles/hdv_1080_50i, profiles/hdv_1080_60i, profiles/hdv_720_25p, profiles/hdv_720_30p, profiles/hdv_720_50p, profiles/hdv_720_60p, 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_wide, profiles/svcd_pal_wide: Make profile descriptions more user friendly. 2009-05-11 Dan Dennedy * src/modules/gtk2/have_mmx.S, src/modules/gtk2/scale_line_22_yuv_mmx.S: Apply patch from Orcan Ogetbil that adds .note.GNU-stack section. 2009-05-09 Dan Dennedy * ChangeLog, Makefile: Change dist make target to use git-archive. * src/swig/configure, src/swig/java/Play.java, src/swig/java/build, src/swig/{mltpp.i => mlt.i}, src/swig/perl/Makefile.PL, src/swig/php/build, src/swig/python/build, src/swig/ruby/build, src/swig/ruby/play.rb, src/swig/ruby/thumbs.rb, src/swig/tcl/build, src/swig/tcl/play.tcl: Fixup the swig bindings. * configure, src/examples/Makefile, src/framework/Makefile, src/framework/mlt_geometry.c, src/framework/mlt_producer.c, src/mlt++/Makefile, src/mlt++/configure, src/modules/avformat/Makefile, src/modules/avformat/configure, src/modules/avformat/consumer_avformat.c, src/modules/avformat/producer_avformat.c, src/modules/core/Makefile, src/modules/dgraft/filter_telecide.c, src/modules/dv/Makefile, src/modules/effectv/Makefile, src/modules/frei0r/Makefile, src/modules/gtk2/Makefile, src/modules/jackrack/Makefile, src/modules/jackrack/configure, src/modules/kino/Makefile, src/modules/normalize/Makefile, src/modules/plus/Makefile, src/modules/qimage/Makefile, src/modules/resample/filter_resample.c, src/modules/sdl/Makefile, src/modules/vmfx/filter_chroma.c, src/modules/xml/consumer_xml.c: Fix over- and under-linking. * src/mlt++/Mlt.h, src/mlt++/MltConsumer.cpp, src/mlt++/MltConsumer.h, src/mlt++/MltDeque.cpp, src/mlt++/MltDeque.h, src/mlt++/MltEvent.cpp, src/mlt++/MltEvent.h, src/mlt++/MltFactory.cpp, src/mlt++/MltFactory.h, src/mlt++/MltField.cpp, src/mlt++/MltField.h, src/mlt++/MltFilter.cpp, src/mlt++/MltFilter.h, src/mlt++/MltFilteredConsumer.cpp, src/mlt++/MltFilteredConsumer.h, src/mlt++/MltFilteredProducer.cpp, src/mlt++/MltFilteredProducer.h, src/mlt++/MltFrame.cpp, src/mlt++/MltFrame.h, src/mlt++/MltGeometry.cpp, src/mlt++/MltGeometry.h, src/mlt++/MltMultitrack.cpp, src/mlt++/MltMultitrack.h, src/mlt++/MltParser.cpp, src/mlt++/MltParser.h, src/mlt++/MltPlaylist.cpp, src/mlt++/MltPlaylist.h, src/mlt++/MltProducer.cpp, src/mlt++/MltProducer.h, src/mlt++/MltProfile.cpp, src/mlt++/MltProfile.h, src/mlt++/MltProperties.cpp, src/mlt++/MltProperties.h, src/mlt++/MltPushConsumer.cpp, src/mlt++/MltPushConsumer.h, src/mlt++/MltRepository.cpp, src/mlt++/MltRepository.h, src/mlt++/MltService.cpp, src/mlt++/MltService.h, src/mlt++/MltTokeniser.cpp, src/mlt++/MltTokeniser.h, src/mlt++/MltTractor.cpp, src/mlt++/MltTractor.h, src/mlt++/MltTransition.cpp, src/mlt++/MltTransition.h: Fix LGPL information in comment headers of mlt++. * Makefile, README, demo/README, demo/consumers.ini, demo/demo, demo/demo.ini, demo/{entity.westley => entity.mlt}, demo/mlt_all, demo/mlt_attributes, 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_black, demo/mlt_fade_in_and_out, demo/mlt_intro, demo/mlt_jcut, demo/mlt_lcut, demo/mlt_levels, demo/mlt_my_name_is, demo/mlt_news, demo/mlt_obscure, demo/mlt_push, demo/mlt_slideshow, demo/mlt_slideshow_black, demo/mlt_squeeze, demo/mlt_squeeze_box, demo/mlt_ticker, demo/mlt_title_over_gfx, demo/mlt_titleshadow_watermark, demo/mlt_voiceover, demo/mlt_watermark, demo/{new.westley => new.mlt}, demo/{pango.westley => pango.mlt}, demo/{svg.westley => svg.mlt}, docs/framework.txt, docs/install.txt, docs/melt.txt, docs/mlt++.txt, docs/mlt-xml.txt, docs/policies.txt, docs/services.txt, mlt++/.gitignore, mlt++/Makefile, mlt++/configure, src/framework/mlt_factory.c, src/framework/mlt_playlist.c, src/framework/mlt_producer.c, src/framework/mlt_properties.c, src/melt/io.c, src/melt/io.h, src/melt/melt.c, src/modules/core/factory.c, src/modules/core/filter_watermark.c, src/modules/core/loader.dict, src/modules/core/producer_consumer.c, src/modules/core/producer_hold.c, src/modules/core/producer_loader.c, src/modules/core/transition_composite.c, src/modules/core/transition_luma.c, src/modules/core/transition_region.c, src/modules/kdenlive/producer_framebuffer.c, src/modules/kino/avi.h, src/modules/kino/riff.cc, src/modules/melt/factory.c, src/modules/melt/producer_melt.c, src/modules/motion_est/Makefile, src/modules/motion_est/README, src/modules/motion_est/producer_slowmotion.c, src/modules/plus/filter_affine.c, src/modules/xml/consumer_xml.c, src/modules/xml/factory.c, src/modules/xml/mlt-xml.dtd, src/modules/xml/producer_xml.c, src/swig/Makefile, src/swig/configure, src/swig/java/Play.java, src/swig/java/build, src/swig/mltpp.i, src/swig/perl/Makefile.PL, src/swig/perl/play.pl, src/swig/php/build, src/swig/php/play.php, src/swig/python/build, src/swig/python/play.py, src/swig/ruby/build, src/swig/ruby/play.rb, src/swig/ruby/thumbs.rb, src/swig/tcl/build, src/swig/tcl/play.tcl, src/tests/charlie.c, src/tests/hello.c: Complete reorganization and renaming to usable state. 2009-05-07 Dan Dennedy * src/modules/mvsp/Makefile, src/modules/mvsp/configure, src/modules/mvsp/consumer_mvsp.c, src/modules/mvsp/factory.c: Remove mvsp - moving to melted project. * .gitignore, Makefile, configure, mlt++.pc.in, setenv, src/examples/Makefile, src/melt/Makefile, src/mlt++/Makefile, src/mlt++/Mlt.h, src/mlt++/configure, src/modules/core/Makefile, src/modules/core/factory.c, src/modules/feeds/Makefile, src/modules/fezzik/Makefile, src/modules/fezzik/factory.c, src/modules/melt/Makefile, src/modules/{valerie => mvsp}/Makefile, src/modules/mvsp/configure, .../consumer_valerie.c => mvsp/consumer_mvsp.c}, src/modules/{valerie => mvsp}/factory.c, src/modules/xml/Makefile, src/modules/xml/configure: Fix the build afer the reorg. * docs/{inigo.txt => melt.txt}, docs/{westley.txt => mlt-xml.txt}, src/{inigo => melt}/Makefile, src/{inigo => melt}/configure, src/{inigo => melt}/io.c, src/{inigo => melt}/io.h, src/{inigo/inigo.c => melt/melt.c}, src/modules/{ => core}/data_fx.properties, src/modules/{fezzik.dict => core/loader.dict}, src/modules/{fezzik.ini => core/loader.ini}, src/modules/{fezzik => core}/producer_hold.c, src/modules/{fezzik/producer_fezzik.c => core/producer_loader.c}, src/modules/{inigo => melt}/Makefile, src/modules/{inigo => melt}/factory.c, src/modules/{inigo/producer_inigo.c => melt/producer_melt.c}, src/modules/{westley => xml}/Makefile, src/modules/{westley => xml}/configure, src/modules/{westley/consumer_westley.c => xml/consumer_xml.c}, src/modules/{westley => xml}/factory.c, src/modules/{westley/westley.dtd => xml/mlt-xml.dtd}, src/modules/{westley/producer_westley.c => xml/producer_xml.c}, src/tests/README: Rename inigo, fezzik, and westley. * docs/mlt++.txt, mlt++/README: Merge mlt++/README into docs/mlt++.txt. * mlt++/HOWTO => docs/mlt++.txt, {mlt++/test => src/examples}/Makefile, {mlt++/test => src/examples}/play.cpp, {mlt++/src => src/mlt++}/Makefile, {mlt++/src => src/mlt++}/Mlt.h, {mlt++/src => src/mlt++}/MltConsumer.cpp, {mlt++/src => src/mlt++}/MltConsumer.h, {mlt++/src => src/mlt++}/MltDeque.cpp, {mlt++/src => src/mlt++}/MltDeque.h, {mlt++/src => src/mlt++}/MltEvent.cpp, {mlt++/src => src/mlt++}/MltEvent.h, {mlt++/src => src/mlt++}/MltFactory.cpp, {mlt++/src => src/mlt++}/MltFactory.h, {mlt++/src => src/mlt++}/MltField.cpp, {mlt++/src => src/mlt++}/MltField.h, {mlt++/src => src/mlt++}/MltFilter.cpp, {mlt++/src => src/mlt++}/MltFilter.h, {mlt++/src => src/mlt++}/MltFilteredConsumer.cpp, {mlt++/src => src/mlt++}/MltFilteredConsumer.h, {mlt++/src => src/mlt++}/MltFilteredProducer.cpp, {mlt++/src => src/mlt++}/MltFilteredProducer.h, {mlt++/src => src/mlt++}/MltFrame.cpp, {mlt++/src => src/mlt++}/MltFrame.h, {mlt++/src => src/mlt++}/MltGeometry.cpp, {mlt++/src => src/mlt++}/MltGeometry.h, {mlt++/src => src/mlt++}/MltMultitrack.cpp, {mlt++/src => src/mlt++}/MltMultitrack.h, {mlt++/src => src/mlt++}/MltParser.cpp, {mlt++/src => src/mlt++}/MltParser.h, {mlt++/src => src/mlt++}/MltPlaylist.cpp, {mlt++/src => src/mlt++}/MltPlaylist.h, {mlt++/src => src/mlt++}/MltProducer.cpp, {mlt++/src => src/mlt++}/MltProducer.h, {mlt++/src => src/mlt++}/MltProfile.cpp, {mlt++/src => src/mlt++}/MltProfile.h, {mlt++/src => src/mlt++}/MltProperties.cpp, {mlt++/src => src/mlt++}/MltProperties.h, {mlt++/src => src/mlt++}/MltPushConsumer.cpp, {mlt++/src => src/mlt++}/MltPushConsumer.h, {mlt++/src => src/mlt++}/MltRepository.cpp, {mlt++/src => src/mlt++}/MltRepository.h, {mlt++/src => src/mlt++}/MltService.cpp, {mlt++/src => src/mlt++}/MltService.h, {mlt++/src => src/mlt++}/MltTokeniser.cpp, {mlt++/src => src/mlt++}/MltTokeniser.h, {mlt++/src => src/mlt++}/MltTractor.cpp, {mlt++/src => src/mlt++}/MltTractor.h, {mlt++/src => src/mlt++}/MltTransition.cpp, {mlt++/src => src/mlt++}/MltTransition.h, {mlt++/src => src/mlt++}/config.h, {mlt++ => src}/swig/Makefile, {mlt++ => src}/swig/configure, {mlt++ => src}/swig/java/Play.java, {mlt++ => src}/swig/java/Play.sh, {mlt++ => src}/swig/java/build, {mlt++ => src}/swig/mltpp.i, {mlt++ => src}/swig/perl/Makefile.PL, {mlt++ => src}/swig/perl/build, {mlt++ => src}/swig/perl/play.pl, {mlt++ => src}/swig/php/build, {mlt++ => src}/swig/php/play.php, {mlt++ => src}/swig/python/build, {mlt++ => src}/swig/python/play.py, {mlt++ => src}/swig/ruby/build, {mlt++ => src}/swig/ruby/play.rb, {mlt++ => src}/swig/ruby/thumbs.rb, {mlt++ => src}/swig/tcl/build, {mlt++ => src}/swig/tcl/play.tcl: Reorganize mlt++ files. * docs/dvcp.txt, docs/testing-20040110.txt, docs/testing.txt, docs/valerie.txt, mlt++/AUTHORS, mlt++/COPYING, mlt++/CUSTOMISING, mlt++/ChangeLog, mlt++/mlt++.sln, mlt++/mlt++.vcproj, mlt++/src/MltMiracle.cpp, mlt++/src/MltMiracle.h, mlt++/src/MltResponse.cpp, mlt++/src/MltResponse.h, mlt++/swig/ruby/miracle.rb, mlt++/test/server.cpp, mlt-miracle.pc.in, mlt-valerie.pc.in, src/albino/Makefile, src/albino/albino.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/miracle/Makefile, 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/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: Remove files that no longer belong. 2009-05-03 ddennedy * configure, src/albino/Makefile, src/humperdink/Makefile, src/miracle/Makefile, src/modules/avformat/Makefile, src/modules/core/Makefile, src/modules/dgraft/Makefile, src/modules/effectv/Makefile, src/modules/fezzik/Makefile, src/modules/frei0r/Makefile, src/modules/inigo/Makefile, src/modules/kdenlive/Makefile, src/modules/kino/Makefile, src/modules/motion_est/Makefile, src/modules/normalize/Makefile, src/modules/oldfilm/Makefile, src/modules/plus/Makefile, src/modules/qimage/Makefile, src/modules/sox/configure, src/modules/valerie/Makefile, src/modules/vmfx/Makefile, src/modules/xine/Makefile, src/tests/Makefile, src/valerie/Makefile: Apply cosmetic cleanup part of ldflags_order patch from Alberto Villa. * src/modules/avformat/configure, src/modules/qimage/configure: Apply FreeBSD fixes part of ldflags_order patch from Alberto Villa. * src/modules/avformat/Makefile, src/modules/dv/Makefile, src/modules/gtk2/Makefile, src/modules/jackrack/Makefile, src/modules/sdl/Makefile, src/modules/sox/Makefile, src/modules/westley/Makefile: Apply ldflags-order part of ldflags_order patch from Alberto Villa. Alberto wrote: "on freebsd (as well as on linuces without /usr/local/lib in default ld path) building concurrent versions of mlt is not possible, because of the wrong linking of -lmlt while using LDFLAGS=-L/usr/local/lib this patch fixes the issue using pkg-config" 2009-04-18 ddennedy * src/modules/resample/Makefile, src/modules/vorbis/Makefile: Apply patch from Alberto Villa to use pkg-config for resample and vorbis modules. 2009-04-16 ddennedy * configure, src/framework/mlt.h, src/modules/kino/configure: Use pkg-config instead of lqt-config. 2009-04-15 ddennedy * mlt++/ChangeLog, mlt++/Makefile: Add ChangeLog and remove svn log from dist make target. * 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-10 ddennedy * mlt++/test/play.cpp, mlt++/test/server.cpp: cleanup some warnings * mlt++/src/MltResponse.cpp, mlt++/src/MltResponse.h: const update for MltResponse * mlt++/src/MltResponse.cpp, mlt++/src/MltResponse.h: Constness changes * mlt++/src/MltTransition.cpp, mlt++/src/MltTransition.h: Constness changes * mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h: Constness changes * mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h: Constness changes * mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h: Constness changes * mlt++/src/MltProperties.cpp, mlt++/src/MltResponse.cpp: Constness changes * mlt++/src/MltProperties.cpp, mlt++/src/MltPushConsumer.cpp: Constness changes * mlt++/src/MltFilter.cpp, mlt++/src/MltFilter.h, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h: Constness changes * mlt++/src/MltConsumer.cpp, mlt++/src/MltField.cpp, mlt++/src/MltFilter.cpp, mlt++/src/MltFrame.cpp, mlt++/src/MltMultitrack.cpp, mlt++/src/MltPlaylist.cpp, mlt++/src/MltProducer.cpp, mlt++/src/MltTractor.cpp, mlt++/src/MltTransition.cpp: Fix up warnings about explicit base initializers in copy constructors 2009-04-07 Ray Lehtiniemi * src/framework/mlt_consumer.c, src/miracle/miracle_connection.c, src/modules/kino/riff.cc: Fix up a few ignored return values * 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 2009-03-10 ddennedy * 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-03 ddennedy * src/modules/frei0r/factory.c, src/modules/frei0r/producer_frei0r.c: frei0r/factory.c, producer_frei0r.c: suppress compiler warnings 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 2009-02-20 blendamedt * 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/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) 2009-02-12 ddennedy * profiles/cif_15, profiles/qcif_15, profiles/quarter_15: profiles/*_15: add some 15fps profiles 2009-02-10 ddennedy * 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/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) 2009-02-04 ddennedy * mlt++/debian/changelog, mlt++/debian/control, mlt++/debian/copyright, mlt++/debian/rules: remove debian package subdirectory (they provide their own) * 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 2009-01-29 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-575) large memory consumption loading many pictures. 2009-01-26 ddennedy * mlt++/swig/configure, mlt++/swig/php/build, mlt++/swig/php/play.php: swig/configure, swig/php/*: add php bindings 2009-01-21 ddennedy * 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-13 ddennedy * 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. 2009-01-08 ddennedy * 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] 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 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-29 ddennedy * NEWS, configure: NEWS, configure: set version to 0.3.4 and add release notes 2008-12-28 ddennedy * mlt++/swig/java/build, mlt++/swig/python/build, mlt++/swig/python/play.py, mlt++/swig/tcl/build: swig/{java,python,tcl}/build: fix linking error __stack_chk_fail_local. swig/python/play.py: fix syntax error reported by Jonathon Thomas. * 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-26 ddennedy * 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). 2008-12-22 ddennedy * 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-20 ddennedy * 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/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-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/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. 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-12 ddennedy * src/modules/gtk2/Makefile, src/modules/gtk2/pixops.c: gtk2/pixops.c, gtk2/Makefile: prevent MMX on all x86_64, not just OS X * 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/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 2008-12-04 ddennedy * mlt++/swig/java/build, mlt++/swig/perl/Makefile.PL, mlt++/swig/python/build, mlt++/swig/ruby/build, mlt++/swig/tcl/build, mlt++/test/Makefile: test/Makefile, swig/*/build: replace more mlt-config with pkg-config * 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. 2008-11-25 ddennedy * 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-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-08 ddennedy * 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 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 2008-10-29 ddennedy * src/albino/albino.c, src/inigo/inigo.c: albino.c, inigo.c: disable realtime scheduling (kdenlive-180). 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-23 ddennedy * src/inigo/Makefile, src/inigo/inigo.c: inigo.c: added -version option 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-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 * mlt++/swig/java/Play.java, mlt++/swig/java/Play.sh, mlt++/swig/java/build: swig/java: fixup the java bindings build script and example (bug 1523941) 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-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-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. 2008-06-26 ddennedy * mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/swig/mltpp.i: MltProducer.{h,cpp}, mltpp.i: remove Producer::get_frame that is unncessary and introduced a memory leak. 2008-06-01 ddennedy * src/modules/core/filter_resize.c, src/modules/core/transition_composite.c: filter_resize,c, filter_composite.c: bugfix redundant rounding. 2008-05-15 ddennedy * 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 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/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, src/modules/dgraft/gpl: modules/dgraft: added module for ports of Donald Graft's GPL filters. * src/modules/avformat/Makefile, src/modules/avformat/configure: avformat/Makefile, configure: fix --avformat-swscale and the removal of the ffmpeg 'lib' make target. 2008-04-23 ddennedy * mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/swig/mltpp.i: MltProducer.{h,cpp}, swig/mltpp.i: add method Producer::get_frame. * 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 * 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-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-07 ddennedy * 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 2008-03-06 ddennedy * src/framework/mlt_repository.c, src/framework/mlt_repository.h: mlt_repository.[hc]: add mlt_repository_languages helper function for localizing metadata 2008-03-04 blendamedt * 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 2008-02-28 blendamedt * 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 * mlt++/src/MltRepository.cpp, mlt++/src/MltRepository.h: MltRepository.{h,cpp}: update to latest mlt_repository.h change - finalization of callback declarations and metadata handling 2008-02-26 ddennedy * 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/avformat/Makefile, src/modules/avformat/configure: avformat/configure, avformat/Makefile: add libavdevice for newer versions of ffmpeg when using --avformat-svn or --avformat-static 2008-02-16 ddennedy * mlt++/src/MltRepository.cpp, mlt++/src/MltRepository.h, mlt++/swig/mltpp.i: MltRepository.{h,cpp}, swig/mltpp.i: added consumers, filters, producers, transitions, register_metadata, and metadata methods to Repository class * 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-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-07 ddennedy * mlt++/configure, mlt++/src/Makefile: configure: add soversion variable src/Makefile: improve library versioning by linking on interface version (soversion) * 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 * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltFactory.cpp, mlt++/src/MltFactory.h, mlt++/src/MltRepository.cpp, mlt++/src/MltRepository.h, mlt++/swig/mltpp.i: Mlt.h, MltFactory.{h,cpp}, MltRepository.{h,cpp}, swig/mltpp.i: update to deal with changes and new capabilities in mlt_factory and mlt_repository. * 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/feeds/configure, 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 * 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 * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h, mlt++/src/MltFactory.cpp, mlt++/src/MltFactory.h, mlt++/src/MltFilter.cpp, mlt++/src/MltFilter.h, mlt++/src/MltFilteredConsumer.cpp, mlt++/src/MltFilteredConsumer.h, mlt++/src/MltFilteredProducer.cpp, mlt++/src/MltFilteredProducer.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltProfile.cpp, mlt++/src/MltProfile.h, mlt++/src/MltPushConsumer.cpp, mlt++/src/MltPushConsumer.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/src/MltTractor.cpp, mlt++/src/MltTractor.h, mlt++/src/MltTransition.cpp, mlt++/src/MltTransition.h, mlt++/swig/mltpp.i, mlt++/swig/perl/play.pl, mlt++/swig/python/play.py, mlt++/swig/ruby/miracle.rb, mlt++/swig/ruby/play.rb, mlt++/swig/ruby/thumbs.rb, mlt++/swig/tcl/play.tcl, mlt++/test/play.cpp, mlt++/test/server.cpp: add MltProfile and update examples * 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-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 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. 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/motion_est/filter_motion_est.c, src/modules/motion_est/filter_vismv.c: filter_vismv.c: bugfix pointer to array of motion vectors 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/effectv/utils.c, src/modules/effectv/utils.h: effectv/utils.*: fix compilation on OS X 2007-07-30 ddennedy * configure, docs/policies.txt: configure: fix broken variables in pkg-config files policies.txt: add bug reporting procedure 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/{atsc_wide_1080i => atsc_1080i_60}, profiles/{atsc_wide_720p => atsc_720p_30}: 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_pal => hdv_1080_50i}, profiles/{hdv_1080_ntsc => hdv_1080_60i}, profiles/{hdv_720_60i => hdv_720_30p}: * profiles/{hdv_720_pal => hdv_720_50p}, profiles/{hdv_720_ntsc => hdv_720_60i}: * 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-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 2007-06-29 ddennedy * 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-12 ddennedy * mlt++/Makefile, mlt++/src/Makefile, mlt++/test/Makefile: added uninstall 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/gpl, 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 * docs/westley.txt, src/modules/fezzik.dict: fezzik.dict: prioritize avformat higher than libdv for better quality 2007-06-09 ddennedy * 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-05-25 ddennedy * 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-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 2007-04-10 ddennedy * ChangeLog, docs/policies.txt, src/modules/core/Makefile, src/modules/core/configure, src/modules/core/factory.c, src/modules/core/filter_boxblur.h, src/modules/core/filter_wave.h, 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/{core => kdenlive}/filter_boxblur.c, src/modules/kdenlive/filter_boxblur.h, src/modules/{core => kdenlive}/filter_wave.c, src/modules/kdenlive/filter_wave.h, .../{core => 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 ddennedy * ChangeLog, src/modules/sox/filter_sox.c: add sox 13.0.0 support 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 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 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 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-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-09-28 dezeroex * 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/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 2006-05-22 ddennedy * 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-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/gpl, 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/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/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 2006-02-23 lilo_booter * mlt++/mlt++.sln, mlt++/mlt++.vcproj, mlt++/src/Mlt.h, mlt++/src/MltConsumer.h, mlt++/src/MltDeque.h, mlt++/src/MltEvent.h, mlt++/src/MltFactory.cpp, mlt++/src/MltFactory.h, mlt++/src/MltField.h, mlt++/src/MltFilter.h, mlt++/src/MltFilteredConsumer.h, mlt++/src/MltFilteredProducer.h, mlt++/src/MltFrame.cpp, mlt++/src/MltFrame.h, mlt++/src/MltGeometry.h, mlt++/src/MltMultitrack.h, mlt++/src/MltParser.h, mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltPushConsumer.h, mlt++/src/MltService.h, mlt++/src/MltTokeniser.h, mlt++/src/MltTractor.h, mlt++/src/MltTransition.h: + Win32 port - dev studio is required to avoid issues with C++ ABI compatibility + Fix for image render in NTSC NB: mlt patch to follow (this one isn't much use without it :-)) - mlt build is purely mingw32 of course * 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 2006-01-08 ddennedy * 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 * mlt++/configure, mlt++/src/Makefile: + Fix for Darwin and soname logic * src/framework/Makefile, src/miracle/Makefile, src/modules/avformat/configure, src/valerie/Makefile: + Fix for Darwin and soname logic * mlt++/debian/changelog, mlt++/debian/control, mlt++/debian/copyright, mlt++/debian/rules: + Functional debian build rules * debian/changelog, debian/control, debian/copyright, debian/rules: + Functional debian build rules * mlt++/Makefile, mlt++/configure, mlt++/src/Makefile, mlt++/test/Makefile: + MLT++ updates for 0.2.1 - distclean corrected, soname usage in linking * 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 * mlt++/Makefile, mlt++/configure: + Correction to a typo * mlt++/configure, mlt++/src/Makefile: + Added a --libdir switch to the configure and build and fixed test case compilation * Makefile, configure, src/framework/Makefile, src/miracle/Makefile, src/valerie/Makefile: + Added a --libdir switch to the configure and build 2005-11-10 lilo_booter * mlt++/Makefile, mlt++/src/Makefile: + DESTDIR patch from Anthony Green (green at redhat dot com) - many thanks :-) * 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-25 lilo_booter * 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/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-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-03 lilo_booter * src/modules/sdl/configure, src/modules/sdl/factory.c: + Correction for uninstalled sdl image lib * mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h: OS/X gcc/g++ 4.x fix * 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 * mlt++/configure, mlt++/src/Makefile, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/test/Makefile: + Whoops - had forgotten these OS/X patches... * 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-09-28 lilo_booter * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltPushConsumer.cpp, mlt++/src/MltPushConsumer.h: + Added a push based consumer wrapper * 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-23 lilo_booter * 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 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-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/gpl, 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/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 * 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) 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/gpl, 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 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/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/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-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-26 lilo_booter * mlt++/swig/Makefile, mlt++/swig/configure, mlt++/swig/perl/Makefile.PL, mlt++/swig/python/build, mlt++/swig/tcl/build: + Cleaned up swig build so it doesn't require an mlt++ install first - Temporarily disabled java 2005-07-25 lilo_booter * 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-21 lilo_booter * src/framework/mlt_playlist.c, src/framework/mlt_service.c: - Remove warnings 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/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, 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 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-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/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 * configure, src/modules/motion_est/configure, src/modules/motion_est/gpl: 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-05 lilo_booter * 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 * mlt++/CUSTOMISING, mlt++/test/server.cpp: CUSTOMISING + Replaced TBD for frame rendering notification event test/server.cpp + Added an example frame rendering callback that removes all shotcut related fx 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 * mlt++/CUSTOMISING, mlt++/swig/ruby/thumbs.rb: CUSTOMISING + Added an example of how to hide a track on reception swig/ruby/thumbs.rb + Changed generator to run, rather than sleep and poll 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-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-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-05-28 lilo_booter * src/framework/mlt_consumer.c, src/framework/mlt_factory.c, src/framework/mlt_producer.c: Frame rate properites and factory initialisation 2005-05-24 lilo_booter * src/modules/kino/filehandler.cc, src/modules/kino/filehandler.h: DVCPRO fix 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 * 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-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-15 lilo_booter * 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/gpl, 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 dezeroex * src/modules/sdl/Makefile, src/modules/sdl/consumer_sdl_still.c: Build fixes. * 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 * src/modules/sox/Makefile, src/modules/sox/configure: Disable sox when unavailable * src/modules/dv/configure, src/modules/vorbis/configure: Disable libdv when unavailable * src/modules/gtk2/Makefile, src/modules/gtk2/configure, src/modules/gtk2/factory.c: Conditional compilation of gtk2 components 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 * mlt++/CUSTOMISING, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltResponse.cpp, mlt++/src/MltResponse.h, mlt++/test/server.cpp: Server customisation * 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 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-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/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/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, src/modules/sdl/consumer_sdl_still.c: Consumer reworked * src/modules/feeds/PAL/border.properties, src/modules/feeds/PAL/data_fx.properties: Minor corrections * src/framework/mlt_filter.c, src/framework/mlt_service.c: Wild card filter tracks 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 2005-01-19 lilo_booter * 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 2005-01-14 lilo_booter * mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h: Const string usage in properties * 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-03 lilo_booter * mlt++/src/MltGeometry.cpp, mlt++/src/MltGeometry.h: Next/Prev key extraction * 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 * mlt++/src/MltMiracle.cpp, mlt++/src/MltMiracle.h, mlt++/swig/mltpp.i: Fetch unit from miracle server * src/miracle/miracle_server.c, src/miracle/miracle_server.h: Fetch unit from miracle server 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_geometry.c, src/framework/mlt_geometry.h: Improved geometry 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/gpl, 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, src/modules/feeds/configure: 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 * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltGeometry.cpp, mlt++/src/MltGeometry.h, mlt++/swig/mltpp.i: Geometry * src/modules/core/transition_composite.c, src/modules/core/transition_luma.c, src/modules/lumas/Makefile, src/modules/lumas/configure, 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-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_producer.c, src/framework/mlt_service.c: Mutex locking in the get frame 2004-12-12 lilo_booter * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h: blank_at method added * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: blank_at method added 2004-12-11 lilo_booter * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h: split_at method added * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: split_at method added 2004-12-09 lilo_booter * mlt++/src/MltTractor.cpp, mlt++/src/MltTractor.h: Tractor constructor modifications * 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/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 * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltDeque.cpp, mlt++/src/MltDeque.h, mlt++/src/MltFactory.cpp, mlt++/src/MltFactory.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltTransition.cpp, mlt++/src/MltTransition.h: Deque added; simplified producer parent access; transition in and out * 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 * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltProperties.cpp, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/test/Makefile: More playlist modifications; service locking * 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 * mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltTractor.cpp, mlt++/src/MltTractor.h: Ref count and event firing method on properties; locate_cut on tractor * 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 * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltTractor.cpp, mlt++/src/MltTractor.h: Playlist reorganisation * 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 * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltTractor.cpp, mlt++/src/MltTractor.h: Simplified playlist and track access * src/framework/mlt_playlist.c, src/framework/mlt_playlist.h: Simplified playlist access 2004-11-05 lilo_booter * mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h: Added cut related methods 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 * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltTokeniser.cpp, mlt++/src/MltTokeniser.h, mlt++/test/server.cpp: Added courtesy tokenising class * 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 * mlt++/configure, mlt++/swig/configure, mlt++/swig/ruby/build, mlt++/swig/ruby/miracle.rb: Config changes * 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 * 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/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 * mlt++/src/MltMiracle.cpp, mlt++/src/MltMiracle.h: id and log level for server * 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 2004-10-14 lilo_booter * mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h, mlt++/test/play.cpp: buffer fix and tractor handling * 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 * mlt++/src/MltMiracle.cpp, mlt++/src/MltMiracle.h: 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 * 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-08 lilo_booter * mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h: Same and following clip identification * docs/framework.txt, docs/inigo.txt, docs/install.txt: Some documentation updates - more to follow 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 * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltFrame.cpp, mlt++/src/MltFrame.h, mlt++/src/MltParser.cpp, mlt++/src/MltParser.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/swig/mltpp.i: Added the parser object and moved type identity into mlt * 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-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 * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/swig/mltpp.i: new mix related methods * 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 * mlt++/README, mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h: Playlist repeat clip functionality * 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/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) 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 * mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/swig/mltpp.i: Cut management part 1 * 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 2004-09-23 lilo_booter * mlt++/src/MltService.cpp, mlt++/swig/mltpp.i: get_frame and ruby listen fix 2004-09-22 lilo_booter * mlt++/src/MltFrame.cpp, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/swig/mltpp.i: Event and frame handling * mlt++/configure, mlt++/src/MltMiracle.cpp: Server shutdown * 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 * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltMiracle.cpp, mlt++/src/MltMiracle.h, mlt++/src/MltResponse.cpp, mlt++/src/MltResponse.h, mlt++/swig/mltpp.i: Added the response object * mlt++/HOWTO, mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltMiracle.cpp, mlt++/src/MltMiracle.h, mlt++/swig/mltpp.i, mlt++/swig/ruby/miracle.rb, mlt++/test/Makefile, mlt++/test/play.cpp, mlt++/test/server.cpp: Adding miracle * 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 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-09 lilo_booter * src/framework/mlt_playlist.c, src/inigo/inigo.c, src/modules/inigo/producer_inigo.c: Fixes for removed tracks before/after mix * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h: Adding the mix part 1 * 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 * mlt++/src/MltFilteredConsumer.cpp, mlt++/src/MltFilteredConsumer.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/swig/mltpp.i: Service attach filters * 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 * mlt++/HOWTO, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/swig/mltpp.i, mlt++/swig/perl/play.pl: More event stuff * src/framework/mlt_multitrack.c, src/framework/mlt_tractor.c: Multitrack and tractor producer-changed event 2004-09-02 lilo_booter * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltEvent.cpp, mlt++/src/MltEvent.h, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/swig/mltpp.i, mlt++/swig/ruby/play.rb, mlt++/test/play.cpp: Event modifications * 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 * mlt++/HOWTO, mlt++/src/Makefile, mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h, mlt++/src/MltFilter.cpp, mlt++/src/MltFilter.h, mlt++/src/MltFrame.cpp, mlt++/src/MltFrame.h, mlt++/src/MltMultitrack.cpp, mlt++/src/MltMultitrack.h, mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/src/MltTractor.cpp, mlt++/src/MltTractor.h, mlt++/src/MltTransition.cpp, mlt++/src/MltTransition.h: Run time type identification * 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-29 ddennedy * src/framework/mlt_frame.c, src/framework/mlt_frame.h: add waveform method to frame 2004-08-28 lilo_booter * mlt++/README, mlt++/src/MltMultitrack.cpp, mlt++/src/MltMultitrack.h, mlt++/src/MltTractor.cpp, mlt++/src/MltTractor.h, mlt++/swig/mltpp.i: Tractor enhancements * src/framework/mlt_multitrack.h, src/framework/mlt_playlist.c, src/framework/mlt_tractor.c, src/framework/mlt_tractor.h: Tractor enhancements * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltField.cpp, mlt++/src/MltField.h, mlt++/src/MltMultitrack.cpp, mlt++/src/MltMultitrack.h, mlt++/src/MltTractor.cpp, mlt++/src/MltTractor.h, mlt++/swig/mltpp.i: Multitrack classes added * 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 * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/swig/mltpp.i: Producer filter extraction method 2004-08-27 lilo_booter * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltFilteredProducer.cpp, mlt++/src/MltFilteredProducer.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/swig/mltpp.i: Removed FilteredProducer * 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 * mlt++/Makefile, mlt++/test/Makefile, mlt++/test/play.cpp: Build modifications * mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h, mlt++/src/MltFilter.cpp, mlt++/src/MltFilter.h, mlt++/src/MltFilteredConsumer.cpp, mlt++/src/MltFilteredConsumer.h, mlt++/src/MltFrame.cpp, mlt++/src/MltFrame.h, mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/src/MltTransition.cpp, mlt++/src/MltTransition.h, mlt++/swig/perl/play.pl: Mlt Ref Counts and Playlist split/join * 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-23 lilo_booter * mlt++/src/Makefile, mlt++/swig/mltpp.i: Workaround for perl 2004-08-21 lilo_booter * mlt++/src/MltConsumer.cpp, mlt++/src/MltFilter.cpp, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltTransition.cpp: Constructor clean up * mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h: consumer purge * 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-20 lilo_booter * mlt++/src/MltFilter.cpp, mlt++/src/MltFilter.h, mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/swig/configure, mlt++/swig/java/build, mlt++/swig/ruby/build, mlt++/swig/ruby/thumbs.rb: mlt_position, /usr/bin/env and Instance fix 2004-08-19 lilo_booter * src/modules/core/filter_rescale.c, src/modules/gtk2/factory.c: Colour space conversion with gdkpixbuf scaling 2004-08-18 lilo_booter * mlt++/src/MltFrame.cpp, mlt++/src/MltFrame.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltService.cpp, mlt++/swig/mltpp.i, mlt++/swig/ruby/play.rb: image handling 2004-08-17 lilo_booter * mlt++/swig/java/Play.java, mlt++/swig/ruby/play.rb, mlt++/swig/ruby/thumbs.rb: Fixes for mods to api * mlt++/src/Makefile, mlt++/src/Mlt.h, mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h, mlt++/src/MltFilteredConsumer.cpp, mlt++/src/MltFilteredConsumer.h, mlt++/src/MltFilteredProducer.cpp, mlt++/src/MltFilteredProducer.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/swig/mltpp.i: Filtered producers and consumers 2004-08-16 lilo_booter * mlt++/swig/configure, mlt++/swig/java/Play.java, mlt++/swig/java/Play.sh, mlt++/swig/java/build, mlt++/swig/mltpp.i, mlt++/swig/perl/Makefile.PL, mlt++/swig/perl/build, mlt++/swig/python/build, mlt++/swig/python/play.py, mlt++/swig/ruby/build, mlt++/swig/ruby/play.rb, mlt++/swig/ruby/thumbs.rb, mlt++/swig/tcl/build, mlt++/swig/tcl/play.tcl: Experimental swig bindings * mlt++/README, mlt++/src/MltFactory.cpp, mlt++/src/MltFactory.h, mlt++/src/MltFilter.cpp, mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/src/MltTransition.cpp, mlt++/test/Makefile: More cleanups * mlt++/README, mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h, mlt++/src/MltFactory.cpp, mlt++/src/MltFactory.h, mlt++/src/MltFilter.cpp, mlt++/src/MltFilter.h, mlt++/src/MltFrame.cpp, mlt++/src/MltFrame.h, mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/src/MltTransition.cpp, mlt++/src/MltTransition.h, mlt++/test/play.cpp: Class rework and simplification * mlt++/src/Makefile, mlt++/src/Mlt.h: Added Mlt.h convenience header * mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltService.cpp: Complete methods for properties and playlist; reversed NULL handling on service class * mlt++/README, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/test/play.cpp: Object validity checks * 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 * mlt++/AUTHORS, mlt++/Makefile, mlt++/README, mlt++/configure, mlt++/src/Makefile, mlt++/src/MltService.cpp, mlt++/test/play.cpp: Build and docs modifications 2004-08-15 lilo_booter * mlt++/src/Makefile, mlt++/src/MltConsumer.cpp, mlt++/src/MltConsumer.h, mlt++/src/MltFactory.cpp, mlt++/src/MltFactory.h, mlt++/src/MltFilter.cpp, mlt++/src/MltFilter.h, mlt++/src/MltFrame.cpp, mlt++/src/MltFrame.h, mlt++/src/MltPlaylist.cpp, mlt++/src/MltPlaylist.h, mlt++/src/MltProducer.cpp, mlt++/src/MltProducer.h, mlt++/src/MltProperties.cpp, mlt++/src/MltProperties.h, mlt++/src/MltService.cpp, mlt++/src/MltService.h, mlt++/src/MltTransition.cpp, mlt++/src/MltTransition.h, mlt++/test/Makefile, mlt++/test/play.cpp: Initial revision 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-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 2004-08-03 lilo_booter * src/modules/core/filter_watermark.c, src/modules/core/transition_region.c: Mutable shapes on regions 2004-07-31 lilo_booter * 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/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/filter_region.c, src/modules/core/transition_composite.c, src/modules/core/transition_region.c: Allows runtime modifications to region fx 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-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-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-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-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-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 * 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/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, src/modules/normalize/gpl, src/modules/resample/gpl, src/modules/xine/gpl: Rescaler and fezzik rework (to allow inclusion of mc scaler) 2004-04-17 lilo_booter * 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/core/Makefile, src/modules/core/configure, src/modules/core/factory.c, src/modules/normalize/Makefile, src/modules/normalize/configure, src/modules/normalize/factory.c, src/modules/{core => normalize}/filter_volume.c, src/modules/{core => 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/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 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_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 lilo_booter * 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 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 * 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 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 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-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 * 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 * 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/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 * 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 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. * 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/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 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/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/Makefile, src/modules/core/Makefile, src/modules/core/configure, src/modules/core/factory.c, 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/{core => xine}/filter_deinterlace.c, src/modules/{core => 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/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 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-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/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 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, ...le_line_22_33_mmx.S => scale_line_22_yuv_mmx.S}: mmx version of non-nearest, 2x2 rescaling 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 * 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 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 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/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/ChangeLog, 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/configure, 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 ddennedy * 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 * 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 * src/miracle/miracle_unit.c, src/modules/dv/consumer_libdv.c: brought by a resizable 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 lilo_booter * 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/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 * 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, 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-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 * 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 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/configure, 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/configure, 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 * ChangeLog, Makefile, README, configure, mlt/ChangeLog, 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 mlt-6.20.0/Dockerfile000066400000000000000000000035271362234133600143770ustar00rootroot00000000000000FROM ubuntu:18.04 AS base ENV DEBIAN_FRONTEND noninteractive ENV HOME /tmp RUN apt-get update -qq && apt-get install -yqq apt-utils FROM base AS build # Install packages for building RUN apt-get install -yqq wget git automake autoconf libtool intltool g++ yasm nasm \ swig libgavl-dev libsamplerate0-dev libxml2-dev ladspa-sdk libjack-dev \ libsox-dev libsdl2-dev libgtk2.0-dev libsoup2.4-dev \ qt5-default libqt5webkit5-dev libqt5svg5-dev \ libexif-dev libtheora-dev libvorbis-dev python3-dev cmake xutils-dev \ libegl1-mesa-dev libeigen3-dev libfftw3-dev libvdpau-dev # Get and run the build script RUN wget --quiet -O /tmp/build-melt.sh https://raw.githubusercontent.com/mltframework/mlt-scripts/master/build/build-melt.sh && \ echo "INSTALL_DIR=\"/usr/local\"" > /tmp/build-melt.conf && \ echo "SOURCE_DIR=\"/tmp/melt\"" >> /tmp/build-melt.conf && \ echo "AUTO_APPEND_DATE=0" >> /tmp/build-melt.conf && \ echo "ENABLE_WEBVFX=1" >> /tmp/build-melt.conf && \ bash /tmp/build-melt.sh -c /tmp/build-melt.conf FROM base # Install packages for running RUN apt-get install -yqq libsamplerate0 libxml2 libjack0 \ libsdl2-2.0-0 libgtk2.0-0 libsoup2.4-1 \ libqt5core5a libqt5gui5 libqt5opengl5 libqt5svg5 libqt5widgets5 \ libqt5x11extras5 libqt5xml5 libqt5webkit5 \ libtheora0 libvorbis0a python3 \ libegl1-mesa libfftw3-3 libvdpau1 \ # Additional runtime libs \ libgavl1 libsox3 libexif12 xvfb libxkbcommon-x11-0 libhyphen0 libwebp6 \ # LADSPA plugins \ amb-plugins ambdec autotalent blepvco blop bs2b-ladspa calf-ladspa caps cmt \ csladspa fil-plugins guitarix-ladspa invada-studio-plugins-ladspa mcp-plugins \ omins rev-plugins ste-plugins swh-plugins tap-plugins vco-plugins wah-plugins \ # Fonts \ fonts-liberation 'ttf-adf-.+' # Install the build COPY --from=build /usr/local/ /usr/local/ ENTRYPOINT ["/usr/local/bin/melt"] mlt-6.20.0/Doxyfile000066400000000000000000001663041362234133600141160ustar00rootroot00000000000000# 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 = 6.20.0 # 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 = YES # 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 = YES # 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\"" ALIASES += "event=\xrefitem event \"Event\" \"Events Dictionary\"" ALIASES += "envvar=\xrefitem envvars \"Environment Variable\" \"Environment Variables\"" # 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 penalty. # 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 roughly 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 = YES # 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 mlt-6.20.0/GPL000066400000000000000000000432541362234133600127530ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 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 Lesser 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General Public License instead of this License. mlt-6.20.0/GPLv3000066400000000000000000001045131362234133600132200ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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 Lesser General Public License instead of this License. But first, please read . mlt-6.20.0/Makefile000066400000000000000000000037031362234133600140410ustar00rootroot00000000000000SUBDIRS = src/framework \ src/mlt++ \ src/melt \ src/modules \ src/swig \ 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 -f mlt-config packages.dat list='$(SUBDIRS)'; \ for subdir in $$list; do \ $(MAKE) -C $$subdir $@ || exit 1; \ done echo > 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)$(moduledir)" ifeq ($(extra_versioning), true) ln -s "$(moduledir)" "$(DESTDIR)$(unversionedmoduledir)" endif install -d "$(DESTDIR)$(libdir)/pkgconfig" install -d "$(DESTDIR)$(mltdatadir)" ifeq ($(extra_versioning), true) ln -s "$(mltdatadir)" "$(DESTDIR)$(unversionedmltdatadir)" endif install -c -m 644 *.pc "$(DESTDIR)$(libdir)/pkgconfig" list='$(SUBDIRS)'; \ for subdir in $$list; do \ $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \ done cp -R presets "$(DESTDIR)$(mltdatadir)" uninstall: rm -f "$(DESTDIR)$(bindir)"/mlt-config rm -f "$(DESTDIR)$(libdir)"/pkgconfig/mlt-framework.pc 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)$(mltdatadir)" ifeq ($(compat_dirs), true) rm -rf "$(DESTDIR)$(prefix)/share/mlt" endif dist: git archive --format=tar --prefix=mlt-$(version)/ v$(version) | gzip >mlt-$(version).tar.gz validate-yml: for file in $$(find src/modules -type f -name \*.yml); do \ echo "validate: $$file"; \ kwalify -f src/framework/metaschema.yaml $$file || exit 1; \ done codespell: codespell -w -q 3 \ -L shotcut,sav,boundry,percentil,readded,uint,ith,sinc,amin,childs,seeked,writen \ -S ChangeLog,cJSON.c,cJSON.h,RtAudio.cpp,RtAudio.h,*.rej,mlt_wrap.* mlt-6.20.0/NEWS000066400000000000000000001630201362234133600130770ustar00rootroot00000000000000MLT Release Notes ----------------- Version 6.20.0 - February 15, 2020 This version adds support for low resolution preview scaling and adds a module based on librubberband for audio pitch-shifting. An official docker image is now available at https://hub.docker.com/repository/docker/mltframework/melt Framework * Added consumer scaling: - mlt_profile_scale_width() - mlt_profile_scale_height() - Mlt::Profile::scale_width() - Mlt::Profile::scale_height() - support for a double "scale" property to melt and the xml producer * Fixed mlt_properties_set() with an invalid expression. * Added new functions that do not evaluate expressions: - mlt_properties_set_string() - Mlt::Properties::set_string() * Improved the service-caching heuristic in mlt_multitrack. * Fixed possible crashes in mlt_playlist get_frame() and mlt_filter_process(). Modules * Added the rubberband module with a rbpitch filter. * Added pitch compensation to timewarp producer. * Added the invert_scale property to the affine filter and transition. * Added the reverse property to shape filter. * Added support for text keyframes to the text and qtext filters. * Added support for the CSRT and MOSSE algorithms in opencv.tracker filter. * Fixed a crash on empty algo property in the opencv.tracker filter. * Changed vorbis module to no longer be deprecated. * Improved colorspace conversions in the avformat module. * Fixed audio artifacts on initial seek to in point in avformat producer. * Fixed the colorspace of the cached image in avformat producer. * Fixed white video flashes on property changes in the qtext filter. * Fixed a crash in the rotoscoping filter with large spline deviations. * Fixed a crash in the sdi consumer if the driver is not loaded. * Improved support for a video clip as luma producer to the luma transition. * Fixed a crash in the matte transition. * Fixed a crash when using invert property =1 in the composite transition. Other * Added a Dockerfile and integrated docker build into Travis CI. * Added more avformat consumer presets: - intermediate/DNxHR-HQ - intermediate/ProRes HQ - ALAC - FLAC * Fixed some parameters in the XDCAM and D10 avformat presets. * Fixed link failure on some CPU architectures. Version 6.18.0 - November 11, 2019 This version is a general maintenance release with a bunch of fixes, improvements, and additions. Framework * Fixed some data races in mlt_consumer, mlt_deque, and mlt_property. * Fixed the mlt_events listener incorrect owner argument. * Added support for the LC_ALL environmant variable on Windows. * Fixed the argument to mlt_factory_init() not working on Windows. * Fixed mlt_service_identify() not reliable in some use cases. * Added some default and copy constructors and assignment operators to mlt++ - Filter() - Filter( const Filter &filter ) - Filter& operator=( const Filter &filter ) - Producer( const Producer &producer ) - Producer& operator=( const Producer &producer ) - Properties( const Properties &properties ) - Properties& operator=( const Properties &properties ) - Service( const Service &service ) - Service& operator=( const Service &service ) - Transition() - Transition( const Transition &transition ) - Transition& operator=( const Transition &transition ) * Added mlt_luma_map: - mlt_luma_map_init - mlt_luma_map_new - mlt_luma_map_render - mlt_luma_map_from_pgm - mlt_luma_map_from_yuv422 * Fixed preset overrides depend on the XML attribute order. * Fixed serializing an animated property with a new length. Modules * Fixed interpolation in rotoscoping filter. * Fixed crop filter not working with color producer. * Fixed some data races in the sdl and sdl2 consumers. * Fixed some data races in the avformat producer. * Added a movit.flip filter to the opengl module. * Fixed using filters on frei0r producers. * Added support for in and out attributes on the "consumer" xml element. * Fixed using an in point with the multi consumer. * Fixed avfilter fails if the image size changes. * Fixed showing superfluous decimals for seconds in the timer filter. * Stop serializing an invalid producer as an "INVALID" text producer in xml. * Fixed an access violation crash in wave filter. * Added the meta.media.color_range property to the avformat producer. * Fixed full range yuv422p not converted correctly in the avformat producer. * Fixed the text filter not working with pango. * Fixed a regression using dynamictext with pango. * Added a position property to avfilter for filters that need position info. * Fixed avfilter.subtitles not using the source position. * Added an analyze property to vidstab filter. When set, analysis only starts and the results file written if true. * Fixed crash combining affine the affine filter with the shape filter. * Added interlace detection from AVCodecContext.field_order. * Changed the avformat producer to not use the rescale.interp frame property. Previously, when interp == nearest, it would relax seeking. Now, seek accuracy is reduced during trick play (rewind or fast forward). * Fixed sws flags for auto-inserted scalers in avfilter. * Fixed a double free crash in ladspa filter on channel count mismatch. * Refactored the composite and luma transitions to use mlt_luma_map. * Refactored the pgm producer and shape filter to use mlt_luma_map. * Refactored the lumas module to use mlt_luma_map. * The lumas module is now disabled by default and must be explicitly enabled. * Added property animation to the threshold filter. * Added a cairoblend_mode filter to the frei0r module to affect a frei0r.cairoblend transition used to composite/blend tracks. * Added support for new vaapi options to the avformat consumer: - connection_type: x11 or drm - driver - kernel_driver * Fixed the timewarp producer with a colon in the filename. * Fixed a relative file name with a colon in it in the xml producer. * Fixed defaulting to album or poster art if there is another video stream. * Fixed parameter animation in frei0r plugins when using frame threads. This change also enables frame-threading for more plugins. * Improved the qtblend filter to not process alpha if no transparency. * Added a background_color property to the qtblend filter. * Fixed the opencv.tracker incorrect behavior on cut clips. * Changed opencv.tracker to store absolute frame numbers. * Fixed incorrect frame offset on render in opencv.tracker. * Add an alpha_over property to luma transition. This addresses a behavior regression in version 6.14.0. * Fixed noimagecache not working in the avformat producer. Other * Mlt++ now requires C11 compiler support. * Fixed closing melt SDL2 window from window manager (i.e. close button). * Added -repository option to the melt command. * Added unit tests for Mlt::Event. * Fixed returning image data for Python 3. * Switch to python3 by default. * Updated the prores encoding presets to set vendor ID and colr atom. * Added a CMake build system. This is not yet prefered over the existing configure script and Makefiles and has less flexibility. It is a start and has limited support. Version 6.16.0 - May 7, 2019 This version is released to facilitate packaging the latest version of Shotcut, which is using new APIs. Framework Added functions to get/set a creation date to a producer: - mlt_producer_get_creation_time() - mlt_producer_set_creation_time() - Mlt::Producer::set_creation_time() - Mlt::Producer::get_creation_time() Modules * Fixed dance filter not showing when lower track is transparent. * Refactored dynamictext filter to use mlt_producer_get_creation_time(). * Marked frei0r rgsplit0r plugin version < 1.1 as not thread-safe. * Fixed possible null pointer crash in mlt_properties_serialise_yaml. Version 6.14.0 - March 30, 2019 This version is mostly fixes plus a few API additions and filters. Framework * Added mlt_profile_lumas_dir(). * Added mlt_frame_get_unique_properties(). * Added mlt_playlist_reorder() and Mlt::Playlist::reorder(). * Added some new convenience constructors to mlt++ - Producer(mlt_profile profile, const char *id, const char *service = NULL) - Consumer(mlt_profile profile, const char *id , const char *service = NULL) - Transition(mlt_profile profile, const char *id, const char *arg = NULL) - Filter(mlt_profile profile, const char *id, const char *service = NULL) - Tractor(mlt_profile profile, char *id, char *arg = NULL) * Added Mlt::Transition::connect(Service&). * Added unit tests for mlt_playlist. * Fixed a crash on invalid transition track values in mlt_transition. * Fixed a deadlock regression in v6.12.0 of mlt_consumer when starting from a paused state (producer speed=0). Modules * The avformat module now requires at least FFmpeg v2.4 or Libav 12. * Added mask_start and mask_apply filters to the core module. * Added qtext filter to qt module. * Changed dynamictext and timer filters to use qtext. * Fixed number of digits for seconds in timer filter. * Added mlt_image_format property to color producer. * Improved color accuracy of libswscale RGB->YUV conversion. * Fixed frei0r producer not working with tractor. * Fixed decklink consumer stalling on dropped frames. * Generate lumas for 16:9, 9:16 (vertical), and square aspect ratios. * Fixed crash in qimage when alpha_size is zero. * Fixed the mlt_consumer channels property not being passed to multi consumer. * Fixed the shape filter for full range color and crashes. * Converted the shape filter to use mlt_animation. * Added a use_mix property to the shape filter. * Fixed invert=1 and mix=100 gives wrong image in shape filter. * Fixed a possible free null pointer in the linsys sdi consumer. * Fixed using destroyed temporary object in qimage. * Fixed a possible null pointer dereference in the spot_remover filter. * Fixed memory leak on swr_convert() failure in swresample filter. * Fixed possible null pointer dereference in affine when not using rect. * Fixed loading image sequence on Windows in qimage. * Fixed some null pointer crashes using Movit opengl services. * Fixed sdl2 consumer crashes during initialization on Linux or BSD. * Fixed distorted image using melt_file. * Fixed qimage build on Qt version < 5.5. * Added offset property to the timer filter. * Changed the boxblur hori & vert properties' minimum to 0. * Fixed crash in duplicate frame on rotated videos. * Added automatic scaling and padding to avfilter. * Fixed field order when encoding progressive as interlace. * Fixed frei0r plugins to use the number of slices from the threads property. * Fixed over compositing with transparent clips in luma transition. * Added sliced processing to dissolve-with-alpha using the threads property. * Added createdate keyword to dynamictext filter. * Fixed possible crash changing audio_index in avformat producer. * Fixed small memory leaks in xml consumer, jackrack, and timewarp producer. * Fixed compiling opencv module with OpenCV > 3. Other * Added vertical video profiles: - vertical_hd_30 - vertical_hd_60 * Mlt++ now requires C++11 compiler support. * Added --disable-windeploy to configure to keep bin & lib folders on Windows. * Added support for consumer in & out to melt. * Fixed color accuracy of lossless/Ut Video preset and use pix_fmt yuv422p. * Fixed x264 lossless preset to use crf=0. * Fixed compiling with mingw32. * Fixed build with Python 3. Version 6.12.0 - November 26, 2018 This version has many important fixes plus a few new filters and support for encoding using VA-API. Framework * Changed buffer property to be mutable and adaptive to speed property in mlt_consumer. * Changed macOS RELOCATABLE build to use standard app bundle layout: - lib/mlt -> ../PlugIns/mlt - lib/frei0r-1 -> ../PlugIns/frei0r-1 - lib/ladspa -> ../PlugIns/ladspa - share/mlt -> ../Resources/mlt - share/movit -> ../Resources/movit * Fixed a_track of transitions matching deleted track in mlt_tractor_remove_track(). * Fixed multi-thread race crash in mlt_properties_clear(). * Fixed possiblle null pointer crash in mlt_property_get_rect() and mlt_property_get_time(). * Fixed non-animated strings containing ';' or '=' in mlt_animation_parse(). * Fixed crash in clear_property() with mlt_animation. Modules * Added a generic text filter to the plus module. * Added a timer filter to the plus module. * Added audio timeout handling to sdl2 consumers. * Added spot_remove filter to the plus module. * Added dds, ico, and webp filename extensions for qimage producer. * Added support for color_range property in avformat consumer: "pc" or "jpeg" for full range, otherwise limited range. * Added a window property to the audiowaveform filter. * Added MM:SS.SS to the timer filter. * Added query string param "multi" to the xml producer to force using the multi consumer. * Improved WebP image support in avformat producer. * Integrated hwupload filter in avformat consumer if using VAAPI codec. * Changed count producer to use pango if qtext not available. * Changed qt moduled to not call XInitThreads() * Changed color producer to only set alpha on frame if rgb24a requested or not opaque. * Changed the xml producer to pass quality and performance parameters to the multi consumer. * Fixed sdl2_audio distortion (regression in v6.10.0). * Fixed dynamictext filter to not error on empty text. * Fixed dynamictext aliased (regression in v6.10.0). * Fixed qimage outputs premultiplied if scaled internally. * Fixed crash in cbrts consumer if running property was never set. * Fixed rendering edges of some typefaces in qtext producer. * Fixed qimage fails to load with wrong filename extension. * Fixed affine dark right and bottom edge artifacts regression in (v6.10.0). * Fixed support for vp8 and vp9 with alpha channel in avformat producer. * Fixed interpolation mode selection in qimage producer. * Fixed crash in qimage with alpha channel. * Fixed some AAC MP4 files start playing from middle in avformat producer. * Fixed crash in avfilter if initialization fails. * Fixed crash in mix when frame rate is very low. * Fixed crash on missing luma file in composite transition. * Fixed A/V sync on some files in avformat producer. * Fixed seeking on audio filter with album art in avformat producer. * Fixed colorspace conversion in avformat consumer. Other * Added more avformat consumer presets: - alpha/Quicktime Animation - alpha/vp8 - alpha/vp9 - alpha/Ut Video - lossless/Ut Video * Added square video profiles: - square_1080p_30 - square_1080p_60 * Added support for nodejs to the swig bindings. * Changed configure script to require opencv module be explicitly enabled. * Numerous spelling fixes in source code and comments thanks to codespell. Version 6.10.0 - July 2, 2018 This version fixes bugs and supports serializing animation keyframes with a specified time format (previously only frame number). Framework * Reverted mlt_pool change in v6.8.0 pending further testing. (USE_MLT_POOL compiler define is now a 0/1 boolean, defaults to 1.) * Fixed crash regression in v6.8.0 "parsing non-animated string as an animation." * Added pointer checks to mlt_animation. * Changed producer cache size heuristic in mlt_multitrack to be more liberal. * Fixed handling reserved characters in names for YAML in mlt_properties. * Added clamping to prevent computing negative in and out points to mlt_producer. * Added functions to serialize animation with a time format: - mlt_animation_serialize_cut_tf() - mlt_animation_serialize_tf() - mlt_property_get_string_tf() - mlt_property_get_string_l_tf() - mlt_properties_get_value_tf() - Mlt::Properties::get(int, mlt_time_format) - Mlt::Animation::serialize_cut(mlt_time_format, int, int) * Added functions to clear a property to mlt_properties: - mlt_property_clear() - mlt_properties_clear() - Mlt::Properties::clear() Modules * Fixed enabling sliced pix_fmt conversion in avformat producer. * Fixed incorrect seek and sync on audio files with discard packets. * Added support for avcodec_send_frame() API to avformat consumer. * Fixed compile errors with Libav master. * Fixed a crash in affine transition. * Fixed a crash in ladspa filters when consumer frame rate is low (e.g. <= 8). * Fixed a crash in boxblur filter. * Added animation support to boxblur hori and vert properties. * Fixed a crash in movit.convert. * Fixed incorrect alpha in affine transition blending routine. * Converted frei0r from deprecated mlt_geometry to mlt_animation API. * Fixed tilde in text string for pango producer. * Fixed using more than one channelcopy filter. * Fixed the mono filter reducing volume level. * Fixed degraded audio scrubbing in sdl2_audio consumer. * Converted dynamictext filter to use affine transition for more correct alpha compositing and sub-pixel positioning. * Added time format support for animation keyframes to the xml consumer. * Added animation support to more affine transition properties: - fix_rotate_x - fix_rotate_y - fix_rotate_z - fix_shear_x - fix_shear_y - fix_shear_z - ox - oy - scale_x - scale_y * Fixed gaps in text when characters overlap in qtext and kdenlive producers. * Fixed a crash in pixbuf producer with multiple render threads. * Converted the oldfilm vignette filter from mlt_geometry to mlt_animation. Other * Numerous updates to mlt-xml.dtd. * Categorized many of the encode presets (using meta.preset.name). Version 6.8.0 - May 10, 2018 This version improves support for multi-channel audio and adds some new manipulation functions to the mlt_animation API. Framework * Added support for musl C library. * Added functions for audio channel layouts: - mlt_channel_layout_name() - mlt_channel_layout_id() - mlt_channel_layout_channels() - mlt_channel_layout_default() * Added channel_layout property to mlt_consumer. * Added mlt_channel_layout enum. * Disabled memory pooling by default and require compile macro USE_MLT_POOL to re-enable it. * Fixed reliability of keyframed properties serializing properly. * Fixed parsing non-animated string as an animation. * Added more functions to mlt_animation: - mlt_animation_key_set_type() - mlt_animation_key_set_frame() - Mlt::Animation::key_set_type() - Mlt::Animation::key_set_frame() Modules * Fixed some crashes in qimage producer especially with alpha channel. * Fixed >2 channel audio output in the SDL consumers. * Fixed >2 channel audio output in the rtaudio consumer on Windows. * Fixed vorbis encoding with FFmpeg v3.4+. * qimage and qtext are now higher priority than gtk2 pixbuf and pango by the loader producer. * Added support for more channel counts to decklink consumer. * Added swresample filter based on libswresample from FFmpeg. This is now the preferred channel count normalizing filter used by the loader producer. * Fixed the strange "Undefined constant" and "Unable to parse option value" log messages in the the avformat consumer. * Fixed GIF and DPX writing in avformat consumer. * Reduced the memory usage of the affine transition and filters. * Fixed a crash in kdenlivetitle producer. * Fixed a crash in the rotoscoping filter. * Fixed frame rate reported in Matroska and WebM files produced by the avformat consumer. * Added sdl2_audio consumer. * Fixed alpha channel support for more pixel formats in the avformat producer. * Converted the affine transition to use mlt_rect and mlt_animation. * Fixed LADSPA plugins with mono channel audio. Other * Fixed a melt command line parsing bug when argument supplied to -transition. * Fixed melt with SDL2 on Windows not using stdio and stderr. * Improved speed of the vp9 avformat consumer preset. Version 6.6.0 - January 22, 2018 This version builds upon the previous release with performance improvements using the sliced image processing framework. It also improves compatibility with dependencies (FFmpeg, Qt 5, SDL 2, NDI, OpenCV, libebur128). Framework * Added a thread pool to mlt_slices: - mlt_slices_run_normal() - mlt_slices_run_rr() - mlt_slices_run_fifo() - mlt_slices_count_normal() - mlt_slices_count_rr() - mlt_slices_count_fifo() - MLT_SLICES_COUNT environment variable * Added mlt_log_timings_now() to mlt_log. * Added mlt_image_yuv422p16 image format. * Added mlt_image_format_planes() and mlt_image_format_id() to mlt_frame. * Added mlt_service_disconnect_all_producers() and Mlt::Service::disconnect_all_producers() * Fixed accuracy of mlt_time_format conversions. * Fixed mlt_filter_get_progress() never reaching 1.0. * Fixed divide by zero in mlt_filter_get_progress(). Modules * Added sdl2 module! * Added sum property to mix transition for simple mixing without dynamics. * Added TLD and KCF tracking modes to opencv module. * Added CSV (comma-separated values) simple slideshow to pixbuf producer. * Added jack filter to jackrack module. * Added sliced processing to frei0r. * Added sliced processing to composite transition. * Added sliced processing to avformat producer pixel format conversion. * Added sliced processing to affine transition and filter. * Converted volume and pan filters to floating point. * Added rotate_center property to qtblend transition and filters. * Added meta.media.variable_frame_rate (VFR detection) to avformat producer. * Added support for external libebur128. * Updated internal libebur128 to latest. * Added 10-bit capture to decklink producer. * Added mlt_image_yuv422p16 to fieldorder filter, avcolour_space filter, and avformat consumer. * Added no_profile property to xml consumer. * Various ndi module fixes and improvements. * Fixed setting color_trc in multi consumer. * Fixed kdenlive title transparency. * Fixed incorrect alpha channel breaking freeze filter with qtblend transition. * Fixed transparency of pixbuf images with qtblend transition. * Fixed some crashes in new qtblend transition. * Fixed building qt module with C++11 for Qt 5.7+. * Fixed reporting accurage length for image sequences in qimage and pixbuf. * Fixed extra audio samples added to end of file in avformat consumer. * Fixed rawvideo export in avformat consumer. * Fixed multi-threading with FFmpeg 3.2+ in avformat module. * Fixed JPEG album art in avformat producer. * Removed filter_avresample (dropped by FFmpeg). * Stop flushing audio buffer in decklink consumer. * Fixed field order handling in decklink consumer. * Make composite transition field order aware. * Fixed nesting mlt_tractor with opengl module. * Fixed kdenlive titler crash on resized frame. Other * Various memory leak fixes. * Fixed opening files with extended chars on Windows (framework and modules). Version 6.4.1 - November 15, 2016 Hot fix for new C++ symbol Mlt::Profile::is_valid() not declare const in symbol versioning. This was breaking script bindings. Version 6.4.0 - November 11, 2016 This is both a bugfix and enhancement release: Framework * Added functions for multi-threaded slice-based image processing: mlt_slices_init, mlt_slices_close, and mlt_slices_run. * Added Mlt::Profile::is_valid(). * Added MLT_DIRLIST_DELIMITER to mlt_types.h. * Renamed mlt++/config.h to mlt++/MltConfig.h. * Fixed mlt_properties_set_lcnumeric() on macOS. * Fixed address of Free Software Foundation in comment headers. Modules * Added crop_to_fill property to composite transition. * Added sliced_composite property to composite transition. * Added peak and true peak properties to loudness_meter filter. * Added qtblend transition and filter to qt module. * Added ndi (NewTek NDI) module with producer and consumer. * Added opencv module with opencv_tracker filter. * Added line_spacing, stretch, wrap_width, and wrap_type properties to pango producer. * Added oblique value for style property to pango producer. * Added fontmap-reload event to pango producer. * Added support for pkg-config to sdl module. * Added .kra (Krita Image) file name extension to loader.dict. * Improved performance of kdenlivetitle producer. * Improved decklink producer and consumer. * Improved accuracy of seeking on lossy compressed audio in avformat producer. * Improved mix transition using 32-bit floating point. * Fixed avfilter when image format changes. * Fixed loading relative file name in vidstab filter. * Fixed crash on Windows with avfilter. * Fixed parsing LADSPA_PATH with semi-colon delimiter on Windows. * Fixed parsing FREI0R_PATH with semi-colon delimiter on Windows. * Fixed reading relative path with backslash (Windows) in xml producer. * Fixed loading relative file name for av.file (avfilter). * Fixed loading multiple LADSPA plugins on some systems. * Fixed compile error when not configured with --enable-gpl. * Fixed loading in avfilter.lut3d in locales with comma decimal point. * Fixed a possible crash in resample filter. * Fixed alpha channel in kdenlivetitle producer. * Fixed possible crash in pixbuf and qimage producers. * Fixed count when counting down in count producer. Other * Moved some avformat presets from lossless to new intermediate folder. * Added a YouTube avformat consumer preset. * Changed metadata.rb metadata publisher to output Markdown. Version 6.2.0 - April 20, 2016 There are no framework changes in this release. The major announcement is the introduction of support for libavfilter! This is still a work-in-progress. It is limited to FFmpeg 2.3 and up, and there are a number of filters that are black-listed because they are known to not integrate with MLT, which is not a full libav* environment or simple wrapper for it. There are likey avfilters that are not yet black-listed but might not work because they have not been completely tested. Also, they do not support MLT's keyframable property animation nor its frame-threaded parallelism due to architectural or integration limitations. However, some avfilters are slice-threaded (internal parallelism), and that works. Finally, libavfilter filtergraph syntax is not supported either. All of the supported libavfilters are exposed as MLT filters beginning with the prefix "avfilter." All of the avfilter parameters are exposed as MLT properties with the "av." prefix to prevent clashes with MLT properties. You can run `melt -query filters` to see the new avfilters, and `melt -query filter=avfilter.rotate`, for example, to view generated documentation for an individual filter. Here is a list of notable fixes and enhancements in this release: * Added support for libavfilter to avformat module. * Added auto-rotate support to avformat producer. * Added animated GIF preset for avformat consumer. * Prevent serializing and deserializing mlt_type property to xml module. * Fixed relative paths for WebVfx "plain:" resources in xml module. * Updated libebur128 to v1.1.0 in plus module. * Added dynamic_loudness filter to plus module. * Added loudness_meter filter to plus module. * Qt 5 fixes for kdenlivetitle producer. * Added gradients and text shadows to kdenlivetitle producer. * Added support for building rtaudio against external build of lib. * Upgraded bundled RtAudio to v4.1.2. * Added status parameters to ladspa producer and filters. * Added 5.1 surround to stereo downmix to audiochannels filter in core module. * Fixed compiling SWIG bindings for Ruby 2.0. Version 6.0.0 - February 17, 2016 This is a bugfix and minor enhancement release. Note that our release versioning scheme has changed. We were approaching 1.0 but decided to synchronize release version with the C library ABI version, which is currently at v6. Here are some of the notable changes and enhancements: Framework * Added unit tests for tractor, multitrack, and field. * Deprecate mlt_frame_get_alpha_mask(). * Added drop_count readable property to mlt_consumer. * Added mlt_factory_repository(). * Added mlt_properties_to_utf8(). * Define MIN, MAX, CLAMP in mlt_types.h in not already defined. * Switched to __APPLE__ and _WIN32 defines throughout codebase. Modules * Added UDP and SMPTE 2022-2 support to cbrts consumer. * Fixed build against latest FFmpeg versions - now requires v1.1 and up. * Added audiospectrum filter to qt module. * Added meta.media.0.codec.rotate property to avformat producer to let apps and other services get the media orientation. * Make the avformat producer handle animated images. * Added style property to dynamictext filter. * Added timewarp producer to core module. * Fixed slowly accumulating A/V sync drift in mix audio transition. * Added width_crop and width_fit properties to pango producer. Melt * Added -abort option to simply exit without full cleanup. * Fix key-press handling on Windows. Version 0.9.8 - July 29, 2015 Here is a list of the enhancements included in this release. There are many API additions to support the ability for apps to add and remove tracks and represent and manipulate property animations. There are still many services, however, that need updating to support animated properties. Framework * Added mlt_service_disconnect_producer() and Mlt::Service::disconnect_producer(). * Added mlt_multitrack_disconnect() and Mlt::Multitrack::disconnect(). * Added mlt_tractor_remove_track() and Mlt::Tractor::remove_track(). * Added mlt_service_insert_producer() and Mlt::Service::insert_producer(). * Added mlt_multitrack_insert() and Mlt::Multitrack::insert(). * Added mlt_tractor_insert_track() and Mlt::Tractor::insert_track(). * Added mlt_transition_set_tracks() and Mlt::Transition::set_tracks(). * Added Mlt::Properties::get_animation(). * Added Mlt::Properties::get_anim(). * Added Mlt:Animation class with methods: - length() - is_key() - keyframe_type() - get_item() - next_key() - previous_key() - set_length() - remove() - interpolate() - serialize_cut() * Added mlt_animation_key_count() and Mlt::Animation::key_count(). * Added mlt_animation_key_get() and Mlt::Animation::key_get(). Modules * Added audiowaveform video filter. * Added fft audio filter. * Added dance video filter (uses fft). * Added lighshow video filter (uses fft). * Added distort property to movit.rect video filter. * Added rotate property to pango video producer. * Added 2K DCI and 4K modes to decklink producer and consumer. * Added audiomap (channel remapping) filter. * Added property animation to all LADSPA audio filters and producers. Version 0.9.6 - March 1, 2015 A major regression slipped into the 0.9.4 release plus some other good fixes rolled in just after that release prompting this new release. Please discontinue using version 0.9.4 and upgrade. Version 0.9.4 - February 15, 2015 This is a bugfix and minor enhancement release. Here are some of the notable changes and enhancements. Framework * Added color_trc (transfer characteristic) property to mlt_consumer and mlt_frame. * Added Mlt::Profile::set_display_aspect(int, int). * Added mlt_pool_stat(). * Added mlt_smpte_df and mlt_smpte_ndf to mlt_time_format for non-drop-frame timecode support. * Added Mlt::Tractor::Tractor(Mlt::Profile&). * Added mlt_frame_get_alpha(). * Added default, copy, and assignment methods to Mlt::Frame. * Added Mlt::Filter::process(Mlt::Frame&). Modules * Added support for color_trc property to avformat and opengl modules. * Performance improvements for composite and matte transitions. * Added fill, halign, and valign properties to affine transition and filter. * Added producer.* and consumer.* properties to consumer producer. * Fixes for libavformat and libavcodec v56. * Dropped support for FFmpeg < v1.0 and Libav < v9. * Added a lumakey filter. * Added a localtime property to dynamictext filter. * Added date/time format string support to dynamictext filter. * Added no_root property to xml consumer. * Added audio-only tone producer. * Added drop property to count producer. * Added caching to pango producer to improve performance. Other * Added WMV and WMA avformat consumer presets. * Added a ProRes-Kostya avformat consumer preset. * Changed VP9 WebM preset to use Opus audio codec. * Added 4K UHD and 2.5K QHD profiles. * Added x265-medium and x265-medium-pass1 avformat consumer presets. * Added a unit test for Mlt::Frame. Version 0.9.2 - June 29, 2014 This is a bugfix and minor enhancement release. Framework * Added "boolean" parameter type and "argument" parameter attribute to service metadata schema. * Added mlt_properties_frames_to_time(). * Added mlt_properties_time_to_frames(). * Changed mlt_events_fire() to return the number of listeners. * Added consumer-thread-create and consumer-thread-join events. * Added LC_NUMERIC handling for Windows. * Added mlt_playlist_mix_out() and mlt_playlist_mix_in() * Added mlt_properties_from_utf8() Modules * Renamed "qimage" module to "qt". * Added support for Qt 5. * Added qtext producer, which is now preferred by dynamictext filter over pango. * Added xml-nogl consumer. * Consolidated dgraft, burningtv, and rotoscoping into new plusgpl module. * Added vid.stab module with vidstab and deshake filters. This depends on external vid.stab library unlike its predecessors. * Rewrote opengl module. * Added loudness filter based on EBU R128. * Added rgblut filter to plus module. * Added luma-only liftgammagain filter to plusgpl module. * Added lift_gamma_gain filter to plus module. * Added support new keyframable animated properties to brightness, volume, panner, boxblur, wave, sepia, charcoal, burn, gamma, grain, dust, lines, tcolor, oldfilm, * Added movit.luma transition to opengl module. * Added cbrts consumer to plusgpl module. * Removed the "ppm" PPM-pipe producer. * Added more VITC functionality to decklink module. * Added matte transition to core module. DEPRECATION WARNINGS * Deprecate videostab module with videostab and videostab filters. This will not be removed soon in order to keep old scripts and projects functional. * Deprecate the dv (libdv), kino, and vorbis modules. These are scheduled to be removed in next release. Version 0.9.0 - June 2, 2013 This is a significant enhancement release. Build (especially interesting for Linux packagers) * Added --rename-melt and --enable-extra-versioning configure options. * Added symbol versioning on Linux. Framework * Improved pause behavior when using buffered rendering in mlt_consumer. * Added mlt_animation API (exposed via Mlt::Properties in C++). * Added mlt_rect and mlt_color types. * Deprecated mlt_geometry API. Modules * Support for the latest versions of FFmpeg and Libav (but dropping support for 0.5 and 0.6 versions). * Added alpha channel output to avformat consumer. * Added reconnect and exit_on_disconnect properties to avformat producer. * Added opengl module that uses Movit for GLSL image processing. * Added qglsl consumer to use opengl with avformat, sdi, and decklink. * Added avsync module with blipflash producer and consumer for testing. * Added new "count" producer to gtk2 module. * Changed frei0r to use index-based property names making it impervious to param name changes (param name still accepted for compatibility). * Added default parameter values to frei0r metadata. Other * Added more python example web services. * Added the beginnings of unit test suite. Version 0.8.8 - January 20, 2013 This is purely a bugfix release. See the ChangeLog or git log. Version 0.8.6 - November 14, 2012 This is a re-issue of the 0.8.4 release with a fix for a performance regression on videos that use full-range colorspaces such as yuv420p. Version 0.8.4 - November 13, 2012 This is a bugfix and minor enhancement release. * Added playlist-next event and PlaylistNextListener to Ruby binding * FFmpeg 1.0 and libAV master compatibility * Improvements to motion_est filter to generate keyframes for apps * Added audiolevel (measurement) filter Version 0.8.2 - August 28, 2012 This is a bugfix and minor enhancement release. * Overhaul of A/V sync with libavformat-based inputs. * Fix a major memory leak introduced in previous release. * Fixes to problems revealed by Coverity Scan static analysis. * Improved encoding presets. * melt can now be built without SDL with define MELT_NOSDL, which is handy for running it as a child process on Windows and OS X. * melt can now be signaled to quit, which also makes it more useful as a child process. Special thanks to Mikko Rapeli who provided many of the Coverity fixes. Version 0.8.0 - June 1, 2012 The minor version is increased due to the addition of time properties! The soname version increased in the process because some mlt_property functions changed; however, very few if any apps actually directly use mlt_property preferring to use mlt_properties instead. In addition: * improve seek speed on AVCHD when using FFmpeg v0.9.1+ (NOT Libav!) * composite and dissolve speed improvements on x86-64 * improve performance of caching in image producers * add device enumeration to decklink producer and consumer Special thanks go to contributors Maksym Veremeyenko and Ed Rogalsky. Version 0.7.8 - February 13, 2012 This is a bugfix and minor enhancement release. * Improved support for v53 of libavcodec/libavformat * Added "multi" consumer - multiple, simultaneous outputs * Added framerate adaption to "consumer" producer and "multi" consumer * Can now use YADIF deinterlacer with decklink producer * Added "rtaudio" consumer for native audio support on multiple platforms * Added ability to request image format closest to source (mlt_image_none) * Added more audio formats * Added vqm (video quality measurement) transition Version 0.7.6 - October 31, 2011 This is a bugfix and minor enhancement release. * Improved support for v53 of libavcodec/libavformat (0.7 and 0.8 releases) * Major DeckLink consumer improvements * Much more metadata * Added audio-only JACK consumer * Added video stabilization filters * Added dual pass audio normalization to sox filter * Added VITC and VANC capture to DeckLink producer * Added support for writing timecode tracks * Added MLT, frei0r, and SoX version to xml serialization * Added D-10, XDCAM, DNxHD, and Sony-PSP encoding presets * Can now use rotoscoping for masking filters * Added dynamictext filter makes burned-in timecode and similar easier * Added support for consumer element in MLT XML * Added outlining, padding, and alignment to pango filter Special thanks go to contributors Maksym Veremeyenko, Brian Matherly, and Marco Gittler. Version 0.7.4 - July 16, 2011 This is a bugfix and minor enhancement release. Framework * Important: change consumer property profile to mlt_profile. * Improve frame-dropping and drop_max property to mlt_consumer. * Added support for presets for any service through special property named "properties" * Added mlt_profile_from_producer() for auto-profile. * Added mlt_properties_set_lcnumeric() and mlt_properties_get_lcnumeric(). * Added LC_NUMERIC to YAML Tiny metadata schema and parser. Modules * Added support for more than 2 channels of greater than 16-bit audio. * Added discrete filters for each SoX effect. * Added discrete filters for each LADSPA audio plugin. * Added automatic service metadata for SoX and LADSPA plugins. * Added at least basic metadata for nearly every service. * Added support for decklink on Windows (tested) and Mac OS X (untested). * Added support for JACK transport synchronization. * Added blacklist.txt to jackrack plugin (contains dssi-vst). * Rewrite of decklink consumer. * Added support for live network, multi-stream device, and pipe/fifo sources in avformat producer. * Added LC_NUMERIC attribute to root XML element. Melt * Added '-query presets' option. * Added -jack option for transport synchronization. * Send -help and -query output to stdout to make it convenient for pagers. Other * Added mlt.Frame.get_image() for Python. * Removed configure option --avformat-svn. * Fixes for locales that use comma for decimal point. * Added presets for DVD, DV, x264, and WebM encoding. Since FFmpeg forked and there were a few releases, there is no recommended version at this time. With that said, there were changes to accommodate the API changes with some moderate testing of the 0.6, 0.7, and 0.8 series releases of both FFmpeg and Libav. Version 0.7.2 - May 1, 2011 This is a minor release to fix a few things between the 0.7.0 release and the release of Kdenlive 0.8. I recommend Kdenlive v0.8 users to upgrade to this version of Mlt. Beyond that there are some exciting additions to the Blackmagic Design DeckLink plugin! Framework * Added mlt_profile_list(). Modules * Added decklink producer (i.e. capture, live encoding). * Added keyer output for decklink consumer. * Added AVOptions to the avformat service metadata. * Added support for new major API versions (53) of FFmpeg. Melt * Added '-query profile' option. * Added '-query formats', '-query audio_codecs' and '-query video_codecs'. The recommended version of FFmpeg for use with this release is 0.6.1. Version 0.7.0 - March 27, 2011 This is a major new release due to significant additions to API, framework, and build. Build * Added support for Windows via MinGW. * Enabled linsys module by default. * Disabled VDPAU by default and added --avformat-vdpau to enable it. * Added support for swfdec 0.7. Framework: * Added parallelism to mlt_consumer when 'real_time' > 1 or < -1. * Added mlt_deque_insert() and mlt_deque_peek(). * Added mlt_profile parameter to mlt_producer_new(). * Let transitions with no out point run forever. * Added mlt_frame_unique_properties(). * Added mlt_frame_set_image() and mlt_frame_set_alpha(). * Added mlt_image_format_size() and mlt_audio_format_size(). * Added mlt_filter_get_length() and mlt_transition_get_length(). * Added mlt_filter_get_progress(), mlt_transition_get_progress(), and mlt_transition_get_progress_delta(). * Added mlt_filter_get_position() and mlt_transition_get_position(). * Added mlt_properties_lock() and mlt_properties_unlock(). Modules * Added rotoscoping filter. * Improve libavdevice support (V4L2, ALSA, libdc1394). * Added support for new FFmpeg metadata API. * Various fixes, refactoring, and improvements. The recommended version of FFmpeg for use with this release is 0.6.1. Version 0.6.2 - January 23, 2011 This is just a minor release to address a few things prior to introducing major changes from other branches. * Added force_aspect_ratio property to pixbuf and qimage producers. * Added opacity handling in geometry property of the affine filter and transition. * Added use_normalised property to affine filter. * Added always_active property to affine transition. * Fix building on NetBSD. The recommended version of FFmpeg for use with this release is 0.6.1. Version 0.6.0 - January 1, 2011 The recommended version of FFmpeg for use with this release is 0.6.1. There were quite a few enhancements and changes including a minor interface change. Therefore, this release gets a jump in the versioning. Framework * mlt_profile - Added (Y'CbCr) colorspace attribute. - Added mlt_profile_clone(). * Added mlt_consumer_position() and Mlt::Consumer::position(). * Added Mlt::Properties::wait_for(string). * Added a version API: mlt_version_get_int() and others. * Added Mlt::Producer::pause(). * Added mlt_frame_write_ppm() for debugging. Melt * Added automatic detection of profile from first input when profile not specified. * Now exits with error result when consumer fails fatally. Modules * Added swfdec producer for Flash files including variables support. (Does not support audio.) * Added consumer for Blackmagic Design DeckLink SDI and Intensity HDMI. * Added Y'CbCr colorspace conversion and option to use full luma range. * Added (de)serialization of profile to XML. * Added support for #frame# variable to the data_show filter. * Added support for frei0r string parameters. * Make FFmpeg formats and codecs available as properties (not just stderr). * Try to load .xml file as MLT XML. * Added a consumer-sdl-paused event to sdl_preview. * Added a consumer-fatal-error event to avformat. * Change composite transition to default to progressive rendering; field- based rendering available only explicitly. Version 0.5.10 - September 13, 2010 This is a quick followup to the 0.5.8 release to address an issue I want to address immediately. I noticed an extra unconditional colorspace conversion to and from RGB was added for all YCbCr (YUV) video sources. In addition, I have enabled the avcolor_space filter on OS X since it works now. Version 0.5.8 - September 12, 2010 The recommended version of FFmpeg for use with this release is 0.6. This is a maintenance release to address some bugs that appeared thus far in the 0.5.x series. Beyond that is a few enhancements: * Added EXIF-based auto-rotation of images to pixbuf and qimage producers. * Improved quality of libswscale-based image conversion and scaling. * Added channelswap and panner audio filters; panner also does balance. * Improve audio waveform and add audiowave video filter. * Enhanced luma filter to work with animated filters such as affine. * Automatically crop 8 bottom lines of 1088 source in a 16:9 project (common in Canon EOS digital cameras). * Added support for inline images in kdenlivetitler. Version 0.5.6 - June 20, 2010 The recommended version of FFmpeg for use with this release is 0.6. This is a maintenance release to address some bugs that appeared this far in the 0.5.x series. Beyond that it a few enhancements: * Added interpolation to the affine transition and filter. * Added multi-track audio encoding to avformat consumer. * Added interlaced field rendering to kdenlivetitle producer. Version 0.5.4 - April 19, 2010 The recommended version of FFmpeg for use with this release is SVN r21322. This is another maintenance release to address some bugs that appeared this far in the 0.5.x series. Beyond that it adds two things that only very specific users will see: * Added C# (ECMA CLR) binding (not enabled by default). * Linsys SDI consumer now configures itself from MLT profile. Version 0.5.2 - March 10, 2010 The recommended version of FFmpeg for use with this release is SVN r21322. This is a minor maintenance release, but it is interesting because it now enables usage of libswscale as the default choice for image scaling, image format conversion, and color space conversion. That gives better quality and performance. In addition, there are some improvements in the sdl_preview consumer to make it suitable for use in OpenShot 1.1. Other things: * Fixed mlt++/MltFilteredProducer * Fixed playing to the end in Kdenlive (mantis bug 1207) * Fixed crash load uncompressed video * Fixed compiling yadif for non-sse2 builds Version 0.5.0 - February 15, 2010 The recommended version of FFmpeg for use with this release is SVN r21322. This is an enhancement release, confined mainly to the modules rather than the framework. In particular, this adds support for VDPAU, YADIF, and HD-SDI technologies! configure: added --disable-sse2 framework: * mlt_cache: added mlt_cache_set_size() * mlt_filter: added data property "service" - set when attached * mlt_frame: - added Doxygen docs - added "previous frame" and "next frame" data properties - available when its producer has _need_previous_next=1 * mlt_playlist: added support for negative out point same as length-1 * mlt_service: - added mlt_service_cache_purge() - added "_need_previous_next" handling in mlt_service_get_frame() - added firing event "service-changed" in mlt_service_attach() modules: * avformat producer: - added decoding H.264 with NVIDIA VDPAU Requires FFmpeg built with vdpau. This is automatically detected and enabled. You can disable this by setting environment variable MLT_NO_VDPAU=1 or property novdpau=1. - added caching of FFmpeg contexts and decoded images This allows large numbers of clips in a project avoiding limitations with number of threads and file descriptors permitted per process. You can disable image caching with property noimagecache=1. - added variant of producer named avformat-novalidate - restored support for video4linux(2) * avformat consumer: added apre, fpre, and vpre preset properties * crop filter: added center_bias integer property * deinterlace filter: added the excellent YADIF as a method * kdenlivetitle producer: added text outlining * linsys/sdi consumer: - added support for HD-SDI - changed name from "linsys_sdi" to just "sdi" * oldfilm filter: added "uneven development" effect * xml producer: add support for unspecified out points profiles: * added several missing ATSC (HD) profiles * change descriptions from using Hz to fps Version 0.4.10 - December 8, 2009 The recommended version of FFmpeg for use with this release is SVN r19873. This is "hotfix" for the 0.4.8 release that fixes a couple of regression bugs introduced just before the release. Version 0.4.8 - December 7, 2009 The recommended version of FFmpeg for use with this release is SVN r19873. This is mainly a maintenance release. Besides bug fixes here are other notable changes. modules: * avformat producer: - refactored producer to use much less properties - added support for audio_index=all for linsys_sdi consumer - added force_fps property (does yet not adjust duration) * core/crop: added "center" property to crop filter * linsys_sdi: - added support for >2 audio channels - added property meta.map.audio..channels= - added property meta.map.audio..start= * qimage/kdenlivetitle: add typewriter effect Version 0.4.6 - October 7, 2009 The recommended version of FFmpeg for use with this release is SVN r19873. This release is an enhancement release along with numerous build, A/V synch, concurrency, and other bug fixes. configure: new option --avformat-svn-version modules: * avformat: much improved seeking on H.264/MPEG2-TS (AVCHD) (Ivan Schreter) * core: new imageconvert and audioconvert filters (framework refactorization) * linsys: new SDI consumer (Broadcast Centre Europe) * qimage: new kdenlivetitle producer (J.B. Mardelle and Marco Gittler) * sdl: new audio_only consumer for OS X mlt++ and swig: update bindings framework: * refactored image format conversion mlt_frame.h: - added convert_image() virtual function - added mlt_image_format_name() - removed many mlt_convert_ and scaling/padding functions * refactored audio format conversion mlt_frame.h: - mlt_get_audio() virtual function parameters changed - added convert_audio() virtual function - mlt_frame_get_audio() parameters changed - added mlt_frame_set_audio() - added mlt_audio_format_name() mlt_types.h: - deprecated mlt_audio_pcm - added mlt_audio_s16 - added mlt_audio_s32 - added mlt_audio_float Version 0.4.4 - June 30, 2009 The recommended version of FFmpeg for use with this release is 0.5.0. This release is a minor maintenance update to the 0.4.2 - just build and bug fixes. * new configure script options: --swig-languages --rename-melt --mandir --datadir * added man page for melt (not installed) * added invert property to composite transition * added frei0r plugin blacklist, currently only contains facedetect * added Lua binding via SWIG Version 0.4.2 - May 30, 2009 The recommended version of FFmpeg for use with this release is 0.5.0. This release is a minor maintenance update to the 0.4.0 - just build and bug fixes. Version 0.4.0 - May 17, 2009 The recommended version of FFmpeg for use with this release is 0.5.0. This release is primarily a reorganization of the mlt and mlt++ projects. In brief: * "inigo" was renamed "melt" * "westley" is no longer the XML file extension and root element - they are simply "mlt" now * mlt++ is included with mlt and no longer a separate project * miracle, valerie, and humperdink were moved to a new, separate project For details: http://www.mltframework.org/twiki/bin/view/MLT/ExtremeMakeover avformat producer improvements: * improve audio synchronization * improve reliability of video playback with FFmpeg newer than v0.5.0 * improve seeking performance of DNxHD and HuffYUV Version 0.3.8 - April 15, 2009 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 - February 2, 2009 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 - December 29, 2008 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 - November 10, 2008 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 - August 5, 2008 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 - August 4, 2007 * 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 - April 9, 2007 * Addition of kdenlive module * Support for ffmpeg from subversion * Support for ffmpeg libswscale * Copyright and license cleanup Version 0.2.2 - May 27, 2006 * 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 - December 5, 2005 * Many improvements since initial releases due to development of Shotcut and Jahshaka editing interfaces. Version 0.1.1 - June 9, 2004 * Minor modifications and bug fixes from the previous release. Better ffmpeg/avformat integration and more reliable playback. Version 0.1.0 - May 6, 2004 * First official release mlt-6.20.0/README000066400000000000000000000026201362234133600132560ustar00rootroot00000000000000MLT FRAMEWORK README -------------------- Written by Charles Yates and Dan Dennedy MLT is a LGPL multimedia framework designed for television broadcasting, and melted 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 details. See the website for development details: https://www.mltframework.org/docs/ https://www.mltframework.org/docs/contributing/ 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. mlt-6.20.0/coccinelle/000077500000000000000000000000001362234133600144765ustar00rootroot00000000000000mlt-6.20.0/coccinelle/delete_if4.cocci000066400000000000000000000033111362234133600175020ustar00rootroot00000000000000@Remove_unnecessary_pointer_checks1@ expression x; identifier release =~ "^(?x) (?:cJSON_InitHooks | (?:mlt_)?free | mlt_(?:(?:animation | cache(?:_item)? | profile )_close | consumer_purge | properties_(?:un)?lock ) | plugin_connect_output_ports | s(?:trbuf_close|et_mlt_normalisation) )$"; @@ -if (\(x != 0 \| x != NULL\)) release(x); @Remove_unnecessary_pointer_checks2@ expression x; identifier release =~ "^(?x) (?:cJSON_InitHooks | (?:mlt_)?free | mlt_(?:(?:animation | cache(?:_item)? | profile )_close | consumer_purge | properties_(?:un)?lock ) | plugin_connect_output_ports | s(?:trbuf_close|et_mlt_normalisation) )$"; @@ -if (\(x != 0 \| x != NULL\)) { release(x); x = \(0 \| NULL\); -} @Remove_unnecessary_pointer_checks3@ expression a, b; identifier release =~ "^(?x) (?:cJSON_InitHooks | (?:mlt_)?free | mlt_(?:(?:animation | cache(?:_item)? | profile )_close | consumer_purge | properties_(?:un)?lock ) | plugin_connect_output_ports | s(?:trbuf_close|et_mlt_normalisation) )$"; @@ -if (\(a != 0 \| a != NULL\) && \(b != 0 \| b != NULL\)) +if (a) release(b); @Remove_unnecessary_pointer_checks4@ expression a, b; identifier release =~ "^(?x) (?:cJSON_InitHooks | (?:mlt_)?free | mlt_(?:(?:animation | cache(?:_item)? | profile )_close | consumer_purge | properties_(?:un)?lock ) | plugin_connect_output_ports | s(?:trbuf_close|et_mlt_normalisation) )$"; @@ -if (\(a != 0 \| a != NULL\) && \(b != 0 \| b != NULL\)) { +if (a) { release(b); b = \(0 \| NULL\); } mlt-6.20.0/configure000077500000000000000000000270741362234133600143170ustar00rootroot00000000000000#!/bin/sh export version=6.20.0 export soversion=6 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) --datadir=directory - data directory (default: $prefix/share) --mandir=directory - man documentation directory (default: $prefix/share/man) --rename-melt - Give melt executable a different name (it will not be versioned) --enable-extra-versioning - Version melt and the data and modules directories --enable-gpl - Enable GPLv2 components --enable-gpl3 - Enable GPLv3 components --enable-debug - Compile without optimizations support (default: off) --disable-debug - Compile without debug support (default: on) --disable-mmx - Compile without MMX support (default: on) --disable-sse - Compile without SSE support (default: on) --disable-sse2 - Compile without SSE2 support (default: on) --arch='arch' - Compile for a specific architecture (default: none) --cpu='cpu' - Compile for a specific CPU (default: none) --target-os='os' - Cross-compile to a specific OS (default: $(uname -s)) --target-arch='arch' - Cross-compile to a specific CPU architecture --disable-windeploy - Keep bin/ lib/ layout on Windows Module disable 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 "datadir=$datadir" echo "mandir=$mandir" echo "extra_versioning=$extra_versioning" echo "melt_noversion=$melt_noversion" echo "targetos=$targetos" echo "windeploy=$windeploy" [ "$mmx" = "true" ] && echo "MMX_FLAGS=-DUSE_MMX -mmmx" [ "$sse" = "true" ] && echo "SSE_FLAGS=-DUSE_SSE -msse" [ "$sse2" = "true" ] && echo "SSE2_FLAGS=-DUSE_SSE2 -msse2" [ "$debug" = "true" ] && echo "DEBUG_FLAGS=-g" echo "LARGE_FILE=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE" [ "$amd64" = "true" ] && echo "ARCH_X86_64=1" && echo "CFLAGS+=-DARCH_X86_64" [ "$arch" != "" ] && echo "TARGETARCH=-march=$arch" [ "$cpu" != "" ] && echo "TARGETCPU=-mcpu=$cpu" if [ "$optimisations" = "true" ] then echo "OPTIMISATIONS=-O2 -pipe" if $($CC --version | grep gcc > /dev/null); then # Since gcc 4.6, this optimization enabled with -O1 causes filter_line_sse2 to crash. echo "OPTIMISATIONS+=-fno-tree-dominator-opts" # Since gcc 4.6, this optimization enabled with -O2 causes filter_line_sse2 to crash. echo "OPTIMISATIONS+=-fno-tree-pre" fi fi echo "CFLAGS+=-Wall -DPIC \$(TARGETARCH) \$(TARGETCPU) \$(OPTIMISATIONS) \$(MMX_FLAGS) \$(SSE_FLAGS) \$(SSE2_FLAGS) \$(DEBUG_FLAGS) \$(LARGE_FILE)" echo "CXXFLAGS+=-Wall -DPIC \$(TARGETARCH) \$(TARGETCPU) \$(OPTIMISATIONS) \$(MMX_FLAGS) \$(SSE_FLAGS) \$(SSE2_FLAGS) \$(DEBUG_FLAGS) \$(LARGE_FILE)" case $targetos in Darwin) echo "CFLAGS+=-fPIC" echo "SHFLAGS=-dynamiclib" ;; Linux|GNU/kFreeBSD|GNU) [ "$optimisations" = "true" ] && echo "OPTIMISATIONS+=-ffast-math" echo "CFLAGS+=-fPIC -pthread" echo "SHFLAGS=-shared" echo "LIBDL=-ldl" echo "RDYNAMIC=-rdynamic" echo "LDFLAGS+=-Wl,--no-undefined -Wl,--as-needed" ;; FreeBSD) [ "$optimisations" = "true" ] && echo "OPTIMISATIONS+=-ffast-math" echo "CFLAGS+=-fPIC -pthread" echo "SHFLAGS=-shared" echo "RDYNAMIC=-rdynamic" echo "LDFLAGS+=-Wl,--no-undefined -Wl,--as-needed" ;; NetBSD) [ "$optimisations" = "true" ] && echo "OPTIMISATIONS+=-ffast-math" echo "CFLAGS+=-fPIC -pthread" echo "SHFLAGS=-shared" echo "RDYNAMIC=-rdynamic" echo "LDFLAGS+=-Wl,--no-undefined -Wl,--as-needed" ;; OpenBSD) echo "CFLAGS+=-fPIC" echo "CXXFLAGS+=-fPIC" echo "SHFLAGS=-shared" echo "RDYNAMIC=-rdynamic" echo "LDFLAGS+=-Wl,--as-needed" ;; MinGW) [ "$windeploy" = false ] && echo "CFLAGS+=-DNODEPLOY" [ "$optimisations" = "true" ] && echo "OPTIMISATIONS+=-ffast-math" echo "SHFLAGS=-shared" echo "LIBDL=-ldl" echo "RDYNAMIC=" echo "LDFLAGS+=-Wl,--no-undefined -Wl,--as-needed" # If the host system is MinGW running on Windows, then cause make to # treat all .c files as C and not use CXX. mingw32-make is case # insensitive, and .C is considered C++. if uname -s | grep -iq mingw ; then echo "%.o: %.c" printf "\t\$(CC) -c \$(CFLAGS) \$(CPPFLAGS) -o \$@ \$<\n" fi ;; *) ;; esac echo "LIBSUF=$LIBSUF" echo "moduledir=${moduledir}" echo "mltdatadir=${mltdatadir}" echo "unversionedmoduledir=${unversionedmoduledir}" echo "unversionedmltdatadir=${unversionedmltdatadir}" echo "meltname=${meltname}" ) > 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() { echo prefix="$prefix" > mlt-framework.pc ( echo exec_prefix=$prefix echo libdir=$libdir echo includedir=$prefix/include echo datadir=$datadir echo mandir=$mandir echo version=$version echo cflags=`grep ^framework packages.dat | cut -f 2` echo libs=`grep ^framework packages.dat | cut -f 3` echo moduledir=${moduledir} echo mltdatadir=${mltdatadir} echo meltbin=${prefix}/bin/${meltname} ) >> mlt-framework.pc cat mlt-framework.pc.in >>mlt-framework.pc echo prefix="$prefix" > mlt++.pc ( echo exec_prefix=$prefix echo libdir=$libdir echo includedir=$prefix/include echo datadir=$datadir echo mandir=$mandir echo version=$version echo cflags=`grep ^mlt++ packages.dat | cut -f 2` echo libs=`grep ^mlt++ packages.dat | cut -f 3` ) >> mlt++.pc cat mlt++.pc.in >>mlt++.pc } # Debug mode # set -x # Define build directory for scripts called export build_dir=`dirname $0` export prefix=/usr/local export libdir="" export datadir="" export mandir="" export help=0 export optimisations=true export debug=true export mmx=true export sse=true export sse2=true export gpl=false export gpl3=false export arch= export cpu= export targetos=$(uname -s) export targetarch= export amd64=false export extra_versioning=false export melt_noversion=false export windeploy=true # Define the compiler used in tests (gcc is not installed everywhere) : ${CC:=gcc} # Test for a supported C++ compiler. if $($CC --version | grep -is -e gcc > /dev/null) then echo "Found gcc version $($CC -dumpversion)" if $CC -dumpversion | awk '{split($0, a, "."); if ((lshift(a[1],16) + lshift(a[2],8) + a[3]) < lshift(4,16) + lshift(8,8) + 1) exit 0; else exit 1;}' then echo "The minimum version of gcc supported is 4.8.1 (for C++11)." exit 1 fi elif $($CC --version | grep -is -e clang > /dev/null) then echo "Found clang version $($CC -dumpversion)" if $CC -dumpversion | awk '{split($0, a, "."); if ((lshift(a[1],8) + a[2]) < lshift(3,8) + 3) exit 0; else exit 1;}' then echo "The minimum version of clang supported is 3.3 (for C++11)." exit 1 fi else echo "MLT requires either the gcc or clang C/C++ compilers." exit 1 fi # Iterate through arguments for i in "$@" do case $i in --help ) help=1 ;; --prefix=* ) prefix="${i#--prefix=}" ;; --libdir=* ) libdir="${i#--libdir=}" ;; --datadir=* ) datadir="${i#--datadir=}" ;; --mandir=* ) mandir="${i#--mandir=}" ;; --rename-melt=* ) meltname="${i#--rename-melt=}"; melt_noversion=true ;; --enable-extra-versioning ) extra_versioning=true ;; --enable-debug ) optimisations=false ;; --disable-debug ) debug=false ;; --disable-mmx ) mmx=false; sse=false; sse2=false ;; --disable-sse ) sse=false; sse2=false ;; --disable-sse2 ) sse2=false ;; --enable-gpl ) gpl=true ;; --enable-gpl3 ) gpl3=true ;; --arch=* ) arch="${i#--arch=}" ;; --cpu=* ) cpu="${i#--cpu=}" ;; --target-os=* ) targetos="${i#--target-os=}" ;; --target-arch=* ) targetarch="${i#--target-arch=}" ;; --disable-windeploy ) windeploy=false ;; esac done if [ -z "${meltname}" ] then if [ "$extra_versioning" = "false" ] then meltname=melt else meltname=melt${soversion} fi fi # Chose appropriate suffix for libraries case $targetos in Darwin) LIBSUF=".dylib" if [ "$targetarch" = "" ] then sysctl -a hw | grep "x86_64: 1" > /dev/null [ "$?" = "0" ] && targetarch="amd64" fi ;; Linux|FreeBSD|NetBSD) LIBSUF=".so" ;; MINGW??_NT-*|MinGW|mingw) targetos="MinGW" LIBSUF=".dll" ;; *) LIBSUF=".so" ;; esac export LIBSUF # Determine if we are compiling for 64-bit Intel architecture [ "$targetarch" = "" ] && targetarch=$(uname -m) [ "$targetarch" = "amd64" -o "$targetarch" = "x86_64" ] && amd64=true # Determine the libdir if it's not specified in the args [ "$libdir" = "" ] && libdir=$prefix/lib [ "$datadir" = "" ] && datadir=$prefix/share [ "$mandir" = "" ] && mandir=$prefix/share/man export unversionedmoduledir=${libdir}/mlt export unversionedmltdatadir=${datadir}/mlt if [ "$extra_versioning" = "false" ] then export moduledir=${libdir}/mlt export mltdatadir=${datadir}/mlt else export moduledir=${libdir}/mlt-${soversion} export mltdatadir=${datadir}/mlt-${soversion} fi # 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 -f /dev/null)" ] || 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 -f /dev/null)" ] || sse=false ;; *) grep sse /proc/cpuinfo > /dev/null 2>&1 || sse=false ;; esac fi # Double check SSE2 (Darwin, Linux and FreeBSD supported, may end up disabling SSE2 on other platforms incorrectly) if [ "$sse2" = "true" ] then case $targetos in Darwin) sysctl -a hw | grep "sse2: 1" > /dev/null || sse2=false ;; Linux) grep sse2 /proc/cpuinfo > /dev/null 2>&1 || sse2=false ;; FreeBSD) [ "$(make -V MACHINE_CPU:Msse2 -f /dev/null)" ] || sse2=false ;; *) grep sse2 /proc/cpuinfo > /dev/null 2>&1 || sse2=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 melt mlt++ swig do if [ -x src/$i/configure ] then [ $help = 0 ] && echo "Configuring `basename $i`:" olddir=`pwd` cd src/$i CC="$CC" ./configure "$@" [ $? != 0 ] && exit 1 cd $olddir fi done # Build the pkg-config files build_pkgconfig # Report license Usage if [ $help != 1 ] then if [ "$gpl" = "false" ] then echo "LGPLv2.1 license used; GPL components disabled" elif [ "$gpl3" = "false" ] then echo "GPLv2 license used; GPLv3 components disabled" else echo "GPLv3 license used" fi fi mlt-6.20.0/demo/000077500000000000000000000000001362234133600133225ustar00rootroot00000000000000mlt-6.20.0/demo/README000066400000000000000000000230271362234133600142060ustar00rootroot00000000000000MLT 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 "xml" consumer generates an XML representation of the service network. That can be played directly due to the XML producer plugin. See docs/mlt-xml.txt for more information. "/dev/dv1394/0" refers to a device file for transmitting DV over FireWire using the Linux dv1394 kernel module. These examples assume the numeric locale LC_NUMERIC decimal separator is a period. Therefore, the demo script sets LC_NUMERIC=C for you, but if you are running these manually or learning from them, remember to use the appropriate separator for your locale. 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 between 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 "photos." 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. Pango Keyframed Markup You can create timed text and subtitles using a .mpl file, which is a properties format file. A properties file contains key=value pairs on separate lines. For .mpl the key is a frame number and the value is Pango Markup Language. A tilde is interpreted as a new line. This example also demonstrates using the watermark and the alignment properties of its encapsulated composite transition where halign is the horizontal, valign is the vertical, c is for center, and m is for middle. mlt-6.20.0/demo/circle.png000066400000000000000000000121671362234133600153000ustar00rootroot00000000000000‰PNG  IHDR,,y}ŽusBIT|dˆ.IDATxœíÝa°\eyÀñÿ½×$0BbÃ$”(HDef ¥¥kÇi;¥N?8µ¢V«vô›ŽÎtª3N¿´ú¡VeTÀ©Ž #&à@'VˆB 4$&!q’÷Þ~xö&››Ý½gwÏ9ï9gÿ¿™göÆ{÷œGö=ϼï»ïyÏÒéÎÖ‹%­/™õó"à `0¿ Ú^Ç£ÀËÀñÖëÌÏÇ€CÀ~àÅÖkûÏ€­K'Œ¥N@IŒ—«Z¯«3S%6ËQàIài`wëuð8ð0*1¥aÁj¾¥ÀeÀ[Ú^×R¢4¨£ÀvàQà‘¶×R&¥bY°šgð.`#p-p~ÒlÊ÷ p/° ø ð\Òl”+ Vý-6´b#1÷¤“vÅks+ö¥MGð`ÕÏbàN¨µø9f5M #g Ø}Ä¿j†^}cÀ DqÚ¬#¾Ó𦀭DñÚÜùÒ@Ö_ö‘Q|ìmý7_—áó‘FÞJàSÀc¤¿xG=k}+{~bÒˆ9 ¸ø10Iú Õ85&[ŸÍ-­ÏJ9ÀõÀ׀ä¿(lq¸õ™]ßú ¥F»øÎK5!ö¶>Ë‹‘fp;ùš“­ÏÖ‰zÕÞzà‡Ä×ç©/,£Ø˜j}Öë‘jæ:bmOê‹ÈH›‰6 UÖp°…ôŒQØB´ h«2&€ÛHÕŒmDñ›E%3ø °ƒô„QØA´™yH%ÚHl$—ú0껈6$ê<àÒ7x£qѦ¤\Í#î+;BúFn4+ŽmËa¢rq-±‡Rê†m4;¶mMÈràë¸èÓ(/¦ˆ6·)£ à£ÀAÒ7`c4ã Ñ]¡ž®"v LÝ` cšh‹W!Í2ø,ÿŒêÅÑ6”Wpò¡œ†QÕx‚h«a&È™º1F–8J´Y˜…À·Hß cøцGÎ(ÞE¾ŽøÀW§NDÂ“Ä Õ[S'R¦QûÚôV¢X-Kˆ4¤ß!n¤>üOÚT”·EÀwHß•7Œ"â;Do¼Q^Iôª.LˆT §ˆ!⃩)R“‡„cÀÇ€Û€¥‰s‘жø à% |²äsJ*ÆZ`%.u(sHx1oåÞVRs#æ³.ãde¬…Dö¦f©yv À ŸÏ*kHøeàú’Î%©\Kˆ§ï|¿è•ÑÃºŠØŒ/õ"UIÅ™"6ýÛRäIŠ.XÀωù+IÍö0ðv`²¨=$¼øË‚Ï!©Î^¤ÀUðEö°– ò!Òè8¼x¾ˆƒ9¯ôÏX¬¤Q³ˆ¸ö QTëZ`SÇ—T]ÓÀFàÞ¼\DA™ü‚¸«[Òhú%ðVà•<ZĤûßRÀq%ÕÇRà·Ä’¦ÜäÝÃ:¨¬¯Íù¸’êç·ÄHëÙ¼˜÷¤û±XI ¯%jBnòìa½‡xð¢$µ»øQÊ«`7@º?»¤Ùž"6>˜ö@y oÁb%©³ ‰1´ÃCtÁ$© ž%özN-XK™µæA’*`qoó)CÂËÒä"I=¨Míë- ‘¤¹œ¨Mö°$U=,Iµq¢6ÍLºO‡3“¤#IÝÎ&gzX«±XIª¦3‰ubHø¦t¹HÒœÞ' Öª„‰HÒ\VÁÉ‚uAº<$iN€=,Iõ`KRm\'—5¼D|m(IUtX8œCëÆBIª°¥ã´mÝ Ivþ8ðúÔYHR¯–¤ÎB’2X2NÌaIRÕcKR],±`Iª ‡„’jÃ!¡¤ÚX2¼.u’”ÁëÆ©³¤ X°$ÕÅ‚q`~ê,$)ƒù,Iu1ß!¡¤ºpH(©6æ¯Ï%”¤*›Ÿûo$©Æã©“¤ Ž/§ÎB’2xÙ–¤º°‡%©6^¶`Iª ‡„’jã¸KR]¦ÎB’288ìO…$e°x1u’”Á‹ö°$ÕÅ~ –¤ºpH(©6JªýãÀÞÔYHR{Lj'?¿:IšÃÒ±Ö/g§ÌD’z8 ,œÙqô鄉HÒ\ž†ØqôÄ?$©¢ž†“kwº<$iN»Á–¤zxìaIª‡SzX¿J˜ˆ$ÍåW3Ë&ˆ¯ ÏL–Ž$uv”XvuâAª“ÀötùHRWÛ‰Eû“ŸM“‹$õt¢6µ¬G$"Is9Q›ìaIªºµi¬í\ ì+?Iêi­ Ú{X/Ï&IG’:{–¶ÝdÆgýrs¹¹HRO§Ô¤ÙkS‰‰HÒ\N©Ic³~ùJªŽó€_Ïücvë×ÀÎRÓ‘¤ÎvÒV¬àô‚ %UÃiµ¨SÁrâ]RœV‹fÏaA¬yx¾Ëï$© ÓÀrf­ íÔÃÚ‡7BKJk;²w*Xà<–¤´:Ö nËy,I)u¬AÝæ©Ëá' KG’:›$îm>0ûÝzX€‡‹ÌH’ºx˜Å º,pX()®µ§WÁº§€D$i.]kO¯µV¯ž#ÖeIRö+€W;ý²WëUà¶"2’¤.n£K±‚Þ àkùæ"I=õ¬9Yn¿y X›O.’ÔÕvàͽþ`®Àæ“‹$õ4g­ÉÒÃZ ÇÅ òYü9AlIÇ’ÔL³É|ß`'yìÓ> |>‡ãHj®Ï3d±‚ün¯'nо0§ãIjާˆ›œ§†=P^O™nÍéX’šåVr(V~„;9H:ÕDmÈEÞ;.œüxmÎÇ•T?¿.žÍë€y¨å1±v]ÎÇ•T?Ÿ~˜ç‹ØÓj±˜ôÒŽ-©~I,}%σñøùW€0ä1Iµ5MÔ€\‹S° ¶øfAÇ–Tmߤ í§ŠÜæx9°XTà9$UË!àÀóE<ïI÷vGˆo ÞSà9$UË'ME¼èIL?®(ø<’Ò{x;9Ü‚ÓMQsX3&¿&§U®’*kŠ¸Ö +VP|Á‚Ø•ô«%œGR:_%®õB•õlÁ…ij ×”t>IåÙI¼Bªƒ§€uÄ ÎI¤˜Ãjw糤:˜™·JV¬ ÝV»ÿ%&ïÜÕAª®ßMDê!a»;€÷¥NBÒiîþ(uP­‚5ø>ÀBª’{ˆ»S^NT«`œ l&¤IJë!`p8u"3ªV°–÷ãÎRJ;«}©iWÅ‚° x87u"ÒÚ¬v§Nd¶Ô˺ÙM|k˜ô+Ti"®½Ê+¨nÁØü!î¡%•åqÍmKH7U.X÷JÁûDKb’¸ÖîKH/UX8:—Äæ7¥NDj°ßHÄ\êP° ¾^"¾b•”¯Ï_LDu)X?–ïHˆÔ ÿ üCê$²ªSÁ¸ XAÜ1.i8_>L§N$«º¬ibÃ{‡‡Òp>|‚+¨_ÁšñSbqÛTÿ›N©J&‰ öZÌYÍVÕ•îYÝ |8#u"R #–.$ß&fPu/X×ûK/JˆTa‡ˆE¡•^g5—&,€Ë὇R'{ˆÛm*»‚=«¦,ˆ¦ïÆ]¤v;¨è½ýjÒ„õnb;Œ‡R'"UÄCÄ5шbÍ*X{÷l vI”FÙ=ĵP©ý¬†Õ´‚±;âÀ÷R'"%ò=â¨ÌN¡yibÁxx/ð7ø1ŽãD›/q 4N“&Ý»¹ø>¬UÍöñÜÀS'R¤¦ö°Ú=HÜ{x{êD¤‚ÜN´ñF«Qt+±ÚwÚ0Lj6=2FaH8Û:bˆ¸:u"Òž$†€[S'R¦Qζ•xîá·S'" èÛD©b¥¸ký(é»ö†‘%ŽmV#ì à Ò7FÃèOmUbðYbcÀÔ Ó0ÚcŠh›óf¹Š˜HÝH cšh‹W!õ0|8HúkŒf$Ú`]wV˯ã0Ñ(/¦ˆ6·i@×ÛIߘfÇv¢­IC›| 8Bú†m4+ŽmËIuåî<àÒ7r£qѦ¤Bmv‘¾ÁõŒ]D’J3ø °ƒô€QØA´‡Jf‚¸ ué/£š±h#.SPeŒ7[HÕˆ-D›Å]QT#×›IÁib3ѤZYüŸŽBLµ>ëõH57³Eó$é/,#ߘääÅR£\ |ØKú Í.ö¶>Ë‹‘n¸øñì¸ÔŸ‘-·>³ëñ?¨³€[€㱊1Ùúlni}V’ZV÷•=Fú uÔã±Ög±²ç'& ˆIÜ/á|W™±·õßÜ ôŠrA[õ7÷žm .¦Q|ÚQ¦ˆ]=7›€»‰Â¥Š²`ÕÏbà¢xmÖâç˜Õ4±ïÔ&¢HÝHš‘úbC¯¿eDñš)`kÒ¦S9;9Y 6ûÒ¦£aX°šgð.¢x] œŸ4›ò=ÜK©ŸÏ%ÍF¹²`5ßRà2à-m¯k3S&•ƒ£ÄðîQà‘¶×R&¥bY°FÓ±Bûà"à`Uëu5Õ)fG'§Ý­×]Àãœ|ø­FˆKœCÌ…-–´þ½dÖÏ‹€3€ÀüV,h{' ÎËÀñÖëÌÏÇ€CÀ~àÅÖkûψ¹§‹þ?ªzùK¸„¡8eÇIEND®B`‚mlt-6.20.0/demo/circle.svg000066400000000000000000000001251362234133600153020ustar00rootroot00000000000000 mlt-6.20.0/demo/consumers.ini000066400000000000000000000004741362234133600160460ustar00rootroot00000000000000SDL Default sdl SDL Half D1 sdl:360x288 rescale=nearest resize=1 SDL High Latency sdl buffer=12 rescale=none SDL Progressive sdl progressive=1 XML to Terminal xml XML to File xml: libdv to /dev/dv1394/0 libdv:/dev/dv1394/0 rescale=nearest buffer=25 DeckLink decklink DeckLink Prog LL decklink progressive=1 buffer=1 mlt-6.20.0/demo/demo000077500000000000000000000040171362234133600141760ustar00rootroot00000000000000#!/bin/bash export MLT_PROFILE=dv_pal export LC_NUMERIC=C 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" == "xml:" ] then export XML_CONSUMER="xml:$demo.mlt" bash $demo -consumer $XML_CONSUMER melt +$demo.txt out=100 $demo.mlt $demo.mlt -filter watermark:watermark1.png composite.fill=1 composite.geometry=85%/5%:10%x10% elif [ "$MLT_CONSUMER" == "xml" ] 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 mlt-6.20.0/demo/demo.ini000066400000000000000000000027221362234133600147520ustar00rootroot00000000000000mlt_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.mlt mlt_title_over_gfx Title over graphic watermark1.png,clip1.dv mlt_slideshow Slideshow photos 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 photos mlt_slideshow2 Ken Burns slideshow photos mlt_pango_keyframes Pango Keyframed Markup pango_keyframes.mpl mlt-6.20.0/demo/demo.kino000066400000000000000000000006611362234133600151330ustar00rootroot00000000000000 mlt-6.20.0/demo/entity.mlt000066400000000000000000000004321362234133600153530ustar00rootroot00000000000000 ]> pango Hello &name;, My name is Inigo Montoya. mlt-6.20.0/demo/luma1.pgm000066400000000000000000014520171362234133600150600ustar00rootroot00000000000000P5 720 576 255 ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßàààààààááááááâââââââããããããääääääååååååææææææççççççèèèèèééééééêêêêêëëëëëììììììíííííîîîîîïïïïïðððððñññññòòòòòóóóóôôôôôõõõõõööööö÷÷÷÷øøøøøùùùùúúúúúûûûûüüüüüýýýýþþþþþÿÿÿÿ  !!!!!!!!"""""""########$$$$$$$$%%%%%%%%%&&&&&&&&''''''''''((((((((()))))))))))***********++++++++++,,,,,,,,,,,,,--------------.............///////////////00000000000000001111111111111111ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßàààààààáááááááââââââããããããäääääääååååååææææææçççççèèèèèèéééééêêêêêêëëëëëìììììíííííîîîîîïïïïïðððððñññññòòòòòóóóóóôôôôõõõõõööööö÷÷÷÷øøøøøùùùùúúúúúûûûûüüüüüýýýýþþþþþÿÿÿÿ  !!!!!!!""""""""#######$$$$$$$$$%%%%%%%&&&&&&&&&&'''''''''(((((((((())))))))))**********++++++++++++,,,,,,,,,,,,-------------..............////////////////00000000000000001111111111111111ÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßàààààààáááááááââââââããããããääääääååååååææææææççççççèèèèèééééééêêêêêëëëëëìììììíííííîîîîîîïïïïðððððñññññòòòòòóóóóóôôôôõõõõõööööö÷÷÷÷øøøøøùùùùúúúúúûûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!""""""""########$$$$$$$%%%%%%%%%&&&&&&&&'''''''''''(((((((()))))))))))***********+++++++++++,,,,,,,,,,,,,------------.............///////////////00000000000000000111111111111111112ÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßààààààààááááááââââââããããããäääääääåååååææææææççççççèèèèèèéééééêêêêêëëëëëììììììíííííîîîîîïïïïïðððððññññòòòòòóóóóóôôôôôõõõõööööö÷÷÷÷øøøøøùùùùùúúúúûûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!!"""""""########$$$$$$$$%%%%%%%%&&&&&&&&&'''''''''(((((((((())))))))))***********+++++++++++,,,,,,,,,,,--------------..............///////////////00000000000000011111111111111111122ÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßàààààààáááááááââââââããããããääääääååååååææææææçççççèèèèèèéééééêêêêêêëëëëëìììììíííííîîîîîïïïïïðððððñññññòòòòóóóóóôôôôôõõõõööööö÷÷÷÷÷øøøøùùùùùúúúúûûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!"""""""########$$$$$$$$%%%%%%%%%&&&&&&&&''''''''''((((((((()))))))))))**********+++++++++++,,,,,,,,,,,,------------..............////////////////000000000000000011111111111111111122ÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßààààààààááááááââââââãããããããäääääåååååååæææææççççççèèèèèééééééêêêêêëëëëëìììììííííííîîîîïïïïïðððððñññññòòòòòóóóóôôôôôõõõõõöööö÷÷÷÷÷øøøøùùùùùúúúúûûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!""""""""#######$$$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((((()))))))))***********+++++++++++,,,,,,,,,,,,-------------.............///////////////00000000000000001111111111111111122222ÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßàààààààááááááâââââââããããããääääääååååååææææææçççççèèèèèèéééééêêêêêëëëëëëìììììíííííîîîîîïïïïðððððñññññòòòòòóóóóôôôôôõõõõõöööö÷÷÷÷÷øøøøùùùùùúúúúûûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!""""""""########$$$$$$$$%%%%%%%%%&&&&&&&''''''''''((((((((()))))))))))*********+++++++++++++,,,,,,,,,,,------------...............///////////////00000000000000011111111111111111222222ÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßàààààààááááááââââââãããããããäääääååååååææææææçççççèèèèèèéééééêêêêêêëëëëëìììììíííííîîîîîïïïïïðððððññññòòòòòóóóóóôôôôõõõõõööööö÷÷÷÷øøøøøùùùùúúúúûûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!"""""""########$$$$$$$$%%%%%%%%&&&&&&&&&'''''''''(((((((((()))))))))***********+++++++++++,,,,,,,,,,,,-------------.............///////////////0000000000000000011111111111111111222222ÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßàààààààááááááâââââââããããããääääääååååååæææææççççççèèèèèééééééêêêêêëëëëëìììììíííííîîîîîïïïïïðððððññññòòòòòóóóóóôôôôõõõõõööööö÷÷÷÷øøøøøùùùùúúúúúûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!""""""""#######$$$$$$$$%%%%%%%%%&&&&&&&&''''''''''(((((((()))))))))))**********++++++++++++,,,,,,,,,,,-------------..............//////////////00000000000000011111111111111111122222222ÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßààààààáááááááââââââããããããäääääääåååååææææææçççççèèèèèèéééééêêêêêëëëëëëììììííííííîîîîïïïïïðððððñññññòòòòòóóóóôôôôôõõõõööööö÷÷÷÷øøøøøùùùùúúúúúûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!"""""""########$$$$$$$$%%%%%%%%&&&&&&&&&''''''''(((((((((()))))))))***********+++++++++++,,,,,,,,,,,,,-----------..............////////////////00000000000000011111111111111111222222222ÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààáááááááââââââããããããääääääååååååæææææççççççèèèèèéééééêêêêêêëëëëëìììììíííííîîîîîïïïïðððððñññññòòòòòóóóóôôôôôõõõõööööö÷÷÷÷øøøøøùùùùúúúúúûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!""""""""#######$$$$$$$$%%%%%%%%%&&&&&&&''''''''''((((((((())))))))))**********++++++++++++,,,,,,,,,,,-------------.............//////////////00000000000000000111111111111111112222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßàààààààááááááâââââââãããããääääääååååååææææææçççççèèèèèèéééééêêêêêëëëëëìììììíííííîîîîîïïïïïðððððññññòòòòòóóóóôôôôôõõõõõöööö÷÷÷÷øøøøøùùùùúúúúúûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!"""""""########$$$$$$$%%%%%%%%&&&&&&&&&''''''''(((((((((())))))))))**********+++++++++++,,,,,,,,,,,,------------...............//////////////00000000000000011111111111111111222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààáááááááââââââããããããääääääåååååææææææççççççèèèèèéééééêêêêêëëëëëëììììíííííîîîîîïïïïïðððððñññññòòòòóóóóóôôôôõõõõõöööö÷÷÷÷÷øøøøùùùùúúúúúûûûûüüüüýýýýýþþþþÿÿÿÿ  !!!!!!!""""""""######$$$$$$$$%%%%%%%%%&&&&&&&&'''''''''((((((((())))))))))***********++++++++++,,,,,,,,,,,,-------------............////////////////000000000000000011111111111111112222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßàààààààááááááââââââãããããããäääääååååååææææææçççççèèèèèééééééêêêêêëëëëëìììììíííííîîîîîïïïïðððððñññññòòòòóóóóóôôôôõõõõõöööö÷÷÷÷÷øøøøùùùùùúúúúûûûûüüüüüýýýýþþþþÿÿÿÿ  !!!!!!"""""""########$$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((()))))))))))*********++++++++++++,,,,,,,,,,,-------------.............//////////////00000000000000001111111111111111122222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßààààààáááááááââââââããããããääääääååååååæææææçççççèèèèèèéééééêêêêêëëëëëìììììíííííîîîîîïïïïðððððñññññòòòòòóóóóôôôôôõõõõööööö÷÷÷÷øøøøùùùùùúúúúûûûûüüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""#######$$$$$$$$%%%%%%%%%&&&&&&&''''''''''((((((((()))))))))***********++++++++++,,,,,,,,,,,,,-----------..............///////////////00000000000000111111111111111112222222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààááááááââââââããããããääääääååååååæææææççççççèèèèèéééééêêêêêëëëëëììììììííííîîîîîïïïïïðððððññññòòòòòóóóóôôôôôõõõõööööö÷÷÷÷øøøøùùùùùúúúúûûûûüüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""########$$$$$$$%%%%%%%%&&&&&&&&&''''''''(((((((((())))))))))*********++++++++++++,,,,,,,,,,,-------------............///////////////0000000000000000111111111111111122222222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßààààààáááááááââââââããããããäääääååååååææææææçççççèèèèèèéééééêêêêêëëëëëìììììíííííîîîîïïïïïðððððññññòòòòòóóóóóôôôôõõõõööööö÷÷÷÷øøøøøùùùùúúúúûûûûüüüüüýýýýþþþþÿÿÿÿ  !!!!!!!!"""""""#######$$$$$$$$%%%%%%%%&&&&&&&&'''''''''((((((((()))))))))***********++++++++++,,,,,,,,,,,,,-----------..............//////////////00000000000000011111111111111111222222222222222222ÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààáááááááâââââããããããääääääååååååæææææççççççèèèèèéééééêêêêêëëëëëìììììíííííîîîîîïïïïðððððñññññòòòòóóóóóôôôôõõõõõöööö÷÷÷÷øøøøøùùùùúúúúûûûûüüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""########$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((())))))))))**********+++++++++++,,,,,,,,,,,-------------............////////////////00000000000000111111111111111122222222222222222223ÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßàààààààááááááââââââããããããääääääåååååææææææçççççèèèèèéééééêêêêêêëëëëìììììíííííîîîîîïïïïïððððñññññòòòòóóóóóôôôôõõõõõöööö÷÷÷÷øøøøøùùùùúúúúûûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!!""""""#######$$$$$$$$%%%%%%%%%&&&&&&&'''''''''(((((((((())))))))***********+++++++++++,,,,,,,,,,,------------............../////////////00000000000000001111111111111111122222222222222222233ÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßààààààáááááááââââââãããããääääääååååååæææææçççççèèèèèèéééééêêêêêëëëëëìììììííííîîîîîïïïïïðððððññññòòòòòóóóóôôôôõõõõõöööö÷÷÷÷øøøøøùùùùúúúúûûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!""""""""#######$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((())))))))))**********+++++++++++,,,,,,,,,,,,-----------..............//////////////00000000000000111111111111111112222222222222222222233ÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààááááááââââââããããããääääääåååååæææææççççççèèèèèéééééêêêêêëëëëëìììììíííííîîîîïïïïïðððððññññòòòòòóóóóôôôôôõõõõöööö÷÷÷÷÷øøøøùùùùúúúúûûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""#######$$$$$$$$%%%%%%%%&&&&&&&&'''''''''((((((((()))))))))**********+++++++++++,,,,,,,,,,,-------------............///////////////000000000000000111111111111111222222222222222222233333ÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßààààààááááááâââââââããããããäääääåååååææææææçççççèèèèèééééééêêêêêëëëëìììììíííííîîîîîïïïïðððððñññññòòòòóóóóôôôôôõõõõööööö÷÷÷÷øøøøùùùùúúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!""""""""#######$$$$$$$%%%%%%%%&&&&&&&&'''''''''(((((((())))))))))***********++++++++++,,,,,,,,,,,,-----------............../////////////00000000000000011111111111111111222222222222222222333333ÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààááááááââââââããããããäääääååååååæææææçççççèèèèèèéééééêêêêêëëëëëìììììííííîîîîîïïïïïððððñññññòòòòóóóóóôôôôõõõõööööö÷÷÷÷øøøøùùùùúúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!""""""########$$$$$$$$%%%%%%%%&&&&&&&'''''''''((((((((()))))))))**********++++++++++++,,,,,,,,,,-------------............///////////////00000000000001111111111111111122222222222222222222333333ÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßààààààááááááââââââããããããääääääåååååææææææçççççèèèèèéééééêêêêêëëëëëìììììíííííîîîîïïïïïððððñññññòòòòóóóóóôôôôõõõõööööö÷÷÷÷øøøøùùùùúúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&&&''''''''((((((((())))))))))**********++++++++++,,,,,,,,,,,------------.............//////////////0000000000000001111111111111111222222222222222222333333333ÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßàààààààááááááââââââããããããäääääååååååæææææççççççèèèèééééééêêêêêëëëëëììììíííííîîîîîïïïïðððððññññòòòòòóóóóôôôôõõõõõöööö÷÷÷÷øøøøùùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!"""""""########$$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((((()))))))))*********+++++++++++,,,,,,,,,,,------------.............//////////////00000000000000011111111111111111222222222222222223333333333ÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààááááááâââââããããããääääääåååååææææææçççççèèèèèéééééêêêêêëëëëëìììììííííîîîîîïïïïðððððññññòòòòòóóóóôôôôôõõõõöööö÷÷÷÷øøøøùùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&&'''''''''(((((((())))))))))**********++++++++++,,,,,,,,,,,-------------...........///////////////000000000000001111111111111111222222222222222222223333333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßààààààááááááââââââããããããäääääååååååæææææçççççèèèèèéééééêêêêêëëëëëìììììííííîîîîîïïïïïððððñññññòòòòóóóóôôôôôõõõõöööö÷÷÷÷øøøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""#######$$$$$$$$%%%%%%%%&&&&&&&'''''''''((((((((()))))))))*********+++++++++++,,,,,,,,,,,,----------............../////////////00000000000000001111111111111112222222222222222223333333333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààááááááââââââãããããääääääåååååææææææçççççèèèèèéééééêêêêêëëëëëììììíííííîîîîïïïïïððððñññññòòòòóóóóóôôôôõõõõöööö÷÷÷÷øøøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""######$$$$$$$$%%%%%%%&&&&&&&&&''''''''((((((((()))))))))**********+++++++++++,,,,,,,,,,------------............///////////////00000000000000111111111111111112222222222222222233333333333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßààààààááááááââââââããããããäääääååååååæææææçççççèèèèèéééééêêêêêëëëëëììììíííííîîîîîïïïïðððððññññòòòòóóóóóôôôôõõõõöööö÷÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!"""""""#######$$$$$$$$%%%%%%%&&&&&&&&''''''''((((((((())))))))))*********++++++++++,,,,,,,,,,,,-----------............./////////////0000000000000001111111111111112222222222222222222233333333333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààááááááââââââãããããääääääåååååæææææçççççèèèèèèéééééêêêêëëëëëìììììíííííîîîîïïïïðððððññññòòòòòóóóóôôôôõõõõööööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!""""""#######$$$$$$$%%%%%%%%&&&&&&&&'''''''''(((((((()))))))))**********+++++++++++,,,,,,,,,,------------............./////////////00000000000000011111111111111112222222222222222233333333333333333ËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßàààààààáááááââââââãããããääääääååååååæææææçççççèèèèèéééééêêêêêëëëëìììììíííííîîîîïïïïïððððñññññòòòòóóóóôôôôõõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!""""""########$$$$$$$%%%%%%%%&&&&&&&''''''''((((((((())))))))))*********++++++++++,,,,,,,,,,,-------------...........///////////////00000000000001111111111111111122222222222222222333333333333333333ËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßàààààààááááááââââââãããããääääääåååååæææææçççççèèèèèéééééêêêêêëëëëëììììíííííîîîîîïïïïððððñññññòòòòóóóóôôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!"""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''((((((((())))))))**********+++++++++++,,,,,,,,,,,----------..............////////////00000000000000001111111111111122222222222222222222333333333333333333ËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßàààààààááááááâââââããããããäääääåååååæææææççççççèèèèèéééééêêêêëëëëëìììììííííîîîîîïïïïðððððññññòòòòóóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!"""""""#######$$$$$$$$%%%%%%%&&&&&&&&''''''''(((((((())))))))))**********+++++++++,,,,,,,,,,,------------............//////////////00000000000000111111111111111122222222222222222333333333333333333334ËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßààààààááááááâââââããããããääääääåååååæææææçççççèèèèèéééééêêêêêëëëëìììììíííííîîîîïïïïðððððññññòòòòóóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿÿ  !!!!!!!""""""#######$$$$$$$%%%%%%%%&&&&&&&&'''''''''(((((((()))))))))*********+++++++++++,,,,,,,,,,,-----------............//////////////000000000000011111111111111111222222222222222223333333333333333334444ËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßàààààààáááááââââââããããããäääääåååååæææææçççççèèèèèéééééêêêêêëëëëëììììíííííîîîîïïïïïððððññññòòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúúûûûûüüüüýýýýþþþÿÿÿÿ  !!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&''''''''((((((((()))))))))**********++++++++++,,,,,,,,,,------------.............////////////00000000000000011111111111111222222222222222222223333333333333333334444ËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââãããããäääääååååååæææææçççççèèèèèéééééêêêêêëëëëìììììííííîîîîîïïïïððððñññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!"""""""#######$$$$$$$%%%%%%%%&&&&&&&&''''''''((((((((())))))))*********++++++++++,,,,,,,,,,,,-----------...........///////////////00000000000001111111111111111222222222222222223333333333333333333344444ËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââããããããäääääåååååæææææçççççèèèèèéééééêêêêêëëëëìììììíííííîîîîïïïïðððððññññòòòòóóóóôôôôõõõõööööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!"""""""#######$$$$$$$%%%%%%%&&&&&&&&'''''''((((((((()))))))))**********++++++++++,,,,,,,,,,-----------............./////////////0000000000000011111111111111112222222222222222233333333333333333344444444ËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââãããããääääääåååååæææææçççççèèèèéééééêêêêêëëëëëììììíííííîîîîïïïïðððððññññòòòòóóóóôôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!"""""""#######$$$$$$$%%%%%%%&&&&&&&&''''''''((((((((()))))))))*********+++++++++,,,,,,,,,,,------------............/////////////00000000000000011111111111111222222222222222222233333333333333333344444444ËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââãããããääääääåååååæææææçççççèèèèèéééééêêêêêëëëëìììììííííîîîîïïïïïððððññññòòòòóóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!""""""#######$$$$$$$%%%%%%%%&&&&&&&''''''''(((((((()))))))))**********++++++++++,,,,,,,,,,,----------.............//////////////00000000000011111111111111111222222222222222233333333333333333333444444444ËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââãããããäääääååååååæææææçççççèèèèèééééêêêêêëëëëìììììííííîîîîîïïïïððððññññòòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!""""""#######$$$$$$$%%%%%%%%&&&&&&&'''''''''(((((((()))))))))*********++++++++++,,,,,,,,,,------------.............///////////00000000000000011111111111111122222222222222222333333333333333333444444444444ËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááâââââããããããäääääåååååæææææçççççèèèèèééééêêêêêëëëëëììììíííííîîîîïïïïððððñññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!"""""""#######$$$$$$$%%%%%%%&&&&&&&&'''''''((((((((())))))))**********++++++++++,,,,,,,,,,,-----------...........//////////////00000000000000111111111111112222222222222222222333333333333333334444444444444ËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââãããããäääääåååååæææææçççççèèèèèéééééêêêêêëëëëìììììííííîîîîïïïïðððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!""""""#######$$$$$$$%%%%%%%&&&&&&&&''''''''(((((((()))))))))**********+++++++++,,,,,,,,,,-----------............./////////////000000000000011111111111111112222222222222222333333333333333333334444444444444ËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááâââââããããããäääääåååååæææææçççççèèèèèééééêêêêêëëëëëììììííííîîîîïïïïïððððññññòòòòóóóóôôôôôõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!"""""""######$$$$$$$%%%%%%%%&&&&&&&''''''''(((((((()))))))))*********++++++++++,,,,,,,,,,------------............////////////00000000000000011111111111111222222222222222223333333333333333334444444444444444ËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááâââââããããããäääääåååååæææææçççççèèèèèééééêêêêêëëëëëììììííííîîîîîïïïïððððññññòòòòóóóóóôôôôõõõõöööö÷÷÷÷øøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!!""""""#######$$$$$$$%%%%%%%&&&&&&&''''''''((((((((())))))))**********++++++++++,,,,,,,,,,----------............//////////////00000000000001111111111111112222222222222222223333333333333333344444444444444444ËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßààààààááááááâââââãããããääääääåååååæææææççççèèèèèéééééêêêêëëëëëììììíííííîîîîïïïïððððñññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùúúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!!""""""#######$$$$$$$%%%%%%%&&&&&&&&'''''''((((((((())))))))*********++++++++++,,,,,,,,,,------------............////////////0000000000000111111111111111112222222222222223333333333333333333344444444444444444ËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßààààààáááááââââââãããããäääääåååååæææææçççççèèèèèééééêêêêêëëëëìììììííííîîîîïïïïðððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!!""""""######$$$$$$$%%%%%%%&&&&&&&&''''''''(((((((())))))))**********++++++++++,,,,,,,,,,-----------.........../////////////00000000000000111111111111112222222222222222233333333333333333344444444444444444444ÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààááááááâââââãããããäääääåååååæææææçççççèèèèèéééééêêêêëëëëëììììííííîîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúûûûûüüüüýýýýþþþþÿÿÿ  !!!!!!"""""""######$$$$$$$%%%%%%%%&&&&&&&''''''''(((((((()))))))))*********+++++++++,,,,,,,,,,-----------............//////////////00000000000011111111111111122222222222222222233333333333333333444444444444444444445ÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááâââââãããããäääääåååååæææææçççççèèèèéééééêêêêëëëëëììììíííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûüüüüýýýýþþþþÿÿÿ  !!!!!!"""""""######$$$$$$$%%%%%%%&&&&&&&''''''''(((((((()))))))))*********+++++++++,,,,,,,,,,,-----------...........////////////00000000000000111111111111111122222222222222233333333333333333333444444444444444444555ÊÊÊÊËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààááááááâââââãããããääääääåååååæææææççççèèèèèééééêêêêêëëëëìììììííííîîîîïïïïðððððñññòòòòòóóóóôôôôõõõõööö÷÷÷÷øøøøùùùùúúúúûûûüüüüýýýýþþþþÿÿÿ  !!!!!!""""""#######$$$$$$$%%%%%%%&&&&&&&&'''''''((((((((())))))))*********++++++++++,,,,,,,,,,----------...........//////////////00000000000000111111111111122222222222222222333333333333333333444444444444444444444555ÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààáááááââââââãããããäääääåååååæææææçççççèèèèéééééêêêêëëëëëììììííííîîîîîïïïïððððññññòòòòóóóóôôôôõõõõööö÷÷÷÷øøøøùùùùúúúúûûûüüüüýýýýþþþþÿÿÿ  !!!!!!""""""#######$$$$$$%%%%%%%&&&&&&&&'''''''(((((((()))))))))*********+++++++++,,,,,,,,,,-----------............/////////////000000000000111111111111111122222222222222222333333333333333344444444444444444444455555ÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààááááááâââââããããããäääääååååæææææçççççèèèèéééééêêêêëëëëëììììííííîîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷øøøøùùùùúúúúûûûûüüüýýýýþþþþÿÿÿ  !!!!!!""""""#######$$$$$$%%%%%%%%&&&&&&&''''''''(((((((()))))))))*********+++++++++,,,,,,,,,,-----------...........////////////00000000000000111111111111111222222222222222333333333333333333344444444444444444455555555ÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààáááááâââââããããããäääääåååååæææææççççèèèèèéééééêêêêëëëëìììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøùùùùúúúúûûûûüüüýýýýþþþþÿÿÿ  !!!!!!"""""""#######$$$$$$%%%%%%%&&&&&&&''''''''((((((()))))))))*********+++++++++,,,,,,,,,,-----------............/////////////00000000000001111111111111222222222222222223333333333333333334444444444444444444455555555ÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààáááááâââââãããããäääääåååååæææææçççççèèèèèééééêêêêëëëëëììììííííîîîîïïïïðððððññññòòòóóóóôôôôõõõõöööö÷÷÷÷øøøùùùùúúúúûûûûüüüýýýýþþþþÿÿÿ  !!!!!!""""""######$$$$$$$%%%%%%%&&&&&&&&'''''''(((((((()))))))))*********+++++++++,,,,,,,,,,-----------...........////////////0000000000000111111111111111222222222222222223333333333333333444444444444444444444555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààáááááââââââãããããäääääåååååææææçççççèèèèèééééêêêêêëëëëììììíííííîîîîïïïïððððññññòòòòóóóôôôôõõõõöööö÷÷÷÷øøøøùùùúúúúûûûûüüüýýýýþþþþÿÿÿ  !!!!!!""""""#######$$$$$$$%%%%%%&&&&&&&''''''''(((((((())))))))*********+++++++++,,,,,,,,,,-----------.........../////////////00000000000000111111111111112222222222222223333333333333333333444444444444444444555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààáááááâââââãããããäääääåååååæææææçççççèèèèéééééêêêêëëëëììììíííííîîîîïïïïððððññññòòòòóóóóôôôôõõõöööö÷÷÷÷øøøøùùùúúúúûûûûüüüýýýýþþþþÿÿÿ  !!!!!!""""""######$$$$$$%%%%%%%%&&&&&&&''''''''(((((((())))))))********++++++++++,,,,,,,,,,----------............/////////////00000000000011111111111112222222222222222223333333333333333344444444444444444444555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààááááááâââââãããããäääääååååæææææçççççèèèèèééééêêêêëëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõööö÷÷÷÷øøøøùùùúúúúûûûûüüüüýýýþþþþÿÿÿ  !!!!!!""""""#######$$$$$$%%%%%%%&&&&&&&'''''''(((((((()))))))))*********+++++++++,,,,,,,,,,----------...........////////////00000000000001111111111111112222222222222222233333333333333334444444444444444444445555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßààààààáááááâââââãããããäääääåååååæææææççççèèèèèééééêêêêêëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõööö÷÷÷÷øøøøùùùùúúúûûûûüüüüýýýþþþþÿÿÿ  !!!!!!!""""""######$$$$$$%%%%%%%&&&&&&&&'''''''(((((((())))))))********++++++++++,,,,,,,,,-----------.........../////////////00000000000001111111111111122222222222222233333333333333333334444444444444444445555555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààááááááâââââãããããääääååååååææææçççççèèèèéééééêêêêëëëëìììììííííîîîîïïïïððððññññòòòòóóóôôôôõõõõöööö÷÷÷øøøøùùùùúúúûûûûüüüüýýýþþþþÿÿÿ  !!!!!!"""""#######$$$$$$$%%%%%%%&&&&&&''''''''(((((((())))))))*********++++++++++,,,,,,,,,-----------...........////////////000000000000111111111111112222222222222222233333333333333333444444444444444444455555555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××ØØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààáááááâââââãããããäääääåååååææææçççççèèèèèééééêêêêëëëëëììììííííîîîîïïïïððððññññòòòòóóóôôôôõõõõöööö÷÷÷øøøøùùùùúúúûûûûüüüüýýýþþþþÿÿÿ  !!!!!!""""""######$$$$$$$%%%%%%%&&&&&&&''''''''((((((())))))))********+++++++++,,,,,,,,,,----------.........../////////////00000000000001111111111111112222222222222222333333333333333444444444444444444444455555555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßßààààààáááááâââââãããããäääääåååååææææççççèèèèèéééééêêêêëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôôõõõõöööö÷÷÷÷øøøùùùùúúúúûûûüüüüýýýþþþþÿÿÿ  !!!!!""""""#######$$$$$$$%%%%%%&&&&&&&'''''''(((((((()))))))))********+++++++++,,,,,,,,,,----------.........../////////////00000000000001111111111111222222222222222333333333333333333444444444444444444455555555555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààáááááâââââãããããäääääåååååæææææççççèèèèéééééêêêêëëëëìììììííííîîîîïïïððððññññòòòòóóóóôôôôõõõöööö÷÷÷÷øøøùùùùúúúúûûûüüüüýýýþþþþÿÿÿ  !!!!!!""""""######$$$$$$%%%%%%%&&&&&&&&'''''''((((((())))))))********++++++++++,,,,,,,,,,----------...........////////////0000000000001111111111111122222222222222222333333333333333334444444444444444445555555555555555555555ÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××××ØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßßààààààáááááâââââãããããäääääååååæææææçççççèèèèééééêêêêëëëëìììììííííîîîîïïïïððððññññòòòóóóóôôôôõõõõööö÷÷÷÷øøøùùùùúúúúûûûüüüüýýýþþþþÿÿÿ  !!!!!""""""######$$$$$$$%%%%%%%&&&&&&'''''''((((((((())))))))********+++++++++,,,,,,,,,,----------...........////////////00000000000001111111111111122222222222222223333333333333334444444444444444444445555555555555555555566ÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞßßßßßààààààáááááâââââãããããäääääåååååææææçççççèèèèéééééêêêêëëëëììììííííîîîîïïïïððððññññòòòòóóóôôôôõõõõööö÷÷÷÷øøøøùùùúúúúûûûüüüüýýýþþþþÿÿÿ  !!!!!"""""""######$$$$$$%%%%%%%&&&&&&&'''''''((((((())))))))*********+++++++++,,,,,,,,,,-----------...........///////////000000000000011111111111112222222222222223333333333333333334444444444444444444555555555555555555555666ÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààááááââââââãããããääääåååååæææææççççèèèèèééééêêêêëëëëììììííííîîîîïïïïððððññññòòòòóóóôôôôõõõõööö÷÷÷÷øøøøùùùúúúúûûûüüüüýýýþþþþÿÿÿ  !!!!!!""""""######$$$$$$$%%%%%%&&&&&&&'''''''(((((((())))))))********+++++++++,,,,,,,,,----------...........////////////00000000000011111111111111122222222222222223333333333333333344444444444444444555555555555555555555555666ÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßßàààààáááááâââââãããããäääääåååååææææçççççèèèèééééêêêêëëëëëììììííííîîîîïïïððððññññòòòòóóóóôôôõõõõöööö÷÷÷øøøøùùùúúúúûûûüüüüýýýþþþþÿÿÿ  !!!!!""""""#######$$$$$%%%%%%%&&&&&&&&'''''''((((((()))))))*********++++++++++,,,,,,,,,----------...........////////////00000000000011111111111111122222222222222233333333333333344444444444444444444555555555555555555555666666ÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××××ØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßàààààááááááâââââãããããääääåååååæææææççççèèèèéééééêêêêëëëëììììííííîîîîïïïïððððñññòòòòóóóóôôôõõõõöööö÷÷÷øøøøùùùúúúúûûûüüüüýýýþþþþÿÿÿ  !!!!!!""""""#####$$$$$$$%%%%%%%&&&&&&'''''''((((((((()))))))********+++++++++,,,,,,,,,-----------...........////////////000000000000111111111111122222222222222233333333333333333344444444444444444445555555555555555555566666666ÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßßàààààáááááâââââãããããäääääååååæææææççççèèèèèééééêêêêëëëëììììííííîîîîïïïïððððññññòòòóóóóôôôôõõõöööö÷÷÷øøøøùùùùúúúûûûûüüüýýýýþþþÿÿÿ  !!!!!""""""######$$$$$$%%%%%%%&&&&&&&'''''''((((((())))))))*********+++++++++,,,,,,,,,----------..........////////////00000000000011111111111111222222222222222233333333333333333444444444444444445555555555555555555555566666666ÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßßààààààáááááâââââãããããäääääååååææææçççççèèèèééééêêêêëëëëëììììííííîîîïïïïððððññññòòòòóóóôôôôõõõöööö÷÷÷÷øøøùùùùúúúûûûûüüüýýýýþþþÿÿÿ  !!!!!!"""""######$$$$$$$%%%%%%&&&&&&&'''''''(((((((()))))))********+++++++++,,,,,,,,,,----------...........////////////00000000000011111111111111222222222222222333333333333333444444444444444444455555555555555555555556666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßßàààààáááááââââââããããäääääåååååææææççççèèèèèééééêêêêëëëëììììííííîîîîïïïððððññññòòòòóóóôôôôõõõõööö÷÷÷÷øøøùùùùúúúûûûûüüüýýýýþþþÿÿÿ  !!!!!"""""#######$$$$$$%%%%%%&&&&&&&&''''''((((((()))))))))********+++++++++,,,,,,,,----------...........////////////0000000000001111111111111222222222222222333333333333333334444444444444444444455555555555555555556666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßààààààáááááâââââãããããääääåååååæææææççççèèèèééééêêêêëëëëììììííííîîîîïïïïððððñññòòòòóóóóôôôõõõõööö÷÷÷÷øøøùùùùúúúûûûûüüüýýýýþþþÿÿÿ  !!!!!""""""######$$$$$$%%%%%%%&&&&&&'''''''(((((((()))))))********++++++++++,,,,,,,,,----------..........////////////00000000000011111111111111222222222222222333333333333333334444444444444444455555555555555555555566666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßßàààààááááááââââãããããäääääååååæææææççççèèèèééééêêêêêëëëëìììííííîîîîïïïïððððñññòòòòóóóóôôôõõõõööö÷÷÷÷øøøùùùùúúúûûûûüüüýýýýþþþÿÿÿ  !!!!!!"""""#######$$$$$%%%%%%%&&&&&&&'''''''(((((()))))))))********++++++++,,,,,,,,,-----------...........///////////000000000000011111111111112222222222222223333333333333334444444444444444445555555555555555555555566666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßàààààáááááâââââãããããääääåååååææææççççèèèèèééééêêêêëëëëììììííííîîîïïïïððððññññòòòóóóóôôôõõõõöööö÷÷÷øøøøùùùúúúûûûûüüüýýýýþþþÿÿÿ  !!!!!!""""""#####$$$$$$$%%%%%%&&&&&&''''''''((((((())))))))********+++++++++,,,,,,,,,---------...........///////////00000000000011111111111112222222222222223333333333333333344444444444444444445555555555555555555566666666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßßàààààááááááââââãããããäääääååååæææææççççèèèèééééêêêêëëëëììììííííîîîîïïïððððññññòòòòóóóôôôôõõõöööö÷÷÷øøøøùùùúúúúûûûüüüýýýýþþþÿÿÿ  !!!!!!"""""######$$$$$$%%%%%%&&&&&&&'''''''((((((()))))))))*******++++++++,,,,,,,,,,----------...........///////////00000000000011111111111112222222222222223333333333333333344444444444444444555555555555555555556666666666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××××ØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááâââââããããäääääåååååææææççççèèèèééééêêêêêëëëëìììííííîîîîïïïïðððññññòòòòóóóôôôôõõõöööö÷÷÷øøøøùùùúúúúûûûüüüýýýýþþþÿÿÿ  !!!!!"""""""#####$$$$$$%%%%%%%&&&&&&'''''''(((((((()))))))********+++++++++,,,,,,,,,---------...........///////////0000000000000111111111111122222222222222233333333333333344444444444444444455555555555555555555556666666666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßààààààáááááââââãããããääääåååååææææççççèèèèèééééêêêêëëëëììììíííîîîîîïïïððððñññòòòòóóóóôôôõõõõööö÷÷÷øøøøùùùúúúúûûûüüüýýýýþþþÿÿÿ  !!!!!!""""""######$$$$$$%%%%%%&&&&&&&''''''((((((()))))))))*******++++++++,,,,,,,,,,----------..........///////////00000000000111111111111122222222222222233333333333333333444444444444444444455555555555555555555666666666666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááâââââããããäääääååååæææææççççèèèèééééêêêêëëëëììììííííîîîîïïïððððññññòòòóóóóôôôõõõõööö÷÷÷÷øøøùùùúúúúûûûüüüýýýýþþþÿÿÿ  !!!!!""""""######$$$$$$%%%%%%&&&&&&''''''''((((((()))))))*********++++++++,,,,,,,,----------...........////////////00000000000111111111111122222222222222233333333333333333444444444444444445555555555555555556666666666666666666666666ÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××××ØØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßààààààáááááâââââããããäääääååååææææçççççèèèèééééêêêêëëëììììííííîîîîïïïððððññññòòòóóóóôôôõõõõööö÷÷÷÷øøøùùùùúúúûûûüüüüýýýþþþÿÿÿ  !!!!!!"""""######$$$$$$%%%%%%&&&&&&&'''''''((((((())))))))*******+++++++++,,,,,,,,,---------..........///////////00000000000001111111111111222222222222222333333333333333444444444444444445555555555555555555556666666666666666666666667ÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááâââââãããããääääåååååææææççççèèèèééééêêêêëëëëììììííííîîîïïïïðððññññòòòòóóóôôôôõõõööö÷÷÷÷øøøùùùùúúúûûûüüüüýýýþþþÿÿÿ  !!!!!""""""######$$$$$%%%%%%%&&&&&&'''''''((((((()))))))*********++++++++,,,,,,,,-----------..........///////////00000000000111111111111122222222222222333333333333333344444444444444444445555555555555555555556666666666666666666677777ÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßßàààààááááââââââããããäääääååååææææççççèèèèééééêêêêëëëëììììííííîîîîïïïððððñññòòòòóóóôôôôõõõöööö÷÷÷øøøùùùùúúúûûûüüüüýýýþþþÿÿÿ  !!!!!!"""""######$$$$$$%%%%%%&&&&&&&''''''(((((((()))))))*******++++++++++,,,,,,,,---------..........////////////000000000000111111111111122222222222222333333333333333344444444444444444455555555555555555566666666666666666666666677777ÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááâââââãããããääääååååææææçççççèèèèééééêêêêëëëììììííííîîîîïïïððððñññòòòòóóóóôôôõõõöööö÷÷÷øøøùùùùúúúûûûüüüüýýýþþþÿÿÿ  !!!!!"""""######$$$$$$%%%%%%&&&&&&''''''''(((((())))))))********++++++++,,,,,,,,,----------.........///////////00000000000011111111111112222222222222223333333333333334444444444444444455555555555555555555666666666666666666666666677777ÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááââââãããããääääåååååææææççççèèèèééééêêêêëëëëììììíííîîîîïïïïðððññññòòòóóóóôôôõõõõööö÷÷÷øøøøùùùúúúûûûüüüüýýýþþþÿÿÿ  !!!!!""""""#####$$$$$$$%%%%%&&&&&&&''''''(((((((()))))))********+++++++++,,,,,,,,---------...........///////////00000000000111111111111222222222222223333333333333333444444444444444444555555555555555555555666666666666666666666677777777ÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßààààààááááâââââããããäääääååååæææææçççèèèèèééééêêêëëëëììììííííîîîïïïïððððñññòòòóóóóôôôõõõõööö÷÷÷øøøøùùùúúúûûûüüüüýýýþþþÿÿÿ  !!!!!!"""""######$$$$$%%%%%%%&&&&&&'''''''(((((())))))))********++++++++,,,,,,,,,,--------..........///////////0000000000000111111111111222222222222223333333333333333444444444444444444555555555555555555566666666666666666666677777777777ÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááââââãããããääääåååååææææççççèèèèééééêêêêëëëëìììííííîîîîïïïððððñññòòòòóóóôôôõõõõööö÷÷÷øøøøùùùúúúûûûûüüüýýýþþþÿÿÿ  !!!!!""""""#####$$$$$$%%%%%%&&&&&&&''''''((((((()))))))********++++++++,,,,,,,,-----------.........///////////00000000000111111111111122222222222222233333333333333344444444444444444555555555555555555566666666666666666666666677777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØØØÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßààààààááááâââââããããäääääååååææææççççèèèèééééêêêêëëëëììììíííîîîîïïïððððñññòòòòóóóôôôôõõõööö÷÷÷÷øøøùùùúúúûûûûüüüýýýþþþÿÿÿ  !!!!!!""""#######$$$$$%%%%%%%&&&&&''''''''(((((())))))))*******++++++++,,,,,,,,,---------..........////////////00000000000111111111111222222222222233333333333333334444444444444444455555555555555555555566666666666666666666666777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÛÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááâââââããããääääååååæææææççççèèèèééééêêêëëëëììììííííîîîïïïïðððññññòòòóóóôôôôõõõööö÷÷÷÷øøøùùùúúúûûûûüüüýýýþþþÿÿÿ  !!!!!"""""#####$$$$$$$%%%%%&&&&&&&''''''(((((((())))))*********+++++++,,,,,,,,,----------.........//////////00000000000001111111111111222222222222233333333333333334444444444444444455555555555555555555666666666666666666667777777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááââââãããããääääååååææææççççèèèèééééêêêêëëëëìììííííîîîïïïïðððññññòòòóóóóôôôõõõöööö÷÷÷øøøùùùúúúúûûûüüüýýýþþþÿÿÿ  !!!!!"""""######$$$$$%%%%%%%&&&&&&'''''''(((((())))))))*******+++++++++,,,,,,,,---------...........//////////00000000001111111111111222222222222222333333333333333444444444444444445555555555555555556666666666666666666666677777777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××××ØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßààààààááááâââââããããääääååååæææææçççèèèèééééêêêêëëëëììììíííîîîîïïïððððñññòòòóóóóôôôõõõöööö÷÷÷øøøùùùúúúúûûûüüüýýýþþþÿÿÿ  !!!!!""""""#####$$$$$$%%%%%%&&&&&&&'''''(((((((())))))*********+++++++,,,,,,,,,---------.........////////////000000000001111111111112222222222222333333333333333444444444444444445555555555555555555566666666666666666666666677777777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØØÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááââââããããäääääååååææææççççèèèèééééêêêêëëëììììíííîîîîïïïððððñññòòòòóóóôôôõõõõööö÷÷÷øøøùùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!"""""######$$$$$%%%%%%%&&&&&'''''''(((((())))))))*******+++++++++,,,,,,,----------..........//////////00000000000011111111111112222222222222333333333333333444444444444444445555555555555555555566666666666666666666677777777777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßààààààááááâââââããããääääåååååæææççççèèèèééééêêêêëëëëìììííííîîîïïïïðððñññòòòòóóóôôôõõõõööö÷÷÷øøøùùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!""""""#####$$$$$$%%%%%&&&&&&&''''''((((((()))))))********+++++++,,,,,,,,,---------..........///////////00000000001111111111112222222222222223333333333333334444444444444444455555555555555555566666666666666666666677777777777777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××ØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááââââããããäääääååååææææççççèèèèééééêêêëëëëìììííííîîîïïïïðððññññòòòóóóôôôôõõõööö÷÷÷øøøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!"""""######$$$$$%%%%%%&&&&&&'''''''(((((()))))))********++++++++,,,,,,,,----------........///////////0000000000001111111111112222222222223333333333333334444444444444444455555555555555555556666666666666666666666677777777777777777777777ÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßàààààááááââââãããããääääååååææææççççèèèèééééêêêêëëëììììíííîîîîïïïððððñññòòòóóóóôôôõõõööö÷÷÷øøøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!"""""######$$$$$$%%%%%&&&&&&''''''((((((()))))))********++++++++,,,,,,,,---------..........//////////00000000000111111111111122222222222223333333333333334444444444444444455555555555555555556666666666666666666666677777777777777777777778ÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××ØØØØØØØÙÙÙÙÙÚÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßßàààààááááâââââããããäääääååååæææçççççèèèééééêêêêëëëììììííííîîîïïïððððñññòòòóóóóôôôõõõöööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!""""""#####$$$$$%%%%%%%&&&&&'''''''(((((()))))))********++++++++,,,,,,,,---------.........////////////00000000001111111111122222222222222233333333333333344444444444444444555555555555555555666666666666666666666777777777777777777777777788ÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßààààáááááââââãããããääääååååææææççççèèèèééééêêêëëëëìììííííîîîïïïïðððññññòòòóóóôôôõõõöööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!"""""#####$$$$$$%%%%%&&&&&&&''''''((((((()))))))*******++++++++,,,,,,,,---------.........//////////00000000000001111111111122222222222223333333333333344444444444444444555555555555555555666666666666666666666677777777777777777777777777788ÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßàààààááááâââââãããããäääåååååææææçççèèèèééééêêêêëëëììììíííîîîîïïïðððññññòòòóóóôôôõõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!"""""######$$$$$%%%%%%&&&&&&''''''((((((())))))*********++++++,,,,,,,,,,-------...........//////////00000000001111111111111222222222222223333333333333344444444444444445555555555555555556666666666666666666666677777777777777777777777778888ÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞÞßßßßßààààáááááâââââããããääääååååææææççççèèèèéééêêêêëëëììììíííîîîîïïïððððñññòòòóóóôôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!""""######$$$$$$%%%%%&&&&&&''''''((((((()))))))*******++++++++,,,,,,,----------.........///////////000000000011111111111222222222222222333333333333333444444444444444445555555555555555556666666666666666666667777777777777777777777788888888ÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××ØØØØØØØÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßàààààáááááââââããããäääääåååææææççççèèèèééééêêêëëëëìììííííîîîïïïððððñññòòòóóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!"""""#####$$$$$$%%%%%%&&&&&'''''''(((((()))))))********++++++,,,,,,,,,--------.........../////////00000000000011111111111222222222222233333333333333444444444444444445555555555555555556666666666666666666677777777777777777777777777888888888ÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßßàààààááááâââââããããääääååååææææçççèèèèééééêêêêëëëììììíííîîîïïïïðððñññòòòóóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!"""""######$$$$$%%%%%&&&&&&&''''''(((((())))))))******++++++++,,,,,,,,---------.........///////////00000000011111111111112222222222222233333333333334444444444444444555555555555555555666666666666666666666677777777777777777777777777888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßààààáááááââââããããääääååååææææççççèèèèéééêêêêëëëììììíííîîîîïïïðððññññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿÿ  !!!!!!"""""#####$$$$$%%%%%%&&&&&&''''''((((((())))))********+++++++,,,,,,,,---------.........//////////0000000000011111111111222222222222223333333333333334444444444444444555555555555555555666666666666666666666677777777777777777777777888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßßàààààááááâââââããããääääååååææææçççèèèèééééêêêëëëëìììííííîîîïïïððððñññòòòóóóôôôõõõööö÷÷÷øøøùùùùúúúûûûüüüýýýþþþÿÿ  !!!!!"""""#####$$$$$$%%%%%&&&&&&''''''(((((())))))))******++++++++,,,,,,,,--------..........//////////00000000000111111111111222222222222333333333333334444444444444444455555555555555555566666666666666666666677777777777777777777778888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÝÝÝÝÝÞÞÞÞÞÞßßßßààààáááááââââããããääääååååææææççççèèèèéééêêêêëëëììììíííîîîïïïððððñññòòòóóóôôôõõõöööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿ  !!!!!!"""""####$$$$$$%%%%%%&&&&&''''''((((((())))))********++++++++,,,,,,,---------........////////////00000000011111111111122222222222222333333333333344444444444444455555555555555555566666666666666666666777777777777777777777777778888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ×××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßàààààáááááâââãããããääääååååæææççççèèèèéééêêêêëëëëìììíííîîîïïïïðððñññòòòóóóôôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿ  !!!!""""""#####$$$$$%%%%%&&&&&&&''''''((((())))))))*******+++++++,,,,,,,,,-------........../////////00000000000011111111112222222222222233333333333333344444444444444455555555555555555566666666666666666666777777777777777777777777778888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßàààààááááââââããããääääååååææææççççèèèééééêêêêëëëìììííííîîîïïïðððñññòòòóóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿ  !!!!!"""""#####$$$$$%%%%%%&&&&&&''''''(((((()))))))******+++++++++,,,,,,,---------.........//////////00000000001111111111112222222222223333333333333344444444444444444555555555555555555666666666666666666666777777777777777777777778888888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßàààààáááááââââãããäääääååååæææççççèèèèéééêêêêëëëììììíííîîîïïïðððññññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿ  !!!!"""""######$$$$$%%%%%&&&&&&''''''(((((()))))))*******+++++++,,,,,,,,---------........///////////000000000011111111111222222222222223333333333333444444444444445555555555555555555666666666666666666667777777777777777777777788888888888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××××ØØØØØØÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßàààààááááââââããããääääååååææææçççèèèèéééêêêêëëëëìììíííîîîïïïððððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿ  !!!!!"""""#####$$$$$%%%%%%&&&&&''''''((((((())))))******+++++++++,,,,,,,--------........../////////00000000000111111111112222222222222333333333333333444444444444445555555555555555566666666666666666666777777777777777777777777788888888888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××ØØØØØØÙÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßààààáááááââââããããäääååååææææççççèèèééééêêêëëëëìììíííîîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿ  !!!!!"""""######$$$$%%%%%&&&&&&'''''''((((()))))))*******+++++++,,,,,,,,---------........///////////00000000011111111111112222222222233333333333333444444444444444445555555555555555566666666666666666666777777777777777777777777788888888888888888888888ÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××××ØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞÞßßßßàààààááááââââããããääääååååææææçççèèèèéééêêêêëëëìììííííîîîïïïðððñññòòòòóóôôôôõõööö÷÷÷øøøùùùúúúûûûüüüýýýþþþÿÿ  !!!!!""""#####$$$$$$%%%%%&&&&&&'''''((((((()))))))******+++++++,,,,,,,,,-------........../////////0000000000111111111112222222222222233333333333344444444444444445555555555555555556666666666666666666677777777777777777777777888888888888888888888888889ÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßààààááááâââââããããääääåååææææççççèèèééééêêêëëëììììíííîîîïïïðððññññòòòóóóôôôõõõööö÷÷øøøùùùúúúûûûüüüýýýþþþÿÿ  !!!!!"""""#####$$$$$%%%%%&&&&&&''''''(((((())))))*******++++++++,,,,,,,---------........//////////00000000001111111111122222222222223333333333333344444444444444555555555555555556666666666666666666677777777777777777777777888888888888888888888888888889ÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßàààààááááââââããããääääååååææææçççèèèééééêêêëëëëìììíííîîîîïïððððñññòòòóóóôôôõõõööö÷÷÷øøùùùúúúûûûüüüýýýþþþÿÿ  !!!!"""""#####$$$$$%%%%%%&&&&&''''''(((((()))))))******+++++++,,,,,,,,,-------.........//////////000000000111111111111122222222222333333333333334444444444444444555555555555555556666666666666666666777777777777777777777777888888888888888888888888888889ÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØØØÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßàààààáááââââãããããääääåååææææçççèèèèéééêêêêëëëìììííííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúûûûüüüýýýþþþÿÿ  !!!!!"""""#####$$$$%%%%%%&&&&&'''''''((((())))))*******++++++++,,,,,,,--------..........////////00000000000111111111122222222222222333333333333444444444444444455555555555555555566666666666666666666777777777777777777777777888888888888888888888888889999ÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖ××××××ØØØØØÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßàààààááááââââããããääääåååææææççççèèèééééêêêëëëììììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷÷øøøùùùúúûûûüüüýýýþþþÿÿ  !!!!"""""#####$$$$$%%%%%%&&&&&'''''(((((()))))))*******++++++,,,,,,,,---------.......///////////00000000011111111111122222222222233333333333333444444444444445555555555555555566666666666666666666777777777777777777777778888888888888888888888888899999999ÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßàààààááááââââãããääääååååææææçççèèèèéééêêêëëëëìììíííîîîïïïðððñññòòòòóóôôôõõõööö÷÷÷øøøùùùúúúûûüüüýýýþþþÿÿ  !!!!!"""""####$$$$$%%%%%&&&&&&''''''(((((())))))******++++++++,,,,,,,,-------........./////////0000000000111111111111222222222223333333333333344444444444444445555555555555556666666666666666666777777777777777777777778888888888888888888888888888999999999ÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖ×××××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞÞßßßßààààááááââââããããääääååååæææçççèèèèééééêêêëëëìììíííîîîîïïïðððñññòòòóóóôôôõõööö÷÷÷øøøùùùúúúûûüüüýýýþþþÿÿ  !!!!!""""#####$$$$$$%%%%%&&&&&'''''(((((()))))))*******+++++++,,,,,,,---------......../////////00000000000111111111222222222222223333333333334444444444444444555555555555555556666666666666666666777777777777777777777778888888888888888888888888888999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞÞßßßßàààààááááââââããããäääååååæææççççèèèèéééêêêëëëììììíííîîîïïïðððñññòòòóóóôôôõõõöö÷÷÷øøøùùùúúúûûüüüýýýþþþÿÿ  !!!!!""""#####$$$$%%%%%%&&&&&''''''(((((())))))******++++++++,,,,,,,,-------........///////////00000000011111111111222222222222333333333333334444444444444555555555555555555666666666666666666667777777777777777777777788888888888888888888888888999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßààààááááââââããããääääåååææææççççèèèéééêêêëëëëìììíííîîîïïïðððñññòòòóóóôôôõõõööö÷÷øøøùùùúúúûûüüüýýýþþþÿÿ  !!!!"""""#####$$$$$%%%%%%&&&&&'''''(((((())))))*******+++++++,,,,,,,--------........./////////0000000000111111111112222222222233333333333333444444444444444555555555555555666666666666666666667777777777777777777777888888888888888888888888889999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßàààààááááââââããããäääååååæææççççèèèééééêêêëëëìììíííîîîïïïððððññòòòóóóôôôõõõööö÷÷øøøùùùúúúûûûüüýýýþþþÿÿ  !!!!"""""####$$$$$%%%%%&&&&&&''''''(((((())))))******+++++++,,,,,,,---------.......//////////00000000001111111112222222222222233333333333444444444444444445555555555555555666666666666666666777777777777777777777788888888888888888888888888899999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßàààààááááâââããããäääååååææææçççèèèèéééêêêëëëìììííííîîîïïïðððñññòòòóóôôôõõõööö÷÷÷øøùùùúúúûûûüüýýýþþþÿÿ  !!!!"""""#####$$$$$%%%%%&&&&&'''''(((((())))))*******++++++++,,,,,,-------........../////////000000000111111111112222222222223333333333333444444444444455555555555555555556666666666666666666777777777777777777777788888888888888888888888888899999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞÞßßßßààààááááââââãããääääååååæææççççèèèéééêêêëëëëìììíííîîîïïïðððñññòòòóóôôôõõõööö÷÷÷øøùùùúúúûûûüüýýýþþþÿÿ  !!!!!""""#####$$$$%%%%%%&&&&&''''''(((((())))))******++++++,,,,,,,,--------.........////////00000000001111111111122222222222333333333333334444444444444455555555555555566666666666666666666677777777777777777777777888888888888888888888888889999999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßàààààááááâââããããääääåååææææçççèèèééééêêêëëëìììíííîîîïïïðððñññòòòóóóôôôõõööö÷÷÷øøøùùúúúûûûüüýýýþþþÿÿ  !!!!"""""#####$$$$%%%%%%&&&&&'''''((((())))))*******+++++++,,,,,,,,-------........//////////00000000011111111112222222222222333333333334444444444444444455555555555555566666666666666666777777777777777777777778888888888888888888888888899999999999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØÙÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßààààááááââââãããääääååååæææçççèèèèéééêêêëëëëììíííîîîïïïðððñññòòòóóóôôôõõööö÷÷÷øøøùùúúúûûûüüýýýþþþÿÿ  !!!!"""""####$$$$$%%%%%&&&&&&'''''(((((())))))*******++++++,,,,,,,--------.........////////0000000001111111111112222222222233333333333334444444444444555555555555555555566666666666666666777777777777777777777888888888888888888888888889999999999999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÝÞÞÞÞßßßßàààààáááââââããããääääåååææææçççèèèéééêêêëëëëìììíííîîîïïïðððññòòòóóóôôôõõõöö÷÷÷øøøùùùúúûûûüüüýýþþþÿÿ  !!!!"""""####$$$$$%%%%%&&&&&'''''(((((())))))******+++++++,,,,,,,,--------......./////////00000000001111111111222222222223333333333333344444444444445555555555555555666666666666666666666777777777777777777777888888888888888888888888889999999999999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖÖ×××××ØØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááááââââããããäääååååæææçççèèèééééêêêëëëìììíííîîîïïïðððñññòòòóóôôôõõõööö÷÷øøøùùùúúûûûüüüýýþþþÿÿ  !!!!""""#####$$$$$%%%%%&&&&&''''''((((()))))))******+++++++,,,,,,-------.........//////////00000000111111111122222222222223333333333344444444444444445555555555555566666666666666666667777777777777777777777788888888888888888888888888999999999999999999999999999ÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááááââââããããäääååååæææççççèèèéééêêêëëëììììííîîîïïïðððñññòòòóóôôôõõõööö÷÷øøøùùùúúûûûüüüýýþþþÿÿ  !!!!""""#####$$$$$%%%%%&&&&&'''''((((())))))******+++++++,,,,,,,--------.........///////0000000000111111111112222222222233333333333334444444444444555555555555555556666666666666666677777777777777777777788888888888888888888888888999999999999999999999999999999:ÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááááââââããããäääååååæææçççèèèèéééêêêëëëìììíííîîîïïðððñññòòòóóóôôõõõööö÷÷øøøùùùúúûûûüüüýýþþþÿÿ  !!!!""""#####$$$$%%%%%&&&&&''''''(((((())))))******+++++++,,,,,,,-------.......//////////000000000011111111122222222222333333333333334444444444445555555555555555556666666666666666667777777777777777777788888888888888888888888899999999999999999999999999999999::ÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞÞßßßßààààáááââââããããäääååååæææçççèèèèéééêêêëëëìììíííîîîïïïðððññòòòóóóôôôõõööö÷÷÷øøùùùúúúûûüüüýýþþþÿÿ  !!!!!""""####$$$$$%%%%%&&&&&'''''(((((())))))*****+++++++,,,,,,,--------......../////////0000000011111111111222222222222333333333334444444444444445555555555555566666666666666666666677777777777777777777788888888888888888888888899999999999999999999999999999999::ÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááááââââãããääääåååææææçççèèèéééêêêëëëìììíííîîîïïïðððñññòòóóóôôôõõööö÷÷÷øøùùùúúúûûüüüýýþþþÿÿ  !!!!""""####$$$$$%%%%%&&&&&'''''(((((())))))******+++++++,,,,,,,-------........////////0000000000111111111112222222222333333333333444444444444445555555555555555666666666666666667777777777777777777777788888888888888888888888888999999999999999999999999999999::::ÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááááââââãããääääåååæææçççèèèèéééêêêëëëìììíííîîïïïðððñññòòòóóôôôõõõöö÷÷÷øøøùùúúúûûüüüýýþþþÿÿ  !!!!""""#####$$$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,-------........//////////000000000111111111222222222223333333333333344444444444555555555555555555566666666666666667777777777777777777788888888888888888888888888999999999999999999999999999999::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØØÙÙÙÙÙÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞÞßßßààààááááââââãããääääåååæææççççèèèéééêêêëëëìììíííîîîïïðððñññòòòóóóôôõõõöö÷÷÷øøøùùúúúûûüüüýýþþþÿÿ  !!!!!""""####$$$$$%%%%&&&&&''''''((((())))))******+++++++,,,,,,,-------........////////00000000111111111112222222222223333333333344444444444444555555555555555666666666666666666667777777777777777777888888888888888888888889999999999999999999999999999999:::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞÞßßßààààááááâââããããäääååååæææçççèèèéééêêêëëëìììíííîîîïïïðððññòòòóóóôôõõõööö÷÷øøøùùúúúûûüüüýýþþþÿÿ  !!!!!""""####$$$$$%%%%%&&&&&'''''(((((())))))******++++++,,,,,,-------......../////////000000000111111111112222222222333333333333444444444444444555555555555566666666666666666667777777777777777777777888888888888888888888889999999999999999999999999999999:::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÝÞÞÞÞßßßààààááááââââãããääääåååææææçççèèèéééêêêëëëìììíííîîïïïðððññòòòóóóôôõõõööö÷÷øøøùùúúúûûüüüýýþþþÿÿ  !!!!"""""####$$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,--------......../////////000000001111111112222222222233333333333333444444444445555555555555555566666666666666667777777777777777777777888888888888888888888888889999999999999999999999999999999:::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞßßßààààááááâââããããäääåååææææçççèèèéééêêêëëëìììíííîîîïïðððñññòòóóóôôôõõööö÷÷øøøùùúúúûûüüüýýþþþÿÿ  !!!!""""#####$$$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,-------.......////////0000000001111111111122222222222333333333334444444444444455555555555555566666666666666666677777777777777777788888888888888888888888888999999999999999999999999999999::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááááâââãããääääåååæææçççèèèéééêêêëëëìììíííîîîïïðððñññòòòóóôôôõõööö÷÷øøøùùùúúûûûüüýýýþþÿÿ  !!!!""""#####$$$$%%%%&&&&&'''''((((())))))******++++++,,,,,,,-------......../////////000000000111111111122222222223333333333334444444444444445555555555556666666666666666666677777777777777777778888888888888888888888999999999999999999999999999999::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßààààááááâââããããäääååååæææçççèèèéééêêêëëëìììííîîîïïïðððññòòòóóôôôõõõöö÷÷÷øøùùùúúûûûüüýýýþþÿÿ  !!!!""""####$$$$$%%%%%&&&&&'''''((((())))))******++++++,,,,,,,-------........////////000000001111111112222222222233333333333333444444444445555555555555555666666666666666667777777777777777777777888888888888888888888899999999999999999999999999999:::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááááâââãããääääåååæææçççèèèéééêêêëëëìììíííîîïïïðððññòòòóóóôôõõõöö÷÷÷øøùùùúúûûûüüýýýþþÿÿ  !!!!""""####$$$$$%%%%&&&&&'''''((((()))))******+++++++,,,,,,-------......./////////00000000011111111112222222222233333333333444444444444455555555555555556666666666666667777777777777777777777888888888888888888888888899999999999999999999999999999:::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßààààááááââââãããäääåååæææçççèèèéééêêêëëëìììíííîîîïïðððñññòòóóóôôõõõöö÷÷÷øøùùùúúûûûüüýýýþþÿÿ  !!!!""""####$$$$$%%%%%&&&&&'''''((((()))))******++++++,,,,,,--------......./////////000000000111111111222222222233333333333444444444444444555555555555566666666666666666677777777777777777788888888888888888888888888999999999999999999999999999999:::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞÞßßßààààáááââââãããäääååååæææçççèèèéééêêêëëëììíííîîîïïðððñññòòóóóôôôõõööö÷÷øøøùùúúûûûüüýýýþþÿÿ  !!!!!""""####$$$$%%%%%&&&&'''''((((())))))******++++++,,,,,,,-------.......////////000000001111111112222222222223333333333334444444444445555555555555556666666666666666667777777777777777777888888888888888888888999999999999999999999999999999::::::::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ×××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßààààááááââââãããäääåååæææçççèèèèééêêêëëëìììííîîîïïïððñññòòòóóôôôõõööö÷÷øøøùùúúûûûüüýýýþþÿÿ  !!!!""""####$$$$%%%%%&&&&&'''''((((()))))******++++++,,,,,,-------......./////////0000000011111111112222222222223333333333444444444444555555555555555556666666666666677777777777777777777777888888888888888888888999999999999999999999999999:::::::::::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ××××××ØØØØÙÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞÞßßßààààáááââââãããääääåååæææçççèèèéééêêêëëìììíííîîîïïðððññòòòóóôôôõõööö÷÷øøøùùúúúûûüüýýýþþÿÿ  !!!!""""#####$$$$%%%%&&&&&''''(((((()))))******+++++++,,,,,,-------......./////////0000000011111111122222222223333333333344444444444444555555555555556666666666666666777777777777777777778888888888888888888888888999999999999999999999999999:::::::::::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ×××××ØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÛÜÜÜÝÝÝÝÞÞÞÞßßßßààààáááâââããããäääåååæææçççèèèéééêêêëëìììíííîîîïïðððñññòòóóóôôõõõöö÷÷øøøùùúúúûûüüýýýþþÿÿ  !!!!"""####$$$$$%%%%%&&&&&''''((((()))))******++++++,,,,,,-------.......////////0000000011111111112222222222233333333333344444444444455555555555556666666666666666666777777777777777778888888888888888888888888999999999999999999999999999999:::::::::::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ×××××ØØØØØÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßààààáááââââãããäääåååæææççççèèèééêêêëëëìììííîîîïïïððñññòòóóóôôõõõöö÷÷øøøùùúúúûûüüýýýþþÿÿ  !!!!""""####$$$$%%%%%&&&&'''''((((())))))******++++++,,,,,,------........////////000000001111111111222222222223333333333444444444445555555555555555666666666666666677777777777777777777888888888888888888888999999999999999999999999999999:::::::::::::::::::::::::::::::::::ÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ××××ØØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÞÞÞÞßßßßààààáááâââããããäääåååæææçççèèèéééêêêëëìììíííîîïïïððñññòòóóóôôõõõöö÷÷÷øøùùúúúûûüüýýýþþÿÿ  !!!!"""####$$$$%%%%%&&&&&'''''(((()))))******++++++,,,,,,,-------.......////////00000000111111111222222222233333333333444444444444455555555555555566666666666666777777777777777777777778888888888888888888899999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;ÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ××××××ØØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÜÝÝÝÝÞÞÞßßßààààááááâââãããäääåååææææççèèèéééêêêëëëììíííîîîïïðððññòòòóóôôôõõöö÷÷÷øøùùùúúûûüüüýýþþÿÿ  !!!""""#####$$$$%%%%&&&&'''''(((((()))))******+++++,,,,,,-------.......////////0000000011111111122222222222333333333333444444444444555555555555566666666666666666777777777777777777788888888888888888888888899999999999999999999999999:::::::::::::::::::::::::::::::::::;;;;;ÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖ×××××ØØØØØÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßààààáááâââããããäääåååæææçççèèèéééêêëëëììíííîîîïïðððññòòòóóôôôõõööö÷÷øøùùùúúûûüüüýýþþÿÿ  !!!!""""####$$$$%%%%%&&&&'''''((((())))******+++++++,,,,,,-------.......////////0000000011111111122222222222333333333344444444444555555555555555666666666666666666777777777777777788888888888888888888888889999999999999999999999999999:::::::::::::::::::::::::::::::::::;;;;;ÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ×××××ØØØØØÙÙÙÙÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞßßßßààààáááâââãããäääåååæææçççèèèéééêêêëëìììííîîîïïïððñññòòóóóôôõõööö÷÷øøùùùúúûûüüüýýþþÿÿ  !!!""""####$$$$$%%%%&&&&'''''((((()))))******+++++,,,,,,-------.......////////0000000011111111122222222223333333333444444444444455555555555555556666666666666677777777777777777777888888888888888888888999999999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ×××××ØØØØÙÙÙÙÙÚÚÚÚÚÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßààààáááââââãããäääåååæææçççèèéééêêêëëëììíííîîïïïððñññòòóóóôôõõõöö÷÷øøøùùúúûûüüüýýþþÿÿ  !!!!""""####$$$%%%%%&&&&&''''((((()))))******++++++,,,,,,------.......////////000000001111111112222222222333333333334444444444444555555555555566666666666666677777777777777777777788888888888888888888999999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÖÖÖÖÖ××××××ØØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞßßßßààààáááâââãããäääåååæææçççèèèéééêêëëëìììííîîîïïðððññòòóóóôôõõõöö÷÷øøøùùúúûûüüüýýþþÿÿ  !!!""""####$$$$%%%%&&&&'''''(((((()))))*****+++++,,,,,,-------........////////000000001111111112222222222333333333344444444444555555555555566666666666666666677777777777777777888888888888888888888888999999999999999999999999::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ××××ØØØØØÙÙÙÙÚÚÚÚÚÛÛÛÛÜÜÜÝÝÝÝÝÞÞÞßßßààààááááâââããääääåååæææççèèèéééêêêëëìììííîîîïïðððññòòòóóôôõõõöö÷÷øøøùùúúûûüüüýýþþÿÿ  !!!"""""###$$$$%%%%%&&&&'''''(((()))))******++++++,,,,,,------......////////000000001111111112222222222333333333344444444444445555555555555566666666666666667777777777777777788888888888888888888888899999999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÖÖÖÖÖ×××××ØØØØØÙÙÙÙÚÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÞÞÞßßßßààààáááâââãããäääåååæææçççèèèééêêêëëëììíííîîïïïððñññòòóóôôôõõöö÷÷øøøùùúúûûûüüýýþþÿÿ  !!!"""####$$$$$%%%%&&&&'''''((((()))))*****+++++,,,,,,,-------......////////000000011111111122222222223333333333344444444444445555555555555666666666666667777777777777777777788888888888888888888999999999999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××ØØØØÙÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞßßßààààáááâââããããäääåååææçççèèèéééêêëëëìììííîîïïïððñññòòóóôôôõõöö÷÷÷øøùùúúûûûüüýýþþÿÿ  !!!""""####$$$$%%%%&&&&&''''(((())))))******+++++,,,,,------........////////000000001111111112222222222333333333344444444444555555555555666666666666666667777777777777777777788888888888888888889999999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÖÖÖÖÖ×××××ØØØØØÙÙÙÙÚÚÚÛÛÛÛÛÜÜÜÜÝÝÝÞÞÞÞßßßààààáááâââãããäääåååæææççèèèéééêêêëëìììííîîîïïðððññòòóóóôôõõööö÷÷øøùùúúûûûüüýýþþÿÿ  !!!!"""####$$$$$%%%&&&&'''''((((()))))*****++++++,,,,,,------.......///////000000011111111122222222223333333333444444444444555555555555556666666666666666677777777777777778888888888888888888888899999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ×××××ØØØØÙÙÙÙÚÚÚÚÚÛÛÛÜÜÜÜÝÝÝÝÞÞÞßßßßàààáááâââããããäääååæææçççèèèééêêêëëëììíííîîïïðððññòòòóóôôõõööö÷÷øøùùúúúûûüüýýþþÿÿ  !!!""""####$$$%%%%%&&&&''''((((()))))*****++++++,,,,,-------.......////////00000001111111112222222223333333333344444444444455555555555555666666666666667777777777777777788888888888888888888888899999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÖÖÖÖÖ××××ØØØØØÙÙÙÙÚÚÚÚÛÛÛÛÛÜÜÜÝÝÝÞÞÞÞßßßàààááááâââãããäääåååæææçççèèéééêêëëëììíííîîïïïððññòòòóóôôõõõöö÷÷øøùùúúúûûüüýýþþÿÿ  !!!!"""###$$$$$%%%%&&&&'''''(((()))))*****++++++,,,,,,------......////////0000000011111111122222222223333333333444444444445555555555555666666666666667777777777777777777788888888888888888888999999999999999999999999999:::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÓÓÔÔÔÔÔÕÕÕÕÖÖÖÖÖÖ××××ØØØØÙÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÞÞÞßßßßàààáááââââãããääååååææçççèèèééêêêëëìììííîîîïïððñññòòóóôôõõõöö÷÷øøùùùúúûûüüýýþþÿÿ  !!!"""####$$$$%%%%&&&&&''''(((())))))****+++++,,,,,,,-------......///////000000011111111222222222233333333334444444444455555555555555666666666666666677777777777777777788888888888888888899999999999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖ×××××ØØØØØÙÙÙÙÚÚÚÛÛÛÛÛÜÜÜÝÝÝÝÞÞÞÞßßààààáááâââãããäääåååæææççèèèéééêêëëìììíííîîïïðððññòòóóôôôõõöö÷÷øøøùùúúûûüüýýþþÿÿ  !!!!""""###$$$$$%%%&&&&'''''((((())))******+++++,,,,,,-----.......////////000000001111111122222222233333333334444444444445555555555555566666666666666677777777777777788888888888888888888889999999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖÖ×××××ØØØØÙÙÙÙÚÚÚÚÚÛÛÛÜÜÜÜÝÝÝÞÞÞÞßßßàààáááââââããääääååæææçççèèéééêêêëëììíííîîïïðððññòòóóóôôõõöö÷÷øøøùùúúûûüüýýþþÿÿ  !!!!"""####$$$%%%%%&&&&''''((((()))))****++++++,,,,,,------......///////00000000111111111222222222233333333334444444444455555555555556666666666666777777777777777777888888888888888888888889999999999999999999999::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖ××××ØØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÝÝÝÝÞÞÞÞßßààààáááâââãããäääåååææçççèèèééêêêëëìììííîîïïïððññòòòóóôôõõöö÷÷øøøùùúúûûüüýýþþÿÿ  !!!!""""###$$$$%%%%&&&&'''''(((())))******+++++,,,,,-------.......///////00000001111111222222222233333333334444444444455555555555566666666666666667777777777777777777888888888888888888899999999999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖÖ×××××ØØØÙÙÙÙÙÚÚÚÚÛÛÛÜÜÜÜÜÝÝÝÞÞÞßßßàààááááâââããäääåååæææççèèèéééêêëëìììííîîîïïððñññòòóóôôõõööö÷÷øøùùúúûûüüýýþþÿÿ  !!!"""#####$$$%%%%&&&&''''((((()))))****++++++,,,,,,------......///////0000000001111111222222222333333333344444444444555555555555566666666666666667777777777777777788888888888888888999999999999999999999999999999::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÖÖÖÖÖ××××ØØØØØÙÙÙÙÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞßßßàààáááâââãããäääåååææçççèèéééêêêëëììíííîîïïðððññòòóóôôõõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!""""####$$$$%%%&&&&'''''(((()))))*****+++++,,,,,-------......///////000000011111111122222222223333333333444444444445555555555555666666666666667777777777777778888888888888888888889999999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÔÔÕÕÕÕÖÖÖÖ×××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÞÞÞÞßßààààáááââããããääåååæææççèèèééêêêëëìììííîîïïðððññòòóóôôõõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!"""####$$$$%%%%&&&&''''((((())))****+++++++,,,,,-----.......////////00000011111111222222222333333333344444444444555555555555666666666666667777777777777777778888888888888888888888999999999999999999999::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÕÕÕÕÕÖÖÖÖÖ×××ØØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÝÝÝÝÞÞÞßßßàààáááâââãããäääååæææçççèèéééêêëëëììííîîîïïððññòòóóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!""""###$$$$%%%%&&&&'''''((()))))*****+++++,,,,,,------......//////0000000001111111122222222333333333344444444445555555555555666666666666666777777777777777777888888888888888888899999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<ÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÖÖÖÖ×××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÜÜÜÜÝÝÝÞÞÞÞßßààààáááâââããäääåååææçççèèèééêêêëëììíííîîïïððññòòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!"""####$$$%%%%&&&&''''((((())))*****++++++,,,,-------.......//////0000001111111112222222222333333333344444444445555555555555666666666666666777777777777777888888888888888889999999999999999999999999999:::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÕÕÕÕÖÖÖÖÖ××××ØØØØØÙÙÙÚÚÚÚÛÛÛÛÜÜÜÝÝÝÝÞÞÞßßßàààááááââãããäääååæææççèèèééêêêëëììíííîîïïðððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!""""###$$$$%%%%&&&'''''((())))))****+++++,,,,,,-----......////////000000011111112222222223333333333444444444445555555555556666666666666677777777777777788888888888888888888899999999999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÔÔÔÔÔÕÕÕÕÕÕÖÖÖ××××××ØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÝÝÝÞÞÞÞßßßàààáááâââããäääåååææçççèèéééêêëëëììííîîïïðððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!"""###$$$$%%%%&&&&'''((((())))******+++++,,,,-------......//////0000000011111111222222223333333334444444444555555555555666666666666667777777777777777778888888888888888888889999999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÜÜÜÜÝÝÝÞÞÞßßßàààáááâââãããääåååæææççèèèééêêêëëììííîîîïïððññòòóóôôõõöö÷÷øøøùùúúûûüüýýþþÿ  !!!"""####$$$%%%%&&&&''''(((()))))****++++++,,,,,-----.......///////000000111111112222222222333333333444444444455555555555566666666666666777777777777777777888888888888888888999999999999999999999:::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÕÕÖÖÖ×××××ØØØØÙÙÙÙÚÚÚÛÛÛÛÜÜÜÝÝÝÝÞÞÞßßßàààáááâââããäääåååææççèèèééêêêëëìììííîîïïððññòòóóôôôõõöö÷÷øøùùúúûûüüýýþþÿ  !!!""""###$$$$%%%&&&&''''((((()))******++++,,,,,,------.....///////00000000111111122222222333333333344444444444555555555555566666666666667777777777777777888888888888888889999999999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖÖ×××ØØØØØÙÙÙÚÚÚÚÛÛÛÛÜÜÜÝÝÝÞÞÞÞßßààààááâââãããäääååæææççèèèééêêëëëììííîîïïððññòòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿ  !!!!"""###$$$$%%%%&&&'''''((()))))****++++++,,,,,-----.......//////000000011111111122222223333333334444444444555555555555666666666666667777777777777778888888888888888888899999999999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÕÖÖÖÖ×××××ØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááâââãããääåååææçççèèééêêêëëììííîîïïðððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿ  !!!"""###$$$$%%%%&&&&'''((((())))*****++++,,,,,,-----......////////00000011111112222222222333333333444444444455555555555666666666666677777777777777777888888888888888888889999999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÒÒÒÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÜÜÜÜÝÝÞÞÞÞßßààààááâââãããäääååæææççèèééêêêëëììííîîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿ  !!!!""####$$$%%%%&&&&''''(((())))****++++++,,,,-------.....//////0000000001111112222222233333333334444444444455555555555566666666666667777777777777777788888888888888888899999999999999999999:::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÕÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÛÛÛÛÜÜÜÝÝÝÝÞÞßßßàààáááâââããäääååæææççèèèééêêëëìììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿ  !!""""###$$$$%%%&&&&''''(((())))*****++++,,,,,,-----.......//////000000111111111222222233333333344444444445555555555556666666666666677777777777777788888888888888888999999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÕÖÖÖÖÖ××××ØØØØÙÙÙÚÚÚÚÛÛÛÜÜÜÜÝÝÝÞÞÞßßßàààááâââãããääåååææçççèèééêêëëëììííîîïïððññòòòóôôôõöö÷÷øøùùúúûûüüýýþþÿ  !!!"""####$$$%%%%&&&''''(((()))))****+++++,,,,,------....////////0000001111111222222222233333333344444444455555555555666666666666777777777777777788888888888888888889999999999999999999999999:::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÕÖÖÖÖÖ××××ØØØÙÙÙÙÚÚÚÚÛÛÛÜÜÜÝÝÝÝÞÞßßßàààáááâââããäääååæææççèèéééêêëëììííîîîïïððññòòóóôôõõö÷÷øøùùúúûûüüýýþþÿ  !!""""###$$$%%%%&&&&'''(((()))))****+++++,,,,,-----......//////0000000011111112222222333333333344444444444555555555556666666666667777777777777777888888888888888888899999999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÕÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÚÛÛÜÜÜÜÝÝÝÞÞÞßßßàààáááââãããääåååææççèèèééêêëëìììííîîïïððññòòóóôôõõöö÷÷øùùúúûûüüýýþþÿ  !!!"""####$$%%%%&&&&''''(((())))****+++++,,,,,-----......///////00000111111111222222233333333444444444445555555555556666666666666677777777777777778888888888888888899999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÕÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÛÛÛÛÜÜÝÝÝÝÞÞÞßßààààááâââããäääååææçççèèéééêêëëììííîîïïððññòòóóôôõõöö÷÷øøùúúûûüüýýþþÿ  !!!""""##$$$$%%%&&&&''''(((())))****+++++,,,,,------.....//////0000000111111222222222233333333444444444555555555556666666666666777777777777777788888888888888888999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÕÖÖÖÖ××××ØØØØÙÙÙÚÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààáááââãããääååæææççèèèééêêëëììííîîïïððññòòóóôôõõöö÷÷øøùùúûûüüýýþþÿ  !!!"""###$$$%%%%&&&''''(((())))*****++++,,,,,,----......./////00000001111111222222233333333334444444444555555555566666666666677777777777777788888888888888888899999999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÒÒÒÒÒÒÒÓÓÓÔÔÔÔÔÔÕÕÕÖÖÖÖÖ××××ØØØØÙÙÙÚÚÚÛÛÛÛÜÜÜÝÝÝÞÞÞßßààààááâââããääåååææçççèèééêêëëìììííîîïïððññòòóôôõõöö÷÷øøùùúûûüüýýþþÿ  !!!"""###$$$$%%%&&&''''(((()))))***++++++,,,-------....////////00000111111112222222233333334444444444455555555555566666666666677777777777777788888888888888888899999999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÕÕÕÕÕÖÖÖ×××××ØØØÙÙÙÙÚÚÚÚÛÛÜÜÜÜÝÝÝÞÞßßßàààáááââããäääåååææççèèéééêêëëììííîîïïððññòòóóôôõöö÷÷øøùùúúûüüýýþþÿ  !!"""####$$%%%%&&&&'''((((()))*****++++,,,,,-----....../////0000000011111222222222233333333444444445555555555556666666666666677777777777777788888888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÖÖÖÖÖ×××ØØØØÙÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßßàààááââãããäääååææççèèèééêêëëììííîîïïððññòòóóôôõõö÷÷øøùùúúûüüýýþþÿ  !!!"""###$$$%%%&&&&''''((()))))***+++++,,,,,-----......//////00000011111112222222333333333344444444455555555556666666666667777777777777778888888888888888899999999999999999999:::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÕÕÕÕÕÖÖÖ××××ØØØØÙÙÙÚÚÚÚÛÛÛÜÜÜÝÝÝÞÞßßßàààááâââãããääåååææççèèééêêëëëìíííîïïððññòòóóôôõõö÷÷øøùùúúûüüýýþþÿ  !!"""###$$$%%%%&&&''''(((()))*****++++,,,,,-----.....///////00000111111112222222233333334444444444455555555555666666666667777777777777788888888888888889999999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÔÕÕÕÕÖÖÖÖÖ×××ØØØØÙÙÙÚÚÚÛÛÛÛÜÜÜÝÝÞÞÞßßßààáááâââããäääååææççèèéééêêëëììííîîïïððññòóóôôõõöö÷øøùùúúûüüýýþþÿ  !!!""###$$$$%%%&&&''''((()))))****++++,,,,,-----....../////0000000111111222222222333333334444444455555555555566666666666667777777777777788888888888888889999999999999999999999::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======ÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÒÒÒÒÒÒÓÓÓÓÔÔÔÔÔÕÕÕÕÖÖÖÖ××××ØØØÙÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßàààáááââãããääååæææççèèééêêëëììííîîïïððññòòóôôõõöö÷÷øùùúúûûüýýþþÿ  !!!"""###$$$%%%&&&&'''(((())))****+++++,,,-------....///////000001111111122222233333333334444444455555555556666666666666777777777777777788888888888888888999999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<============ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÖÖÖÖÖ××ØØØØÙÙÙÙÚÚÛÛÛÛÜÜÜÝÝÞÞÞßßßàààááâââããääåååææççèèééêêëëëìííîîïïððññòòóôôõõöö÷÷øùùúúûûüýýþþÿ  !!!""###$$$$%%&&&&''''(()))))****++++,,,,,-----.....//////000000111111122222222333333344444444444555555555666666666667777777777777778888888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÒÓÓÓÓÔÔÔÔÔÕÕÕÕÖÖÖÖ××××ØØØÙÙÙÚÚÚÚÛÛÛÜÜÝÝÝÞÞÞßßàààáááââãããääååææçççèèééêêëëììííîïïððññòòóóôôõöö÷÷øøùúúûûüýýþþÿ  !!!"""##$$$%%%%&&&'''(((())))****+++++,,,,-----.....//////0000000111112222222223333333444444444555555555555666666666667777777777777888888888888888999999999999999999999:::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÕÕÕÕÕÖÖÖÖ×××ØØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßàààááâââããääåååææççèèééêêëëììííîîïïðññòòóóôôõöö÷÷øøùúúûûüýýþþÿ  !!!"""###$$$%%%&&&''''((())))*****+++,,,,,-----.....///////0000111111112222223333333333444444445555555555666666666666677777777777777888888888888888999999999999999999999:::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÓÓÓÓÔÔÔÔÔÕÕÕÖÖÖÖ××××ØØØÙÙÙÙÚÚÚÛÛÛÜÜÝÝÝÝÞÞÞßßààáááâââãäääååææççèèèééêëëììííîîïïððññòóóôôõõö÷÷øøùúúûûüýýþþÿ  !!!""""##$$$%%%&&&&'''(((())))***+++++,,,,----.......////0000000111111222222223333333444444444445555555566666666666677777777777777788888888888888888999999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÒÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÕÕÖÖÖ×××ØØØØÙÙÙÙÚÚÛÛÛÜÜÜÜÝÝÞÞÞßßàààááâââããäääåæææççèèééêêëëììííîîïððññòòóôôõõö÷÷øøùùúûûüüýþþÿ  !!!""####$$$%%&&&&''''((()))*****++++,,,,-----....///////000000111111222222223333333444444444555555555556666666666777777777777788888888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÓÓÓÓÓÔÔÔÔÔÕÕÕÖÖÖÖ××××ØØØÙÙÙÚÚÚÛÛÛÛÜÜÝÝÝÞÞÞßßààáááââãããääååææççèèééêêëëììííîîïððññòòóóôõõöö÷øøùùúûûüüýþþÿ  !!!"""##$$$$%%%&&&'''(((())))***+++++,,,,,---......//////00000111111112222233333333334444444555555555556666666666667777777777778888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÏÏÐÐÐÐÐÑÑÑÑÑÒÒÒÒÒÒÓÓÓÔÔÔÔÕÕÕÕÕÖÖÖ×××ØØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞßßàààáááââããääåååæççèèèééêëëììííîîïïððñòòóóôõõöö÷øøùùúûûüüýþþÿ  !!"""###$$$%%%&&&''''((()))*****++++,,,------...../////000000011111222222223333333444444444455555555666666666666677777777777777888888888888889999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÕÕÕÕÖÖÖÖ×××רØÙÙÙÚÚÚÚÛÛÛÜÜÝÝÝÞÞÞßßàààááââãããääååææççèèééêêëëììíîîïïððññòóóôôõöö÷÷øùùúûûüüýþþÿ  !!!"""##$$$%%%%&&&''(((())))****+++,,,,,-----....///////000001111112222222233333334444444445555555555666666666777777777777777788888888888888889999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÕÕÖÖ××××ØØØÙÙÙÙÚÚÛÛÛÜÜÜÝÝÝÞÞÞßßààáááââããääååæææçèèééêêëëììííîîïððññòóóôôõöö÷÷øùùúúûüüýþþÿ  !!!"""###$$$%%&&&&'''(((())*****++++,,,,----....../////000001111111122222333333333344444455555555555566666666667777777777778888888888888888899999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÒÒÒÒÒÓÓÓÓÓÔÔÔÕÕÕÕÖÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÞÞÞßßàààááââããäääååææççèèééêêëììííîîïððññòòóôôõõö÷÷øùùúúûüüýþþÿ  !!!""###$$$%%%&&&'''((())))****+++,,,,,-----....//////000000111112222222233333334444444445555555566666666666666777777777778888888888888899999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÒÒÒÓÓÓÓÓÔÔÔÔÕÕÕÕÖÖÖ×××ØØØØÙÙÙÚÚÚÛÛÜÜÜÝÝÝÞÞÞßßààáááââããääååææççèèééêêëëììíîîïïððñòòóóôõõö÷÷øøùúúûüüýþþÿ  !!!"""###$$%%%&&&''''((()))****++++,,,,,---.....//////000001111111222222233333344444444445555555566666666666677777777777777888888888888899999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÑÑÑÑÑÒÒÒÒÒÓÓÓÓÔÔÔÕÕÕÕÕÖÖÖ×××רØÙÙÙÚÚÚÚÛÛÛÜÜÝÝÝÞÞßßàààááââããäääååææççèééêêëëììííîïïððñòòóóôõõöö÷øøùúúûüüýþþÿ  !!"""##$$$%%%%&&&''(((()))*****+++,,,,-----.....////00000011111112222233333333344444445555555555566666666677777777777777888888888888888899999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===============================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÓÓÔÔÔÔÕÕÕÕÖÖ××××ØØØØÙÙÙÚÚÛÛÛÜÜÜÝÝÞÞÞßßààááâââããääååææççèèééêêëììííîîïððññòóóôõõöö÷øøùúúûüüýþþÿ  !!!""###$$$%%&&&''''(((())****++++,,,,,----....//////00000111112222222233333334444444455555555556666666666677777777778888888888888888889999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÓÓÓÓÔÔÔÕÕÕÕÖÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÞÞßßàààááâââããäååææççèèééêêëëììíîîïððññòóóôôõöö÷øøùúúûüüýþþÿ  !!""###$$$%%%&&&'''((())))****+++,,,,----....../////00001111111222222233333344444444445555555666666666666677777777777888888888888899999999999999999999:::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÑÑÑÑÒÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÖÖÖ××××ØØØÙÙÙÚÚÚÛÛÜÜÜÝÝÞÞÞßßàààááââããääååææççèèéêêëëììíîîïïððñòòóôôõöö÷øøùúúûüüýþþÿ  !!!""###$$%%%&&&'''(((()))***++++,,,,-----..../////000000111111222223333333334444444555555555566666666677777777777777788888888888899999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÒÒÒÒÓÓÓÔÔÔÔÕÕÕÕÖÖÖÖ××רØÙÙÙÚÚÚÛÛÛÜÜÜÝÝÝÞÞßßààááââãããääååæççèèééêêëëìííîïïððñòòóôôõöö÷÷øùùúûûüýýþÿ  !!"""###$$$%%%&&&''((())))****++++,,,----.....//////00001111122222222333333344444445555555555566666666777777777777778888888888888889999999999999999:::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÑÑÑÑÑÒÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÖÖÖ×××ØØØØÙÙÙÚÚÛÛÛÜÜÝÝÝÞÞßßàààááââããääååææççèèéêêëëìííîîïððñòòóóôõõö÷÷øùùúûûüýýþÿ  !!"""##$$$%%&&&''''((()))***++++,,,,-----.....////0000011111122222223333334444444445555555666666666666777777777788888888888888888999999999999999999:::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÒÒÒÓÓÓÓÔÔÔÔÔÕÕÕÖÖÖÖ××ØØØÙÙÙÚÚÚÛÛÜÜÜÝÝÝÞÞßßààááââããääååææççèèéêêëëììíîîïððññòóóôõõö÷÷øùùúûûüýýþÿ  !!"""###$$%%%&&&'''(())))***+++++,,,,---....//////000000111112222233333333344444445555555566666666666777777777777888888888888899999999999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÐÐÐÐÐÑÑÑÑÑÒÒÒÒÓÓÓÓÔÔÔÕÕÕÖÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÜÜÝÝÝÞÞßßàààááââããääååæççèèééêêëììííîïïðññòòóôôõöö÷øùùúûûüýýþÿ  !!"""##$$%%%&&&'''((())))***+++,,,,----...../////000011111122222223333333444444555555555556666666677777777777777788888888888999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================================================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÓÓÔÔÔÔÕÕÕÖÖÖ××רØÙÙÙÚÚÚÛÛÛÜÜÝÝÝÞÞßßàààáââããääååææççèèéêêëëìííîîïððñòòóôôõöö÷øøùúûûüýýþÿ  !!"""##$$$%%%&&&''((()))***++++,,,,,----....////0000011111112222223333344444444455555555666666666677777777777788888888888888899999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================ÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÔÔÔÔÕÕÕÖÖÖÖ×××ØØØÙÙÙÚÚÛÛÛÜÜÝÝÞÞßßàààááââããääååææçèèééêëëììíîîïððñòòóôôõöö÷øøùúûûüýýþÿ  !!""##$$$%%&&&'''((())))***++++,,,----....//////0000011111222223333333344444444555555566666666666677777777788888888888888888999999999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================================>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÖÖÖ××ØØØÙÙÙÚÚÛÛÛÜÜÜÝÝÞÞßßàààááââããäååææçèèééêêëììííîïïðññòóóôõöö÷øøùúúûüýýþÿ  !!""###$$$%%&&&'''(()))***++++,,,,-----....////000001111122222223333333444444555555555566666666677777777777788888888888899999999999999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================================>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËËËÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÑÑÑÑÒÒÒÓÓÓÓÔÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÚÛÛÜÜÝÝÝÞÞßßààááââããääåææççèèééêëëìííîïïðññòóóôõõö÷øøùúúûüýýþÿ  !!"""##$$%%%&&&'''((()))***++++,,,,---..../////000001111112222223333344444444455555555666666667777777777777778888888888999999999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=========================================================>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÐÑÑÑÑÑÒÒÒÒÓÓÓÓÔÔÔÕÕÕÖÖÖ××רØÙÙÙÚÚÚÛÛÜÜÜÝÝÞÞßßàààááââãääååææççèééêëëìííîîïððñòòóôõõö÷÷øùúúûüýýþÿ  !!""###$$%%%&&&''((()))***+++,,,,----...../////00001111122222333333334444444455555566666666666777777777778888888888888899999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÑÑÑÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÙÚÚÛÛÛÜÜÝÝÞÞßßààááââããääåææççèééêêëììíîîïððñòòóôôõö÷÷øùúúûüýýþÿ  !!""##$$$%%&&&'''((()))***++++,,,,----...////000001111112222223333333444445555555555666666666677777777788888888888888889999999999999:::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================================>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÑÑÑÑÑÒÒÒÓÓÓÔÔÔÔÕÕÕÖÖÖ××ØØØÙÙÙÚÚÛÛÛÜÜÝÝÞÞßßàààáââããääååææçèèééêëëìííîïïðñòòóôôõöö÷øùùúûüüýþÿ  !!""##$$$%%&&&'''(()))***++++,,,---...../////000001111112222233333444444445555555555666666777777777777788888888888899999999999999999:::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================================================>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÑÑÒÒÒÒÓÓÓÔÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÚÚÚÛÛÜÜÝÝÞÞßßààááââããääååæççèèéêêëììíîïïðññòóóôõöö÷øùùúûüüýþÿ  !!""##$$%%%&&'''((()))***++++,,,----.....////000011111222223333333344444444555555666666666777777777777788888888899999999999999999999::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===========================================================================>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ×××ØØØÙÙÚÚÚÛÛÜÜÝÝÞÞßßààááââããääååæççèèéêêëììíîîïððñòóóôõöö÷øùùúûüüýþÿ  !!""##$$%%%&&'''((()))***+++,,,----....////000001111122222233333334444455555555666666666667777777778888888888888999999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÏÐÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÔÔÔÔÕÕÕÖÖÖ××ØØØÙÙÙÚÚÛÛÜÜÝÝÞÞÞßààááââããääåææçèèééêëëìííîïððñòòóôõõö÷øøùúûüüýþÿ  !!!""##$$%%&&&'''(()))***++++,,,----..../////000001111122222333334444444555555555666666666777777777888888888888888999999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÒÒÒÒÓÓÓÔÔÔÔÕÕÕÖÖÖ××ØØØÙÙÙÚÚÛÛÜÜÜÝÝÞÞßßààááââãääååææçèèéêêëììíîïïðñòòóôõõö÷øøùúûüüýþÿ  !!""###$$%%&&&'''(()))***++++,,,----...////00001111122222333333344444444555555566666667777777777777888888888889999999999999999::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÐÐÐÐÐÑÑÑÑÒÒÒÓÓÓÓÔÔÔÕÕÕÖÖÖ××ØØØÙÙÚÚÛÛÜÜÜÝÝÞÞßààááââããäååææçèèéêêëììíîïïðññòóôôõö÷øøùúûüüýþÿ  !!""###$$%%&&'''(()))***+++,,,,---..../////000011111122222333333344444455555566666666667777777777778888888899999999999999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊËËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÐÑÑÑÑÒÒÒÓÓÓÓÔÔÔÕÕÕÖÖÖ××ØØØÙÙÚÚÛÛÛÜÜÝÝÞÞßßààááââãääååæççèééêëëìíîîïððñòóôôõö÷÷øùúûûüýþÿ  !!""##$$$%%&&'''(()))***+++,,,,---..../////00001111122222333334444445555555556666666666777777778888888888889999999999999999::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<===============================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÒÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ××רØÙÙÚÚÚÛÛÜÜÝÝÞÞßààááââããäååææçèèéêêëìííîïððñòóóôõöö÷øùúûûüýþÿ  !!""##$$%%%&&''((()))***+++,,,----...////000011111222222333333444444455555555666666677777777778888888888888899999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÐÑÑÑÒÒÒÒÓÓÓÔÔÔÕÕÕÖÖ××רØÙÙÚÚÚÛÛÜÜÜÝÝÞÞßààááââãääåææçèèééêëììíîîïðñòòóôõöö÷øùúûûüýþÿ  !!""###$$%%%&&''((())***+++,,,----.../////00001111122222233333344444455555566666666777777777777888888888889999999999999:::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÓÓÓÓÔÔÔÕÕÕÖÖÖ××ØØØÙÙÚÚÛÛÜÜÝÝÞÞßßààáââããäååæççèééêëììíîîïðññòóôõõö÷øùúúûüýþÿ  !!""##$$%%&&'''(()))***+++,,,,---....////000011111222223333344444555555556666666666777777777788888888899999999999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖ××ØØØÙÙÚÚÛÛÛÜÜÝÝÞÞßààááâããääåææçèèéêêëìííîïðññòóôõõö÷øùúúûüýþÿ  !!""##$$$%%&&'''(())***+++,,,---....////0000111122222233333344444455555555666666667777777788888888888899999999999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ××רØÙÙÚÚÛÛÜÜÝÝÞÞßßààááâããäååæççèééêëììíîïððñòóôôõö÷øùùúûüýþÿ  !!""##$$%%&&''((()))***+++,,,---....////00001111222222333333444444555555666666677777777778888888888888999999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÓÓÓÔÔÔÕÕÖÖÖ×רØÙÙÚÚÚÛÛÜÜÝÝÞÞßààááââãääåææçèééêëëìíîîïðñòóóôõö÷øøùúûüýþÿ  !!""##$$%%%&&''(()))**+++,,,---....////000011111222223333344444555555566666666677777777777888888888899999999999:::::::::::::::::::::::;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÐÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖÖ××ØØØÙÙÚÚÛÛÜÝÝÞÞßßààááâãääååæççèéêêëìíîîïðññòóôõö÷øøùúûüýþÿ  !!""#$$%%&&'''(()))***+++,,,---...////0001111122222333334444444555555566666666677777777788888888999999999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÔÔÔÕÕÖÖÖ×רØÙÙÚÚÛÛÛÜÜÝÞÞßààááââãääåæççèééêëììíîïðññòóôõöö÷øùúûüýþÿ  !!"##$$$%%&&''(()))**+++,,,----....////0001111122222333334444444555555666666677777777888888888889999999999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÒÒÒÓÓÔÔÔÕÕÖÖÖ××רØÙÙÚÚÛÛÜÜÝÝÞÞßààáââããäååæçèèéêëëìíîïððñòóôõõö÷øùúûüýþÿ  !!""##$$%%&&''((()))**+++,,---...////0000111112222233333444445555555666666677777777778888888888889999999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÏÏÏÐÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖ×רØÙÙÚÚÚÛÛÜÜÝÞÞßßààáââãäååææçèéêêëìííîïðñòóôõõö÷øùúûüýþÿ  !!"##$$%%%&&''(())***+++,,,---...////00011112222233333444444555555566666666777777777788888888889999999999:::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÓÓÓÔÔÕÕÖÖÖ××ØØØÙÙÚÚÛÜÜÝÝÞÞßààáââããäåææçèèéêëìííîïðñòóóôõö÷øùúûüýþÿ  !!""##$%%&&'''(()))**++,,,---....////00001111222223333344444455555556666666777777778888888889999999999999::::::::::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÎÏÏÏÐÐÐÐÑÑÒÒÒÓÓÓÔÔÔÕÕÕÖÖ×רØÙÙÚÚÛÛÜÜÝÝÞßßààáââãääåæçèèéêêëìíîïððñòóôõö÷øùúûüýþÿ  !""##$$%%&&''(())***+++,,,---..////0001111122222333334444455555556666666777777788888888888999999999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËÌÌÌÌÌÍÍÍÍÎÎÎÎÏÏÏÐÐÐÐÑÑÑÒÒÒÒÓÓÓÔÔÕÕÖÖ××רØÙÙÚÚÛÜÜÜÝÝÞßààááâããäåææçèéêêëìíîïððñòóôõö÷øùúûüýþÿ  !""###$%%&&''((())**++,,,----...////00011112222333334444455555566666666777777777888888888889999999999999:::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËÌÌÌÌÌÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÒÒÒÓÓÔÔÕÕÕÖÖÖ×רØÙÚÚÚÛÛÜÝÝÞÞßààáâããääåæçèèéêëìííîïðñòóôõö÷øùúûüýþÿ  !!""#$$%%%&''(()))***++,,---...////000011111222233333444445555556666666677777777788888888889999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<=================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÏÏÏÐÐÐÑÑÑÒÒÒÓÓÓÔÔÔÕÕÖÖ××ØØØÙÙÚÛÛÜÜÝÝÞßààááâãääåææçèéêêëìíîïðñòóôõö÷øùúûüýþÿ  !""##$$%&&'''(())**+++,,,---...///000111122222333334444455555556666666777777778888888899999999999::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËÌÌÌÌÍÍÍÍÎÎÎÏÏÏÐÐÐÐÑÑÑÑÒÒÒÓÓÔÔÕÕÕÖÖÖרØÙÙÚÚÛÛÜÝÝÞÞßààáââãäåææçèéêêëìíîïðñòóôôõö÷øùúûüýþ  !!""#$$%%&&''()))***++,,---....////000111222233334444455555566666667777777788888888889999999999999::::::::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊÊÊËËËËËÌÌÌÌÍÍÍÍÎÎÎÎÎÏÏÏÏÐÐÐÑÑÑÒÒÓÓÓÔÔÔÕÕÖÖ×רØÙÙÚÚÛÜÜÜÝÞßßààáâããäåæççèéêëìíîïððñòóôõö÷øùúûüýþ  !"###$%%&&''(())**+++,,,--...///000011111222233334444455555566666677777777788888888889999999999999::::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÎÏÏÐÐÐÑÑÑÒÒÒÒÓÓÔÔÕÕÖÖÖ×רØÙÚÚÚÛÜÜÝÞÞßààáââãäååæçèéêëììíîïðñòóôõö÷øùúûüýþ  !!"##$%%%&''(()))**++,,----...///00111122222333334444445555556666667777777778888888889999999999:::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËÌÌÌÌÍÍÍÎÎÎÎÏÏÏÐÐÐÐÑÑÑÒÒÓÓÔÔÔÔÕÕÖÖ×ØØØÙÙÚÛÛÜÜÝÞßßààáâããäåæçèééêëìíîïðñòóôõö÷øùúûüýþ  !"##$$%&&'''())**++++,,--...////00011112223333444445555556666666777777778888888889999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<=========================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊÊËËËËÌÌÌÌÍÍÍÍÎÎÎÎÎÏÏÏÐÐÐÑÑÒÒÒÓÓÓÔÔÕÕÖÖÖ×רÙÙÚÚÛÛÜÝÝÞßààááâãäåæççèéêëìíîïðñòóôõö÷øùúûüýþ  !""#$$%%&&'(()))**++,,,---..///00011111222233334444555556666667777777788888888899999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<=====================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊÊËËËËËÌÌÌÌÌÍÍÍÍÍÎÎÎÏÏÐÐÐÐÑÑÑÒÒÒÓÔÔÔÕÕÕÖ×רØÙÙÚÛÛÜÜÝÞÞßààáâãäååæçèééëììíîïðñòôôö÷øùúûüýþ  !!"##$$%&&''(()***+++,---...////00111222223333344444555556666667777777788888888899999999999::::::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<=============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊÊËËËËËÌÌÌÌÍÍÍÎÎÎÏÏÏÏÐÐÐÐÑÑÒÒÓÓÓÔÔÕÕÖÖ×רØÙÚÚÚÛÜÝÝÞßààáâããäåææçèéêëìíîïðñòóôõ÷øùúûüýþ  !""#$%%%&''(())**++,,,--..////000011122233334444455555556666666777777778888888889999999999:::::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=====================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊËËËÌÌÌÌÍÍÍÍÎÎÎÎÎÏÏÐÐÐÑÑÑÒÒÒÓÓÔÕÕÕÖÖ×ØØØÙÙÚÛÛÜÝÞÞßàááâãääåæçèéêëìíîïðñòóôõöøùúûüýþ  !!"#$$%&&'''())***+,,---...///001111122223333444555556666666777777778888888899999999999::::::::::::;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÈÈÈÈÈÈÈÉÉÉÉÉÊÊÊÊÊËËËËÌÌÌÌÌÍÍÍÍÍÎÎÏÏÏÐÐÐÐÑÑÒÒÓÓÓÔÔÕÕÖÖ×רÙÙÚÚÛÜÜÝÞßßàáââãäåæçèèéëëìîïðñòóôõö÷ùúûüýþ  !"##$%%&&'(())**++,,,--..////000112222233333444455555666667777777888888899999999999:::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<=======================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÈÈÈÈÈÈÈÉÉÉÉÉÊÊÊÊÊÊÊËËËËËÌÌÌÌÍÍÍÎÎÎÏÏÏÏÐÐÑÑÒÒÒÒÓÓÔÕÕÕÖÖרØÙÙÚÛÛÜÝÝÞÞààáâãääåæçèéêëìíîïðòóôõö÷øúûüýþ !!""#$$%&&''())***+,,----..//00001112223333444445555555666667777777888888899999999999:::::::::::::;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<==============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊËËËËÌÌÌÍÍÍÍÎÎÎÎÏÏÐÐÐÑÑÑÑÒÓÓÔÔÔÕÕÖ××רÙÚÚÛÜÜÝÝÞßàáââãäåæçèéêëìíîïðñòóõö÷øùûüýþ  !""##$%%&'((()**+++,,-....///00111122223334444555556666666777777778888888899999999999:::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<====================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÊÊÊÊËËËËÌÌÌÌÌÍÍÍÍÎÎÏÏÏÏÐÐÐÑÑÒÒÓÓÓÔÕÕÖÖÖרØÙÚÛÛÛÜÝÞÞààáâããååæèèéëëíîïðñòóôö÷øùûüýþ !!"#$$$%&''()))**+,,,--..///000011222233333444455556666777777778888888889999999999:::::::::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<===================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÈÈÈÈÈÈÉÉÉÉÊÊÊÊÊÊËËËËËÌÌÌÌÍÍÎÎÎÎÏÏÏÐÐÑÑÑÒÒÒÓÔÔÔÕÕÖ×רÙÙÚÚÛÜÜÝÞßàááâãäåæçèéêëìíîðñòóôõ÷øùúüýþ  !"##$%%&&'(()**+++,---...//000111122333344444555555666677777788888889999999999::::::::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<===========================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊÊÊËËËÌÌÌÍÍÍÍÎÎÎÎÏÐÐÐÐÑÑÒÒÓÓÓÔÕÕÖÖרØÙÙÚÛÛÜÝÝÞßàáâãäååççéêëìíîïðòóôõ÷øùúüýþ  !""#$$%&&''())**+,,,--..////0111122223334445555556666666777777888888999999999::::::::::::;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<===================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊËËËÌÌÌÌÌÍÍÍÍÎÎÏÏÏÐÐÐÑÒÒÒÒÓÔÔÕÕÖ××רÙÚÚÛÜÜÝÞßàááâãäåæçèéêëíîïðñóôõöøùúüýþ  !"##$%%&'((()**++,----.///000112222333334445556666666777777778888888999999999::::::::::::;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÉÉÉÉÊÊÊÊËËËËËÌÌÌÌÍÍÎÎÎÎÏÏÐÐÑÑÑÑÒÓÓÔÔÕÖÖÖרØÙÚÛÛÜÝÝÞßàáâãääæçèéêëìîïðñòôõöøùúüýþ  !""#$$%&''()))*++,,-....//001111223333444445555666677777778888888899999999999:::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÈÈÈÈÈÉÉÉÉÉÊÊÊÊÊÊËËËÌÌÌÍÍÍÍÎÎÎÏÏÐÐÐÐÑÒÒÓÓÔÔÕÕÖ×רØÙÚÚÜÜÝÞÞààáâãäåæçèêëìíîðñòóõö÷ùúûýþ !!"##%%&''(()**++,,--.////00111222233344455555566666777778888889999999999:::::::::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<=======================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÇÇÇÇÇÈÈÈÈÈÈÉÉÉÉÉÉÉÊÊÊÊËËÌÌÌÌÌÍÍÍÎÎÏÏÏÏÐÐÑÑÒÒÓÓÔÔÔÖÖÖרÙÙÚÛÛÝÝÞßàáâãäåæçèéêìíîïñòóôö÷ùúûýþ  !""$$%&&'()))+++,,--..//000011222333334455556666666777777888889999999::::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÊÊÊËËËËËÌÌÌÌÍÎÎÎÎÎÏÐÐÐÑÑÒÒÓÓÓÔÕÕÖ×רÙÚÚÛÜÜÞÞààáâãäåæèéêëìíïðòóôö÷øúûýþ !!##$%%&'(()**+,,,--..///011111233334444455566667777777788888889999999::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÈÉÉÉÊÊÊÊÊÊËËËËÌÌÍÍÍÍÎÎÏÏÏÐÐÑÑÒÒÒÓÔÔÕÖÖרØÙÚÛÛÜÝÞßàááãäåæçèéëìíîðñòôõ÷øúûýþ  !"#$$%&''())*++,---..//00011222233444455555566677777888888888999999999::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<========================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÈÈÈÈÉÉÉÉÉÉÊÊÊÊÊËËÌÌÌÌÍÍÍÎÎÏÏÏÐÑÑÑÑÒÓÓÔÔÕÕÖרØÙÚÛÜÜÝÞßàáâãäææèéêìíîðñòôõ÷øúûýþ  !"##$%&''()**++,,-..../000112223333445555566666677778888889999999999:::::::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<===============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÇÇÇÇÈÈÈÈÈÈÈÉÉÉÉÉÉÊÊËËËËÌÌÌÌÍÎÎÎÎÏÐÐÐÐÑÒÒÒÓÔÔÕÖÖרÙÙÚÛÜÝÞßàáâãäåæçéêëíîïñòóõöøùûüþ  !"#$%&&'())*++,---.////0111123333444455666666777777788889999999:::::::::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÊÊÊÊËËËËÌÌÍÍÍÍÎÏÏÏÏÐÑÑÒÒÓÓÔÔÕÖÖרÙÚÚÛÜÝÞßàáâãäåçèéëìíïðòóõöøùûüþ  !"#$%%&'())*++,,--../000012222334444555566677777777888888999999:::::::::;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<===========================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÈÈÉÉÉÉÊÊÊÊÊÊËÌÌÌÌÍÍÎÎÎÏÏÐÐÑÑÑÒÓÓÔÕÕÖ×רÙÚÛÜÜÝÞààââäåæèéêìíîðñóôöøùûüþ !"##$%&'(()**+,,-...//001112233334555555666677778888888889999999:::::::;;;;;;;;;;;<<<<<<<<<<<<<<<<<<================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÈÈÈÉÉÉÉÉÉÉÊÊÊËËËÌÌÌÍÍÎÎÎÎÏÐÐÐÑÒÒÒÔÔÔÕÖרØÙÚÛÜÝÝßàáâãåæçèêëìîðñòôö÷ùûüþ  ""#$%&''()*+++---.///011112233344455566666667778888899999999999::::::::;;;;;;;;;;;<<<<<<<<<<<<<<<<<=================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÇÇÇÈÈÈÈÈÈÈÉÉÉÉÊÊÊËËËËÌÍÍÍÍÍÎÏÏÏÐÑÑÑÒÓÓÔÕÕÖרÙÙÚÛÜÝÞààâãäåçèéëìíïñòôõ÷ùúüþ !"#$%&&'()**+,,-.../000122222344445556666777777788899999999:::::::::::::;;;;;;;;;;;<<<<<<<<<<<<<<<<<=================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÇÇÇÇÇÇÈÈÈÈÈÈÉÉÊÊÊÊÊÊËÌÌÌÌÍÍÎÎÎÏÐÐÐÑÒÒÒÔÔÔÖÖרÙÚÛÛÝÝßàáâãäæèéêëíïðòóõ÷ùúüþ  ""$$%&'())+++---.///01112233334555555667777778888889999::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<=================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÆÆÆÆÆÇÇÇÇÇÇÇÇÇÈÈÉÉÉÉÉÊÊÊËËËÌÌÌÍÍÎÎÎÏÏÐÐÑÑÒÓÓÔÕÕ×רÙÚÛÜÜÞßàáãäåçèêëìîðñóõ÷øúüþ  !##$%&'((**+,,-..//0011122333444555666667788888888899999:::::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<===============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÆÆÆÆÆÆÆÆÆÇÇÇÇÇÈÈÈÈÉÉÉÉÉÊÊËËËËËÍÍÍÍÍÏÏÏÐÐÑÑÒÓÔÔÕÖÖØØÙÚÛÜÝÞààâäåæçéëìîðñóõöøúüþ !"#$%&''))*++,-..//00022222444445566666777788888999999999:::::;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<=============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÇÇÈÈÈÈÈÈÈÉÉÊÊÊÊÊËËÌÌÌÍÍÎÎÎÐÐÐÐÒÒÓÓÔÕÕ××ÙÙÚÜÜÞßàáãäæçèêëíïðòôöøúüþ  !##%&&((**+,,--////11122333445555566777777788999999999::::::::;;;;;;;;<<<<<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÇÇÇÇÇÈÈÈÈÈÉÉÉÊÊÊÊËËÌÌÌÍÍÎÎÏÏÏÐÑÑÒÓÓÔÕÖÖØØÚÛÛÝÞààâãåæèêëíïðòôöøúüþ !"$$%''))*+,,-../000112233344555566677777888889999:::::::::::::;;;;;;;;<<<<<<<<<<<=========================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÆÆÇÇÇÇÇÇÇÇÇÈÉÉÉÉÉÉÉËËËËËÍÍÍÍÎÏÏÐÐÑÒÒÒÔÔÕÖרÙÚÛÜÝßàáâäæçéêìîðòôöøúüþ  "#$%&'()*++---.//0012222444446666666788888888899::::::::::;;;;;;;;;;;;;<<<<<<<<<<<=======================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÆÆÆÆÆÆÆÇÇÇÇÈÈÈÈÉÉÉÉÊÊÊËËÌÌÌÍÎÎÎÎÐÐÑÑÒÓÓÕÕÖØØÙÚÛÝÞßàâãåæèêìîðñóõ÷ùûý  !"$%&'')**,,-..//11112333445556666777788889999999:::::;;;;;;;;;;;;;;;<<<<<<<<<<<<<<=======================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÅÅÅÆÆÆÆÆÆÆÆÆÆÇÈÈÈÈÈÈÈÈÊÊÊÊÊËÌÌÌÍÍÎÎÏÏÐÑÑÒÓÔÔÕÖ×ÙÙÚÜÝßàáâäæèêëíïñóõ÷ùûý  "#%&&()*++,-../0011223334555557777777789999999999:::;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<=======================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÅÅÅÅÅÅÅÅÆÆÆÆÆÇÇÇÇÇÈÈÈÈÉÉÉÊÊËËËËÍÍÍÍÎÏÐÐÐÒÒÓÓÕÕÖØØÚÛÜÞßàâäåçéêìîðòô÷ùûý  !#$%'')**,,--///01222244445566677778888899999::::::::;;;;;<<<<<<<<<<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÇÇÇÇÇÇÇÇÈÉÉÉÉÉÊÊËËÌÌÌÍÎÎÎÏÐÑÑÒÓÔÔÖ××ÙÚÜÝÞàáãäæèéìîðòôöùûý !"#%&(()++,-../011123334455666667888888889::::::::::::;;;;;<<<<<<<<<<<<<<===============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÆÆÆÆÆÇÇÇÇÈÈÈÈÈÉÊÊÊÊÊÌÌÌÍÍÎÏÏÐÐÒÒÓÔÕÕרÙÛÜÞÞáâäåçéëíïñôöøûý !!#$&'(**+,--//0012233355555677777888899999:::::::;;;;;;;;;;<<<<<<<=============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÆÆÆÆÆÆÆÆÆÈÈÈÈÈÈÈÉÊÊÊËËËÍÍÍÎÎÐÐÐÑÓÓÔÔÖ×ÙÚÚÜÝàáãäæèêíîñóöøûý "#%%&()++,,.///1122244455567777777999999999::;;;;;;;;;;;;;;;<<<<<<=====================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÆÆÆÆÆÆÆÇÇÇÈÈÈÉÉÉÉÉËËËÌÌÎÎÎÏÏÑÑÒÒÔÕÖØØÚÛÜÞàâãåèéìîðóõøúý !#$%'')*+--..0011133444666667778889999999::::;;;;;;;;;;;<<<<<<<<<<<=============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÅÅÅÅÅÅÅÅÅÆÆÇÇÇÇÇÇÈÉÉÉÉÊÊÊÌÌÌÍÍÏÏÐÐÐÒÓÔÖÖ×ÙÚÜÝÞáâäçèëîðòõøúý !"#%&())+,-///00223335556666788888899:::::::::;;;;;<<<<<<<<<<<<<<<<<============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÅÅÅÅÅÅÅÅÅÅÆÆÆÇÇÇÈÈÈÈÈÊÊÊÊËËÍÍÍÎÎÐÐÑÒÔÔÕרÙÛÜÞàâãæèêíïòô÷úý !#$&'(*++-.//1122244555577777888999::::::::::;;<<<<<<<<<<<<<<<<<<<==============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÅÅÅÅÅÆÆÆÆÆÆÆÈÈÈÈÈÉÉÉËËËÌÌÎÎÏÏÐÒÒÓÕÖÖÙÚÛÝÞáâåæéìîñô÷úý !"$%&))*,--/001133444666777779999999:::::;;;;;;;<<<<<<<<<<<<=====================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÆÆÆÆÆÆÆÆÇÇÇÈÉÉÉÉÊÊÌÌÌÍÍÏÐÐÑÓÓÔÕרÛÜÝàáäåèëíðóöùü "#$'(*+,,.//022333556666788899999999;;;;;;;;;;;;<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÆÆÆÇÇÇÇÇÈÈÉÊÊÊÊËÍÍÎÎÎÑÑÒÒÕÖ×ÙÚÝÞàâåçêìðóöùü !"%&()*--..111224555567788888999::::;;;;;;;;;;;<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÄÄÄÄÄÅÅÅÅÅÅÅÅÅÇÇÇÈÈÈÈÈÉÊËËÌÌÎÎÏÐÐÒÓÔרÙÛÝàáäæéëïòõùü "$&'(+,-//01133445677777888:::::::::;;;;;<<<<<<<<<===================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÅÅÅÅÅÅÅÅÆÆÆÆÈÈÈÈÉÉÉÊÌÌÌÍÍÐÐÑÑÔÕÖÙÚÛÞàãåèëîñõøü !$%&)*+..//22333566677779999::::::::<<<<<<<<<<<<<<<==========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÅÅÅÆÆÆÆÆÆÇÇÉÉÉÊÊÊËÍÍÎÎÏÑÒÓÔרÛÜÞáãçêíðôøü !#$'(+,-.01122455566688999999:::;;;;<<<<<<<<<<<<<<<<==>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÆÆÆÆÇÇÇÇÈÈÊÊËËËÌÎÏÐÐÑÔÕÖÙÚÞàãåéìðôøü !%&)*+.//013444557788889999;;;;;;;;;<<<<<<<<<<=======>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÄÄÄÄÄÄÄÄÄÄÅÅÅÅÇÇÇÈÈÈÈÉÉËÌÌÍÍÐÑÒÓÖרÜÞáäèêîó÷û !#'(),-./22334667777888::::;;;;;;;;;;<<<==============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÄÄÄÄÄÄÅÅÅÅÅÅÆÆÆÈÈÉÉÉÊÊÍÍÎÏÐÓÔÕØÚÜàâæéíòöû #%'*+,/01225566677999::::::;;;;;;=====================>>>>>>>>>>>>>>>>>>>>>>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÅÅÅÅÅÅÆÆÆÆÆÇÇÉÊÊËËÌÏÐÐÑÒÖØÜÝàäéìñöû "#')-.//03445568899999::::::<<<<<======================>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÄÆÆÆÆÆÇÇÇÈÈÈËÌÌÍÎÑÒÔÕÙÛàâçêðõú $&*+-.1233477788899999;<<<<<<<<<<====================>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÇÇÇÈÈÈÉÉÊÍÎÏÐÑÕÖØÝàåèîôú "')*./012566777888;;;;;;<<<<<<<<<<=========>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÅÅÅÅÈÉÉÊÊËÌÐÑÒÓØÚÝâæìóù "%',-./3455667::::;;;;;;;<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÃÄÄÄÄÄÄÄÅÅÅÅÆÆÆÇÊËÌÌÍÎÓÕ×Ýàãêñø"(*,1233458999::::;;;;;;;<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÄÅÅÅÅÆÆÆÇÇÈÈÉÍÎÐÑÓÙÜãèðø#&,./1267788999::::;=======>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÃÃÃÆÇÇÈÈÉÉÊËÑÓÕØàäíö '*,.456677889<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÄÄÄÉÉÊËÌÎÐØÛàêõ $'/134566;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÄÄÄÄÅÅÆÇÎÐÒÕàæó *-/189::;;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÄÄÄÄÅÅÆÇÈÉÊÕÙàð&*56789::;;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÄÄÄÄÅÅÆÇÈÉÊÌÐÕê*/356789::;;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÄÄÄÄÅÅÆÇÈÉÊÌÐÕà*/356789::;;;;<<<<<===========>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½¼¼¼¼¼»»»»ºº¹¸·¶µ³¯ªŸ`UPLJIHGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½¼¼¼¼¼»»»»ºº¹¸·¶µ³¯ªŸ`UPLJIHGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½¼¼¼¼¼»»»»ºº¹¸·¶µ³¯ª•jUPLJIHGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½¼¼¼¼¼»»»»ºº¹¸·¶µª¦Ÿp`YUJIHGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½¼¼¼¼¼»»»»ºº¹¸±¯­ªŸ™Œsf`URPNGFEEDDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½¼¼¼¼¼»»»¶¶µ´³±¯§¤Ÿ•Šuj`[XPNLKJIIDDDCCCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½¼¼¼¹¸¸··¶¶µ´®¬ª§Ÿ›’‰vmd`XUSQKJIIHHGGFCCCBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½»ºººº¹¹¹¸¸··¶²±¯®¬¦£œ—‡xphc\YSQPNMIHHGGFFFEEEEDBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¼»»»»»»»ºººº¹¹¹¸µ´³³²±¬ª¨¢Ÿœ•އxqjc`]WUSNMLLKJGFFFEEEEDDDDDDDCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¼¼¼¼¼¼¼¼¼¼»»»»»»»ºººº·¶¶µµ´³¯®­¬§¥¢™“Œ†yslfb]ZXSRQPLKJJIIHEEEEDDDDDDDCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½¼¼¼¼¼¼¼¼¼¼»»»»»»¸¸¸···¶¶µ²±°¯®ª©§¢Ÿš—‘‹…ztnhe`]XVUQPONMJIIHHHGGGDDDDDDCCCCCCCCCCBBBBBBBBBAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼»¹¹¹¹¹¸¸¸···´³³²±®­«ª¦¤Ÿ˜•Š…zupjgb`[YUTRQNMLLKHHHGGGFFFFFDCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼ºººººº¹¹¹¹¹¸¸¶µµ´´³°¯¯®­©§£¢Ÿ›–“މ„{vqlid`]\XVRQPPOLKKJJIGGFFFFFEEEEEECCCCCBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½»»»»»»ºººººº¹¹¹··¶¶¶µµ²²±°¯¬«ª§¥£Ÿ™–’‰„{vrmifb`\ZXUTSPONMMJJIIIHHFFFEEEEEEDDDDDDBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½¼¼¼»»»»»»»»»»ºººº¸¸¸····¶¶´³³²²¯®­¬©¨§£¡ž›—•‘Œˆ„{wsnjhda^\XWVSRQPMMLLKIIHHHHGGGEEEEDDDDDDDDDDCCCBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½¼¼¼¼¼¼¼¼¼¼»»»»»»»»»¹¹¹¹¸¸¸¸··µµ´´´³±°¯¯®«ª©¦¥¡Ÿœš–“‹‡ƒ|xtpliec`^ZYVUTQPPONLKKKJJHHGGGGFFFFDDDDDDDDDCCCCCCCCCCBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»ººº¹¹¹¹¹¹¸¸¶¶¶µµµ´²²±±°®­¬«¨§¤£¡žœ˜•’‹‡ƒ|xtpmjgca^\[XWTSRQONNMMKJJJIIIGGFFFFFFEEEDDDDCCCCCCCCCCCCCCCCBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼ºººººººº¹¹¹¹····¶¶¶µ³³³²²¯¯®®«ª©¦¥¤¡Ÿœš—”‘ŽŠ‡ƒ|xuqnkhec`^[ZYVUTQQPPMMLLLJIIIHHHHFFFFEEEEEEEECCCCCCCCCCCCCCCBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼»»»»»ººººººººº¸¸¸·····¶µ´´³³±±°¯¯­¬«¨§¦¤¢Ÿž›™–”Іƒ|yurokifda`][YXWTSRPPONNLLKKJIHHHHHGGGEEEEEEEEEDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼»»»»»»»»»»»ºººº¹¹¹¸¸¸¸¸··¶µµµµ´²²±±±®®­­ª©¨¦¥¢¡Ÿš˜•“Œ‰†ƒ|yvspljgeb`^]ZYWVURRQQNNNMMKJJJJIHHGGGGGFFFEEEEDDDDDDDDDDDCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼»»»»»»»»»»»»¹¹¹¹¹¹¹¹¸¸¸·¶¶¶¶µµ³³³²²°¯¯®¬¬«ª¨§¤£¢Ÿž›š—”’Œ‰†ƒ|yvspmkheda`]\[XWUTSSQPPOMMLLLJJIIIIHGGGFFFFFFFFDDDDDDDDDDDDCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»ººººº¹¹¹¹¹¹¹·····¶¶¶´´´³³±±°°¯­­¬ª©©¦¥¤¢¡žš™–“‘Ž‹ˆ…‚}zwtqnlifeba^][ZYVVUSRRPOONNLLKKKIIIHHHHHFFFFFFFEEEEEDDDDDDDCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»ºººººººººº¹¹¹¸¸¸·····µµµµ´´²²²±±¯¯®­««ª¨§¦¤£¡Ÿœ™—•’‹ˆ…‚}zwtromjhfcb`^\[YXWUTTRQPPNNMMMKKJJJJHHHHHGGGFFFEEEEEEEEEEDDCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»ººººººººº¹¹¸¸¸¸¸¸·¶¶¶¶µµµ³³³²²°°¯¯¯­¬«©©¨¦¥£¢¡ž›˜—”‘Ї…‚}zxurpnkhgdba^]\ZYWVVTSRPPPOOMMLLLJJJIIIIHGGGGGGFFEEEEEEEEEDDDDDCCCCCCCCCCCCCCCCCBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»ºººº¹¹¹¹¹¹¹¸¸¸···¶¶¶¶¶´´´³³±±±°°®®­­«ª©§§¥¤£¡Ÿœš—–“‘ŒŠ‡…‚}zxuspnlihecb`^\[ZXXVUTRRQQOONNNLLKKKIIIIIHHHGGGFFFFFFFEEEEDDDDDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼»»»»»»»»»»»»»»»ºº¹¹¹¹¹¹¹¹¹·······¶µµµ´´´²²²±±¯¯¯®¬¬««©¨¦¥¥£¢Ÿžœ›™—•’‘ŽŒ‰‡„‚}{xvsqnmjhfdca`]\ZZYWVTTSSQPPPNNMMMKKKJJJIHHHHHHHFFFFFFFFFEEDDDDDDDDDDDDDDDCCCCCCBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼»»»»»»»»»»ººººººº¹¹¹¹¹¸¸¸¸·····¶µµµµµ³³³²²±°°¯¯­­¬«ªª¨§¦¤£¡¡ž›š˜–”’Ž‹‰‡„‚}{xvtqomkigedba^^\[YXWUUTSRRPPOONMMLLLJJJJJIHHHHHGGGGFFFFFEEEEEEEDDDDDDDDDDCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»ºººººººººººº¹¸¸¸¸¸¸¸¸·¶¶¶¶¶µµ´´³³³²±±±°¯®®­¬««©¨¨¦¥£¢¡Ÿžœ›™—–“‘‹‰†„‚}{yvtrpnlihfdca`^]\ZYWWVTTSRQQPONNNMLLLKKJJIIIIIHGGGGGGGGFEEEEEEEEEEEEDDDDDCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»ºººººººº¹¹¹¹¹¸¸¸¸¸····¶¶¶µµ´´´´²²²²±°¯¯¯­­¬¬ªª©§§¥¤£¡ Ÿ›š˜–•“‘‹ˆ†„‚}{ywtrpnljigedb`_^\[ZXXVUUSSRRPPPONMMMMKKKKJJIIIHHHHGGGGGFFFFFEEEEEEEEDDDDDCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»ººº¹¹¹¹¹¹¹¹¹¹¸········µµµµµ´³³³²²±±°°¯®®­¬««ª©¨¦¦¥£¢ Ÿž›™—•”’ŽŒŠˆ†„‚}{ywusqomkjhfdba`_]\ZYYWVUTTSRQQPOONNMMLLLKJJJJJHHHHHHHHGFFFFFFFFFFEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»ººººº¹¹¹¹¹¹¹¸¸¸¸····¶¶¶¶µµµ´´³³³²±±±±¯¯®®­¬¬ªª©§§¦¥¤¢¡ Ÿœš™—•“‘ŽŒŠˆ†„‚}{ywusqpnljhfecb`_^][ZYXXVUUSSRQQPPNNNNMLLLKKJJJIIIIHHHHGGGGFFFFFFFEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»ºººººººººº¹¹¸¸¸¸¸¸¸¸¸·¶¶¶¶¶¶¶´´´´´²²²²±°°¯¯®­­­««ª©¨§¦¥¤£¢ Ÿž›™˜–•“‘‹‰‡…ƒ~|zxvtrpnljigfdba`_]\[ZYXWVUTTRRRQPPOONMMMMKKKKKIIIIIIIHGGGGGGGGGFFEEEEEEEEEEDDDDDDDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»ººººººººººººº¹¹¹¹¸¸¸¸¸·····¶¶¶µµµµ´´³³³²²±±°°°¯®®­¬¬«ª©©§§¥¤¤¢¡ŸŸœš™—•”’‹‰‡…ƒ~|zxvtrpomkjhfecb``^][[ZXXVVUTSSRQQPOOONNMMLLLKKJJJJIIIHHHHHGGGGGFFFFEEEEEEEEEEEEEDDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»ºººººººº¹¹¹¹¹¹¹¹¹¸¸·······¶¶µµµµµ´´³³³²²±±±¯¯¯¯­­¬¬«ªª¨¨¦¦¥££¡ Ÿžœ›™˜—•”’‹‰‡…ƒ~|zxvtrpomkjhgfdca`_^\\ZYYWWUUTSSRRPPPPNNNMMLLLKKJJJJJIIHHHHHHHGGFFFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»ººººº¹¹¹¹¹¹¹¹¹¸¸¸¸¸····¶¶¶¶¶µµ´´´´´²²²²²°°°¯¯®®­¬««ª©©§§¦¥¤£¢¡ŸŸ›š™˜–”“‘ŽŒŠ‰‡…ƒ~|zxvusqpnlkigfedb``^]\[ZYXXVVUTTSRQQPPOOOMMMMMKKKKKJJIIIIIHHHHGGGGGFFFFFFFFFEEEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»ººººººº¹¹¹¹¹¸¸¸¸¸¸¸¸¸··¶¶¶¶¶µµµ´´´³³³²²±±±°°¯¯®®­¬¬«ªª¨¨§¦¥¤££¡ Ÿžœ›š˜—•”“‘ŽŒŠˆ‡…ƒ~|zxwusqpnlkjhgedca`_^\\[ZYXWWUUTSSRQQPPOONNNMMLLLKKKJJJIIIIIHHGGGGGGGGGFFFFFEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»ºººººººººººº¹¹¹¹¸¸¸¸¸¸······¶¶µµµµµµ´³³³³²²±±±°¯¯¯®­­­«««©©¨§¦¥¤¤¢¢ Ÿžœ›™—–•”’ŒŠˆ†…ƒ~|zywusrpomkjihfdcba`_]][[ZYXWVVTTTRRRQPPPONNNMMLLLLKJJJJJJIIHHHHHHGGGGGGFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¹¸¸¸·······¶¶¶¶µµµ´´´´³²²²²²±°°°¯®®®­¬¬«ªª©¨§¦¦¥¤£¢¡ŸŸœ›š˜—–”“’Ž‹Šˆ†…ƒ~|zywutrqomlkihgedcb``^]\[ZYYXWVUUTSSRQQQPOOONMMMMMLKKKKJJJIIIIHHHHHHHGGGFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»ºººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸···¶¶¶¶¶¶¶µµµ´´´³³³²²±±±±°¯¯¯®­­­«««ª©¨§§¦¥¤£¢¢ Ÿžœš™˜—•”“‘Ž‹‰ˆ†„ƒ~|{ywvtrqpnlkjhgfecba`_]]\[ZYXXWVUTTTRRRQPPPONNNNMMLLLKKKJJJIIIIIIIHHHGGGGGFFFFFFFFFFFEEEEEEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»ººººººº¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸····¶¶¶¶µµµµµµ´³³³³²²±±±°°¯¯®®®­¬¬«ªª©¨¨§¦¥¤££¢¡ŸŸ›š™—–•“’‘ŽŒ‹‰‡†„ƒ~|{yxvtsqpnmljihfedbb``^]\\[ZYXWWVUUTSSRQQQPPOONNNMMLLLLKJJJJJJIIIIHHHHGGGGGGGGGFFFFFFFEEEEEEEDDDDDDDDDDDCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»ººººººººº¹¹¹¹¹¹¸¸¸¸¸¸········¶¶¶µµµµ´´´´³³²²²²±°°°°¯®®­­¬¬««ª©©¨§¦¥¥¤£¢¡ Ÿžœ›š˜—–”“’ŒŠ‰‡†„ƒ~|{yxvusrpomlkihgedcba`_^]\[ZZYXWVVUTTSSRRQQPOOOONMMMMLLKKKKJJJJIIIHHHHHHHHGGGGGGFFFFFFEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¸¸¸¸·······¶¶¶¶¶¶µµ´´´´³³³³²±±±±°¯¯¯¯®­­­¬««ª©©¨§¦¦¥¤£¢¡ Ÿžœ›š™˜–•”’‘ŽŒŠ‰‡†„ƒ~|{yxvusrqonmkjigfedcba`_^]\[ZYYXWVVUTTSRRRQPPPPONNNNMLLLLKKKKJJIIIIIIHHHHHHHGGGGFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸····¶¶¶¶¶¶µµµµµ´´³³³³²²²±±°°°¯®®®®­¬¬««ªª©¨§§¦¥¤££¢¡ Ÿžœ›™™—–•“’‘Ž‹Šˆ‡…„‚~}{zxwutrqpnmljihffdcba`_^]\\[ZYXXWVUUTTSSRQQQQPOOONNMMMLLLLKKJJJJJIIIIIIHHHHGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»ºººººººººº¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸·····¶¶¶µµµµµµ´´´´³³²²²²±±°°°¯¯®®­­­¬««ª©©¨§§¦¥¤¤£¢¡ Ÿžžœ›š™˜—–”“’‘Ž‹Šˆ‡…„‚~}{zxwutrqpnmlkihgfedcaa`_^]\[[ZYXXWVVUTTSRRRQQPPOOONNMMMMLLKKKKJJJJJJIIIHHHHHGGGGGGGGGFFFFFFFFFEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»ºººººººººº¹¹¹¹¹¹¹¸¸¸¸¸¸¸········¶¶¶¶µµµ´´´´´³³³³²±±±±±°¯¯¯®®­­¬¬¬«ªª©¨¨§¦¥¥¤££¡¡ŸŸžœ›š™—–•”“’Œ‹‰ˆ‡…„‚~}{zxwvtsrpomlkjihfedcba``^^\\[ZZYXWWVUUTSSSRRQQPPPONNNNNMLLLLKKKKKJJJIIIIHHHHHHHHGGGGGGGFFFFFFFEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»ºººººººººººº¹¹¹¹¹¹¹¸¸¸¸¸······¶¶¶¶¶¶¶µµµµ´´³³³³³²²²±±°°°°¯¯®®­­¬¬«««©©©¨§¦¦¥¤¤¢¢¡ Ÿžœ›š™˜—–•“’‘ŽŒ‹‰ˆ†…„‚~}{zywvtsrqonmljihgfedcba`_^]][[ZYYXWVVVTTTSSRRQQPPOOOONNMMMLLLLLKKJJJJIIIIIIIHHHHHHGGGGGFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸·····¶¶¶¶¶µµµµµµ´´´³³³²²²²±±±°°¯¯¯¯®­­¬¬««ªª©¨¨§§¦¥¥££¢¡¡ŸŸžœ›š™˜—•”“’‘ŽŒŠ‰ˆ†…„‚~}{zywvusrqpnmlkjhgfedcba``^^]\\ZZYXXWWVUUTTSSRRQPPPPOONNNMMMMLLLKKKJJJJJJIIIIIHHHHHGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸·······¶¶¶¶µµµµ´´´´´³³³³²²±±±±°°¯¯®®®®­¬¬««ª©©©¨§§¦¥¤¤£¢¢¡ Ÿžœ››™˜—–•”“‘ދЉ‡†…ƒ‚~}|zyxvutrqponlkjihgfddcba`_^]]\[[ZYXXWVVVUTTSSRQQQQPPOONNNNMMLLLLKKKKKJJJJIIIIHHHHHHHGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»ºººººººººººº¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµ´´´³³³³³²²²²±±°°°¯¯¯®­­­­¬««ªª©¨¨¨§¦¥¥¤££¢¡ Ÿžžœ›š™˜—–•”’‘ŽŒ‹Š‰‡†…ƒ‚~}|zyxvutsqponmkjihgfedcbaa`_^]\\[ZZYXWWWVUUTTSRRRRQPPPOOONNMMMMLLLLLKKKJJJIIIIIIIHHHHHHHHGGGGGGGFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»ºººººººººººº¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸······¶¶¶¶¶¶¶µµµµµµ´´´³³³²²²²±±±±°¯¯¯¯®®­­¬¬¬«ªª©©¨§§¦¦¥¤¤£¢¢¡ Ÿžœ›šš˜˜–•”“’‘Œ‹Šˆ‡†…ƒ‚~}|zyxwutsrponmlkjiggeedcba`_^]]\[[ZYYXXWVVUUTSSSRRQQPPPPONNNNMMMMLLLKKKJJJJJJIIIIIIIHHHHHHGGGGGGFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»ºººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸······¶¶¶¶µµµµµµ´´´´´³³³³²²±±±±°°°¯¯®®®­­­¬«««ªª©¨¨§¦¦¥¥¤££¢¡ Ÿžžœ›š™˜—–•”“’‘ŽŒ‹Šˆ‡†…ƒ‚~}|zyxwutsrqpnmlkjihgfedcbaa`_^]\\[ZZYYXWWVUUTTTSRRRQQQPPOOONNNNMMLLLLKKKKKJJJJJJIIIIHHHHHHGGGGGGGFFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸········¶¶¶¶µµµµ´´´´³³³³³²²²²±±°°°°¯¯¯®®­­¬¬¬«ªª©©©¨§§¦¥¤¤¤£¢¡¡ŸŸžœœšš™——–””’‘ŽŒ‹‰ˆ‡†„ƒ‚~}|{yxwvtsrqponmkkihhfeeccba``^^]\[[[ZYXXWVVVUUTSSSRRQQPPPOOOONNMMMMLLLLLKKKKJJJJIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµµµ´´´´³³³²²²²±±±±°°¯¯¯®®®®­¬¬«««ªª©¨¨¨§¦¥¥¤££¢¢¡ Ÿžœ›š™˜—–•”“’‘ŽŒŠ‰ˆ‡†„ƒ‚~}|{yxwvusrqponmlkjihgfedcbba`_^]]\\[ZZYXWWWVUUTTTSSRQQQQPPPOONNNNMMMMLLLKKKKJJJJJIIIIIIIHHHHHHHHGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸·······¶¶¶¶¶µµµµµµµ´´´´´³³³³²²²±±±°°°°¯¯®®­­­­¬¬«ªªª©©¨§§¦¦¥¤¤£¢¢¡¡ŸŸžœ››š™˜—–•”“’‘Œ‹Š‰ˆ‡…„ƒ‚~}|{zxwvutsrponmlkjihgfeddcba``^^]]\[[ZYYXXWVVUUUTSSRRRRQQPPOOOONNNMMMLLLLKKKKKJJJJJJJIIIIIHHHHHHHGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸·······¶¶¶¶¶µµµµµ´´´´³³³³³²²²²²±±°°°¯¯¯¯®®­­¬¬¬««ªª©©¨¨§¦¦¥¥¤££¢¡  Ÿžœ›š™˜——–””“‘ŽŒ‹Š‰ˆ†…„ƒ‚~}|{zywvutsrqponlkkihhgfedcbba`__^]\\[ZZYYXWWVVUUTTSSSRRQQPPPPOOONNMMMMMLLLLLKKKKJJJJJIIIIIHHHHHHHGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»ºººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµµµ´´´³³³³²²²²±±±±±°°¯¯¯®®®­­­¬¬«ªªª©©¨§§§¦¦¥¤¤£¢¡¡ Ÿžžœ››š™˜—–•”“’‘ŽŒ‹Š‰‡†…„ƒ‚~}|{zyxvutsrqponmlkjihgfeddcbaa`_^^]\[[ZYYXXXWVVUUUTSSRRRQQQPPPOONNNNNMMMMLLLLKKKJJJJJIIIIIIIHHHHHHHHGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµµµµµ´´´´´³³³³²²²±±±°°°°¯¯¯¯®®­­¬¬¬««ªª©©¨¨§§¦¥¥¥¤£¢¢¡ ŸŸžœœ›š™™˜—–•”“’‘ŽŒ‹Šˆ‡†…„ƒ‚~}|{zyxwutsrqponmlkjihgffedccba``_^]]\[ZZZYXXWWVVUUTTSSSRRQQPPPPOOOONNNMMMLLLLKKKKKJJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»ºººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶µµµµµ´´´´´³³³³³²²²²²±±±°°¯¯¯¯®®®­­­¬«««ªªª©¨¨§§¦¦¥¤¤££¢¡¡ ŸŸžœ›šš™˜—––”““’‘Ž‹‹‰ˆ‡†…„ƒ‚~}|{zyxwvttrqponmllkiihgfeedcba``_^^]\\[[ZYYXXWWVUUUTTTSRRRQQQPPPPOONNNMMMMMLLLLLKKKKKJJJJJIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶µµµµµ´´´´³³³³²²²²±±±±±°°°¯¯¯®®­­­¬¬¬««ªª©©©¨¨§¦¦¥¥¤¤£¢¢¡ ŸŸžžœ›š™˜˜—–•”“’‘ŽŒ‹Š‰ˆ‡†…„ƒ‚~}|{zyxwvutsrqponmlkjihggfedcbaa``_^]]\[[ZZYYXWWVVVUUTTSSSRRRQQPPPOOONNNNNMMMMLLLLKKKKJJJJJIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµµµµ´´´´´³³³³²²²±±±±°°°¯¯¯¯®®®­­¬¬««««ªª©©¨§§§¦¦¥¤¤££¢¡  ŸŸžœœ›š™˜—––•”“’‘ŽŒ‹Š‰ˆ‡†…„ƒ‚~}|{zyxwvutsrqponmlkjiihgfedccba``__^]\\[[ZYYXXXWVVUUTTTTSSRRQQQPPPPOOONNNNMMMLLLLKKKKKJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶µµµµµµ´´´´´´³³³³³²²²²²±±±±°°¯¯¯®®®­­­­¬¬««ªª©©©¨¨§§¦¥¥¥¤££¢¡¡ ŸŸžœ›šš™˜—–•”““’‘ŽŒ‹Š‰ˆ‡†…„ƒ‚~}|{zyxwvutsrqponmllkjihgfeedcbba``_^^]\\[ZZZYXXWWVVVUUTTSSRRRRQQQPPPOONNNNMMMMMLLLLLKKKKKKJJJJJJIIIIIIHHHHHHHHHGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶µµµµµµ´´´´´³³³³²²²²±±±±±°°°°¯¯¯®®®­­¬¬¬«««ªª©©¨¨§§¦¦¥¥¤£££¢¡  ŸŸžœœ›š™˜˜—–•”“’‘ŽŒ‹Š‰ˆ‡†…„ƒ‚~}|{zyxwvutsrqpponmlkjihggfedccba``__^]\\\[ZZYYXXWWVVUUTTTSSSRRQQQPPPOOOONNNNNMMMMLLLLKKKKKJJJJJJIIIIIIHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»ºººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµµµµ´´´´´³³³³²²²²±±±°°°¯¯¯¯®®®®­­­¬¬««ªªª©©©¨§§¦¦¥¥¤¤£¢¢¡¡ ŸŸžœ›š™™˜—–••”“’‘ŽŒ‹‹Š‰ˆ‡†…„ƒ‚~}|{zyxwvuttsrqponmlkjjihgffedcbba``_^^]]\[[ZZYYXXWVVVUUUTTSSRRRQQQQPPPPOOONNNMMMMLLLLKKKKKJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµµµµµ´´´´´³³³³³²²²²²±±±±°°°¯¯¯®®®­­­¬¬¬«««ªª©©¨¨§§§¦¦¥¤¤££¢¢¡ ŸŸžžœ››š™™˜—–••”“’‘ŽŒ‹Š‰ˆ‡†…„ƒ‚€~}|{zyxwvutsrqponmlkjjihgffeddcbaa``_^]]\\[[ZYYXXXWWVVUUTTTSSSRRRQQQPPPOOONNNNMMMMMLLLLLKKKKKJJJJJJJIIIIIIIHHHHHHHHGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶¶¶µµµµµµ´´´´´³³³³³²²²²±±±±±°°°°¯¯¯¯®®®­­­¬¬««ªªª©©©¨¨§§¦¥¥¥¤¤£¢¢¡¡ ŸŸžœœ››š™˜——–•”“’’‘ŽŒ‹Š‰ˆ‡†…„ƒ‚€~}|{zyxwvutsrqponmmlkjihhgfeddccba``_^^]]\[[ZZZYXXWWVVVUUUTTSSRRRQQQPPPPOOOONNNNNMMMMLLLLLKKKKKJJJJJJIIIIIIIIHHHHHHHHHGGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶¶¶µµµµµµ´´´´´³³³³³²²²²±±±±°°°¯¯¯¯®®®­­­­¬¬¬««ªª©©¨¨¨§§¦¦¥¥¤£££¢¢¡ ŸŸžžœœ›š™™˜—–••”“’‘ŽŒ‹Š‰ˆ‡†…„ƒ‚€~}|{zyxwvutsrqpponmlkjjihgffedccbaa``_^]]\\\[ZZYYXXWWWVVUUTTSSSRRRRQQQPPPPOOONNNNMMMMLLLLLKKKKKJJJJJJIIIIIIIIHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»ºººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸·······¶¶¶¶¶¶¶µµµµµµµ´´´´´³³³³³²²²²²±±±±±°°°¯¯¯¯®®­­­¬¬¬«««ªªª©©¨¨§§¦¦¥¥¤¤££¢¢¡  ŸŸžœ››š™˜——–••”“’‘ŽŒ‹Š‰ˆ‡†…„ƒ‚€~}|{zyxwvutsrqpponmlkjjihhgfeddcbba``__^]]\\[[ZZYYXXWWVVUUUTTTSSSRRRQQPPPPOOONNNNNMMMMMLLLLLKKKKKJJJJJJJIIIIIIIHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»ºººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµµµµµ´´´´´´³³³³³²²²²²±±±±°°°°¯¯¯¯®®®®­­­¬¬¬««ªª©©©¨¨§§§¦¦¥¥¤££¢¢¡¡ ŸŸžœœ›š™™˜——–•”“’’‘ŽŒŒ‹Š‰ˆ‡†…„ƒ‚€~}|{zyxwvutssrqponmmlkjihhgffedccbba``_^^]]\\[ZZYYXXXWWVVVUUTTSSSRRRQQQQPPPPOOOONNNNMMMMMLLLLLKKKKKKJJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸··········¶¶¶¶¶¶¶¶µµµµµµµ´´´´´´³³³³³²²²²²±±±±°°°¯¯¯¯®®®­­­¬¬¬«««ªªª©©¨¨§§¦¦¥¥¥¤¤££¢¡¡  ŸŸžœ›šš™™˜—–••”“’’‘ŽŒ‹ŠŠ‰ˆ‡†…„ƒ‚€~}|{zyxwvuutsrqponmmlkjjihgffeedcbba``__^^]\\[[ZZZYYXXWWVVUUUTTTSSSRRRQQQPPPPOOONNNNMMMMMLLLLLKKKKKKJJJJJJJIIIIIIIIHHHHHHHHHHGGGGGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸··········¶¶¶¶¶¶¶µµµµµµµ´´´´´³³³³³²²²²²±±±±±°°°°¯¯¯¯®®®­­­¬¬«««ªª©©©¨¨¨§§¦¦¥¥¤¤££¢¢¡¡ ŸŸžœœ›šš™˜——–•””“’‘ŽŒ‹ŠŠ‰ˆ‡†…„ƒ‚€~}|{zyxwvuutsrqpponmlkkjihhgfeedccbba``_^^]]\\[[ZZYYXXWWWVVVUUTTTSSRRRQQQPPPPOOOONNNNNMMMMMLLLLLKKKKKJJJJJJJIIIIIIIHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶µµµµµµ´´´´´´´³³³³³²²²²²±±±±±°°°¯¯¯¯®®®®­­­­¬¬¬«««ªª©©©¨¨§§¦¦¥¥¤¤¤££¢¡¡ ŸŸžžœ››š™˜˜—––•”““’‘ŽŽŒ‹Š‰‰ˆ‡†…„ƒ‚€~}|{zyxwvvutsrqqponmllkjiihggfeddcbbaa``_^^]\\[[[ZZYYXXWWVVVUUTTTSSSRRRRQQQQPPPPOOONNNNNMMMMMLLLLLKKKKKKKJJJJJJIIIIIIIHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶¶¶¶µµµµµµµ´´´´´´´³³³³³²²²²²±±±±±°°°¯¯¯¯®®®­­­¬¬¬«««ªªª©©©¨¨§§§¦¦¥¥¤¤£¢¢¡¡  ŸŸžžœ››šš™˜˜—–••”“’‘‘ŽŽŒ‹Š‰ˆ‡‡†…„ƒ‚€~}|{zyxxwvutsrqqponnmlkjjihggfeeddcbaa``__^^]]\[[ZZYYXXXWWVVVUUUTTTSSSRRRQQQPPPPOOONNNNNMMMMMLLLLLKKKKKKKJJJJJJJIIIIIIIIIHHHHHHHHHGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸···········¶¶¶¶¶¶¶¶¶µµµµµµµ´´´´´³³³³³²²²²²±±±±±°°°°¯¯¯¯®®®®­­­¬¬¬«««ªª©©©¨¨§§¦¦¥¥¥¤¤££¢¢¡¡ ŸŸžžœ››š™™˜—––•””“’‘‘ŽŒŒ‹Š‰ˆ‡‡†…„ƒ‚€~}|{zyxxwvutssrqponnmlkkjiihgffeddcbbaa``_^^]]\\[[ZZZYYXXWWVVVUUTTTSSSRRRQQQQPPPPOOOONNNNNMMMMMLLLLLKKKKKJJJJJJJIIIIIIIIIHHHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸··········¶¶¶¶¶¶¶µµµµµµ´´´´´´³³³³³³²²²²²²±±±±°°°°¯¯¯¯®®®®­­­¬¬¬«««ªªª©©©¨¨¨§§¦¦¥¥¤¤££¢¢¡¡  ŸŸžžœœ›šš™˜˜—––•”““’‘ŽŒ‹‹Š‰ˆ‡††…„ƒ‚€~}|{zyyxwvuttsrqpponmllkjiihggfeedccbaa``__^^]]\\[[ZZYYXXWWWVVVUUUTTTSSSRRRQQQQPPPPOOOONNNNMMMMMMLLLLLLKKKKKKJJJJJJIIIIIIIHHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´³³³³³³²²²²²²±±±±°°°°¯¯¯¯®®®®­­­¬¬¬«««ªªª©©¨¨§§§¦¦¥¥¤¤¤££¢¢¡¡ ŸŸžžœœ››š™™˜——–••”“’’‘ŽŽŒ‹ŠŠ‰ˆ‡†……„ƒ‚€~}|{zzyxwvuutsrqqponmmlkjjihhgffeddccbaa``_^^]]\\[[[ZZYYXXXWWVVUUUTTTSSSRRRQQQQPPPPOOOONNNNMMMMMMLLLLLLKKKKKKJJJJJJJJIIIIIIIIHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸··········¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´³³³³³²²²²²±±±±±°°°°¯¯¯¯®®®®­­­¬¬¬¬«««ªªª©©©¨¨§§§¦¦¥¥¤¤££¢¢¡¡  ŸŸžœœ›šš™˜˜—––•”““’‘‘ŽŽŒ‹ŠŠ‰ˆ‡†……„ƒ‚€~}|{zzyxwvuutsrqqponnmllkjiihggfeedccbba``__^^]]\\[[ZZYYXXXWWVVVUUUTTTSSSSRRRQQQQPPPPOOOONNNNNMMMMMLLLLLKKKKKJJJJJJJJIIIIIIIIIIHHHHHHHHHHGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶µµµµµµ´´´´´´³³³³³³²²²²²²±±±±±°°°°¯¯¯¯¯®®®­­­­¬¬¬«««ªªª©©¨¨¨§§¦¦¥¥¥¤¤£££¢¢¡¡ ŸŸžžœ››š™™˜——––•”““’‘‘ŽŒ‹Š‰‰ˆ‡†…„„ƒ‚€~}|{{zyxwvvutsrrqponnmllkjiihhgffeddcbbaa``_^^]]\\\[[ZZZYYXXWWWVVUUUTTTSSSRRRRQQQPPPPPOOOONNNNNMMMMMMLLLLLLKKKKKKJJJJJJIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸··········¶¶¶¶¶¶¶µµµµµµµµ´´´´´´´³³³³³³²²²²²²±±±±±°°°°¯¯¯¯®®®­­­­¬¬¬«««ªªª©©©¨¨¨§§¦¦¥¥¥¤¤££¢¢¡¡ ŸŸžžœœ›šš™™˜——–••”“’’‘ŽŒŒ‹Š‰‰ˆ‡†…„„ƒ‚€~}|{{zyxwvvutssrqpponmmlkjjihhgffeedccbbaa``_^^]]\\[[ZZZYYXXWWWVVVUUUTTTSSSRRRRQQQPPPPOOOONNNNNMMMMMMLLLLLLKKKKKKKJJJJJJJJIIIIIIIHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶¶¶¶¶µµµµµµµµµ´´´´´´³³³³³²²²²²±±±±±°°°°¯¯¯¯¯®®®®­­­¬¬¬¬«««ªªª©©©¨¨§§§¦¦¥¥¤¤¤££¢¢¡¡  ŸŸžžœ››šš™˜˜—––•””“’‘‘ŽŒ‹‹Š‰ˆˆ‡†…„„ƒ‚€~}|{{zyxwwvuttsrqpponnmlkkjiihggfeeddcbbaa``__^^]]\\[[[ZZYYXXXWWVVVUUUTTTSSSSRRRQQQQPPPPPOOOONNNNNMMMMMLLLLLKKKKKKJJJJJJJJJIIIIIIIIIIHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶µµµµµµ´´´´´´³³³³³³³²²²²²±±±±±±°°°°¯¯¯¯¯®®®®­­­¬¬¬¬«««ªªª©©©¨¨§§§¦¦¥¥¤¤£££¢¢¡¡ ŸŸžžœœ›šš™™˜——–••”““’‘ŽŽŒ‹‹Š‰ˆ‡‡†…„ƒƒ‚€~}||{zyxxwvuttsrqqpoonmllkjjihhgffeedccbbaa``_^^]]\\\[[ZZYYXXXWWVVVUUUTTTSSSSRRRQQQQPPPPPOOOONNNNNNMMMMMLLLLLLLKKKKKKJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸·············¶¶¶¶¶¶¶µµµµµµµ´´´´´´´´³³³³³³³²²²²²±±±±±°°°°¯¯¯¯®®®­­­­¬¬¬««««ªªª©©©¨¨§§§¦¦¦¥¥¤¤£££¢¢¡¡  ŸŸžžœ››šš™™˜——–••”““’‘ŽŒ‹ŠŠ‰ˆ‡‡†…„ƒƒ‚€~}||{zyxxwvuutsrrqpoonmllkjjihhgffeeddcbbaa``__^^]]\\\[[ZZYYYXXXWWVVVUUUTTTTSSSRRRRQQQPPPPOOOONNNNNMMMMMLLLLLLLKKKKKKKKJJJJJJJIIIIIIIHHHHHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶¶¶¶µµµµµµµµµ´´´´´´´³³³³³²²²²²±±±±±°°°°°¯¯¯¯¯®®®®­­­­¬¬¬««««ªªª©©©¨¨§§§¦¦¦¥¥¤¤££¢¢¡¡¡ ŸŸžžœœ››š™™˜——––•””“’’‘ŽŒ‹ŠŠ‰ˆ‡‡†…„ƒƒ‚€~}||{zyxxwvuutsrrqpponmmlkkjiihhgffeddccbbaa``_^^^]]\\[[ZZYYYXXXWWVVVUUUTTTTSSSRRRRQQQQPPPPPOOOOONNNNNMMMMMLLLLLKKKKKKKJJJJJJJJJIIIIIIIIIHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´³³³³³³³²²²²²²±±±±±°°°°°¯¯¯¯®®®®­­­­¬¬¬«««ªªª©©©¨¨¨§§§¦¦¥¥¥¤¤££¢¢¡¡  ŸŸžžœœ››šš™˜˜——–••”““’‘‘ŽŒŒ‹Š‰‰ˆ‡††…„ƒƒ‚€~}||{zyyxwvvutssrqpponnmllkjjihhggfeeddccbbaa``__^^]]\\[[ZZZYYXXXWWWVVVUUUTTTSSSRRRRQQQQPPPPOOOOONNNNNMMMMMMLLLLLLLKKKKKJJJJJJJJIIIIIIIIIIIHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸·············¶¶¶¶¶¶¶¶¶µµµµµµ´´´´´´´´³³³³³³³³²²²²²±±±±±°°°°¯¯¯¯®®®®®­­­­¬¬¬««««ªªª©©©¨¨¨§§§¦¦¥¥¥¤¤££¢¢¡¡  ŸŸžžœœ››šš™˜˜——–••”““’‘ŽŽŒŒ‹Š‰‰ˆ‡††…„ƒƒ‚€~}||{zyyxwvvutssrqqpoonmllkjjihhggfeeddccbbaa``__^^]]\\[[ZZZYYXXXWWWVVVUUUTTTTSSSRRRRQQQQQPPPPOOOONNNNNMMMMMLLLLLLLLKKKKKKKKJJJJJJIIIIIIIIIHHHHHHHHHHHHHGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸·············¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´³³³³³²²²²²±±±±±±°°°°°¯¯¯¯¯®®®®®­­­¬¬¬««««ªªª©©©¨¨§§§¦¦¦¥¥¤¤¤££¢¢¡¡  ŸŸŸžœœ››šš™™˜——––•””“’’‘ŽŒ‹‹Š‰‰ˆ‡††…„ƒƒ‚€~}||{zyyxwvvuttsrrqpoonmmlkkjiihhgffeeddccbba```__^^]]\\[[[ZZYYYXXXWWVVVUUUTTTTSSSRRRQQQQQPPPPPOOOOONNNNNNMMMMMLLLLLKKKKKKKKJJJJJJJJJJIIIIIIHHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´³³³³³³³²²²²²²±±±±±±°°°°°¯¯¯¯®®®­­­­¬¬¬¬««««ªªª©©©¨¨¨§§§¦¦¦¥¥¤¤¤££¢¢¡¡  ŸŸžžœœ››š™™˜˜—––••”““’‘‘ŽŒ‹‹Š‰ˆˆ‡†……„ƒ‚‚€~}}|{zzyxwwvuttsrrqpponnmllkjjiihggffeddccbbaa``__^^]]\\[[[ZZYYYXXXWWWVVVUUUTTTTSSSSRRRRQQQPPPPOOOOONNNNNNMMMMMMLLLLLLLKKKKKJJJJJJJJJJIIIIIIIIIIHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸···········¶¶¶¶¶¶¶¶¶¶¶µµµµµµ´´´´´´´´³³³³³³³³²²²²²±±±±±°°°°¯¯¯¯¯®®®®®­­­­¬¬¬¬«««ªªª©©©¨¨¨§§¦¦¦¥¥¥¤¤£££¢¢¡¡  ŸŸŸžžœ››šš™™˜˜—––•””“’’‘‘ŽŒ‹ŠŠ‰ˆˆ‡†……„ƒ‚‚€~}}|{zzyxwwvuutsrrqpponnmmlkkjiihggffeeddcbbaa```__^^]]\\\[[ZZZYYYXXWWWVVVUUUTTTSSSSRRRRQQQQQPPPPPOOOONNNNNMMMMMLLLLLLLLKKKKKKKKJJJJJJIIIIIIIIIIIHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´´´´³³³³³²²²²²²±±±±±±°°°°°¯¯¯¯¯®®®®­­­¬¬¬¬««««ªªª©©©¨¨¨§§§¦¦¦¥¥¥¤¤££¢¢¢¡¡  ŸŸžžœœ››š™™˜˜——––•””“’’‘ŽŽŒŒ‹ŠŠ‰ˆ‡‡†……„ƒ‚‚€~}}|{zzyxxwvuutssrqqpoonmmlkkjiihhggffeddccbbaa``__^^]]]\\[[ZZZYYYXXXWWWVVVUUUTTTTSSSSRRRQQQQPPPPPOOOOONNNNNNMMMMMMLLLLLKKKKKKKKKJJJJJJJJIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´³³³³³³³²²²²²²²±±±±±°°°°°¯¯¯¯®®®®­­­­­¬¬¬¬««««ªªª©©©¨¨§§§¦¦¦¥¥¤¤¤£££¢¢¡¡  ŸŸŸžžœœ›šš™™˜——––••”““’’‘ŽŽŒŒ‹Š‰‰ˆ‡‡†……„ƒ‚‚€~}}|{zzyxxwvvutssrqqpoonmmllkjjiihhgffeedccbbaa```__^^]]\\\[[[ZZYYYXXXWWVVVUUUTTTTSSSSRRRRRQQQQPPPPOOOOONNNNNMMMMMMMLLLLLLLKKKKKKJJJJJJJJJJIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAA¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµ´´´´´´´´³³³³³³³³²²²²²±±±±±°°°°°¯¯¯¯¯¯®®®®­­­­¬¬¬««««ªªª©©©©¨¨¨§§§¦¦¦¥¥¤¤¤££¢¢¡¡  ŸŸŸžžœœ››šš™™˜——––•””““’‘‘ŽŒ‹‹Š‰‰ˆ‡‡†…„„ƒ‚‚€~}}|{{zyxxwvvuttsrrqpponnmllkkjiihhgffeeddccbbaa```__^^]]\\[[[ZZYYYXXXWWWVVVVUUUTTTTSSSRRRRQQQQPPPPPPOOOOONNNNNMMMMMLLLLLLLLKKKKKKKKJJJJJJJIIIIIIIIIIIIHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶µµµµµµµ´´´´´´´´´´³³³³³²²²²²²±±±±±±±°°°°°¯¯¯¯®®®®­­­­¬¬¬¬¬««««ªªª©©©¨¨¨§§¦¦¦¥¥¥¤¤¤££¢¢¢¡¡  ŸŸŸžœœ››šš™™˜˜——–••””“’’‘‘ŽŒ‹‹Š‰‰ˆ‡‡†…„„ƒ‚‚€~}}|{{zyxxwvvuttsrrqpponnmmlkkjjihhggffeeddccbba```__^^]]]\\[[[ZZZYYYXXWWWVVVUUUTTTTSSSSSRRRRQQQQPPPPOOOOONNNNNNNMMMMMMLLLLLKKKKKKKKKJJJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´³³³³³³³²²²²²²²±±±±±±°°°°¯¯¯¯¯®®®®®­­­­¬¬¬¬«««ªªª©©©©¨¨¨§§§¦¦¦¥¥¥¤¤££¢¢¢¡¡  ŸŸŸžžœœ››šš™˜˜——––••”““’’‘ŽŽŒ‹‹Š‰‰ˆ‡††…„„ƒ‚‚€~}}|{{zyyxwvvuttsrrqqpoonmmllkjjiihhggfeeddccbbaa```__^^]]]\\[[ZZZYYYXXXWWWVVVVUUUTTTSSSSRRRRQQQQQPPPPPOOOONNNNNNMMMMMMMLLLLLLLKKKKKKJJJJJJJJJJJIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´´³³³³³³³³³²²²²²±±±±±°°°°°°¯¯¯¯¯¯®®®®­­­¬¬¬¬«««««ªªª©©©©¨¨§§§¦¦¦¥¥¥¤¤£££¢¢¢¡¡  ŸŸžžœœ››šš™™˜˜——–••””““’‘‘ŽŽŒŒ‹ŠŠ‰ˆˆ‡††…„„ƒ‚‚€~}}|{{zyyxwwvuutssrqqpponnmllkkjjihhggffeeddccbbaa``__^^]]]\\\[[ZZZYYYXXXWWVVVVUUUTTTTTSSSSRRRQQQQPPPPPPOOOOOONNNNNMMMMMLLLLLLLLLKKKKKKKJJJJJJJJIIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµ´´´´´´´´´³³³³³³²²²²²²²±±±±±±°°°°°¯¯¯¯®®®®®­­­­­¬¬¬¬««««ªªª©©©¨¨¨§§§§¦¦¦¥¥¤¤¤££¢¢¢¡¡  ŸŸŸžžœœ››šš™™˜˜——–••””“’’‘‘ŽŒŒ‹ŠŠ‰ˆˆ‡††…„„ƒ‚‚€~}}|{{zyyxwwvuutssrrqpponnmmlkkjjihhggffeeddccbbaa```__^^]]]\\[[[ZZYYYXXXXWWWVVVUUUTTTTSSSSRRRRRQQQQQPPPPOOOOONNNNNNMMMMMMMLLLLLLKKKKKKKKKJJJJJJJIIIIIIIIIIIIHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·············¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´³³³³³³³²²²²²²²²±±±±±°°°°¯¯¯¯¯¯®®®®®­­­­¬¬¬««««ªªªª©©©©¨¨¨§§¦¦¦¥¥¥¤¤¤£££¢¢¢¡¡  ŸŸžžœœœ››šš™˜˜——––••””“’’‘ŽŒ‹‹Š‰‰ˆˆ‡††…„„ƒ‚‚€~}}|{{zyyxwwvvuttsrrqppoonmmlkkjjiihhggfeeddcccbbaa``__^^]]]\\\[[[ZZZYYYXXWWWVVVVUUUUTTTTSSSRRRRQQQQQPPPPPPOOOONNNNNMMMMMMMMLLLLLLLKKKKKKKJJJJJJJJJJJIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´³³³³³³³³³²²²²²±±±±±±°°°°°°¯¯¯¯¯®®®®­­­­­¬¬¬¬««««ªªª©©©¨¨¨¨§§§¦¦¦¥¥¥¤¤£££¢¢¡¡¡  ŸŸŸžžœœ››šš™™˜˜——–••””““’‘‘ŽŒ‹‹Š‰‰ˆ‡‡†……„ƒƒ‚€~~}||{zzyxxwvvuttsrrqppoonnmllkkjjihhggffeeddccbbaa```__^^^]]\\\[[ZZZYYYXXXWWWWVVVUUUTTTTSSSSRRRRRQQQQPPPPPOOOOOONNNNNNMMMMMLLLLLLLLLKKKKKKKJJJJJJJJJJIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸···········¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµ´´´´´´´´´´³³³³³³²²²²²²²±±±±±±±°°°°¯¯¯¯¯®®®®®®­­­­¬¬¬¬«««ªªªª©©©©¨¨¨§§§¦¦¦¥¥¥¤¤¤£££¢¢¡¡  ŸŸŸžžœœ›šš™™˜˜——––••””““’‘‘ŽŽŒŒ‹‹Š‰‰ˆ‡‡†……„ƒƒ‚€~~}||{zzyxxwvvuttssrqqpponnmllkkjjiihhggffeedccbbbaa```__^^]]\\\[[[ZZZYYYXXXWWWVVVVUUUUTTTSSSSRRRRQQQQQQPPPPPOOOONNNNNNNMMMMMMMLLLLLLKKKKKKKKKKJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··········¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´³³³³³³³²²²²²²²²±±±±±°°°°°¯¯¯¯¯¯®®®®­­­­¬¬¬¬¬««««ªªªª©©¨¨¨¨§§§§¦¦¦¥¥¤¤¤£££¢¢¡¡¡  ŸŸžžœœ››šš™™˜˜——––••”““’’‘‘ŽŽŒŒ‹ŠŠ‰‰ˆ‡‡†……„ƒƒ‚€~~}||{zzyxxwvvuutssrqqpponnmmllkjjiihhggffeeddccbbbaa``__^^^]]\\\[[[ZZYYYXXXXWWWWVVUUUUTTTTSSSSSRRRRQQQQPPPPPPOOOOONNNNNMMMMMMMMLLLLLLLKKKKKKKKJJJJJJJJJJIIIIIIIIIIIHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´³³³³³³³³³²²²²²±±±±±±±°°°°°°¯¯¯¯®®®®®­­­­­¬¬¬¬«««ªªªªª©©©¨¨¨¨§§¦¦¦¥¥¥¥¤¤¤££¢¢¢¡¡  ŸŸŸžžœœ›››šš™™˜˜—––••””““’’‘ŽŒŒ‹ŠŠ‰‰ˆ‡‡†……„ƒƒ‚€~~}||{zzyxxwvvuutssrrqppoonmmllkkjjiihggffeedddccbbaa```__^^]]]\\[[[ZZZZYYYXXWWWWVVVUUUUUTTTSSSSRRRRRQQQQQPPPPOOOOOONNNNNNNMMMMMLLLLLLLLLKKKKKKKJJJJJJJJJJJIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸··············¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´´´´´³³³³³³²²²²²²²±±±±±±±°°°°°¯¯¯¯¯¯®®®®®­­­¬¬¬¬¬««««ªªªª©©©¨¨¨§§§§¦¦¦¥¥¥¤¤£££¢¢¢¡¡¡  ŸŸžžžœœ››šš™™˜˜——––••””““’‘‘ŽŒŒ‹ŠŠ‰ˆˆ‡‡†……„ƒƒ‚€~~}||{zzyxxwwvuutssrrqppoonnmllkkjjiihhggffeeddccbbaaa``__^^^]]]\\\[[ZZZYYYXXXXWWWVVVUUUUTTTTSSSSSRRRQQQQQPPPPPPOOOOONNNNNNNMMMMMMMLLLLLLKKKKKKKKKKJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸···········¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´´´´³³³³³³³²²²²²²²²±±±±±°°°°°°¯¯¯¯¯¯®®®®­­­­­¬¬¬¬¬«««ªªªª©©©©¨¨¨§§§¦¦¦¥¥¥¤¤¤£££¢¢¡¡¡  ŸŸŸžžœœ›››šš™™˜˜——––••”““’’‘‘ŽŽŒ‹‹ŠŠ‰ˆˆ‡††……„ƒƒ‚€~~}||{zzyyxwwvuuttsrrqqpponnmmllkjjiihhggffeedddccbbaa```__^^^]]\\\[[[ZZZYYYXXXWWWVVVVUUUUTTTSSSSSRRRRRQQQQPPPPPPOOOOOONNNNNMMMMMMMMLLLLLLLKKKKKKKKKJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´³³³³³³³³³³²²²²²±±±±±±±±°°°°°¯¯¯¯¯®®®®®®­­­­¬¬¬¬««««ªªªªª©©¨¨¨¨§§§¦¦¦¦¥¥¤¤¤£££¢¢¢¡¡¡  ŸŸžžžœœ››šš™™™˜——––••””““’’‘‘ŽŽŒŒ‹‹Š‰‰ˆˆ‡††……„ƒƒ‚€~~}||{zzyyxwwvvuttssrqqpponnmmllkkjjiihhgfffeeddccbbaaa``__^^^]]]\\\[[[ZZYYYYXXXWWWWVVUUUUUTTTTSSSSRRRRQQQQQQPPPPPOOOOONNNNNNNNMMMMMLLLLLLLLLLKKKKKKJJJJJJJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´´³³³³³³³²²²²²²²²±±±±±±°°°°°¯¯¯¯¯¯¯®®®®­­­­­¬¬¬¬¬«««ªªªª©©©©¨¨¨¨§§¦¦¦¥¥¥¥¤¤¤££¢¢¢¡¡¡  ŸŸŸžžœœœ››šš™™˜˜——––••””““’‘‘ŽŽŒŒ‹‹Š‰‰ˆˆ‡††…„„ƒƒ‚€~~}||{{zyyxwwvvuttssrqqppoonnmllkkjjiihhggffeeddcccbbaa```__^^^]]]\\[[[ZZZZYYYXXWWWWVVVVUUUUTTTSSSSSRRRRRQQQQPPPPPPPOOOOONNNNNNMMMMMMMMLLLLLLLKKKKKKKKKJJJJJJJJJJIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´´´´´³³³³³³³²²²²²²²²±±±±±°°°°°°°¯¯¯¯¯®®®®®­­­­­­¬¬¬««««ªªªªª©©©¨¨¨§§§§¦¦¦¥¥¥¤¤¤£££¢¢¢¡¡  ŸŸŸžžžœœ››ššš™˜˜———––•””““’’‘‘ŽŒŒ‹ŠŠ‰‰ˆ‡‡††…„„ƒƒ‚€~~}||{{zyyxxwvvuutssrrqppoonnmmllkkjiihhhggfeeeddccbbaaa```__^^]]]\\\[[[ZZZYYYXXXXWWWVVVUUUUUTTTTSSSRRRRRRQQQQQPPPPPOOOOOOONNNNNMMMMMMMMLLLLLLLKKKKKKKKKKJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´³³³³³³³³³³²²²²²±±±±±±±±°°°°°¯¯¯¯¯¯®®®®®®­­­¬¬¬¬¬«««««ªªª©©©©¨¨¨¨§§§¦¦¦¥¥¥¤¤¤¤££¢¢¢¡¡¡  ŸŸžžžœœœ››šš™™˜˜——––••””““’’‘‘ŽŽŒŒ‹ŠŠ‰‰ˆ‡‡††…„„ƒƒ‚€~~}||{{zyyxxwvvuutssrrqqpponnmmllkkjjiihhggffeeddcccbbaaa``__^^^]]]\\[[[[ZZZYYYXXXWWWWVVVVUUUTTTTTSSSSSRRRQQQQQQPPPPPPOOOOONNNNNNNNMMMMMLLLLLLLLLLKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·············¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´´´³³³³³³³²²²²²²²²±±±±±±°°°°°°¯¯¯¯¯¯¯®®®®­­­­­¬¬¬¬««««ªªªªª©©©¨¨¨§§§§¦¦¦¦¥¥¤¤¤££££¢¢¡¡¡  ŸŸŸžžœœ›››š™™™˜˜——––••””““’’‘‘ŽŽŒ‹‹ŠŠ‰ˆˆ‡‡††…„„ƒƒ‚€~~}||{{zyyxxwwvuuttsrrqqpponnmmllkkjjiihhggfffedddccbbbaa```__^^^]]\\\\[[[ZZYYYYXXXXWWWVVVUUUUUTTTTSSSSRRRRRQQQQPPPPPPPOOOOOONNNNNNMMMMMMMMLLLLLLLKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´´´´´´³³³³³³³²²²²²²²²±±±±±±°°°°°°°¯¯¯¯®®®®®®®­­­­¬¬¬¬«««««ªªª©©©©¨¨¨¨§§§¦¦¦¦¥¥¥¤¤¤££¢¢¢¢¡¡¡  ŸŸžžžœ›››šš™™˜˜———––•””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰ˆˆ‡‡†……„„ƒ‚‚€~~}}|{{zzyxxwwvuuttssrqqppoonnmmllkkjiihhhggffeedddcbbbaaa``__^^^]]]]\\[[[ZZZYYYYXXXWWWWVVVVUUUTTTTTSSSSRRRRQQQQQQQPPPPOOOOOOONNNNNNMMMMMMMMLLLLLLLKKKKKKKKKKKJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´³³³³³³³³³³²²²²²²±±±±±±±±°°°°¯¯¯¯¯¯¯®®®®®­­­­­¬¬¬¬¬«««ªªªªª©©©©¨¨¨§§§§¦¦¦¥¥¥¤¤¤£££¢¢¢¡¡¡  ŸŸŸžžœœ››ššš™™˜˜——––••””““’’‘‘ŽŽŒŒ‹‹Š‰‰ˆˆ‡‡†……„„ƒ‚‚€~~}}|{{zzyxxwwvvuttssrrqqpoonnmmllkkjjiihhggffeeeddccbbbaa```__^^^]]]\\\[[[ZZZYYYXXXXWWWVVVVUUUUUTTTSSSSSRRRRRQQQQQPPPPPPPOOOONNNNNNNNMMMMMMLLLLLLLLLLKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBB½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·············¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´³³³³³³³²²²²²²²²²±±±±±°°°°°°°¯¯¯¯¯¯®®®®®­­­­­¬¬¬¬«««««ªªªª©©©©¨¨¨¨§§§¦¦¦¥¥¥¥¤¤¤££¢¢¢¡¡¡  ŸŸŸžžžœœœ››šš™™˜˜˜——––••””““’’‘ŽŽŒŒ‹‹Š‰‰ˆˆ‡‡†……„„ƒ‚‚€~~}}|{{zzyxxwwvvuttssrrqqppoonmmllkkjjiihhgggffeeddcccbbaaa```__^^^]]]\\[[[ZZZZYYYXXXWWWWVVVVUUUUTTTTTSSSSRRRRRQQQQQPPPPPPOOOOOOONNNNNMMMMMMMMMLLLLLLLKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIHHHHHHHHHHHHHGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBB½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµ´´´´´´´´´´´³³³³³³³²²²²²²²²±±±±±±±°°°°°°¯¯¯¯¯¯®®®®®­­­­¬¬¬¬¬¬««««ªªªª©©©©©¨¨§§§§¦¦¦¦¥¥¤¤¤¤£££¢¢¡¡¡   ŸŸŸžžœœ››ššš™™˜˜——––••”””“’’‘‘ŽŽŒ‹‹ŠŠ‰‰ˆˆ‡††……„„ƒ‚‚€~~}}|{{zzyyxwwvvuuttsrrqqppoonnmmlkkkjjiihhggffeeeddccbbbaa```___^^^]]\\\[[[[ZZYYYYXXXXWWVVVVVUUUUTTTTSSSSSRRRRRQQQQQPPPPPPOOOOOONNNNNNNMMMMMMMMLLLLLLLKKKKKKKKKKKJJJJJJJJJIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBB½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´³³³³³³³³³³²²²²²²±±±±±±±±°°°°°¯¯¯¯¯¯¯®®®®­­­­­­¬¬¬¬«««««ªªªª©©©©¨¨¨¨§§§¦¦¦¦¥¥¥¤¤¤£££¢¢¢¡¡¡  ŸŸŸžžžœœœ››šš™™™˜˜——––••””““’’‘‘ŽŽŒ‹‹ŠŠ‰‰ˆˆ‡††……„„ƒ‚‚€~~}}|{{zzyyxwwvvuuttsrrqqppoonnmmllkkjjiihhggfffeeddcccbbaaa```__^^^]]]\\\[[[ZZZYYYYXXXWWWWVVVVUUUUTTTTTSSSRRRRRRRQQQQPPPPPPPOOOOONNNNNNNNMMMMMMLLLLLLLLLLKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBB½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´³³³³³³³³²²²²²²²²²±±±±±°°°°°°°°¯¯¯¯¯®®®®®®­­­­­¬¬¬¬¬««««ªªªª©©©©©¨¨¨§§§§¦¦¦¥¥¥¤¤¤¤£££¢¢¡¡¡   ŸŸžžžœœ›››šš™™˜˜——–––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆ‡‡††……„ƒƒ‚‚€~~}}||{zzyyxxwvvuuttssrqqppoonnmmllkkjjiiihhggffeedddccbbbaaa``___^^^]]\\\[[[[ZZZYYYXXXXWWWVVVVVUUUUTTTTSSSSSRRRRRQQQQQQPPPPPOOOOOOONNNNNNMMMMMMMMMLLLLLLLLKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´´´´´³³³³³³³²²²²²²²²±±±±±±±°°°°°°¯¯¯¯¯¯¯®®®®®­­­­­¬¬¬¬¬««««ªªªªª©©©¨¨¨¨§§§§¦¦¦¥¥¥¥¤¤¤£££¢¢¢¡¡   ŸŸŸžžœœœ››ššš™™˜˜——––••”””“’’’‘ŽŽŒŒ‹‹ŠŠ‰ˆˆ‡‡††……„ƒƒ‚‚€~~}}||{zzyyxxwwvuuttssrrqqppoonmmmlkkkjjiihhggffeeeddcccbbbaa```___^^]]]\\\[[[ZZZZYYYXXXXWWWWVVVUUUUUTTTTSSSSSRRRRRQQQQQPPPPPPPOOOOONNNNNNNNMMMMMMMMLLLLLLLKKKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´´³³³³³³³³³³²²²²²²²±±±±±±±°°°°°°¯¯¯¯¯¯®®®®®®­­­­­¬¬¬¬¬«««««ªªª©©©©©¨¨¨§§§§¦¦¦¦¥¥¥¤¤¤£££¢¢¢¡¡¡   ŸŸŸžžœœœ›››šš™™˜˜———––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰ˆˆ‡‡††……„ƒƒ‚‚€~~}}||{zzyyxxwwvuuttssrrqqppoonnmmllkkjjiihhhggffeedddcccbbaa```___^^^]]]\\\[[[ZZZYYYYXXXXWWWVVVVVUUUTTTTTSSSSSRRRRRQQQQQQPPPPPPOOOOOONNNNNNNMMMMMMMLLLLLLLLLLKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´³³³³³³³³²²²²²²²²²²±±±±±°°°°°°°°¯¯¯¯¯®®®®®®­­­­­¬¬¬¬¬««««ªªªªª©©©¨¨¨¨¨§§§¦¦¦¦¥¥¥¥¤¤££££¢¢¢¡¡   ŸŸŸžžžœœ›››ššš™™˜˜——–––••””““’’‘‘ŽŽŒŒ‹‹Š‰‰ˆˆ‡‡††……„ƒƒ‚‚€~~}}||{zzyyxxwwvvuttssrrqqppoonnmmllkkjjiiihhggffeeedddccbbaaa```___^^]]]\\\\[[ZZZZYYYYXXXWWWWWVVVUUUUUTTTTSSSSSRRRRRQQQQQQPPPPPOOOOOOOONNNNNMMMMMMMMMMLLLLLLLLKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³²²²²²²²²±±±±±±±±°°°°°¯¯¯¯¯¯¯¯®®®®­­­­­­­¬¬¬««««««ªªª©©©©©¨¨¨¨§§§§¦¦¦¥¥¥¤¤¤¤£££¢¢¢¡¡¡  ŸŸŸŸžžœœ››ššš™™˜˜˜——––••””“““’’‘‘ŽŽŒ‹‹ŠŠ‰‰ˆˆ‡‡††…„„ƒƒ‚‚€~~}}||{{zyyxxwwvvuuttsrrqqppoonnmmlllkkjjiihhgggffeeeddccbbbaa````__^^^]]]\\\[[[[ZZZYYYXXXXWWWWVVVVVUUUTTTTTTSSSRRRRRRRQQQQPPPPPPPPOOOOONNNNNNNNMMMMMMMMLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´´´³³³³³³³³³³²²²²²²²±±±±±±±°°°°°°°¯¯¯¯¯®®®®®®®­­­­¬¬¬¬¬¬««««ªªªªª©©©©¨¨¨¨§§§§¦¦¦¥¥¥¥¤¤¤£££¢¢¢¡¡¡   ŸŸŸžžžœœœ››šš™™™˜˜———––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††…„„ƒƒ‚‚€~~}}||{{zyyxxwwvvuuttssrrqqppoonnmmllkkjjiihhhggfffeeddcccbbaaa```___^^^]]]\\\[[[ZZZZYYYXXXXWWWWVVVVUUUUUTTTTSSSSSSRRRRQQQQQQQPPPPPOOOOOOONNNNNNNMMMMMMMLLLLLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´´´³³³³³³³³²²²²²²²²²²±±±±±±°°°°°°°¯¯¯¯¯¯®®®®®­­­­­­¬¬¬¬¬«««««ªªªª©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¤¤¤¤££¢¢¢¢¡¡¡  ŸŸŸŸžžœœ›››šš™™˜˜˜——–––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡†……„„ƒƒ‚‚€~~}}||{{zzyxxwwvvuuttssrrqqppoonnmmllkkjjiiihhgggffeedddccbbbaa````__^^^]]]]\\[[[[ZZZYYYYXXXXWWWWVVVVUUUUTTTTTSSSSSRRRRRRQQQQQPPPPPPOOOOOOONNNNNNMMMMMMMMMMLLLLLLLLKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³²²²²²²²²±±±±±±±±±°°°°°¯¯¯¯¯¯¯®®®®®®­­­­­¬¬¬¬¬«««««ªªªª©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤££££¢¢¢¡¡¡   ŸŸŸžžžœœœ››ššš™™˜˜———––••””“““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡††……„„ƒƒ‚‚€~~}}||{{zzyyxwwvvuuttssrrqqppoonnmmlllkkjjiihhhggffeeeddcccbbaaa```___^^^]]]\\\\[[ZZZZYYYYXXXXWWWWVVVVUUUUTTTTTSSSSSRRRRRQQQQQQPPPPPPPOOOOONNNNNNNNNMMMMMMMLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²±±±±±±±°°°°°°°°¯¯¯¯¯®®®®®®®­­­­­¬¬¬¬¬«««««ªªªª©©©©©¨¨¨¨§§§¦¦¦¦¥¥¥¥¤¤¤£££¢¢¢¢¡¡   ŸŸŸžžžœœ›››šš™™™˜˜——–––••””““’’‘‘‘ŽŽŒŒ‹‹ŠŠ‰ˆˆ‡‡††……„„ƒƒ‚‚€~~}}||{{zzyyxxwwvuuttssrrqqppoonnnmmllkkjjiiihhggfffeedddccbbbaaa```___^^]]]]\\\[[[ZZZZYYYYXXXWWWWVVVVVUUUUTTTTTSSSSSRRRRRQQQQQQPPPPPPOOOOOOOONNNNNNNMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´´´³³³³³³³³³²²²²²²²²²²±±±±±±±°°°°°°¯¯¯¯¯¯¯®®®®®­­­­­­¬¬¬¬¬«««««ªªªª©©©©©¨¨¨¨§§§§¦¦¦¥¥¥¥¤¤¤££££¢¢¢¡¡¡   ŸŸŸžžœœœ››ššš™™˜˜˜——––••”””““’’‘‘ŽŽŒ‹‹‹Š‰‰ˆˆ‡‡††……„„ƒƒ‚‚€~~}}||{{zzyyxxwwvvutttsrrrqqppoonnmmllkkkjjiihhgggffeeeddcccbbbaa```___^^^]]]\\\\[[[ZZZZYYYXXXXWWWWVVVVVUUUUTTTTTSSSSSRRRRRRQQQQPPPPPPPPOOOOOONNNNNNNMMMMMMMMMMLLLLLLLLLKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´³³³³³³³³³²²²²²²²±±±±±±±±±°°°°°°¯¯¯¯¯¯®®®®®®®­­­­­¬¬¬¬¬¬««««ªªªªª©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¤¤¤¤£££¢¢¢¢¡¡   ŸŸŸžžžœœ›››šš™™™˜˜———––••””“““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmlllkkjjiihhhggfffeedddccbbbaaa```___^^]]]]\\\[[[[ZZZYYYYXXXXWWWWVVVVUUUUUTTTTSSSSSSRRRRRQQQQQQQPPPPPPOOOOOONNNNNNNNNMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²±±±±±±°°°°°°°°°¯¯¯¯¯¯®®®®®­­­­­­­¬¬¬¬««««««ªªªª©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤¤££££¢¢¡¡¡¡  ŸŸŸŸžžœœœ›››šš™™™˜˜——––•••””““’’‘‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€~~}}||{{zzyyxxwwvvuuttssrrqqppoonnnmmllkkjjjiihhggfffeedddcccbbbaa````__^^^^]]\\\\[[[ZZZZYYYYXXXXWWWWVVVVUUUUTTTTTTSSSSRRRRRRRQQQQQPPPPPPOOOOOOOOONNNNNNMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´´´´³³³³³³³³³²²²²²²²²²²±±±±±±±°°°°°°¯¯¯¯¯¯¯¯®®®®®®­­­­­¬¬¬¬¬¬««««ªªªªª©©©©¨¨¨¨¨§§§¦¦¦¦¥¥¥¥¤¤¤¤£££¢¢¢¡¡¡   ŸŸŸžžžœœœ››ššš™™˜˜˜——––•••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€~~}}||{{zzyyxxwwvvuuttssrrqqpppoonnmmllkkjjjiihhgggffeeeddcccbbbaaa```___^^^]]]\\\[[[[ZZZZYYYYXXXWWWWWVVVVUUUUUTTTTSSSSSSRRRRRQQQQQQPPPPPPPPOOOOOONNNNNNNMMMMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´³³³³³³³³³²²²²²²²±±±±±±±±±°°°°°°°¯¯¯¯¯¯®®®®®®®­­­­­¬¬¬¬¬««««««ªªªª©©©©©¨¨¨§§§§§¦¦¦¥¥¥¥¤¤¤¤£££¢¢¢¡¡¡¡  ŸŸŸŸžžœœœ›››šš™™™˜˜———––••”””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€~~}}||{{zzyyxxwwvvuuttssrrrqqppoonnmmllkkkjjiihhhggfffeedddcccbbbaa````__^^^^]]]\\\[[[[ZZZZYYYXXXXXWWWVVVVVUUUUTTTTTTSSSSSRRRRRQQQQQQQPPPPPPOOOOOOONNNNNNNNNMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²±±±±±±±°°°°°°°°¯¯¯¯¯¯¯®®®®®­­­­­­¬¬¬¬¬¬««««ªªªªªª©©©¨¨¨¨¨§§§§¦¦¦¦¥¥¥¤¤¤¤£££¢¢¢¢¡¡¡   ŸŸŸžžžœœ›››ššš™™˜˜———––•••””“““’’‘‘ŽŽŒŒ‹‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€~~}}||{{zzyyxxwwvvuutttssrrqqppoonnmmlllkkjjjiihhhggffeeedddccbbbaaa```___^^^]]]]\\\[[[[ZZZYYYYXXXXWWWWWVVVUUUUUUTTTTSSSSSSRRRRRRQQQQQPPPPPPPOOOOOOOONNNNNNNMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³²²²²²²²²²²±±±±±±±±°°°°°°¯¯¯¯¯¯¯®®®®®®®­­­­­¬¬¬¬¬««««««ªªªª©©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤¤££££¢¢¢¡¡¡   ŸŸŸžžžœœœ››ššš™™™˜˜———––•••””““’’‘‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡‡††……„„ƒƒ‚‚€~~}}||{{zzyyxxxwwvvuuttssrrqqppoonnnmmllkkjjjiihhhggfffeeeddcccbbbaaa```___^^^]]]\\\\[[[ZZZZYYYYXXXXWWWWVVVVVUUUUTTTTTTSSSSSRRRRRQQQQQQQPPPPPPPOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³²²²²²²²²±±±±±±±±°°°°°°°°¯¯¯¯¯¯®®®®®®­­­­­­­¬¬¬¬«««««ªªªªªª©©©¨¨¨¨¨¨§§§¦¦¦¦¥¥¥¥¤¤¤¤£££¢¢¢¡¡¡¡   ŸŸŸžžžœœ›››ššš™™˜˜˜——–––••”””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuttssrrqqpppoonnmmllkkkjjiiihhgggffeeedddccbbbaaa```___^^^^]]]\\\[[[[ZZZZYYYYXXXWWWWWVVVVUUUUUUTTTTTSSSSRRRRRRRQQQQQQPPPPPPOOOOOOOONNNNNNNNMMMMMMMMLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²±±±±±±±°°°°°°°¯¯¯¯¯¯¯¯®®®®®®­­­­­¬¬¬¬¬¬«««««ªªªª©©©©©¨¨¨¨§§§§§¦¦¦¥¥¥¥¤¤¤¤£££¢¢¢¢¡¡¡   ŸŸŸžžžžœœœ›››šš™™™˜˜———––•••””““’’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuttssrrqqpppoonnmmmllkkjjjiihhhggfffeedddcccbbaaaa```___^^^]]]]\\\[[[[ZZZZYYYXXXXXWWWVVVVVVUUUUTTTTTSSSSSSRRRRRQQQQQQPPPPPPPPOOOOOOONNNNNNNMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬««««««ªªªªª©©©©¨¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤¤££££¢¢¢¡¡¡¡  ŸŸŸŸžžžœœ›››ššš™™˜˜˜———––•••””““’’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuttssrrrqqppoonnmmmllkkjjjiihhhgggffeeedddccbbbaaa````__^^^^]]]\\\\[[[ZZZZYYYYXXXXWWWWWVVVVUUUUUTTTTTTSSSSRRRRRRRQQQQQQQPPPPPPOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCC¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´³³³³³³³³³³²²²²²²²²±±±±±±±±°°°°°°°°°¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬¬¬«««««ªªªªª©©©©©¨¨¨§§§§§¦¦¦¦¥¥¥¥¤¤¤¤£££¢¢¢¢¡¡¡   ŸŸŸžžžœœœ›››šš™™™˜˜˜——–––••”””““’’‘‘‘ŽŽŒŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuttsssrrqqppoonnnmmllkkkjjiiihhgggfffeedddcccbbbaaa```___^^^]]]]\\\[[[[ZZZZYYYYXXXXXWWWVVVVVUUUUUTTTTTSSSSSSRRRRRRQQQQQQPPPPPPOOOOOOOOONNNNNNNNMMMMMMMMLLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²±±±±±±±±°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­¬¬¬¬¬«««««««ªªªª©©©©¨¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¡¡¡¡  ŸŸŸŸžžžœœœœ››ššš™™™˜˜———––•••””“““’’‘‘ŽŽŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuuttssrrqqpppoonnmmlllkkjjjiihhhggfffeeeddccccbbaaa````__^^^^]]]\\\\[[[[ZZZZYYYYXXXXWWWWWVVVVUUUUTTTTTTTSSSSSRRRRRQQQQQQQPPPPPPPPOOOOOONNNNNNNNMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°¯¯¯¯¯¯¯®®®®®®­­­­­­­¬¬¬¬¬«««««ªªªªª©©©©©¨¨¨¨§§§§§¦¦¦¦¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡   ŸŸŸžžžœœœ›››ššš™™˜˜˜——–––•••””““’’’‘‘ŽŽŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuuttssrrqqpppoonnmmmllkkjjjiiihhgggffeeedddcccbbbaaa```___^^^]]]]\\\\[[[[ZZZYYYYXXXXXWWWWVVVVVUUUUUTTTTTSSSSSRRRRRRRQQQQQQPPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬¬¬««««««ªªªª©©©©©¨¨¨¨¨§§§¦¦¦¦¦¥¥¥¥¤¤¤£££££¢¢¢¡¡¡   ŸŸŸžžžžœœ›››ššš™™™˜˜———–––••””“““’’‘‘‘ŽŽŽŒŒ‹‹ŠŠ‰‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvvuuttssrrqqqppoonnnmmlllkkjjiiihhhggfffeeedddccbbbaaaa```___^^^]]]\\\\\[[[ZZZZYYYYXXXXWWWWWVVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPOOOOOOOOONNNNNNNMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±°°°°°°°¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬«««««ªªªªªª©©©©¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¤¤¤¤£££¢¢¢¢¡¡¡¡  ŸŸŸŸžžžœœœ›››ššš™™˜˜˜———––•••””“““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxxwwvvuuttssrrrqqppooonnmmlllkkjjjiihhhgggffeeedddcccbbbaaa````__^^^^]]]]\\\[[[[ZZZZYYYYXXXXXWWWWVVVVUUUUUUTTTTTSSSSSRRRRRRRQQQQQQQPPPPPPPOOOOOOONNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬¬¬««««««ªªªª©©©©©¨¨¨¨¨§§§§¦¦¦¦¥¥¥¥¥¤¤¤££££¢¢¢¡¡¡¡   ŸŸŸžžžœœ››››šš™™™˜˜˜——–––•••””““’’’‘‘ŽŽŒŒŒ‹‹ŠŠ‰‰ˆˆ‡‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxxwwvvuuttsssrrqqpppoonnmmmllkkjjjiiihhgggfffeeddddccbbbbaaa```___^^^^]]]\\\\[[[ZZZZZYYYYXXXXWWWWWVVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···············¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­¬¬¬¬¬¬«««««ªªªªªª©©©©¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¤¤¤¤¤£££¢¢¢¢¡¡¡¡  ŸŸŸŸžžžœœœ›››ššš™™™˜˜———–––••””“““’’’‘‘ŽŽŒŒ‹‹‹ŠŠ‰‰ˆˆ‡‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxxwwvvuutttssrrqqpppoonnmmmlllkkjjiiihhhggfffeeedddcccbbbaaa````__^^^^]]]]\\\[[[[[ZZZYYYYXXXXXWWWWWVVVVUUUUUUTTTTTSSSSSSRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±°°°°°°°¯¯¯¯¯¯¯®®®®®®­­­­­­­¬¬¬¬¬¬¬«««««ªªªª©©©©©©¨¨¨¨§§§§¦¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¡¡¡    ŸŸŸžžžœœœ››šššš™™˜˜˜———––•••””“““’’‘‘‘ŽŽŽŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡†††……„„ƒƒ‚‚€€~~}}||{{zzyyyxxwwvvuuuttssrrqqqppoonnnmmlllkkjjjiihhhgggffeeeeddcccbbbbaaa```____^^^]]]\\\\[[[[ZZZZYYYYYXXXXWWWWVVVVVVUUUUTTTTTSSSSSSSRRRRRRRQQQQQQPPPPPPPOOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬¬¬««««««ªªªªª©©©©©¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¤¤¤¤¤£££¢¢¢¡¡¡¡   ŸŸŸžžžžœœœ›››ššš™™™˜˜˜——–––••”””““’’’‘‘ŽŽŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡††………„„ƒƒ‚‚€€~~}}||{{zzzyyxxwwvvuuuttssrrrqqppooonnmmmllkkkjjiiihhgggfffeeedddcccbbbaaaa```___^^^^]]]\\\[[[[[ZZZZYYYYXXXXXWWWWVVVVVUUUUUTTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²±±±±±±±±±°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬«««««ªªªªªª©©©©©¨¨¨¨¨§§§§¦¦¦¦¥¥¥¥¥¤¤¤££££¢¢¢¢¡¡¡    ŸŸŸžžžœœœœ›››šš™™™˜˜˜———––•••”””““’’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰‰ˆˆ‡‡††………„„ƒƒ‚‚€€~~}}||{{zzzyyxxwwvvvuuttssrrrqqpppoonnmmmllkkkjjjiihhhgggfffeedddccccbbbaaa```____^^^]]]]\\\\[[[ZZZZZYYYYXXXXWWWWWVVVVVUUUUUTTTTTTSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°¯¯¯¯¯¯¯®®®®®®®­­­­­­¬¬¬¬¬¬««««««ªªªªª©©©©©¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¤¤¤¤¤££££¢¢¢¡¡¡¡   ŸŸŸŸžžžœœœ›››ššš™™™˜˜———–––•••””“““’’‘‘‘ŽŽŒŒŒ‹‹ŠŠ‰‰‰ˆˆ‡‡††……„„„ƒƒ‚‚€€~~}}||{{{zzyyxxwwvvvuuttsssrrqqpppoonnnmmlllkkjjjiiihhhggfffeeedddcccbbbaaa````___^^^^]]]\\\\[[[[[ZZZYYYYXXXXXWWWWWVVVVVUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQPPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­¬¬¬¬¬«««««ªªªªªª©©©©©©¨¨¨¨§§§§¦¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡   ŸŸŸŸžžžœœœœ›››ššš™™˜˜˜———–––••”””“““’’‘‘ŽŽŽŒŒ‹‹‹ŠŠ‰‰ˆˆˆ‡‡††……„„„ƒƒ‚‚€€~~}}||{{{zzyyxxwwwvvuutttssrrqqqppooonnmmlllkkkjjiiihhhgggffeeedddccccbbbaaa````___^^^]]]]\\\\[[[[ZZZZYYYYYXXXXWWWWVVVVVVUUUUUUTTTTTSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°¯¯¯¯¯¯¯¯®®®®®®­­­­­­­¬¬¬¬¬¬¬«««««ªªªªª©©©©©¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¤¤¤¤¤££££¢¢¢¡¡¡    ŸŸŸŸžžžœœœ›››ššš™™™˜˜˜———––•••”””““’’’‘‘ŽŽŽŒŒ‹‹‹ŠŠ‰‰ˆˆ‡‡‡††……„„„ƒƒ‚‚€€~~}}||{{{zzyyxxxwwvvuutttssrrqqqppooonnmmmllkkkjjjiihhhgggfffeeedddcccbbbaaa````____^^^]]]\\\\[[[[[ZZZZYYYYXXXXXWWWWWVVVVVUUUUUTTTTTSSSSSSSRRRRRRRQQQQQQPPPPPPPPOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»ººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬¬¬««««««ªªªªªª©©©©©¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¥¤¤¤¤£££¢¢¢¢¢¡¡¡   ŸŸŸŸžžžžœœ››››ššš™™™˜˜———–––•••””“““’’‘‘‘ŽŽŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡‡††……„„ƒƒƒ‚‚€€~~}}|||{{zzyyxxxwwvvuuuttssrrrqqpppoonnnmmlllkkjjjiiihhhggfffeeeddddccbbbaaaa````___^^^]]]]]\\\[[[[ZZZZZYYYYXXXXXWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­¬¬¬¬¬¬«««««ªªªªª©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡    ŸŸŸŸžžžœœœ›››ššš™™™˜˜˜———–––••”””“““’’‘‘‘ŽŽŒŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡‡††……„„ƒƒƒ‚‚€€~~}}|||{{zzyyxxxwwvvuuuttsssrrqqpppoonnnmmlllkkkjjiiihhhgggfffeeedddcccbbbaaa````____^^^]]]]\\\\[[[[ZZZZYYYYXXXXXWWWWWWVVVVVUUUUUTTTTTSSSSSSRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDD»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­¬¬¬¬¬¬««««««ªªªªªª©©©©©¨¨¨¨¨§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤££££¢¢¢¢¡¡¡¡   ŸŸŸŸžžžœœœ›››ššš™™™˜˜˜——–––•••”””““’’’‘‘ŽŽŽŒŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡‡††……„„ƒƒƒ‚‚€€~~}}|||{{zzyyxxxwwvvuuuttsssrrqqqppooonnmmmllkkkjjjiiihhgggfffeeedddcccbbbbaaa````___^^^^]]]]\\\\[[[ZZZZZYYYYYXXXXWWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDD»»»»»»»ºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬«««««ªªªªªª©©©©©¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¤¤¤¤¤££££¢¢¢¢¡¡¡    ŸŸŸŸžžžœœœ›››ššš™™™˜˜˜———–––•••””“““’’‘‘‘ŽŽŽŒŒŒ‹‹ŠŠ‰‰‰ˆˆ‡‡†††……„„ƒƒƒ‚‚€€~~}}|||{{zzyyyxxwwvvvuuttsssrrqqqppooonnnmmlllkkjjjiiihhhgggfffeeedddcccbbbaaa````____^^^]]]]\\\\[[[[[ZZZZYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDD»»»»»»ººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬«««««««ªªªªªª©©©©¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡¡   ŸŸŸŸžžžœœœœ›››ššš™™™˜˜˜———–––••”””““’’’‘‘‘ŽŽŒŒ‹‹‹ŠŠ‰‰‰ˆˆ‡‡†††……„„ƒƒƒ‚‚€€~~}}|||{{zzyyyxxwwvvvuutttssrrrqqpppoonnnmmmllkkkjjiiihhhgggfffeeedddccccbbbaaa````___^^^^]]]]\\\\[[[[ZZZZZYYYYXXXXXWWWWWVVVVUUUUUUTTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDD»»»»»»ººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬«««««ªªªªªª©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¤¤¤¤£££££¢¢¢¢¡¡¡   ŸŸŸŸžžžžœœœ›››ššš™™™™˜˜———–––•••”””““’’’‘‘‘ŽŽŒŒ‹‹‹ŠŠ‰‰ˆˆˆ‡‡†††……„„ƒƒƒ‚‚€€~~}}|||{{zzyyyxxwwwvvuutttssrrrqqpppoonnnmmmllkkkjjjiiihhhggffffeeedddcccbbbaaaa````___^^^]]]]\\\\\[[[[ZZZZYYYYXXXXXWWWWWWVVVVVUUUUUUTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDD»»»»»ºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªª©©©©©¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤££££¢¢¢¡¡¡¡    ŸŸŸŸžžžœœœœ›››ššš™™™˜˜˜———–––•••””“““’’’‘‘ŽŽŽŒŒŒ‹‹ŠŠŠ‰‰ˆˆˆ‡‡††………„„ƒƒ‚‚‚€€~~}}}||{{zzzyyxxwwwvvuuuttsssrrqqqppooonnmmmlllkkjjjiiihhhgggfffeeedddccccbbbaaa````____^^^^]]]\\\\[[[[ZZZZZYYYYYXXXXXWWWWVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡¡   ŸŸŸŸžžžœœœ›››ššš™™™˜˜˜˜———––•••”””“““’’‘‘‘ŽŽŽŒŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡‡††………„„ƒƒ‚‚‚€€~~}}}||{{zzzyyxxxwwvvuuuttsssrrqqqppooonnnmmlllkkkjjjiihhhggggfffeeedddcccbbbbaaa````___^^^^]]]]\\\\[[[[ZZZZZYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEºººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬««««««ªªªªªª©©©©©¨¨¨¨¨§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££¢¢¢¢¡¡¡¡    ŸŸŸŸžžžœœœœ›››ššš™™™˜˜˜———–––•••””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹ŠŠŠ‰‰ˆˆ‡‡‡††………„„ƒƒ‚‚‚€€~~}}}||{{zzzyyxxxwwvvuuuttsssrrqqqpppoonnnmmmlllkkjjjiiihhhgggfffeeedddccccbbbaaa````____^^^^]]]\\\\[[[[[ZZZZZYYYYYXXXXWWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬«««««««ªªªªªª©©©©©¨¨¨¨¨¨§§§§¦¦¦¦¦¥¥¥¥¤¤¤¤£££££¢¢¢¢¡¡¡¡   ŸŸŸŸžžžœœœ››››ššš™™™˜˜˜———–––•••””“““’’’‘‘‘ŽŽŒŒ‹‹‹ŠŠ‰‰‰ˆˆ‡‡‡††………„„ƒƒ‚‚‚€€~~}}}||{{zzzyyxxxwwvvvuutttssrrrqqpppoonnnmmmlllkkjjjiiihhhgggfffeeeddddcccbbbbaaa````___^^^^]]]]\\\\\[[[[ZZZZYYYYYXXXXWWWWWWVVVVVUUUUUUTTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬««««««ªªªªªª©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡¡   ŸŸŸŸžžžžœœœ›››ššš™™™˜˜˜————––•••”””“““’’‘‘‘ŽŽŽŒŒ‹‹‹ŠŠ‰‰‰ˆˆ‡‡‡††……„„„ƒƒ‚‚‚€€~~}}}||{{{zzyyxxxwwvvvuutttssrrrqqqppooonnnmmlllkkkjjjiihhhhgggfffeeedddcccbbbbaaaa````___^^^^]]]]\\\\[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¥¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡¡¡   ŸŸŸŸžžžœœœ›››šššš™™™˜˜˜———–––•••”””““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠ‰‰‰ˆˆ‡‡‡††……„„„ƒƒ‚‚‚€€~~}}}||{{{zzyyxxxwwvvvuutttsssrrqqqpppoonnnmmmllkkkjjjiiihhhgggfffeeeedddcccbbbbaaa````___^^^^^]]]]\\\\[[[[ZZZZZYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEºººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªª©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¡¡¡¡   ŸŸŸŸžžžžœœœ›››ššš™™™˜˜˜———–––•••”””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹ŠŠŠ‰‰ˆˆˆ‡‡†††……„„„ƒƒ‚‚‚€€~~}}}||{{{zzyyyxxwwwvvuuuttsssrrqqqpppoonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbbaaaa````___^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTTSSSSSSRRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEºººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬«««««««ªªªªªª©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¤¤¤¤¤££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœ››››ššš™™™˜˜˜———–––•••”””“““’’’‘‘ŽŽŒŒŒ‹‹ŠŠŠ‰‰ˆˆˆ‡‡†††……„„„ƒƒ‚‚‚€€~~}}}||{{{zzyyyxxwwwvvuuuttsssrrrqqpppooonnmmmlllkkkjjjiiihhhgggfffeeeddddcccbbbaaaa````____^^^^]]]]\\\\[[[[[ZZZZYYYYYXXXXXWWWWWVVVVVUUUUUUTTTTTTTSSSSSSRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEºººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¡¡¡¡   ŸŸŸŸžžžžœœœœ›››šššš™™™˜˜˜———–––•••”””“““’’‘‘‘ŽŽŒŒ‹‹‹ŠŠŠ‰‰ˆˆˆ‡‡†††……„„„ƒƒ‚‚‚€€~~}}}||{{{zzyyyxxwwwvvuuutttssrrrqqpppooonnnmmlllkkkjjjiiihhhgggfffeeeedddccccbbbaaaa````___^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¤¤¤¤£££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœ››››ššš™™™˜˜˜———–––•••”””“““’’’‘‘‘ŽŽŽŒŒ‹‹‹ŠŠ‰‰‰ˆˆ‡‡‡†††……„„„ƒƒ‚‚‚€€~~}}}||{{{zzyyyxxxwwvvvuutttssrrrqqqpppoonnnmmmlllkkkjjjiiihhhgggfffeeeddddcccbbbaaaa````____^^^^]]]]\\\\\[[[[ZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¡¡¡¡   ŸŸŸŸžžžžœœœœ›››ššš™™™™˜˜˜———–––•••”””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠ‰‰‰ˆˆ‡‡‡††………„„ƒƒƒ‚‚€€~~~}}|||{{zzzyyxxxwwvvvuutttsssrrqqqpppoonnnmmmlllkkkjjjiiihhhgggffffeeedddccccbbbaaaa````___^^^^]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQPPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEºººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤££££¢¢¢¢¢¡¡¡¡   ŸŸŸŸžžžžœœœ››››ššš™™™™˜˜˜———–––•••”””“““’’’‘‘ŽŽŒŒŒ‹‹ŠŠŠ‰‰‰ˆˆ‡‡‡††………„„ƒƒƒ‚‚€€~~~}}|||{{zzzyyxxxwwvvvuuuttsssrrrqqpppooonnmmmlllkkkjjjiiihhhgggffffeeeddddcccbbbbaaaa````___^^^^]]]]]\\\\[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEºººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¡¡¡¡¡   ŸŸŸŸžžžžœœœœ›››šššš™™™˜˜˜———–––•••”””“““’’’‘‘‘ŽŽŒŒŒ‹‹ŠŠŠ‰‰‰ˆˆ‡‡‡††………„„ƒƒƒ‚‚€€~~~}}|||{{zzzyyxxxwwvvvuuuttsssrrrqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeeedddccccbbbaaaa````___^^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUTTTTTTSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEºººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§§¦¦¦¦¦¥¥¥¥¤¤¤¤¤££££¢¢¢¢¡¡¡¡¡   ŸŸŸŸžžžžœœœ››››ššš™™™˜˜˜˜———–––•••”””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹ŠŠŠ‰‰ˆˆˆ‡‡‡††………„„ƒƒƒ‚‚€€~~~}}|||{{zzzyyxxxwwwvvuuuttsssrrrqqqpppoonnnmmmlllkkkjjjiiihhhggggfffeeeddddcccbbbbaaaa````___^^^^^]]]]\\\\[[[[[ZZZZYYYYYXXXXXXWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœ››››ššš™™™˜˜˜————–––•••”””“““’’’‘‘ŽŽŽŒŒ‹‹‹ŠŠŠ‰‰ˆˆˆ‡‡‡††………„„ƒƒƒ‚‚€€~~~}}|||{{zzzyyxxxwwwvvuuutttssrrrqqqpppooonnmmmlllkkkjjjiiihhhhgggfffeeeddddcccbbbbaaaa````____^^^^]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœ››››ššš™™™™˜˜˜———–––•••”””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠ‰‰‰ˆˆˆ‡‡†††………„„ƒƒƒ‚‚€€~~~}}|||{{zzzyyyxxwwwvvvuutttsssrrqqqpppooonnnmmmlllkkkjjjiiihhhgggffffeeeddddcccbbbbaaaa````____^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEºº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªª©©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¡¡¡¡¡    ŸŸŸŸžžžœœœœ›››šššš™™™˜˜˜————–––•••”””“““’’’‘‘‘ŽŽŒŒŒ‹‹‹ŠŠ‰‰‰ˆˆˆ‡‡†††………„„ƒƒƒ‚‚€€~~~}}|||{{zzzyyyxxwwwvvvuutttsssrrrqqpppooonnnmmmlllkkkjjjiiihhhhgggfffeeeedddccccbbbbaaa````____^^^^^]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEºº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤£££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœœ›››šššš™™™˜˜˜————–––•••”””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹ŠŠŠ‰‰‰ˆˆ‡‡‡†††……„„„ƒƒƒ‚‚€€~~~}}|||{{{zzyyyxxxwwvvvuuuttsssrrrqqqpppoonnnmmmlllkkkjjjiiihhhhgggfffeeeedddccccbbbbaaaa````____^^^^]]]]\\\\\[[[[ZZZZZYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEºº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœœ›››šššš™™™˜˜˜˜———–––•••”””““““’’‘‘‘ŽŽŽŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆ‡‡‡†††……„„„ƒƒƒ‚‚€€~~~}}|||{{{zzyyyxxxwwvvvuuutttssrrrqqqpppooonnnmmllllkkkjjjiiihhhggggfffeeeedddccccbbbbaaaa````____^^^^]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­¬¬¬¬¬¬«««««««ªªªªªª©©©©©©©¨¨¨¨¨§§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœœ›››šššš™™™˜˜˜———––––•••”””“““’’’‘‘‘ŽŽŽŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆ‡‡‡†††……„„„ƒƒƒ‚‚€€~~~}}|||{{{zzyyyxxxwwvvvuuutttssrrrqqqpppooonnnmmmlllkkkjjjiiiihhhgggfffeeeedddccccbbbbaaaa````____^^^^]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXXWWWWWVVVVVVVUUUUUUTTTTTTTSSSSSSRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸŸžžžœœœœ››››ššš™™™™˜˜˜———–––•••””””“““’’’‘‘‘ŽŽŒŒŒ‹‹‹ŠŠŠ‰‰ˆˆˆ‡‡‡†††……„„„ƒƒƒ‚‚€€~~~}}|||{{{zzyyyxxxwwwvvuuutttsssrrrqqpppooonnnmmmlllkkkkjjjiiihhhgggffffeeeddddccccbbbbaaa`````____^^^^]]]]]\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬««««««ªªªªªªª©©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœ››››šššš™™™˜˜˜————–––•••””””““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠ‰‰‰ˆˆˆ‡‡‡††………„„„ƒƒ‚‚‚€€~~~}}}||{{{zzzyyxxxwwwvvvuutttsssrrrqqqpppooonnnmmmllkkkkjjjiiihhhhgggfffeeeeddddcccbbbbaaaa````____^^^^]]]]]\\\\[[[[[ZZZZZYYYYYYXXXXXWWWWWWVVVVVVUUUUUUUTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQPPPPPPPPOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­¬¬¬¬¬¬¬¬«««««««ªªªªªªª©©©©©©¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸŸžžžžœœœœ››››ššš™™™™˜˜˜———––––•••”””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠ‰‰‰ˆˆˆ‡‡‡††………„„„ƒƒ‚‚‚€€~~~}}}||{{{zzzyyxxxwwwvvvuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiiihhhgggffffeeeddddccccbbbaaaa`````____^^^^]]]]]\\\\\[[[[ZZZZZZYYYYYXXXXXWWWWWVVVVVVUUUUUUUTTTTTTTSSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¡¡¡¡¡    ŸŸŸŸžžžžœœœ››››šššš™™™˜˜˜˜———–––•••””””“““’’’‘‘‘ŽŽŽŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡†††………„„„ƒƒ‚‚‚€€~~~}}}||{{{zzzyyyxxwwwvvvuuutttssrrrqqqpppooonnnmmmlllkkkkjjjiiihhhggggfffeeeeddddcccbbbbaaaa````____^^^^^]]]]\\\\\[[[[ZZZZZZYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸŸžžžžœœœœ›››šššš™™™™˜˜˜————–––•••”””“““’’’’‘‘‘ŽŽŽŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡†††………„„„ƒƒ‚‚‚€€~~~}}}||{{{zzzyyyxxwwwvvvuuutttssrrrqqqpppooonnnmmmmlllkkkjjjiiihhhhgggffffeeeedddccccbbbaaaa`````____^^^^]]]]]\\\\\[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUUTTTTTTTTSSSSSSRRRRRRRQQQQQQQQQQPPPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸŸžžžžœœœœ›››šššš™™™˜˜˜˜———––––•••”””“““’’’‘‘‘ŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆ‡‡‡†††………„„„ƒƒ‚‚‚€€~~~}}}||{{{zzzyyyxxxwwvvvuuutttsssrrrqqppppooonnnmmmlllkkkjjjiiiihhhggggfffeeeedddccccbbbbaaaa`````____^^^^]]]]]\\\\[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªªªª©©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœœ››››ššš™™™™˜˜˜˜———–––•••””””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆ‡‡‡†††………„„ƒƒƒ‚‚‚€€~~~}}}|||{{zzzyyyxxxwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkkjjjiiihhhggggffffeeeddddccccbbbbaaaa````____^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYXXXXXWWWWWWVVVVVVUUUUUUUTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤£££££¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸžžžžœœœœ›››šššš™™™˜˜˜˜————–––•••”””““““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰ˆˆˆ‡‡‡†††………„„ƒƒƒ‚‚‚€€~~~}}}|||{{zzzyyyxxxwwwvvuuutttsssrrrqqqpppooonnnmmmllllkkkjjjiiihhhhggggfffeeeedddccccbbbbaaaa`````____^^^^^]]]]\\\\\[[[[ZZZZZZYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRQQQQQQQQPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªªªª©©©©©©¨¨¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¡¡¡¡¡    ŸŸŸŸžžžžœœœœ››››šššš™™™˜˜˜————––––•••”””“““’’’‘‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„ƒƒƒ‚‚‚€€~~~}}}|||{{zzzyyyxxxwwwvvvuutttsssrrrqqqpppooonnnnmmmlllkkkjjjiiiihhhhgggfffeeeeddddccccbbbbaaaa````____^^^^^]]]]\\\\\[[[[[[ZZZZZYYYYYXXXXXWWWWWWVVVVVVVUUUUUUUTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPOOOOOOOOOOONNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸžžžžœœœ››››šššš™™™™˜˜˜————–––•••””””“““’’’‘‘‘ŽŽŽŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„ƒƒƒ‚‚‚€€~~~}}}|||{{zzzyyyxxxwwwvvvuuutttssrrrrqqqpppooonnnmmmlllkkkkjjjiiihhhhgggffffeeeeddddcccbbbbaaaa`````____^^^^^]]]]]\\\\[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRQQQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFF¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬««««««ªªªªªªª©©©©©©©¨¨¨¨¨¨§§§§§¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸŸžžžžœœœœ››››ššš™™™™˜˜˜˜———––––•••”””““““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††……„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmllllkkkjjjiiiihhhggggffffeeeddddccccbbbbaaaa`````____^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYXXXXXWWWWWWVVVVVVVUUUUUUUTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFF¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©¨¨¨¨¨§§§§§§§¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤£££££¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸžžžœœœœœ››››ššš™™™™˜˜˜————–––••••”””“““’’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††……„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmmlllkkkjjjjiiihhhhgggffffeeeddddccccbbbbbaaa`````____^^^^^]]]]]\\\\\[[[[ZZZZZZYYYYYXXXXXXXWWWWWVVVVVVUUUUUUUTTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPOOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFF¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­¬¬¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜———––––•••””””“““’’’‘‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxwwwvvvuuutttsssrrrqqqppppoonnnnmmmlllkkkkjjjiiiihhhgggffffeeeeddddccccbbbbaaaa`````____^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYXXXXXXWWWWWWVVVVVVVUUUUUUTTTTTTTSSSSSSSSSRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸžžžžœœœœ››››ššš™™™™˜˜˜˜———––––•••”””““““’’’‘‘‘ŽŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxxwwvvvuuutttsssrrrqqqqpppooonnnmmmllllkkkjjjiiiihhhggggffffeeeddddccccbbbbbaaaa````____^^^^^]]]]]\\\\\[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUUTTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­¬¬¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜————–––••••”””“““’’’’‘‘‘ŽŽŽŒŒ‹‹‹‹ŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuttttssrrrrqqqpppooonnnmmmmlllkkkjjjjiiihhhhgggffffeeeeddddccccbbbbaaaa`````____^^^^^]]]]\\\\\[[[[[[ZZZZZYYYYYYXXXXXWWWWWWWVVVVVVVUUUUUUTTTTTTTSSSSSSSSSRRRRRRRQQQQQQQQQQPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬«««««««ªªªªªªª©©©©©©©¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸžžžžžœœœœ›››šššš™™™™˜˜˜˜———––––•••””””“““’’’‘‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnnmmmlllkkkjjjjiiiihhhggggffffeeeedddccccbbbbaaaaa````____^^^^^]]]]]\\\\\\[[[[ZZZZZYYYYYYXXXXXXXWWWWWVVVVVVVUUUUUUUTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬«««««««««ªªªªªª©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜————–––••••””””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqppppooonnnmmmlllkkkkjjjjiiihhhhgggffffeeeeddddccccbbbbaaaa`````____^^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUTTTTTTTTTSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬«««««««ªªªªªªª©©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡    ŸŸŸŸŸžžžžžœœœ›››››šššš™™™˜˜˜˜————–––••••”””““““’’’‘‘‘ŽŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqqpppooonnnmmmllllkkkjjjjiiihhhhggggfffeeeedddddcccbbbbaaaaa`````____^^^^]]]]]\\\\\\[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVUUUUUUUTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜˜———––––••••”””“““’’’’‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttssssrrrqqqpppooonnnmmmmlllkkkjjjjiiiihhhggggffffeeeeddddccccbbbbaaaa`````____^^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVUUUUUUTTTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­¬¬¬¬¬¬¬¬¬«««««««ªªªªªªª©©©©©©©©¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¢¡¡¡¡    ŸŸŸŸŸžžžžžœœœœœ››››šššš™™™˜˜˜˜————–––••••””””“““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuuttttsssrrrqqqpppoooonnnmmmlllkkkkjjjjiiihhhhggggfffeeeeddddcccccbbbaaaaa`````____^^^^]]]]]]\\\\\[[[[[ZZZZZYYYYYYYXXXXXXWWWWWVVVVVVVVUUUUUUUTTTTTTTSSSSSSSSSRRRRRRRQQQQQQQQQQPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££¢¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸžžžžžœœœœ››››šššš™™™™˜˜˜˜————–––••••”””““““’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyxxxwwwvvvvuuutttsssrrrqqqppppooonnnmmmllllkkkjjjjiiihhhhggggffffeeeeddddccccbbbbaaaaa````____^^^^^^]]]]]\\\\[[[[[[ZZZZZZYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSRRRRRRRRRQQQQQQQQPPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡     ŸŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜————––––•••””””“““’’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡††††………„„„ƒƒƒ‚‚‚€€~~~}}}|||{{{zzzyyyyxxxwwwvvvuuutttsssrrrqqqppppooonnnmmmmlllkkkkjjjiiiihhhhgggffffeeeeddddccccbbbbbaaaa`````____^^^^^]]]]]\\\\\[[[[[[ZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬«««««««ªªªªªªªª©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸžžžžžœœœœ››››šššš™™™™˜˜˜˜————–––••••”””““““’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqqpppooonnnnmmmllllkkkjjjjiiihhhhggggffffeeeeddddccccbbbbaaaaa````____^^^^^^]]]]]\\\\\[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUUUTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸··························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡     ŸŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜————––––••••”””““““’’’‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttssssrrrqqqpppoooonnnmmmllllkkkjjjjiiiihhhhgggffffeeeeddddccccbbbbbaaaa`````_____^^^^]]]]]\\\\\\[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPPOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGG¸¸¸¸¸¸¸¸¸¸··························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬¬««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸžžžžžœœœœ›››››ššš™™™™˜˜˜˜————––––•••””””“““’’’’‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttssssrrrqqqppppooonnnmmmmlllkkkkjjjiiiihhhhggggffffeeedddddccccbbbbaaaaa`````____^^^^^]]]]]\\\\\[[[[[[ZZZZZZYYYYYXXXXXXXWWWWWWVVVVVVVUUUUUUUUTTTTTTSSSSSSSSSRRRRRRRRQQQQQQQQQQQPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGG¸¸¸¸¸¸¸¸¸·······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¡¡¡¡¡     ŸŸŸŸžžžžžœœœœ››››šššš™™™™˜˜˜˜————–––••••”””““““’’’‘‘‘‘ŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuuttttsssrrrqqqppppooonnnnmmmllllkkkjjjjiiihhhhggggffffeeeeddddccccbbbbbaaaaa````_____^^^^^]]]]\\\\\\[[[[[[ZZZZZYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUTTTTTTTTSSSSSSSRRRRRRRRRRQQQQQQQQQPPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGG¸¸¸¸¸·························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««ªªªªªªªªª©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸžžžžœœœœœ›››ššššš™™™™˜˜˜————––––••••”””““““’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxwwwvvvuuuutttsssrrrqqqqpppooonnnnmmmllllkkkjjjjiiiihhhhgggffffeeeeedddcccccbbbbbaaaa`````____^^^^^]]]]]\\\\\\[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWVVVVVVUUUUUUUUUTTTTTTSSSSSSSSSSRRRRRRRQQQQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHGGGG¸¸¸···························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸžžžžžœœœœœ››››šššš™™™™˜˜˜˜————––––•••””””“““’’’’‘‘‘ŽŽŽŽŒŒŒ‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxwwwvvvvuuutttsssrrrqqqqpppoooonnnmmmmlllkkkkjjjiiiihhhhggggffffeeeeddddcccccbbbbaaaa`````_____^^^^^]]]]]\\\\\[[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHGG¸¸¸·························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤£££££¢¢¢¢¢¢¡¡¡¡     ŸŸŸŸŸžžžžœœœœ›››››šššš™™™˜˜˜˜˜———––––••••”””““““’’’’‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxwwwvvvvuuutttssssrrrqqqppppooonnnmmmmllllkkkjjjjiiiihhhggggffffeeeedddddccccbbbbbaaaa`````_____^^^^]]]]]]\\\\\[[[[[ZZZZZZZYYYYYXXXXXXXWWWWWWVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHGG¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžœœœœœ››››šššš™™™™˜˜˜˜————––––••••”””““““’’’‘‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxxwwwvvvuuutttssssrrrqqqppppooonnnnmmmllllkkkjjjjiiiihhhhggggffffeeeeddddcccccbbbbaaaa`````_____^^^^^]]]]]\\\\\\[[[[[[ZZZZZYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHG························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœ›››››šššš™™™™˜˜˜˜————––––•••””””“““’’’’‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰ˆˆˆ‡‡‡‡†††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyxxxxwwwvvvuuuttttsssrrrqqqqpppooonnnnmmmmlllkkkkjjjiiiihhhhggggffffeeeedddddccccbbbbaaaaa`````_____^^^^^]]]]]\\\\\\[[[[[ZZZZZZYYYYYYXXXXXXXWWWWWWVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHH························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸŸžžžžœœœœ››››ššššš™™™˜˜˜˜————––––••••””””“““’’’’‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆ‡‡‡††††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyyxxxwwwvvvuuuutttsssrrrrqqqpppoooonnnmmmmlllkkkkjjjjiiiihhhhggggfffeeeeeddddccccbbbbbaaaa``````____^^^^^]]]]]]\\\\\[[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUUTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHH·····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬«««««««««ªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœ›››››šššš™™™™˜˜˜˜————––––••••”””““““’’’‘‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆ‡‡‡††††………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzyyyyxxxwwwvvvuuuutttsssrrrrqqqppppooonnnnmmmllllkkkjjjjiiiihhhhggggffffeeeedddddccccbbbbaaaaa`````_____^^^^^]]]]]\\\\\\[[[[[ZZZZZZZYYYYYXXXXXXXWWWWWWVVVVVVVVUUUUUUUTTTTTTTTTSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHH··················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««ªªªªªªªªª©©©©©©¨¨¨¨¨¨¨¨§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¢¢¡¡¡¡¡    ŸŸŸŸŸŸžžžžœœœœ››››šššš™™™™™˜˜˜————––––••••””””““““’’’‘‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡†††…………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzzyyyxxxwwwvvvvuuutttssssrrrqqqppppooonnnnmmmllllkkkkjjjjiiihhhhhgggfffffeeeeddddccccbbbbbaaaa``````____^^^^^]]]]]]\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXWWWWWWWWVVVVVVUUUUUUUUUTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHH··················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬«««««««««ªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœœ››››šššš™™™™˜˜˜˜————––––••••””””“““’’’’‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡†††…………„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{zzzzyyyxxxwwwvvvvuuutttssssrrrqqqppppoooonnnmmmmlllkkkkjjjjiiiihhhhggggffffeeeeddddcccccbbbbaaaaa`````_____^^^^^]]]]]\\\\\\[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWVVVVVVVVUUUUUUUTTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHH·················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««ªªªªªªªªª©©©©©©¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤¤££££¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœ››››šššš™™™™™˜˜˜˜————––––•••””””““““’’’’‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰ˆˆˆˆ‡‡‡†††………„„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{{zzzyyyxxxwwwwvvvuuuttttsssrrrqqqqpppoooonnnmmmmllllkkkkjjjiiiihhhhggggfffffeeeeddddccccbbbbbaaaaa`````_____^^^^^]]]]]\\\\\[[[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWWWVVVVVVUUUUUUUUUTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHH·············¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸŸŸžžžžœœœœ›››››šššš™™™™˜˜˜˜————––––••••””””““““’’’‘‘‘‘ŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰ˆˆˆˆ‡‡‡†††………„„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{{zzzyyyxxxwwwwvvvuuuttttsssrrrrqqqppppooonnnnmmmllllkkkkjjjjiiiihhhhggggffffeeeedddddccccbbbbbaaaa``````____^^^^^^]]]]\\\\\\\[[[[[ZZZZZZZYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHH············¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœœ››››ššššš™™™™˜˜˜˜————––––••••””””“““’’’’‘‘‘‘ŽŽŽŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆ‡‡‡‡†††………„„„„ƒƒƒ‚‚‚€€€~~~}}}|||{{{{zzzyyyxxxxwwwvvvuuuutttsssrrrrqqqppppooonnnnmmmmlllkkkkjjjjiiiihhhhggggffffeeeeeddddcccccbbbbaaaaa`````_____^^^^^]]]]]]\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRRQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHH············¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸŸŸžžžžœœœœ›››››šššš™™™™™˜˜˜—————––––•••””””““““’’’’‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆ‡‡‡‡†††………„„„ƒƒƒƒ‚‚‚€€€~~~}}}||||{{{zzzyyyxxxxwwwvvvuuuutttssssrrrqqqppppoooonnnmmmmllllkkkkjjjiiiihhhhhgggfffffeeeedddddccccbbbbbaaaa``````____^^^^^^]]]]]\\\\\\[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHH·········¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¤¤¤¤¤¤¤£££££¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœœ››››ššššš™™™™˜˜˜˜————––––••••””””““““’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆ‡‡‡‡†††………„„„ƒƒƒƒ‚‚‚€€€~~~}}}||||{{{zzzyyyxxxxwwwvvvuuuutttssssrrrqqqqpppoooonnnnmmmllllkkkkjjjjiiiihhhhggggffffeeeeeddddcccccbbbbaaaaa`````_____^^^^^]]]]]]\\\\\[[[[[[[ZZZZZYYYYYYYXXXXXXWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHH······¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœœ››››šššš™™™™˜˜˜˜˜————––––••••”””““““’’’’‘‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡††††………„„„ƒƒƒƒ‚‚‚€€€~~~}}}||||{{{zzzyyyyxxxwwwvvvvuuutttssssrrrrqqqppppooonnnnmmmmllllkkkjjjjiiiihhhhgggggffffeeeeddddcccccbbbbbaaaaa`````_____^^^^^]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIHHHHH······¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬««««««««««ªªªªªªª©©©©©©©¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸžžžžœœœœ›››››šššš™™™™˜˜˜˜————––––••••””””““““’’’’‘‘‘‘ŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡††††………„„„ƒƒƒƒ‚‚‚€€€~~~}}}||||{{{zzzyyyyxxxwwwvvvvuuuttttsssrrrrqqqppppooonnnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeedddddccccbbbbbbaaaa`````_____^^^^^^]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXWWWWWWWWVVVVVVVUUUUUUUTTTTTTTTTTSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHH······¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœœ››››ššššš™™™™˜˜˜˜————––––••••””””““““’’’’‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰ˆˆˆˆ‡‡‡††††………„„„ƒƒƒƒ‚‚‚€€€~~~}}}||||{{{zzzyyyyxxxwwwwvvvuuuttttsssrrrrqqqqpppoooonnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeeeddddcccccbbbbbaaaaa`````_____^^^^^]]]]]]\\\\\\[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWVVVVVVVUUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHH··¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªª©©©©©©©©¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸŸŸžžžžžœœœœ›››››šššš™™™™˜˜˜˜˜————––––••••”””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡††††………„„„ƒƒƒƒ‚‚‚€€€~~~}}}||||{{{zzzyyyyxxxwwwwvvvuuuutttssssrrrqqqqppppooonnnnmmmmllllkkkjjjjiiiihhhhgggggffffeeeedddddccccbbbbbaaaaa``````____^^^^^^]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœ›››››šššš™™™™™˜˜˜˜————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡†††…………„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{zzzzyyyxxxwwwwvvvuuuutttssssrrrqqqqppppooonnnnmmmmllllkkkkjjjjiiiihhhhggggfffffeeeedddddccccbbbbbaaaaa`````_____^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWVVVVVVVUUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸŸŸžžžžžœœœœœ››››ššššš™™™™˜˜˜˜—————––––••••””””““““’’’‘‘‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆ‡‡‡‡†††…………„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{zzzzyyyxxxxwwwvvvuuuutttssssrrrrqqqppppooonnnnnmmmllllkkkkjjjjiiiihhhhhggggffffeeeedddddcccccbbbbaaaaa``````____^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXWWWWWWWVVVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤£££££¢¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœ›››››ššššš™™™™˜˜˜˜————––––•••••””””“““’’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡†††…………„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{zzzzyyyxxxxwwwvvvvuuuttttsssrrrrqqqqpppoooonnnnmmmmlllkkkkjjjjjiiiihhhhggggffffeeeeedddddccccbbbbbaaaaa`````_____^^^^^]]]]]]]\\\\\[[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸžžžžžžœœœœœ›››››šššš™™™™™˜˜˜˜————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡†††…………„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{zzzzyyyxxxxwwwvvvvuuuttttsssrrrrqqqqppppooonnnnmmmmllllkkkkjjjjiiiihhhhggggfffffeeeedddddcccccbbbbaaaaaa`````_____^^^^^^]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWVVVVVVVVVUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤¤£££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœœ››››ššššš™™™™˜˜˜˜—————––––••••””””““““’’’’‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡†††………„„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{{zzzyyyxxxxwwwvvvvuuuttttssssrrrqqqqppppoooonnnmmmmllllkkkkjjjjiiiihhhhhggggffffeeeeeddddcccccbbbbbaaaaa`````_____^^^^^^]]]]]]\\\\\[[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©©©¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤£££££££¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸžžžžžžœœœœœ›››››šššš™™™™™˜˜˜˜————––––•••••””””“““’’’’‘‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡††††………„„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvuuuutttssssrrrrqqqppppoooonnnnmmmllllkkkkjjjjjiiiihhhhggggfffffeeeedddddcccccbbbbaaaaaa`````_____^^^^^^]]]]]\\\\\\\[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWVVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸŸžžžžžœœœœœ››››ššššš™™™™™˜˜˜˜————––––••••””””““““’’’’‘‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡††††………„„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvuuuutttssssrrrrqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhggggfffffeeeeeddddcccccbbbbbaaaaa``````_____^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤£££££££¢¢¢¢¢¡¡¡¡¡      ŸŸŸŸŸžžžžžœœœœ›››››ššššš™™™™˜˜˜˜—————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡††††………„„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvuuuuttttsssrrrrqqqqppppooonnnnmmmmllllkkkkjjjjiiiihhhhhggggffffeeeeedddddccccbbbbbbaaaaa`````______^^^^^]]]]]\\\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXWWWWWWWVVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžœœœœœ›››››šššš™™™™˜˜˜˜˜————––––••••”””””““““’’’’‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡††††………„„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{{zzzyyyyxxxwwwwvvvvuuuttttsssrrrrqqqqppppoooonnnmmmmllllkkkkkjjjjiiiihhhhgggggffffeeeedddddcccccbbbbbaaaaa``````_____^^^^^^]]]]]\\\\\\[[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIII¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡      ŸŸŸŸŸžžžžžœœœœœ›››››šššš™™™™™˜˜˜˜—————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡††††………„„„„ƒƒƒ‚‚‚‚€€€~~~}}}}|||{{{{zzzyyyyxxxxwwwvvvvuuuttttssssrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhhggggfffffeeeedddddcccccbbbbbaaaaa`````______^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJIIIIIIII¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸžžžžžžœœœœœ››››ššššš™™™™™˜˜˜˜————–––––••••””””““““’’’’‘‘‘‘ŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡†††…………„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{zzzzyyyxxxxwwwvvvvuuuutttssssrrrrqqqppppoooonnnnmmmmllllkkkkjjjjiiiiihhhhggggfffffeeeeeddddcccccbbbbbaaaaaa`````_____^^^^^^]]]]]]\\\\\[[[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWWWVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIII¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡      ŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™˜˜˜˜˜————––––••••”””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡†††…………„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{zzzzyyyxxxxwwwvvvvuuuutttssssrrrrqqqqppppooonnnnmmmmllllkkkkkjjjjiiiihhhhgggggffffeeeeedddddcccccbbbbbaaaaa`````______^^^^^]]]]]]\\\\\\\[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIIIIII¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžœœœœœ››››ššššš™™™™™˜˜˜˜—————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡‡†††…………„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{zzzzyyyxxxxwwwwvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhhggggfffffeeeeeddddcccccbbbbbbaaaa``````_____^^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIII¶¶¶¶µµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™˜˜˜˜˜————–––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡‡†††…………„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{zzzzyyyxxxxwwwwvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiihhhhgggggffffeeeeedddddcccccbbbbbaaaaa``````_____^^^^^]]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJIII¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡      ŸŸŸŸŸŸžžžžžœœœœœ›››››šššš™™™™™˜˜˜˜˜————––––••••””””“““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡††††…………„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{zzzzyyyyxxxwwwwvvvuuuuttttssssrrrqqqqppppoooonnnnmmmmlllllkkkkjjjjiiiihhhhgggggfffffeeeedddddcccccbbbbbaaaaa``````______^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜————–––––••••””””“““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡††††…………„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{zzzzyyyyxxxwwwwvvvvuuuttttssssrrrrqqqqppppooonnnnmmmmlllllkkkkjjjjiiiiihhhhggggfffffeeeeedddddcccccbbbbbaaaaa``````_____^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡      ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™˜˜˜˜—————–––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡††††…………„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{zzzzyyyyxxxwwwwvvvvuuuutttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiihhhhhggggffffeeeeedddddcccccbbbbbaaaaa``````______^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™˜˜˜˜˜—————––––••••”””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡††††………„„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{{zzzyyyyxxxxwwwvvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmllllkkkkkjjjjiiiihhhhhgggggffffeeeeedddddcccccbbbbbaaaaa``````_____^^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžžœœœœœ››››šššššš™™™™˜˜˜˜˜————–––––••••””””“““““’’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡††††………„„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{{zzzyyyyxxxxwwwvvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmlllllkkkkjjjjiiiiihhhhgggggffffeeeeeeddddcccccbbbbbaaaaaa``````_____^^^^^^]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜—————––––•••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡‡††††………„„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{{zzzyyyyxxxxwwwwvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjjiiiihhhhhggggfffffeeeeedddddcccccbbbbbaaaaa``````_____^^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžžœœœœœ›››››šššš™™™™™˜˜˜˜˜—————––––••••”””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡‡†††…………„„„„ƒƒƒƒ‚‚‚€€€~~~~}}}||||{{{{zzzzyyyxxxxwwwwvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkkjjjjiiiihhhhhgggggfffffeeeedddddcccccbbbbbaaaaaa``````_____^^^^^^]]]]]]\\\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜˜————–––––••••””””““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡†††…………„„„„ƒƒƒ‚‚‚‚€€€~~~~}}}}|||{{{{zzzzyyyxxxxwwwwvvvvuuuttttssssrrrrqqqqppppoooonnnnmmmmmllllkkkkjjjjiiiiihhhhgggggfffffeeeeedddddcccccbbbbbaaaaa``````______^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™˜˜˜˜˜—————––––•••••””””““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡†††…………„„„„ƒƒƒ‚‚‚‚€€€~~~~}}}}|||{{{{zzzzyyyxxxxwwwwvvvvuuuuttttsssrrrrqqqqppppoooonnnnmmmmmllllkkkkjjjjjiiiihhhhhgggggffffeeeeedddddcccccbbbbbbaaaaa``````_____^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————––––••••”””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡††††…………„„„„ƒƒƒ‚‚‚‚€€€~~~~}}}}|||{{{{zzzzyyyyxxxwwwwvvvvuuuuttttssssrrrqqqqpppppoooonnnnmmmmllllkkkkkjjjjiiiihhhhhgggggfffffeeeeedddddcccccbbbbbaaaaa``````______^^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžœœœœœœ›››››ššššš™™™™™˜˜˜˜—————–––––••••””””“““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡††††…………„„„„ƒƒƒ‚‚‚‚€€€~~~~}}}}|||{{{{zzzzyyyyxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmlllllkkkkjjjjiiiiihhhhhggggfffffeeeeedddddcccccbbbbbbaaaaa``````_____^^^^^^]]]]]]]\\\\\\\[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžžœœœœœœ›››››šššš™™™™™˜˜˜˜˜————–––––••••”””””““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒ‚‚‚‚€€€~~~~}}}}|||{{{{zzzzyyyyxxxxwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjiiiiihhhhgggggfffffeeeedddddcccccbbbbbbaaaaaa``````_____^^^^^^]]]]]]\\\\\\\[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJµµµµµµ´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜˜————–––––••••”””””““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{zzzzyyyyxxxxwwwwvvvuuuuttttssssrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjiiiiihhhhgggggfffffeeeeedddddcccccbbbbbbaaaaa``````______^^^^^^]]]]]]\\\\\\\[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJJJµµµµ´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžžœœœœœ››››››ššššš™™™™™˜˜˜˜—————––––•••••””””“““““’’’’‘‘‘‘ŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{zzzzyyyyxxxxwwwwvvvuuuuttttssssrrrrrqqqpppppoooonnnnmmmmlllllkkkkjjjjjiiiihhhhhggggfffffeeeeeddddddcccccbbbbbaaaaaa``````_____^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKJJJµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜˜————–––––••••”””””““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmmllllkkkkkjjjjiiiiihhhhgggggfffffeeeeedddddcccccbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJµµ´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————–––––••••”””””““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjiiiiihhhhhgggggfffffeeeeedddddcccccbbbbbaaaaaa``````_____^^^^^^]]]]]]]\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJ´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————––––•••••””””“““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqpppppoooonnnnmmmmlllllkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddcccccbbbbbbaaaaa``````______^^^^^^^]]]]]]\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKK´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡¡     ŸŸŸŸŸŸžžžžžžœœœœœ››››››ššššš™™™™™˜˜˜˜—————–––––••••”””””““““’’’’’‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡†††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqqppppoooonnnnmmmmmllllkkkkjjjjjiiiiihhhhhggggfffffeeeeeddddddcccccbbbbbaaaaaa``````_____^^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKK´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————––––•••••”””””““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxwwwwvvvvuuuuttttssssrrrrrqqqqppppoooonnnnnmmmmllllkkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddccccccbbbbbaaaaaa``````_____^^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKK´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————––––•••••”””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuutttttssssrrrrqqqqppppooooonnnnmmmmllllkkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddccccccbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKK´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————–––––•••••””””“““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqpppppoooonnnnmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeedddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKK´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————––––•••••”””””““““’’’’’‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqqppppoooonnnnmmmmmllllkkkkkjjjjjiiiihhhhhgggggfffffeeeeedddddccccccbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLKKKKKKKKKKKKK´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœ›››››šššššš™™™™™˜˜˜˜˜—————––––•••••””””“““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrrqqqqppppoooonnnnnmmmmlllllkkkkjjjjjiiiihhhhhgggggfffffeeeeeedddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKK´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœ››››››ššššš™™™™™˜˜˜˜˜—————–––––•••••””””“““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttsssssrrrrqqqqppppooooonnnnmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeeddddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKK´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœ››››››ššššš™™™™™˜˜˜˜˜————–––––•••••”””””““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuutttttssssrrrrqqqqpppppoooonnnnmmmmmllllkkkkkjjjjjiiiiihhhhgggggfffffeeeeeddddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKK´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————–––––•••••””””“““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuuttttssssrrrrqqqqpppppoooonnnnmmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeedddddccccccbbbbbbaaaaa```````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKK´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœ›››››šššššš™™™™™˜˜˜˜˜—————–––––•••••””””“““““’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuuttttssssrrrrqqqqqppppoooonnnnnmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeeedddddcccccbbbbbbaaaaaa``````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKK´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžœœœœœœ›››››ššššš™™™™™˜˜˜˜˜—————–––––•••••”””””““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrrqqqqppppooooonnnnmmmmmllllkkkkkjjjjjiiiiihhhhhgggggfffffeeeeedddddccccccbbbbbbaaaaa```````______^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKK´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ››››››ššššš™™™™™˜˜˜˜˜—————–––––•••••””””“““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttsssssrrrrqqqqpppppoooonnnnmmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeeddddddccccccbbbbbaaaaaa``````______^^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKK´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœœ›››››ššššš™™™™™˜˜˜˜˜˜—————–––––••••”””””“““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttsssssrrrrqqqqpppppoooonnnnnmmmmlllllkkkkkjjjjiiiiihhhhhgggggffffffeeeeedddddccccccbbbbbaaaaaa```````______^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLK´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœ››››››ššššš™™™™™˜˜˜˜˜—————–––––•••••”””””““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuutttttssssrrrrqqqqqppppoooonnnnnmmmmmllllkkkkkjjjjjiiiiihhhhhgggggfffffeeeeeddddddcccccbbbbbbaaaaaa```````______^^^^^^]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLL´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžœœœœœ››››››šššššš™™™™™˜˜˜˜˜—————–––––•••••””””“““““’’’’’‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuuttttssssrrrrqqqqqppppooooonnnnmmmmmlllllkkkkjjjjjiiiiihhhhhgggggfffffeeeeeeddddddcccccbbbbbbaaaaa```````______^^^^^^^]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLL³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœ››››››ššššš™™™™™˜˜˜˜˜——————–––––••••”””””“““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuuttttssssrrrrrqqqqpppppoooonnnnmmmmmlllllkkkkkjjjjiiiiihhhhhhgggggfffffeeeeeddddddcccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLL³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ›››››šššššš™™™™™˜˜˜˜˜—————–––––•••••”””””““““’’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuuttttsssssrrrrqqqqpppppoooonnnnnmmmmmllllkkkkkjjjjjiiiiihhhhhgggggfffffeeeeeedddddccccccbbbbbbaaaaaa``````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLL³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœ››››››ššššš™™™™™™˜˜˜˜˜—————–––––•••••”””””““““’’’’’‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuuttttsssssrrrrqqqqqppppoooonnnnnmmmmmllllkkkkkjjjjjiiiiihhhhhgggggffffffeeeeeddddddcccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]]\\\\\\[[[[[[[[ZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLL³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ›››››šššššš™™™™™˜˜˜˜˜—————–––––•••••”””””“““““’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuutttttssssrrrrqqqqqppppooooonnnnnmmmmlllllkkkkkjjjjjiiiihhhhhhgggggfffffeeeeeedddddccccccbbbbbbaaaaaa``````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLL³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››ššššš™™™™™™˜˜˜˜˜—————–––––•••••”””””“““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡†††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuutttttssssrrrrrqqqqpppppoooonnnnnmmmmlllllkkkkkjjjjjiiiiihhhhhgggggffffffeeeeeddddddccccccbbbbbaaaaaa```````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLL³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœœ›››››šššššš™™™™™˜˜˜˜˜˜————––––––•••••”””””““““’’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡†††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyyxxxxwwwwvvvvuuuuuttttssssrrrrrqqqqpppppoooonnnnnmmmmmllllkkkkkjjjjjiiiiiihhhhggggggfffffeeeeeedddddccccccbbbbbbaaaaaa```````______^^^^^^]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMLLLLLLLLLL³³³³³³³³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ››››››ššššš™™™™™™˜˜˜˜˜—————–––––•••••”””””“““““’’’’’‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwvvvvvuuuuttttsssssrrrrqqqqpppppooooonnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhgggggffffffeeeeeddddddccccccbbbbbbaaaaaa``````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLL³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœœ›››››šššššš™™™™™˜˜˜˜˜——————–––––•••••”””””“““““’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwvvvvvuuuuttttsssssrrrrqqqqqppppooooonnnnnmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggfffffeeeeeedddddccccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLL³³³³³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ›››››ššššš™™™™™™˜˜˜˜˜—————––––––•••••””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzzyyyyxxxxwwwwvvvvvuuuutttttssssrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkjjjjjiiiiiihhhhhgggggffffffeeeeedddddccccccbbbbbbbaaaaaa``````______^^^^^^^]]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLL³³³³³³²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››ššššš™™™™™˜˜˜˜˜˜—————–––––•••••”””””“““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆˆ‡‡‡‡††††…………„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{{zzzzyyyyxxxxwwwwwvvvvuuuutttttssssrrrrrqqqqpppppooooonnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhggggggfffffeeeeeddddddccccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMLLLLL³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡       ŸŸŸŸŸŸžžžžžžžœœœœœ››››››šššššš™™™™™˜˜˜˜˜——————–––––•••••”””””“““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††…………„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{{zzzzyyyyxxxxxwwwwvvvvuuuuuttttssssrrrrrqqqqpppppooooonnnnnmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggfffffeeeeeeddddddcccccbbbbbbaaaaaaa``````_______^^^^^^]]]]]]]]\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLL³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››ššššš™™™™™™˜˜˜˜˜—————–––––••••••””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††…………„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{{zzzzyyyyxxxxxwwwwvvvvuuuuuttttsssssrrrrqqqqqppppooooonnnnnmmmmmlllllkkkkjjjjjjiiiiihhhhhgggggffffffeeeeeddddddccccccbbbbbbaaaaaa```````______^^^^^^^^]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLL³³²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžžœœœœœœ››››››šššššš™™™™™˜˜˜˜˜˜—————–––––•••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††…………„„„„ƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}|||||{{{{zzzzyyyyxxxxxwwwwvvvvuuuuuttttsssssrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhggggggfffffeeeeeeddddddccccccbbbbbaaaaaaa```````______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMML²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžžœœœœœœ›››››šššššš™™™™™™˜˜˜˜˜——————–––––•••••”””””“““““’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††…………„„„„ƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}|||||{{{{zzzzyyyyxxxxxwwwwvvvvvuuuuttttsssssrrrrrqqqqpppppooooonnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggffffffeeeeeddddddccccccbbbbbbaaaaaaa``````______^^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMM²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™˜˜˜˜˜˜—————–––––•••••”””””““““““’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡†††††…………„„„„ƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}|||||{{{{zzzzyyyyyxxxxwwwwvvvvvuuuutttttssssrrrrrqqqqpppppooooonnnnnmmmmllllllkkkkkjjjjjiiiiihhhhhggggggfffffeeeeeeddddddccccccbbbbbbaaaaaa```````______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMM²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡       ŸŸŸŸŸŸžžžžžžžœœœœœœ››››››šššššš™™™™™˜˜˜˜˜——————–––––•••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡†††††…………„„„„ƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}|||||{{{{zzzzyyyyyxxxxwwwwvvvvvuuuutttttssssrrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggfffffeeeeeeddddddccccccbbbbbbaaaaaaa``````_______^^^^^^^]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYXXXXXXXXXXWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMM²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžœœœœœœœ›››››šššššš™™™™™™˜˜˜˜˜—————––––––•••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆ‡‡‡‡†††††…………„„„„ƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}|||||{{{{zzzzyyyyyxxxxwwwwwvvvvuuuuuttttsssssrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkjjjjjiiiiiihhhhhgggggffffffeeeeeedddddcccccccbbbbbbaaaaaa```````_______^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWVVVVVVVVVVVUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMMMM²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžžœœœœœœ››››››ššššš™™™™™™˜˜˜˜˜˜—————–––––•••••””””””““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆ‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}|||||{{{{zzzzzyyyyxxxxwwwwwvvvvuuuuuttttsssssrrrrqqqqqpppppooooonnnnnmmmmmllllkkkkkkjjjjjiiiiihhhhhggggggffffffeeeeeddddddccccccbbbbbbaaaaaaa```````______^^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYYXXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMM²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžœœœœœ››››››šššššš™™™™™™˜˜˜˜˜——————–––––•••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{zzzzzyyyyxxxxxwwwwvvvvuuuuutttttssssrrrrrqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjiiiiihhhhhhgggggffffffeeeeeeddddddcccccbbbbbbbaaaaaa```````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMMMM²²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸžžžžžžžœœœœœœ››››››šššššš™™™™™˜˜˜˜˜˜—————–––––••••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuutttttssssrrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkjjjjjjiiiiihhhhhggggggfffffeeeeeeddddddccccccbbbbbbaaaaaaa```````______^^^^^^^]]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYXXXXXXXXXXWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMMM²²²²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡       ŸŸŸŸŸŸžžžžžžžœœœœœœ›››››››ššššš™™™™™™˜˜˜˜˜——————–––––•••••””””””““““’’’’’’‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuutttttssssrrrrrqqqqqpppppooooonnnnmmmmmlllllkkkkkkjjjjjiiiiihhhhhhgggggffffffeeeeedddddddccccccbbbbbbaaaaaaa``````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSSRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMMMMM²²²²²²²²²±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™˜˜˜˜˜˜—————––––––•••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuutttttsssssrrrrrqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjiiiiiihhhhhggggggfffffeeeeeeddddddccccccbbbbbbbaaaaaa```````_______^^^^^^]]]]]]]]\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWVVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMM²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžœœœœœœœ›››››šššššš™™™™™™˜˜˜˜˜——————–––––••••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††…………„„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{{zzzzyyyyxxxxxwwwwvvvvvuuuuuttttsssssrrrrrqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjjiiiiihhhhhhgggggffffffeeeeeedddddcccccccbbbbbbaaaaaa```````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMM²²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜——————–––––•••••””””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆ‡‡‡‡†††††…………„„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{{zzzzyyyyyxxxxwwwwwvvvvuuuuutttttssssrrrrrqqqqqpppppoooonnnnnmmmmmlllllkkkkkkjjjjjiiiiihhhhhhgggggffffffeeeeeeddddddccccccbbbbbbbaaaaaa```````_______^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYXXXXXXXXXXWWWWWWWWWVVVVVVVVVVVUUUUUUUUUTTTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMMM²²²²²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªªª©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœœ›››››ššššššš™™™™™˜˜˜˜˜˜—————––––––•••••”””””“““““’’’’’’‘‘‘‘ŽŽŽŽŽŒŒŒŒ‹‹‹‹‹ŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆ‡‡‡‡†††††…………„„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{{zzzzyyyyyxxxxwwwwwvvvvuuuuutttttssssrrrrrqqqqqpppppooooonnnnmmmmmmlllllkkkkkjjjjjiiiiiihhhhhggggggfffffeeeeeeedddddcccccccbbbbbbaaaaaa````````______^^^^^^^]]]]]]]]\\\\\\\[[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXWWWWWWWWWWWVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNMMMMM²²²±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªª©©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜——————–––––••••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆˆ‡‡‡‡†††††…………„„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{{zzzzyyyyyxxxxwwwwwvvvvvuuuutttttsssssrrrrqqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjjiiiiihhhhhhgggggffffffeeeeeeddddddccccccbbbbbbaaaaaaa```````_______^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWVVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMM²²²±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ›››››››ššššš™™™™™™˜˜˜˜˜˜——————–––––•••••”””””““““““’’’’’‘‘‘‘‘ŽŽŽŽŒŒŒŒŒ‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡†††††…………„„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{{zzzzyyyyyxxxxxwwwwvvvvvuuuutttttsssssrrrrrqqqqpppppooooonnnnnmmmmmllllllkkkkkjjjjjiiiiihhhhhhggggggffffffeeeeedddddddccccccbbbbbbaaaaaa````````______^^^^^^^^]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMM²²±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««ªªªªªªªªªªª©©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜—————––––––•••••”””””“““““’’’’’‘‘‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††……………„„„„„ƒƒƒƒ‚‚‚‚‚€€€€~~~~}}}}}||||{{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuuuttttsssssrrrrrqqqqqpppppoooonnnnnnmmmmmlllllkkkkkjjjjjiiiiiihhhhhggggggffffffeeeeeeddddddccccccbbbbbbaaaaaaa```````_______^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[ZZZZZZZZZYYYYYYYYXXXXXXXXXXXWWWWWWWWVVVVVVVVVVVUUUUUUUUUUUTTTTTTTTTTTSSSSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNM±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤£££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžžœœœœœœ›››››››šššššš™™™™™™˜˜˜˜˜——————–––––••••••”””””“““““’’’’’‘‘‘‘‘ŽŽŽŽŽŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠ‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡††††……………„„„„ƒƒƒƒƒ‚‚‚‚€€€€~~~~~}}}}|||||{{{{zzzzzyyyyxxxxxwwwwvvvvvuuuuuttttsssssrrrrrqqqqqpppppooooonnnnnmmmmmlllllkkkkkjjjjjjiiiiihhhhhhgggggffffffeeeeeedddddddccccccbbbbbbaaaaaaa```````_______^^^^^^^]]]]]]]]\\\\\\\[[[[[[[[[ZZZZZZZYYYYYYYYYYXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNmlt-6.20.0/demo/mlt_all000066400000000000000000000000221362234133600146630ustar00rootroot00000000000000melt \ clip* \ $* mlt-6.20.0/demo/mlt_attributes000066400000000000000000000004651362234133600163140ustar00rootroot00000000000000melt 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:%etv.properties \ $* mlt-6.20.0/demo/mlt_audio_stuff000066400000000000000000000002001362234133600164210ustar00rootroot00000000000000melt \ clip*.dv \ -track music1.ogg \ -filter volume:0.5 normalise= track=0 \ -transition mix out=9999 a_track=0 b_track=1 \ $* mlt-6.20.0/demo/mlt_avantika_title000066400000000000000000000000261362234133600171160ustar00rootroot00000000000000melt \ pango.mlt \ $* mlt-6.20.0/demo/mlt_bouncy000066400000000000000000000003611362234133600154200ustar00rootroot00000000000000melt \ 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 \ composite.sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_bouncy_ball000066400000000000000000000004351362234133600164140ustar00rootroot00000000000000melt \ 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 \ composite.sliced_composite=1 \ a_track=0 \ b_track=1 \ in=0 \ out=5000 \ $* mlt-6.20.0/demo/mlt_clock_in_and_out000066400000000000000000000004171362234133600174150ustar00rootroot00000000000000melt \ 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 \ $* mlt-6.20.0/demo/mlt_composite_transition000066400000000000000000000003751362234133600204020ustar00rootroot00000000000000melt \ 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 sliced_composite=1 \ -transition mix:-1 in=50 out=74 a_track=0 b_track=1 \ $* mlt-6.20.0/demo/mlt_effect_in_middle000066400000000000000000000001111362234133600173520ustar00rootroot00000000000000melt \ clip1.mpeg in=100 out=500 \ -filter greyscale in=100 out=199 \ $* mlt-6.20.0/demo/mlt_fade_black000066400000000000000000000004441362234133600161560ustar00rootroot00000000000000melt \ 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 \ $* mlt-6.20.0/demo/mlt_fade_in_and_out000066400000000000000000000005111362234133600172140ustar00rootroot00000000000000melt \ 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 \ $* mlt-6.20.0/demo/mlt_intro000066400000000000000000000002611362234133600152530ustar00rootroot00000000000000melt \ 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 \ $* mlt-6.20.0/demo/mlt_jcut000066400000000000000000000002751362234133600150720ustar00rootroot00000000000000melt \ -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 \ $* mlt-6.20.0/demo/mlt_lcut000066400000000000000000000003651362234133600150740ustar00rootroot00000000000000melt \ 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 \ $* mlt-6.20.0/demo/mlt_levels000066400000000000000000000001061362234133600154100ustar00rootroot00000000000000melt \ *.dv \ -filter gamma:1.5 \ -filter volume normalise=-20db \ $* mlt-6.20.0/demo/mlt_my_name_is000066400000000000000000000010141362234133600162350ustar00rootroot00000000000000melt \ 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 sliced_composite=1 \ -transition composite:0%/70%:100%x10% end=100%/70%:100%x10% in=75 out=149 a_track=0 b_track=2 sliced_composite=1 \ -transition composite:25%/25%:50%x50%! in=150 out=249 a_track=0 b_track=1 sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_news000066400000000000000000000016231362234133600150770ustar00rootroot00000000000000melt \ 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 sliced_composite=1 \ -transition composite geometry=10%/15%:37.5%x40% a_track=0 b_track=2 in=0 out=199 sliced_composite=1 \ -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 sliced_composite=1 \ -transition composite geometry=10%/65%:90%x20% a_track=0 b_track=3 in=0 out=199 sliced_composite=1 \ -transition composite geometry=10%/65%:90%x20% a_track=1 b_track=3 in=200 out=499 sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_obscure000066400000000000000000000003101362234133600155550ustar00rootroot00000000000000melt \ 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 region.composite.sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_pango_keyframes000066400000000000000000000002041362234133600172670ustar00rootroot00000000000000melt \ color:#03CF0 \ -filter watermark:pango_keyframes.mpl composite.halign=c composite.valign=m composite.sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_push000066400000000000000000000007021362234133600150770ustar00rootroot00000000000000melt \ -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 \ sliced_composite=1 \ -transition \ composite in=50 out=75 a_track=0 b_track=2 \ start=-100%/0:100%x100%:100 \ end=0/0:100%x100%:100 \ sliced_composite=1 \ -transition \ mix:-1 in=50 out=75 a_track=1 b_track=2 \ $* mlt-6.20.0/demo/mlt_slideshow000066400000000000000000000001341362234133600161200ustar00rootroot00000000000000melt \ photos/.all.jpg ttl=75 \ -filter luma:luma1.pgm luma.softness=0.1 luma.invert=0 \ $* mlt-6.20.0/demo/mlt_slideshow2000066400000000000000000000004671362234133600162130ustar00rootroot00000000000000melt \ photos/.all.jpg ttl=75 \ -attach crop center=1 \ -attach affine transition.cycle=225 transition.geometry="0=0/0:100%x100%;74=-100/-100:120%x120%;75=-60/-60:110%x110%;149=0/0:110%x110%;150=0/-60:110%x110%;224=-60/0:110%x110%" \ -filter luma cycle=75 duration=25 \ -track music1.ogg \ -transition mix \ $* mlt-6.20.0/demo/mlt_slideshow_black000066400000000000000000000004041362234133600172540ustar00rootroot00000000000000melt photos/.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 \ $* mlt-6.20.0/demo/mlt_squeeze000066400000000000000000000011771362234133600156100ustar00rootroot00000000000000melt \ 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 sliced_composite=1 \ -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 sliced_composite=1 \ -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 sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_squeeze_box000066400000000000000000000011461362234133600164540ustar00rootroot00000000000000melt \ 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 sliced_composite=1 \ -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 sliced_composite=1 \ -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 sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_ticker000066400000000000000000000006451362234133600154070ustar00rootroot00000000000000melt \ clip1.dv out=299 \ -track \ colour:0 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 \ sliced_composite=1 \ -transition \ composite a_track=0 b_track=2 out=299 titles=1 \ start=100%/70%:999%x20% \ end=-299%/70%:999%x20% \ sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_title_over_gfx000066400000000000000000000007521362234133600171450ustar00rootroot00000000000000melt \ 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 \ sliced_composite=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 \ sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_titleshadow_watermark000066400000000000000000000007721362234133600205330ustar00rootroot00000000000000melt \ "+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 sliced_composite=1 \ -transition composite:20%/10%:100%x100% end=60%/40%:100%x100% out=99 a_track=3 b_track=0 sliced_composite=1 \ -transition composite:85%/80%:10%x10%:30 out=1000 a_track=3 b_track=2 sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_voiceover000066400000000000000000000010611362234133600161200ustar00rootroot00000000000000melt \ "+voice over demo.txt" \ family="Sans" \ size="72" \ weight="700" \ 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 \ sliced_composite=1 \ $* mlt-6.20.0/demo/mlt_watermark000066400000000000000000000002551362234133600161200ustar00rootroot00000000000000melt \ 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% sliced_composite=1 \ $* mlt-6.20.0/demo/new.mlt000066400000000000000000000026551362234133600146410ustar00rootroot00000000000000 clip2.mpeg clip3.mpeg greyscale luma luma 1 mlt-6.20.0/demo/pango.mlt000066400000000000000000000024751362234133600151540ustar00rootroot00000000000000 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 mlt-6.20.0/demo/pango_keyframes.mpl000066400000000000000000000000761362234133600172110ustar00rootroot000000000000000=Hello 40= 50=World~the end 90= mlt-6.20.0/demo/svg.mlt000066400000000000000000000054221362234133600146420ustar00rootroot00000000000000 ]> pixbuf mlt-6.20.0/demo/watermark1.png000066400000000000000000000025111362234133600161050ustar00rootroot00000000000000‰PNG  IHDRr ß”sBIT|dˆIDATxœÅ–klSe€Ÿïœ^Ö]º²K·Ø SÙÄýq QƒšEBЂ  Ô#ˆÊ`D¢MH:&2A.M AÁ~(ˆ(Œàl#(Þ ™®ƒmÀºuëÚœµ”­]õ—ï¯s¾Ëû|ïõûiDQ½9ÀSÀlÐÊA8€ ðö+ˆýÀQ·Ë9NŸT¼ ¨€1"À4›Ü.g0Õ")l!h>`i2˜É(X97øp.°ø]Q½Uÿ ¨¨^YQ½ É6˜Œ‚Æ%EÌ}ÌJq^Ò%À£¨ÞçÒ ÀòT§WhdÚd S+-©–]Šê]¨¨Þùé`^_ˆ¶®0¤‰æRT¯2¨¨Þ"`{ÊãÊ‚åµyd=ÇNžÓsbje"eÚ3°[Q½qßÇ,\ä$Ûb2 _.b~M.Kì˜ ‚o~ìÀ–-3i¼iT{…q ¢z­è©ŸÜ:ƒ ϪŸëÁªLÛ¹pe€ŽnÝ­ fØÈ4'MöDYûŠê}Ø3Úê‹Äæºb&O0Ðüsöažžn …3¿9x:Àwç‚„ÂZ25Un—ó¼ì¨^¶˜<0Ö8îéC©°`·(-2R`“ÉÊÐ-“”ÚÔTgQûˆ^.7Q:»#‰jþöyšNIÀ¤ÄѬ ‰u/Øùô­±|ü¦ƒšê,ÐÛåÕMW¹pEï^v›žšÇ=}D¢ºUÖ,‰ÚG¬ì~ÃÁ×JÉÉŒ»{€š#6RQjæà;ãy|Jw5QYff½jgK]1³4 ì<ÒÍŒ•­4î¿Îo…âs{ùé Fc¿Ðc$‹YðÉš±Œ+4âï‹rðt/³¦ecË–ðu…y¾ÁG—?B¶EbKBL[.öS·åƒC±«(51±ÄÄáï ¾ÓÜnWù ´«‹fÚWhäzO„Y«ZÙôù æ­mãÂeÝGíõ%äÊú£¼’`éÔJ ‹õ’¸ÔC.í%znë 30¨Ÿ´Ëá¥÷Ú9ûÇ-ÊŠŒl[Q‚5K}¨*“^+­´9p³WϨû&š©¾'#¾j`PcéûWi>¯w— ÅF>¬+!Ã$F@OŸ¢%­ˆqdGõ2€y¶ 2gº£AP£dsâlÝ=à‘(ó¹ÿî Jò äÊdAËÅ~Ba#-ÚºÂ|v¢'KÓ@,ñyšüpèéè³~o&ÁŽúÊ·ÛV8¢Q¿õ­ƒÌ¯±’oÕêVHã‹æÞ”¦?¸]ÎVÉír†Ðoj °ç¨€192ÛVß ôGiÜwƒ,R݇ID¬}Ūr#CAÝ|àNöÞo¿}éË’ž·ãžFšÝ.çWñý>OSØQ½Ì<ȧ~ Rk ²ÌŒÅ,Qû¨Y1ðú¼|,f‰}Ç{8êîKëfúaC}rÌ-—ZXôn{¼ÐSHxØír¶$ލš¡·ÈÀ 0¥ÂBýÜ<Ê&Â]‡ý|t¨›pdTX'0Çírž>‘´LÕ; ´/AäƒÞ/ÍFA8B:À·Àl·Ëy3ÙähïRX¬ÊÓQ†@ä?‡Á«€Z  ½ëêïVá.[SYô¿Ë?wdÆ|Šp¹IEND®B`‚mlt-6.20.0/docs/000077500000000000000000000000001362234133600133265ustar00rootroot00000000000000mlt-6.20.0/docs/framework.txt000066400000000000000000001527141362234133600160760ustar00rootroot00000000000000Framework Documentation Copyright (C) 2004-2014 Meltytech, LLC Last Revision: 2005-05-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 libmvsp.txt and mvsp.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 "loader". 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 loader 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=xml ./hello file.avi This will create a XML document on stdout. $ MLT_CONSUMER=xml 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 |"loader" 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% compatible 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 MLT XML. 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 loader for scaling here mlt_producer track1 = mlt_factory_producer( "loader", "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 xml 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: mlt-6.20.0/docs/install.txt000066400000000000000000000144231362234133600155410ustar00rootroot00000000000000---+ Installation Documentation Last Revision: 2013-09-07 This document provides a description of the MLT project installation and organisation. ---++ Directories The directory hierarchy is defined as follows: * demo - A selection of samples to show off capabilities. * docs - Location of all documentation * presets - Properties presets for various services * profiles - MLT profile configurations * src - All project source is provided here * framework - The MLT media framework * melt - A media playing test application (*) * mlt++ - C++ wrapper for framework * modules - All services are defined here * avformat - FFmpeg/Libav dependent services * avsync - services to help test audio/video synchronization * core - independent MLT services * decklink - Blackmagick Design SDI/HDMI services * dgraft - ports of Donald Graft's filters (*) * dv - libdv dependent services * effectv - ports of !EffecTV filters (*) * feeds - templates for use with core's data filters * frei0r - adapter for frei0r video plugins * gtk2 - GTK+ pango and pixbuf dependent services * jackrack - adapter for LADSPA audio plugins and JACK server * kdenlive - services contributed by Kdenlive project * kino - DV/AVI demuxer from Kino project (*) * linsys - DVEO SDI card consumer (*) * lumas - wipe file generator for core's luma transition * motion_est - motion estimation-based filters (*) * normalize - audio normalisation functions (*) * oldfilm - filters to make pristine video dirty * opengl - !OpenGL dependent services (*) * plus - miscellaneous services (pending move to core) * qt - Qt dependent services (*) * resample - libresample dependent services (*) * rotoscoping - spline-based alpha mask filter (*) * rtaudio - audio consumer based on !RtAudio project code * sdl - SDL dependent services * sox - !SoX dependent audio filters * swfdec - Swfdec dependent producer for Flash files * videostab - video stabilization filters (*) * vmfx - services contributed by (defunct) Visual Media FX * vorbis - vorbis dependenent services * xine - Xine-derived sources (*) * xml - XML (de)serialization services * swig - High level language bindings using SWIG * tests - Reserved for regression and unit tests * win32 - Windows-specific helper functions Additional subdirectories may be nested below those shown and should be documented in their parent. (*) Contains GPL dependencies or code. ---++ Dependencies The MLT core is dependent on: * a C99 compliant C compiler * posix threading (pthread) * standard posix libraries (libc) The MLT applications and libraries provided are all dependent on the core. The modules have the following dependencies: | *Module* | *Description* | | avformat | [[http://www.ffmpeg.org][FFmpeg]] or [[http://www.libav.org][libav]] v0.7 or later | | dv | [[http://libdv.sf.net][libdv]] 0.102 or later | | gtk2 | [[http://www.gtk.org][GTK2]] and associated dependencies | | jackrack | [[http://jackaudio.org][JACK]], [[http://www.xmlsoft.org/][libxml2]], and ladspa.h | | opengl | [[http://git.sesse.net/movit][Movit]] | | qt | [[http://www.qt-project.org][Qt]] 4.4 or later | | resample | [[http://www.mega-nerd.com/SRC][libsamplerate]] 0.15 or later | | sdl | [[http://www.libsdl.org][SDL]] 1.2 or later | | sox | [[http://sox.sourceforge.net][SoX]] 13 or later | | swfdec | [[http://github.com/mltframework/swfdec][swfdec]] 0.8 or later | | vorbis | [[http://www.vorbis.com][libvorbis]] 1.0.1 or later | | xml | [[http://www.xmlsoft.org][libxml2]] 2.5 or later | ---++ 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= Note: This script must be run to register new services after a git clone 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 in a Bash shell run: =source setenv= Note: 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 and $prefix/include/mlt++. The modules produce a shared object per module installed to $prefix/lib/mlt. Also, each module may have support files installed to $prefix/share/mlt/modules. For the development of modules and applications, pkg-config metadata files are generated and installed in $prefix/lib/pkgconfig. ---++ 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. mlt-6.20.0/docs/melt.1000066400000000000000000000060121362234133600143500ustar00rootroot00000000000000.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.38.4. .TH MELT "1" "February 2020" "melt 6.20.0" "User Commands" .SH NAME melt \- author, play, and encode multitrack audio/video compositions .SH SYNOPSIS .B melt [\fIoptions\fR] [\fIproducer \fR[\fIname=value\fR]\fI* \fR]\fI+\fR .SH OPTIONS .TP \fB\-attach\fR filter[:arg] [name=value]* Attach a filter to the output .TP \fB\-attach\-cut\fR filter[:arg] [name=value]* Attach a filter to a cut .HP \fB\-attach\-track\fR filter[:arg] [name=value]* Attach a filter to a track .TP \fB\-attach\-clip\fR filter[:arg] [name=value]* Attach a filter to a producer .TP \fB\-audio\-track\fR | \fB\-hide\-video\fR Add an audio\-only track .TP \fB\-blank\fR frames Add blank silence to a track .TP \fB\-consumer\fR id[:arg] [name=value]* Set the consumer (sink) .TP \fB\-debug\fR Set the logging level to debug .TP \fB\-filter\fR filter[:arg] [name=value]* Add a filter to the current track .TP \fB\-group\fR [name=value]* Apply properties repeatedly .TP \fB\-help\fR Show this message .TP \fB\-jack\fR Enable JACK transport synchronization .TP \fB\-join\fR clips Join multiple clips into one cut .TP \fB\-mix\fR length Add a mix between the last two cuts .TP \fB\-mixer\fR transition Add a transition to the mix .TP \fB\-null\-track\fR | \fB\-hide\-track\fR Add a hidden track .TP \fB\-profile\fR name Set the processing settings .TP \fB\-progress\fR Display progress along with position .TP \fB\-query\fR List all of the registered services .TP \fB\-query\fR "consumers" | "consumer"=id List consumers or show info about one .TP \fB\-query\fR "filters" | "filter"=id List filters or show info about one .TP \fB\-query\fR "producers" | "producer"=id List producers or show info about one .TP \fB\-query\fR "transitions" | "transition"=id List transitions, show info about one .TP \fB\-query\fR "profiles" | "profile"=id List profiles, show info about one .TP \fB\-query\fR "presets" | "preset"=id List presets, show info about one .TP \fB\-query\fR "formats" List audio/video formats .TP \fB\-query\fR "audio_codecs" List audio codecs .TP \fB\-query\fR "video_codecs" List video codecs .TP \fB\-remove\fR Remove the most recent cut .TP \fB\-repeat\fR times Repeat the last cut .TP \fB\-repository\fR path Set the directory of MLT plugins .TP \fB\-serialise\fR [filename] Write the commands to a text file .TP \fB\-silent\fR Do not display position/transport .TP \fB\-split\fR relative\-frame Split the last cut into two cuts .TP \fB\-swap\fR Rearrange the last two cuts .TP \fB\-track\fR Add a track .TP \fB\-transition\fR id[:arg] [name=value]* Add a transition .TP \fB\-verbose\fR Set the logging level to verbose .TP \fB\-version\fR Show the version and copyright .TP \fB\-video\-track\fR | \fB\-hide\-audio\fR Add a video\-only track .PP For more help: .SH COPYRIGHT Copyright \(co 2002\-2020 Meltytech, LLC .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. mlt-6.20.0/docs/melt.txt000066400000000000000000000343251362234133600150370ustar00rootroot00000000000000Melt Documentation Copyright (C) 2004-2019 Meltytech, LLC Last Revision: 2019-06-24 MELT ----- Preamble: Melt 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: melt [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 -jack Enable JACK transport synchronization -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 -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 -query "profiles" | "profile"=id List profiles or show info about one -query "presets" | "preset"=id List presets or show info about one -query "formats" List audio/video formats -query "audio_codecs" List audio codecs -query "video_codecs" List video codecs -remove Remove the most recent cut -repeat times Repeat the last cut -repository path Set the directory of MLT plugins -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 melt 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 overridden 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: $ melt file Note that 'file' can be anything that melt has a known 'producer' mapping for (so this can be anything from .dv to .txt). You can also specify the producer directly, for example: $ melt 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: $ melt 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: $ melt a.dv b.mpg c.png Properties can be assigned to each file: $ melt 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. $ melt 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: $ melt 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: $ melt -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: $ melt -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: $ melt -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 programming point of view...). The -attach family of switches simplify things enormously. By default, -attach will attach a filter to the last service created, so: $ melt 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: $ melt 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: $ melt 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: $ melt 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 supersedes the enforced use of the -track and -transition switches from earlier versions of melt 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: $ melt 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: $ melt 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: $ melt 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: $ melt 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: $ melt 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: $ melt 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: $ melt 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: Melt has a built in serialisation mechanism - you can build up your command, test it via any consumer and then add a -serialise file.melt switch to save it. The saved file can be subsequently used as a clip by melt or other MLT applications. Take care though - paths to files are saved as provided on the command line.... A more expressive serialisation can be obtained with the xml consumer - this will provide an xml document which can be used freely in melt and other MLT applications. See mlt-xml.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.... melt only supports this in two invocations - as a simple example: $ melt a.dv -track -blank 100 b.dv -consumer xml:basic.mlt $ melt basic.mlt -filter watermark:watermark.png mlt-6.20.0/docs/mlt++.txt000066400000000000000000000404321362234133600150140ustar00rootroot00000000000000MLT++ ----- This mlt sub-project provides a C++ wrapping for the MLT library. Usage ----- Use the following definitions in a Makefile to compile and link with mlt++: CXXFLAGS=`pkg-config --cflags mlt-framework` -Wall LDFLAGS=-lmlt++ `pkg-config --libs mlt-framework` Include files for the classes can either be explicitly included, ie: #include etc Or you can include all using: #include All definitions are placed in an Mlt namespace, and adhere closely to the C naming convention. Mappings always follow the pattern: Factory methods: mlt_factory_init ==> Mlt::Factory::init mlt_factory_producer ==> Mlt::Factory::producer mlt_factory_filter ==> Mlt::Factory::filter mlt_factory_transition ==> Mlt::Factory::transition mlt_factory_consumer ==> Mlt::Factory::consumer mlt_factory_close ==> Mlt::Factory::close NB: Factory usage for service construction is optional. Types: mlt_properties ==> Mlt::Properties mlt_frame ==> Mlt::Frame mlt_service ==> Mlt::Service mlt_producer ==> Mlt::Producer mlt_filter ==> Mlt::Filter mlt_transition ==> Mlt::Transition mlt_consumer ==> Mlt::Consumer Methods: mlt_type_method ==> Mlt::Type.method ie: mlt_playlist_append ==> Mlt::Playlist.append Parent methods are available directly on children. Additionally, you can specify: using namespace Mlt; To avoid the enforced use of the Mlt:: prefix. Enumerators and macros are reused directly from the C library. Class Hierarchy --------------- The currently mapped objects are shown in the following hierarchy: Factory Properties Frame Service Consumer Field Filter Multitrack Producer Playlist Tractor Transition Special Cases ------------- Care should be taken with wrapper objects. Taking, as an example, the C function that returns the immediate consumer of a service: mlt_service mlt_service_consumer( mlt_service ); This maps to: Mlt::Service *Mlt::Service.consumer( ); Note that you get an object back - it is never the original c++ object, but a wrapping object. This is done to keep consistency with the C api which may instantiate C instances - therefore it cannot be assumed that a C++ object exists for all mlt service instances. As such, it is mandatory that you delete these objects. The original will not be affected. However, all other modifications (to properties or its state of connection) will be reflected in the original object. This approach excludes the use of RTTI to determine the real type of the object - this can only be done by parsing the objects properties. Objects may be invalid - always use the is_valid method to check validity before use. Limitations ----------- The mechanisms for the definition of new services are deliberately excluded from the C++ wrappings - this is done to ensure that service networks constructed can be serialised and used by existing applications which are based on the C API (such as melted). Hello World ----------- The mlt++ wrapper is a c++ wrapper for the mlt C library. As such, it provides clean C++ access to the underlying library. An example of use is as follows: #include using namespace Mlt; int main( void ) { Factory::init( ); Producer p( "pango:", "Hello World" ); Consumer c( "sdl" ); c.connect( p ); c.run( ); return 0; } This is a fairly typical example of mlt++ usage - create a 'producer' (an object which produces 'frames'), create a 'consumer' (an object which consumes frames), connect them together, start the consumer and wait until done (here we just wait for the user to close the window). In this case, we construct a window as a consumer using the 'sdl' consumer (SDL is a standard portable library which provides platform independent access to accelerated video display and audio) and use the 'pango' producer to generate frames with the words 'Hello World' (pango is a library from the gtk toolkit). The main point of this example is to show that mlt uses existing libraries to provide its functionality - this keeps the framework itself very small. Note that mlt is designed to be housed in GUI or server type applications - typically, applications don't wait around for the consumer to be stopped in the manner shown. So far, we've introduced the Producer and Consumer mlt classes. We'll cover each of these in more detail later in the tutorial, but for now, we'll briefly cover the remaining classes. Playlists --------- Another simple class is the Playlist - this is direct extension of Producer and it allows you to maintain a list of producer objects. As a simple example of the Playlist in action, we'll convert the example above into an application which plays multiple video or audio files. #include using namespace Mlt; int main( int argc, char **argv ) { Factory::init( ); Playlist list; for ( int i = 1; i < argc; i ++ ) { Producer p( argv[i] ); if ( p.is_valid( ) ) list.append( p ); } Consumer c( "sdl" ); c.connect( list ); c.run( ); return 0; } Now you can run the program as: ./player *.avi *.mp3 *.jpg etc In this case, we construct a playlist by simply appending producers to it. Notice that although the scope of the Producer is limited to the inner for loop, we can safely add it to the playlist - this is due to the fact that all mlt objects maintain reference counts and no object is really destroyed until all the references are gone. In this case, when the list object goes out of scope, all the producers we created will automatically be destroyed. Filters ------- So far, we've shown how you can load and play media. We've given a brief intro to the Playlist container, now it's time to start manipulating things... For the next example, I'll add a 'watermark' to the video - a watermark is used by broadcasters to brand the channel and normally consists of a logo of some sort. We'll just use some black text on a partially transparent red background. #include using namespace Mlt; int main( int argc, char **argv ) { Factory::init( ); Playlist list; for ( int i = 1; i < argc; i ++ ) { Producer p( argv[i] ); if ( p.is_valid( ) ) list.append( p ); } Filter f( "watermark", "pango:" ); f.set( "producer.text", "MLT++" ); f.set( "producer.fgcolour", "0x000000ff" ); f.set( "producer.bgcolour", "0xff000080" ); list.attach( f ); Consumer c( "sdl" ); c.connect( list ); c.run( ); return 0; } Notice that the watermark filter reuses the 'pango' producer we showed in the first example. In fact, you could use any producer here - if you wanted to use a graphic or a video, you would just construct the filter with a full path to that as the second argument. We manipulate the filter using the set method - this method was also shown in the first example. Finally, we attach the filter to the playlist. This ensure that all frames that are obtained from the playlist are watermarked. Cuts ---- When you add a clip to a playlist, the a cut object is created - this is merely a wrapper for the producer, spanning the specified in and out points. Whenever you retrieve a clip from a playlist, you will always get a cut object. This allows you to attach filters to a specific part of a producer and should the position of the cut in the playlist change, then the filter will remain correctly associated to it. A producer and a cut are generally identical in behaviour, but should you need to distinguish between them, you can use: if ( producer.is_cut( ) ) and to retrieve the parent of a cut, you can use: Producer parent = producer.parent_cut( ); Filters that are attached directly to a parent are executed before any filters attached to the cut. Tractor ------- A tractor is an object that allows the manipulation of multiple video and audio tracks. Stepping away from the player example we've been tinkering with for a minute, let's assume we want to do something like dub a video with some audio. This a very trivial thing to do: Tractor *dub( char *video_file, char *audio_file ) { Tractor *tractor = new Tractor( ); Producer video( video_file ); Producer audio( audio_file ); tractor->set_track( video, 0 ); tractor->set_track( audio, 1 ); return tractor; } That's all that needs to be done - you can now connect the returned object to a consumer, or add it to a playlist, or even apply it as a track to another tractor. Transition ---------- Let's now assume we want to mix the audio between two tracks - to do this, we need to introduce the concept of a transition. A transition in mlt is a service which combines frames from two producers to produce a new frame. Tractor *mix( char *video_file, char *audio_file ) { Tractor *tractor = new Tractor( ); Transition mix( "mix" ); Producer video( video_file ); Producer audio( audio_file ); tractor.set_track( video, 0 ); tractor.set_track( audio, 1 ); tractor.field.plant_transition( mix, 0, 1 ); return tractor; } The tractor returned will now mix the audio from the original video and the audio. Mix --- There is a convenience function which simplifies the process of applying transitions betwee adjacent cuts on a playlist. This is often preferable to use over the construction of your own tractor and transition set up. To apply a 25 frame luma transition between the first and second cut on the playlist, you could use: Transition luma; playlist.mix( 0, 25, luma ); Events ------ Typically, applications need to be informed when changes occur in an mlt++ object. This facilitates application services such as undo/redo management, or project rendering in a timeline type widget and many other types of operations which an application needs. As an example, consider the following: class Xml { private: Consumer consumer; Tractor &tractor; public: Xml( MltTractor &tractor ) : tractor( tractor ), consumer( "xml" ) { consumer.connect( tractor ); tractor.listen( tractor, "producer-changed", ( mlt_listener )Xml::listener ); } static void listener( Properties *tractor, Xml *object ) { object->activate( ); } void activate( ) { consumer.start( ); } }; Now, each time the tractor is changed, the XML representation is output to stderr. That's All Folks... ------------------- And that, believe it or not, is a fairly complete summary of the classes you'll typically be interfacing with in mlt++. Obviously, there's a little more to it than this - a couple of intrinsic classes have been glossed over (notably, the Properties and Service base classes). The next section will cover all of the above, but in much more detail... DIGGING DEEPER -------------- The previous section was designed to give you a whistle stop tour through the major framework classes. This section will take you through the scenic route. Introducing Base Classes ------------------------ Services in mlt are the collective noun for Producers, Filters, Transitions and Consumer. A Service is also the base class from which all of these classes extend. It provides the basic connectivity which has been shown throughout the examples in the previous section. Properties are the main way in which we communicate with the Services - essentially, it provides get/set methods for named values. All services extend Properties. Properties ---------- Properties provide the general mechanism for communicating with Services - through the Properties interface, we are able to manipulate and serialise a services state. For example, to dump all the properties to stdout, you can use something like: void dump( Properties &properties ) { for ( int i = 0; i < properties.count( ); i ++ ) cout << Properties.get_name( i ) << " = " << Properties.get( i ) << endl; } Note that the properties object handles type conversion, so the following is acceptable: properties.set( "hello", "10.5" ); int hello_int = properties.get_int( "hello" ); double hello_double = properties.get_double( "hello" ); A couple of convenience methods are provide to examine or serialise property objects. For example: properties.debug( ); will report all serialisable properties on stderr, in the form: Object: [ ref=1, in=0, out=0, track=0, u=75, v=150, _unique_id=15, mlt_type=filter, mlt_service=sepia ] Services -------- Typically, all the services are constructed via the specific classes constructor. Often, you will receive Service objects rather than their specific type. In order to access the extended classes interface, you will need to create a reference. For example, given an arbitrary Service object, you can determine its type by using the type method - this will return a 'service_type' which has values of producer_type, filter_type etc. Alternatively, you can create a wrapping object and check on its validity. bool do_we_have_a_producer( Service &service ) { Producer producer( service ); return producer.is_valid( ); } Events ------ Servers and MLT XML Docs ------------------------ For various reasons, you might want to serialise a producer to a string. To do this, you just need to specify a property to write to: Consumer xml( "xml", "buffer" ); xml.connect( producer ); xml.start( ); buffer = xml.get( "buffer" ); You can use any name you want, and you can change it using the "resource" property. Any name with a '.' in it is considered to be a file. Hence, you can use a xml consumer to store multiple instances of the same MLT object - useful if you want to provide undo/redo capabilities in an editing application. Should you receive an XML document as a string, and you want to send it on to a server, you can use: Consumer client( "mvsp", "localhost:5250" ); client.set( "xml", buffer ); client.start( ); If you need to obtain an MLT object from a XML string: Producer producer( "xml-string", buffer ); The following shows a working example of an extended server: class ShotcutServer : public Melted { public: ShotcutServer( char *id, int port ) : Melted( id, port ) { } void set_receive_doc( bool doc ) { set( "push-parser-off", doc ); } // Reject all commands other than push/receive Response *execute( char *command ) { mvsp_response response = mvsp_response_init( ); mvsp_response_set_error( response, 400, "Not OK" ); return new Response( response ); } // Push document handler Response *received( char *command, char *doc ) { mvsp_response response = mvsp_response_init( ); // Use doc in some way and assign Response if ( doc != NULL ) mvsp_response_set_error( response, 200, "OK" ); return new Response( response ); } // Push service handler Response *push( char *command, Service *service ) { mvsp_response response = mvsp_response_init( ); // Use service in some way and assign Response if ( service != NULL ) mvsp_response_set_error( response, 200, "OK" ); return new Response( response ); } }; NB: Should you be incorporating this into a GUI application, remember that the execute, received and push methods are invoked from a thread - make sure that you honour the locking requirements of your GUI toolkit before interacting with the UI. mlt-6.20.0/docs/mlt-xml.txt000066400000000000000000000515731362234133600154740ustar00rootroot00000000000000MLT XML Schema Documentation Copyright (C) 2004-2014 Meltytech, LLC Last Revision: 2009-05-08 MLT XML ------- Preamble: This 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/xml/mlt-xml.dtd, which gets installed at $(prefix)/share/mlt/modules/xml/mlt-xml.dtd. Currently, the XML parser is non-validating. Introduction: A MLT XML 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 sdi) or even the XML serialising consumer itself (the latter doesn't receive frames - it merely interrogates the connected producer for its configuration). Although MLT XML was defined as a serialisation mechanism for instantiated MLT components, this document will concentrate on the hand authoring of MLT XML documents. Rules: As shall become apparent through the remainder of this document, the basic tenet of MLT XML 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 MLT XML exist, the approach taken here is to maximise efficiency for complex projects. Basic Producers: The simplest MLT XML document is: clip1.dv The XML 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 MLT XML 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 MLT XML doc can be specified as a resource, so XML docs can naturally encapsulate other XML docs. 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) MLT XML 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 melt 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 XML, 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 specified 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 XML "normal" form. This is the output generated by the xml consumer, for example, when used with melt. It is the output generated when you use the "XML to File" consumer in the demo script, which beginners will find most useful for learning to use MLT XML. This section describes alternative forms the xml 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. The producer 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, the producer 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 mlt 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 the producer 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. The producer 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, the producer 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 the producer collects embedded XML until it reaches a closing property tag. Entities and Parameterisation: The MLT XML 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.mlt for an example. Entities can be used to parameterise MLT XML! 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 xml 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 melted LOAD command and melt command line tool require you to preface the URL with "xml:" because the query string destroys the filename extension matching performed by the auto-loader. Also, melt looks for '=' to tokenise property settings. Therefore, one uses ':' between name and value instead of '='. Finally, since melt 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 MLT XML compositions is an extremely powerful tool. An example for you to play with is available in demo/entity.mlt. Try overriding the name from melt: melt 'xml:entity.mlt?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 behaviour 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). mlt-6.20.0/mlt++.pc.in000066400000000000000000000002271362234133600142520ustar00rootroot00000000000000 Name: mlt++ Description: C++ API for MLT multimedia framework Version: ${version} Requires: mlt-framework Libs: -L${libdir} ${libs} Cflags: ${cflags} mlt-6.20.0/mlt-config-template000066400000000000000000000015061362234133600161730ustar00rootroot00000000000000export 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." mlt-6.20.0/mlt-framework.pc.in000066400000000000000000000002051362234133600161130ustar00rootroot00000000000000 Name: mlt-framework Description: MLT multimedia framework Version: ${version} Requires: Libs: -L${libdir} ${libs} Cflags: ${cflags} mlt-6.20.0/presets/000077500000000000000000000000001362234133600140635ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/000077500000000000000000000000001362234133600157165ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/000077500000000000000000000000001362234133600175355ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/AAC000066400000000000000000000001731362234133600200450ustar00rootroot00000000000000f=mp4 acodec=aac ab=256k vn=1 video_off=1 meta.preset.extension=m4a meta.preset.note=audio only meta.preset.name=audio/AAC mlt-6.20.0/presets/consumer/avformat/ALAC000066400000000000000000000001661362234133600201630ustar00rootroot00000000000000f=mov acodec=alac vn=1 video_off=1 meta.preset.extension=mov meta.preset.note=audio only meta.preset.name=audio/ALAC mlt-6.20.0/presets/consumer/avformat/FLAC000066400000000000000000000001731362234133600201660ustar00rootroot00000000000000f=matroska acodec=flac vn=1 video_off=1 meta.preset.extension=mka meta.preset.note=audio only meta.preset.name=audio/FLAC mlt-6.20.0/presets/consumer/avformat/Flash000066400000000000000000000003651362234133600205210ustar00rootroot00000000000000f=flv acodec=libmp3lame ab=128k ar=44100 vcodec=flv minrate=0 vb=1M bf=0 progressive=1 meta.preset.extension=flv meta.preset.note=This is the old Sorenson H.263-based codec. Most people use H.264 with Flash now. meta.preset.name=legacy/Flash mlt-6.20.0/presets/consumer/avformat/GIF000066400000000000000000000001471362234133600200670ustar00rootroot00000000000000progressive=1 f=gif vcodec=gif an=1 g=1 bf=0 meta.preset.extension=gif meta.preset.name=GIF Animation mlt-6.20.0/presets/consumer/avformat/MJPEG000066400000000000000000000001321362234133600203160ustar00rootroot00000000000000f=avi vcodec=mjpeg qscale=4 acodec=libmp3lame ab=256k g=1 bf=0 meta.preset.extension=avi mlt-6.20.0/presets/consumer/avformat/MP3000066400000000000000000000002031362234133600200520ustar00rootroot00000000000000f=mp3 acodec=libmp3lame ab=256k vn=1 video_off=1 meta.preset.extension=mp3 meta.preset.note=audio only meta.preset.name=audio/MP3 mlt-6.20.0/presets/consumer/avformat/MPEG-2000066400000000000000000000003131362234133600203440ustar00rootroot00000000000000f=mpeg acodec=mp2 ab=256k ar=48000 vcodec=mpeg2video minrate=0 vb=8M trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=mpg meta.preset.note=a general purpose MPEG-2 preset mlt-6.20.0/presets/consumer/avformat/MPEG-4000066400000000000000000000004131362234133600203470ustar00rootroot00000000000000f=mp4 acodec=libmp3lame ab=128k ar=44100 vcodec=mpeg4 minrate=0 vb=2M mbd=rd trellis=1 flags=+mv4+aic cmp=satd subcmp=satd progressive=1 movflags=+faststart meta.preset.extension=mp4 meta.preset.note=Part 2 Simple Profile meta.preset.name=legacy/MPEG-4 Part 2 SP mlt-6.20.0/presets/consumer/avformat/MPEG-4-ASP000066400000000000000000000004211362234133600207670ustar00rootroot00000000000000f=mp4 acodec=libmp3lame ab=128k ar=44100 vcodec=mpeg4 minrate=0 vb=2M mbd=rd trellis=1 cmp=satd subcmp=satd bf=2 flags=+mv4+aic+qpel movflags=+faststart meta.preset.extension=mp4 meta.preset.note=Part 2 Advanced Simple Profile meta.preset.name=legacy/MPEG-4 Part 2 ASP mlt-6.20.0/presets/consumer/avformat/Sony-PSP000066400000000000000000000004721362234133600210530ustar00rootroot00000000000000width=480 height=272 aspect=@16/9 progressive=1 f=psp acodec=aac ar=32000 ab=96k vcodec=libx264 threads=0 vpre=medium preset=medium vprofile=main flags2=-dct8x8 vb=1M refs=1 bf=1 x264opts=ref=1:bframes=1 meta.preset.extension=mp4 meta.preset.note=for Sony PlayStation Portable meta.preset.name=device/Sony PSP mlt-6.20.0/presets/consumer/avformat/Vorbis000066400000000000000000000002061362234133600207220ustar00rootroot00000000000000f=ogg acodec=vorbis ab=256k vn=1 video_off=1 meta.preset.name=audio/Ogg Vorbis meta.preset.extension=ogg meta.preset.note=audio only mlt-6.20.0/presets/consumer/avformat/WAV000066400000000000000000000001721362234133600201150ustar00rootroot00000000000000f=wav acodec=pcm_s16le vn=1 video_off=1 meta.preset.extension=wav meta.preset.note=audio only meta.preset.name=audio/WAV mlt-6.20.0/presets/consumer/avformat/WMA000066400000000000000000000002071362234133600201030ustar00rootroot00000000000000f=asf acodec=wmav2 ab=256k vn=1 video_off=1 meta.preset.extension=wma meta.preset.note=Windows Media Audio meta.preset.name=audio/WMA mlt-6.20.0/presets/consumer/avformat/WMV000066400000000000000000000002301362234133600201240ustar00rootroot00000000000000f=asf acodec=wmav2 ab=160k vcodec=wmv2 g=100 vb=2M bf=0 meta.preset.extension=wmv meta.preset.note=Windows Media Video (Windows Media Player 8 and up) mlt-6.20.0/presets/consumer/avformat/XDCAM-HD422000066400000000000000000000005461362234133600210420ustar00rootroot00000000000000f=mxf vcodec=mpeg2video top_field_first=1 pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M dc=10 intra_vlc=1 non_linear_quant=1 lmin=1*QP2LAMBDA rc_max_vbv_use=1 rc_min_vbv_use=1 qmin=1 qmax=12 bufsize=36408333 g=12 bf=2 vtag=xd5c acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Sony "workflow innovation" meta.preset.name=camcorder/XDCAM-HD422 mlt-6.20.0/presets/consumer/avformat/YouTube000066400000000000000000000003021362234133600210470ustar00rootroot00000000000000f=mp4 movflags=+faststart acodec=aac ar=48000 ab=384k vcodec=libx264 progressive=1 preset=fast threads=0 crf=23 g=15 bf=2 meta.preset.extension=mp4 meta.preset.note=H.264/AAC MP4 for YouTube mlt-6.20.0/presets/consumer/avformat/alpha/000077500000000000000000000000001362234133600206225ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/alpha/Quicktime Animation000066400000000000000000000002351362234133600244000ustar00rootroot00000000000000f=mov vcodec=qtrle g=1 bf=0 progressive=1 mlt_image_format=rgb24a pix_fmt=argb meta.preset.extension=mov meta.preset.note=lossless video with alpha channel mlt-6.20.0/presets/consumer/avformat/alpha/Ut Video000066400000000000000000000003031362234133600221600ustar00rootroot00000000000000f=avi vcodec=utvideo mlt_image_format=rgb24a pix_fmt=gbrap g=1 bf=0 acodec=pcm_s24le meta.preset.extension=avi meta.preset.note=Ut Video with alpha channel and 24-bit PCM audio in AVI container mlt-6.20.0/presets/consumer/avformat/alpha/vp8000066400000000000000000000006501362234133600212630ustar00rootroot00000000000000f=webm acodec=vorbis ab=128k vcodec=libvpx g=120 rc_lookahead=16 quality=good speed=0 vprofile=0 qmax=51 qmin=11 slices=4 vb=2M maxrate=24M minrate=100k arnr_max_frames=7 arnr_strength=5 arnr_type=3 auto-alt-ref=0 mlt_image_format=rgb24a pix_fmt=yuva420p meta.preset.name=alpha/WebM VP8 with alpha channel meta.preset.extension=webm meta.preset.note=VP8 video with Ogg Vorbis audio in Matroska container: "Don't be evil" mlt-6.20.0/presets/consumer/avformat/alpha/vp9000066400000000000000000000006611362234133600212660ustar00rootroot00000000000000f=webm acodec=libopus ar=48000 ab=128k vcodec=libvpx-vp9 vb=2M g=120 bf=2 threads=0 rc_lookahead=16 quality=good speed=3 vprofile=0 qmax=51 qmin=4 slices=4 tile-columns=6 frame-parallel=1 lag-in-frames=25 row-mt=1 auto-alt-ref=0 mlt_image_format=rgb24a pix_fmt=yuva420p meta.preset.name=alpha/WebM VP9 with alpha channel meta.preset.extension=webm meta.preset.note=VP9 video with Opus audio in Matroska container: "Don't be evil" mlt-6.20.0/presets/consumer/avformat/atsc_1080i_50/000077500000000000000000000000001362234133600216145ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080i_50/DNxHD000066400000000000000000000003641362234133600224470ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=185M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080i 25 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080i_5994/000077500000000000000000000000001362234133600220025ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080i_5994/DNxHD000066400000000000000000000003671362234133600226400ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080i 29.97 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080p_2398/000077500000000000000000000000001362234133600220045ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080p_2398/DNxHD000066400000000000000000000003671362234133600226420ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=175M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 23.98 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080p_24/000077500000000000000000000000001362234133600216245ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080p_24/DNxHD000066400000000000000000000003641362234133600224570ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=175M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 24 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080p_25/000077500000000000000000000000001362234133600216255ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080p_25/DNxHD000066400000000000000000000003641362234133600224600ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=185M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 25 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080p_2997/000077500000000000000000000000001362234133600220115ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080p_2997/DNxHD000066400000000000000000000003671362234133600226470ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 29.97 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080p_30/000077500000000000000000000000001362234133600216215ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080p_30/DNxHD000066400000000000000000000003641362234133600224540ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 30 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080p_50/000077500000000000000000000000001362234133600216235ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080p_50/DNxHD000066400000000000000000000003641362234133600224560ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=185M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 50 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080p_5994/000077500000000000000000000000001362234133600220115ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080p_5994/DNxHD000066400000000000000000000003671362234133600226470ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 59.94 fps) mlt-6.20.0/presets/consumer/avformat/atsc_1080p_60/000077500000000000000000000000001362234133600216245ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_1080p_60/DNxHD000066400000000000000000000003641362234133600224570ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 60 fps) mlt-6.20.0/presets/consumer/avformat/atsc_720p_2398/000077500000000000000000000000001362234133600217245ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_720p_2398/DNxHD000066400000000000000000000003651362234133600225600ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=90M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 720p 23.98 fps) mlt-6.20.0/presets/consumer/avformat/atsc_720p_50/000077500000000000000000000000001362234133600215435ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_720p_50/DNxHD000066400000000000000000000003631362234133600223750ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=175M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 720p 50 fps) mlt-6.20.0/presets/consumer/avformat/atsc_720p_5994/000077500000000000000000000000001362234133600217315ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_720p_5994/DNxHD000066400000000000000000000003671362234133600225670ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 1080p 59.94 fps) mlt-6.20.0/presets/consumer/avformat/atsc_720p_60/000077500000000000000000000000001362234133600215445ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/atsc_720p_60/DNxHD000066400000000000000000000003631362234133600223760ustar00rootroot00000000000000f=mov vcodec=dnxhd vb=220M threads=2 acodec=pcm_s16le g=1 bf=0 meta.preset.extension=mov meta.preset.note=A lightly compressed intermediate codec developed by Avid also known as SMPTE VC-3 meta.preset.name=intermediate/DNxHD (HD 720p 60 fps) mlt-6.20.0/presets/consumer/avformat/dv_ntsc/000077500000000000000000000000001362234133600211755ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/dv_ntsc/D10000066400000000000000000000006201362234133600214420ustar00rootroot00000000000000f=mxf_d10 vcodec=mpeg2video pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M g=1 bf=0 flags=+ildct+low_delay dc=10 intra_vlc=1 non_linear_quant=1 ps=1 qmin=1 qmax=3 bufsize=1669k rc_init_occupancy=1669k rc_buf_aggressivity=0.25 acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Intra-frame only, 50 Mb/s MPEG-2 also known as Sony IMX or SMPTE 365M meta.preset.name=camcorder/D10 (SD NTSC) mlt-6.20.0/presets/consumer/avformat/dv_ntsc/DV000066400000000000000000000003201362234133600214240ustar00rootroot00000000000000f=dv pix_fmt=yuv411p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format meta.preset.name=camcorder/DV (SD NTSC) mlt-6.20.0/presets/consumer/avformat/dv_ntsc/DVCPRO50000066400000000000000000000003001362234133600222530ustar00rootroot00000000000000f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV meta.preset.name=camcorder/DVCPRO50 (SD NTSC) mlt-6.20.0/presets/consumer/avformat/dv_ntsc/DVD000066400000000000000000000004641362234133600215410ustar00rootroot00000000000000f=dvd vcodec=mpeg2video acodec=ac3 vb=6000k maxrate=9000k minrate=0 bufsize=1835008 packetsize=2048 muxrate=10080000 ab=192k ar=48000 g=18 me_range=63 trellis=1 meta.preset.extension=vob meta.preset.note=Process the output with a DVD authoring tool such as dvdauthor. meta.preset.name=device/DVD (SD NTSC) mlt-6.20.0/presets/consumer/avformat/dv_ntsc_wide/000077500000000000000000000000001362234133600222055ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/dv_ntsc_wide/D10000066400000000000000000000006331362234133600224560ustar00rootroot00000000000000f=mxf_d10 vcodec=mpeg2video pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M g=1 bf=0 flags=+ildct+low_delay dc=10 intra_vlc=1 non_linear_quant=1 ps=1 qmin=1 qmax=3 bufsize=1669k rc_init_occupancy=1669k rc_buf_aggressivity=0.25 acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Intra-frame only, 50 Mb/s MPEG-2 also known as Sony IMX or SMPTE 365M meta.preset.name=camcorder/D10 (SD Widescreen NTSC) mlt-6.20.0/presets/consumer/avformat/dv_ntsc_wide/DV000066400000000000000000000003331362234133600224400ustar00rootroot00000000000000f=dv pix_fmt=yuv411p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format meta.preset.name=camcorder/DV (SD Widescreen NTSC) mlt-6.20.0/presets/consumer/avformat/dv_ntsc_wide/DVCPRO50000066400000000000000000000003131362234133600232670ustar00rootroot00000000000000f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV meta.preset.name=camcorder/DVCPRO50 (SD Widescreen NTSC) mlt-6.20.0/presets/consumer/avformat/dv_ntsc_wide/DVD000066400000000000000000000004771362234133600225550ustar00rootroot00000000000000f=dvd vcodec=mpeg2video acodec=ac3 vb=6000k maxrate=9000k minrate=0 bufsize=1835008 packetsize=2048 muxrate=10080000 ab=192k ar=48000 g=18 me_range=63 trellis=1 meta.preset.extension=vob meta.preset.note=Process the output with a DVD authoring tool such as dvdauthor. meta.preset.name=device/DVD (SD Widescreen NTSC) mlt-6.20.0/presets/consumer/avformat/dv_pal/000077500000000000000000000000001362234133600210025ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/dv_pal/D10000066400000000000000000000006401362234133600212510ustar00rootroot00000000000000f=mxf_d10 top_field_first=1 vcodec=mpeg2video pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M g=1 bf=0 flags=+ildct+low_delay dc=10 intra_vlc=1 non_linear_quant=1 ps=1 qmin=1 qmax=3 bufsize=2000k rc_init_occupancy=2000k rc_buf_aggressivity=0.25 acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Intra-frame only, 50 Mb/s MPEG-2 also known as Sony IMX or SMPTE 365M meta.preset.name=camcorder/D10 (SD PAL) mlt-6.20.0/presets/consumer/avformat/dv_pal/DV000066400000000000000000000003171362234133600212370ustar00rootroot00000000000000f=dv pix_fmt=yuv420p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format meta.preset.name=camcorder/DV (SD PAL) mlt-6.20.0/presets/consumer/avformat/dv_pal/DVCPRO50000066400000000000000000000002771362234133600220750ustar00rootroot00000000000000f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV meta.preset.name=camcorder/DVCPRO50 (SD PAL) mlt-6.20.0/presets/consumer/avformat/dv_pal/DVD000066400000000000000000000004621362234133600213440ustar00rootroot00000000000000f=dvd vcodec=mpeg2video acodec=ac3 vb=5000k maxrate=8000k minrate=0 bufsize=1835008 packetsize=2048 muxrate=10080000 ab=192k ar=48000 g=15 me_range=63 trellis=1 meta.preset.extension=vob meta.preset.note=Process the output with a DVD authoring tool such as dvdauthor. meta.preset.name=device/DVD (SD PAL) mlt-6.20.0/presets/consumer/avformat/dv_pal_wide/000077500000000000000000000000001362234133600220125ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/dv_pal_wide/D10000066400000000000000000000006531362234133600222650ustar00rootroot00000000000000f=mxf_d10 top_field_first=1 vcodec=mpeg2video pix_fmt=yuv422p minrate=50M maxrate=50M vb=50M g=1 bf=0 flags=+ildct+low_delay dc=10 intra_vlc=1 non_linear_quant=1 ps=1 qmin=1 qmax=3 bufsize=2000k rc_init_occupancy=2000k rc_buf_aggressivity=0.25 acodec=pcm_s16le meta.preset.extension=mxf meta.preset.note=Intra-frame only, 50 Mb/s MPEG-2 also known as Sony IMX or SMPTE 365M meta.preset.name=camcorder/D10 (SD Widescreen PAL) mlt-6.20.0/presets/consumer/avformat/dv_pal_wide/DV000066400000000000000000000003321362234133600222440ustar00rootroot00000000000000f=dv pix_fmt=yuv420p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=The popular standard definition camcorder digital video format meta.preset.name=camcorder/DV (SD Widescreen PAL) mlt-6.20.0/presets/consumer/avformat/dv_pal_wide/DVCPRO50000066400000000000000000000003121362234133600230730ustar00rootroot00000000000000f=dv pix_fmt=yuv422p vcodec=dvvideo acodec=pcm_s16le g=1 bf=0 meta.preset.extension=dv meta.preset.note=Double the amount of chroma as normal DV meta.preset.name=camcorder/DVCPRO50 (SD Widescreen PAL) mlt-6.20.0/presets/consumer/avformat/dv_pal_wide/DVD000066400000000000000000000004761362234133600223610ustar00rootroot00000000000000f=dvd vcodec=mpeg2video acodec=ac3 vb=5000k maxrate=8000k minrate=0 bufsize=1835008 packetsize=2048 muxrate=10080000 ab=192k ar=48000 g=15 me_range=63 trellis=1 meta.preset.extension=vob meta.preset.note=Process the output with a DVD authoring tool such as dvdauthor. meta.preset.name=device/DVD (SD Widescreen PAL) mlt-6.20.0/presets/consumer/avformat/hdv_1080_25p/000077500000000000000000000000001362234133600214545ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/hdv_1080_25p/HDV000066400000000000000000000003721362234133600220220ustar00rootroot00000000000000f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 1080p 25 fps) mlt-6.20.0/presets/consumer/avformat/hdv_1080_30p/000077500000000000000000000000001362234133600214505ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/hdv_1080_30p/HDV000066400000000000000000000003751362234133600220210ustar00rootroot00000000000000f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 1080p 29.97 fps) mlt-6.20.0/presets/consumer/avformat/hdv_1080_50i/000077500000000000000000000000001362234133600214435ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/hdv_1080_50i/HDV000066400000000000000000000003721362234133600220110ustar00rootroot00000000000000f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 1080i 25 fps) mlt-6.20.0/presets/consumer/avformat/hdv_1080_60i/000077500000000000000000000000001362234133600214445ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/hdv_1080_60i/HDV000066400000000000000000000003751362234133600220150ustar00rootroot00000000000000f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 1080i 29.97 fps) mlt-6.20.0/presets/consumer/avformat/hdv_720_25p/000077500000000000000000000000001362234133600213745ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/hdv_720_25p/HDV000066400000000000000000000003711362234133600217410ustar00rootroot00000000000000f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 720p 25 fps) mlt-6.20.0/presets/consumer/avformat/hdv_720_30p/000077500000000000000000000000001362234133600213705ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/hdv_720_30p/HDV000066400000000000000000000003741362234133600217400ustar00rootroot00000000000000f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 720p 29.97 fps) mlt-6.20.0/presets/consumer/avformat/hdv_720_50p/000077500000000000000000000000001362234133600213725ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/hdv_720_50p/HDV000066400000000000000000000003711362234133600217370ustar00rootroot00000000000000f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 720p 50 fps) mlt-6.20.0/presets/consumer/avformat/hdv_720_60p/000077500000000000000000000000001362234133600213735ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/hdv_720_60p/HDV000066400000000000000000000003741362234133600217430ustar00rootroot00000000000000f=mpegts acodec=mp2 ab=384k ar=48000 ac=2 vcodec=mpeg2video vb=25M g=15 trellis=1 bf=2 b_strategy=1 mbd=rd cmp=satd subcmp=satd meta.preset.extension=m2t meta.preset.note=HD MPEG-2 camcorder format meta.preset.name=camcorder/HDV (HD 720p 59.94 fps) mlt-6.20.0/presets/consumer/avformat/intermediate/000077500000000000000000000000001362234133600222075ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/intermediate/DNxHR-HQ000066400000000000000000000004251362234133600233640ustar00rootroot00000000000000pix_fmt=yuv422p progressive=1 vcodec=dnxhd vprofile=dnxhr_hq qscale=1 vb=0 g=1 bf=0 acodec=pcm_s24le f=mov movflags=+write_colr+faststart meta.preset.extension=mov meta.preset.note=Avid's intermediate codec for resolutions up to UHD/C4K meta.preset.name=intermediate/DNxHR HQ mlt-6.20.0/presets/consumer/avformat/intermediate/MJPEG000066400000000000000000000002251362234133600227730ustar00rootroot00000000000000progressive=1 f=avi acodec=pcm_s16le vcodec=mjpeg qscale=1 g=1 bf=0 meta.preset.extension=avi meta.preset.note=not lossless, but still high quality mlt-6.20.0/presets/consumer/avformat/intermediate/MPEG-2000066400000000000000000000002541362234133600230220ustar00rootroot00000000000000f=mpeg acodec=ac3 ab=512k vcodec=mpeg2video intra=1 vb=0 g=1 bf=0 qscale=1 meta.preset.extension=mpg meta.preset.note=a little lossy, but intra-frame only with AC-3 audio mlt-6.20.0/presets/consumer/avformat/intermediate/MPEG-4000066400000000000000000000002601362234133600230210ustar00rootroot00000000000000f=avi acodec=pcm_s16le vcodec=mpeg4 qscale=1 intra=1 g=1 vb=0 bf=0 meta.preset.extension=avi meta.preset.note=somewhat lossy, intra-frame only MPEG-4, with uncompressed audio mlt-6.20.0/presets/consumer/avformat/intermediate/ProRes000066400000000000000000000003451362234133600233460ustar00rootroot00000000000000f=mov acodec=pcm_s16le vcodec=prores vb=0 g=1 bf=0 vprofile=2 vendor=apl0 movflags=+write_colr meta.preset.extension=mov meta.preset.note=Designed by Apple in California. Set vprofile=1 for LT or =3 for HQ. meta.preset.hidden=1 mlt-6.20.0/presets/consumer/avformat/intermediate/ProRes HQ000066400000000000000000000004051362234133600236340ustar00rootroot00000000000000f=mov acodec=pcm_s16le vcodec=prores_ks vb=0 g=1 bf=0 vprofile=3 vendor=apl0 qscale=1 movflags=+write_colr meta.preset.extension=mov meta.preset.note=Designed by Apple in California. Set vprofile=1 for LT or =2 for 422. meta.preset.name=intermediate/ProRes HQ mlt-6.20.0/presets/consumer/avformat/intermediate/ProRes-Kostya000066400000000000000000000004051362234133600246130ustar00rootroot00000000000000f=mov acodec=pcm_s16le vcodec=prores_ks vb=0 g=1 bf=0 vprofile=2 vendor=apl0 qscale=1 movflags=+write_colr meta.preset.extension=mov meta.preset.note=Designed by Apple in California. Set vprofile=1 for LT or =3 for HQ. meta.preset.name=intermediate/ProRes 422 mlt-6.20.0/presets/consumer/avformat/lossless/000077500000000000000000000000001362234133600214045ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/lossless/FFV1000066400000000000000000000002651362234133600220340ustar00rootroot00000000000000f=matroska acodec=flac vcodec=ffv1 g=1 bf=0 coder=1 context=1 pix_fmt=yuv422p meta.preset.extension=mkv meta.preset.note=FFmpeg video codec 1 with FLAC audio in Matroska container mlt-6.20.0/presets/consumer/avformat/lossless/H.264000066400000000000000000000003071362234133600220300ustar00rootroot00000000000000f=mp4 acodec=aac ab=384k vcodec=libx264 intra=1 vb=0 g=1 bf=0 preset=medium crf=0 coder=ac meta.preset.extension=mp4 meta.preset.note=Intra-frame only, lossless compressed MPEG-4 AVC with AAC audio mlt-6.20.0/presets/consumer/avformat/lossless/HuffYUV000066400000000000000000000002011362234133600226140ustar00rootroot00000000000000f=matroska acodec=flac vcodec=huffyuv g=1 bf=0 meta.preset.extension=mkv meta.preset.note=with FLAC audio in Matroska container mlt-6.20.0/presets/consumer/avformat/lossless/Ut Video000066400000000000000000000003061362234133600227450ustar00rootroot00000000000000f=matroska vcodec=utvideo pix_fmt=yuv422p g=1 bf=0 color_range=mpeg pix_fmt=yuv422p acodec=pcm_s24le meta.preset.extension=mkv meta.preset.note=Ut Video with 24-bit PCM audio in Matroska container mlt-6.20.0/presets/consumer/avformat/stills/000077500000000000000000000000001362234133600210475ustar00rootroot00000000000000mlt-6.20.0/presets/consumer/avformat/stills/BMP000066400000000000000000000001271362234133600214100ustar00rootroot00000000000000progressive=1 f=image2 vcodec=bmp an=1 audio_off=1 g=1 bf=0 meta.preset.extension=bmp mlt-6.20.0/presets/consumer/avformat/stills/DPX000066400000000000000000000001271362234133600214250ustar00rootroot00000000000000progressive=1 f=image2 vcodec=dpx an=1 audio_off=1 g=1 bf=0 meta.preset.extension=dpx mlt-6.20.0/presets/consumer/avformat/stills/JPEG000066400000000000000000000001421362234133600215140ustar00rootroot00000000000000progressive=1 f=image2 vcodec=mjpeg qscale=1 an=1 audio_off=1 g=1 bf=0 meta.preset.extension=jpg mlt-6.20.0/presets/consumer/avformat/stills/PNG000066400000000000000000000001271362234133600214160ustar00rootroot00000000000000progressive=1 f=image2 vcodec=png an=1 audio_off=1 g=1 bf=0 meta.preset.extension=png mlt-6.20.0/presets/consumer/avformat/stills/PPM000066400000000000000000000001271362234133600214260ustar00rootroot00000000000000progressive=1 f=image2 vcodec=ppm an=1 audio_off=1 g=1 bf=0 meta.preset.extension=ppm mlt-6.20.0/presets/consumer/avformat/stills/TGA000066400000000000000000000001311362234133600214000ustar00rootroot00000000000000progressive=1 f=image2 vcodec=targa an=1 audio_off=1 g=1 bf=0 meta.preset.extension=tga mlt-6.20.0/presets/consumer/avformat/stills/TIFF000066400000000000000000000001301362234133600215140ustar00rootroot00000000000000progressive=1 f=image2 vcodec=tiff an=1 audio_off=1 g=1 bf=0 meta.preset.extension=tif mlt-6.20.0/presets/consumer/avformat/vp9000066400000000000000000000005571362234133600202050ustar00rootroot00000000000000f=webm acodec=libopus ar=48000 ab=128k vcodec=libvpx-vp9 vb=2M g=120 bf=2 threads=0 rc_lookahead=16 quality=good speed=3 vprofile=0 qmax=51 qmin=4 slices=4 tile-columns=6 frame-parallel=1 auto-alt-ref=1 lag-in-frames=25 row-mt=1 meta.preset.name=WebM VP9 meta.preset.extension=webm meta.preset.note=VP9 video with Opus audio in Matroska container: "Don't be evil" mlt-6.20.0/presets/consumer/avformat/webm000066400000000000000000000005231362234133600204120ustar00rootroot00000000000000f=webm acodec=vorbis ab=128k vcodec=libvpx g=120 rc_lookahead=16 quality=good speed=0 vprofile=0 qmax=51 qmin=11 slices=4 vb=2M maxrate=24M minrate=100k arnr_max_frames=7 arnr_strength=5 arnr_type=3 meta.preset.name=WebM meta.preset.extension=webm meta.preset.note=VP8 video with Ogg Vorbis audio in Matroska container: "Don't be evil" mlt-6.20.0/presets/consumer/avformat/webm-pass1000066400000000000000000000003411362234133600214350ustar00rootroot00000000000000f=webm an=1 audio_off=1 vcodec=libvpx g=120 rc_lookahead=16 quality=good speed=0 vprofile=0 qmax=51 qmin=11 slices=4 vb=2M maxrate=24M minrate=100k arnr_max_frames=7 arnr_strength=5 arnr_type=3 pass=1 meta.preset.hidden=1 mlt-6.20.0/presets/consumer/avformat/x264-medium000066400000000000000000000002421362234133600214370ustar00rootroot00000000000000f=mp4 acodec=aac ab=256k vcodec=libx264 threads=0 vpre=medium preset=medium movflags=+faststart meta.preset.extension=mp4 meta.preset.name=H.264 High Profile mlt-6.20.0/presets/consumer/avformat/x264-medium-baseline000066400000000000000000000003431362234133600232210ustar00rootroot00000000000000f=mp4 acodec=aac ab=256k vcodec=libx264 threads=0 vpre=medium preset=medium vprofile=baseline coder=0 bf=0 flags2=-wpred-dct8x8 wpredp=0 movflags=+faststart meta.preset.extension=mp4 meta.preset.name=H.264 Baseline Profile mlt-6.20.0/presets/consumer/avformat/x264-medium-main000066400000000000000000000002771362234133600223710ustar00rootroot00000000000000f=mp4 acodec=aac ab=256k vcodec=libx264 threads=0 vpre=medium preset=medium vprofile=main flags2=-dct8x8 movflags=+faststart meta.preset.extension=mp4 meta.preset.name=H.264 Main Profile mlt-6.20.0/presets/consumer/avformat/x264-medium-pass1000066400000000000000000000004261362234133600224700ustar00rootroot00000000000000f=mp4 vcodec=libx264 threads=0 vpre=medium preset=medium fastfirstpass=1 partitions=-parti8x8-parti4x4-partp8x8-partb8x8 me_method=dia subq=2 refs=1 trellis=0 flags2=+bpyramid-mixed_refs+wpred-dct8x8+fastpskip pass=1 an=1 audio_off=1 movflags=+faststart meta.preset.hidden=1 mlt-6.20.0/presets/consumer/avformat/x265-medium000066400000000000000000000002251362234133600214410ustar00rootroot00000000000000f=mp4 acodec=aac ab=256k vcodec=libx265 threads=0 preset=medium movflags=+faststart meta.preset.extension=mp4 meta.preset.name=HEVC Main Profile mlt-6.20.0/presets/consumer/avformat/x265-medium-pass1000066400000000000000000000001751362234133600224720ustar00rootroot00000000000000f=mp4 vcodec=libx265 threads=0 preset=medium x265-params=pass=1 an=1 audio_off=1 movflags=+faststart meta.preset.hidden=1 mlt-6.20.0/presets/filter/000077500000000000000000000000001362234133600153505ustar00rootroot00000000000000mlt-6.20.0/presets/filter/brightness/000077500000000000000000000000001362234133600175205ustar00rootroot00000000000000mlt-6.20.0/presets/filter/brightness/from_black000066400000000000000000000000321362234133600215350ustar00rootroot00000000000000out=50 in=0 start=0 end=1 mlt-6.20.0/presets/filter/brightness/to_black000066400000000000000000000000321362234133600212140ustar00rootroot00000000000000out=50 in=0 start=1 end=0 mlt-6.20.0/presets/filter/movit.blur/000077500000000000000000000000001362234133600174515ustar00rootroot00000000000000mlt-6.20.0/presets/filter/movit.blur/blur_in000066400000000000000000000000311362234133600210200ustar00rootroot00000000000000radius=0=10.0; :1.0=0.0; mlt-6.20.0/presets/filter/movit.blur/blur_in_out000066400000000000000000000000531362234133600217130ustar00rootroot00000000000000radius=0=10.0; :1.0=0; :-1.0=0.0; -1=10.0; mlt-6.20.0/presets/filter/movit.blur/blur_out000066400000000000000000000000401362234133600212210ustar00rootroot00000000000000radius=0=0; :-1.0=0.0; -1=10.0; mlt-6.20.0/presets/filter/movit.opacity/000077500000000000000000000000001362234133600201555ustar00rootroot00000000000000mlt-6.20.0/presets/filter/movit.opacity/fade_in000066400000000000000000000000351362234133600214630ustar00rootroot00000000000000opacity=0=0;:2.0=1.0 alpha=1 mlt-6.20.0/presets/filter/movit.opacity/fade_in_out000066400000000000000000000000561362234133600223550ustar00rootroot00000000000000opacity=0=0;:2.0=1.0; :-2.0=1.0; -1=0 alpha=1 mlt-6.20.0/presets/filter/movit.opacity/fade_out000066400000000000000000000000401362234133600216600ustar00rootroot00000000000000opacity=:-2.0=1.0; -1=0 alpha=1 mlt-6.20.0/presets/filter/volume/000077500000000000000000000000001362234133600166575ustar00rootroot00000000000000mlt-6.20.0/presets/filter/volume/fade_in000066400000000000000000000000311362234133600201610ustar00rootroot00000000000000out=50 in=0 gain=0 end=1 mlt-6.20.0/presets/filter/volume/fade_out000066400000000000000000000000311362234133600203620ustar00rootroot00000000000000out=50 in=0 gain=1 end=0 mlt-6.20.0/profiles/000077500000000000000000000000001362234133600142215ustar00rootroot00000000000000mlt-6.20.0/profiles/Makefile000066400000000000000000000005141362234133600156610ustar00rootroot00000000000000include ../config.mak all: depend: distclean: clean: install: all uninstall install -d "$(DESTDIR)$(mltdatadir)/profiles" install -m 644 * "$(DESTDIR)$(mltdatadir)/profiles" rm -f "$(DESTDIR)$(mltdatadir)/profiles/"*~ rm -f "$(DESTDIR)$(mltdatadir)/profiles/Makefile" uninstall: rm -rf "$(DESTDIR)$(mltdatadir)/profiles" mlt-6.20.0/profiles/atsc_1080i_50000066400000000000000000000003061362234133600162220ustar00rootroot00000000000000description=HD 1080i 25 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080i_5994000066400000000000000000000003171362234133600164120ustar00rootroot00000000000000description=HD 1080i 29.97 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080i_60000066400000000000000000000003061362234133600162230ustar00rootroot00000000000000description=HD 1080i 30 fps frame_rate_num=30 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080p_2398000066400000000000000000000003171362234133600164140ustar00rootroot00000000000000description=HD 1080p 23.98 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080p_24000066400000000000000000000003061362234133600162320ustar00rootroot00000000000000description=HD 1080p 24 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080p_25000066400000000000000000000003061362234133600162330ustar00rootroot00000000000000description=HD 1080p 25 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080p_2997000066400000000000000000000003171362234133600164210ustar00rootroot00000000000000description=HD 1080p 29.97 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080p_30000066400000000000000000000003061362234133600162270ustar00rootroot00000000000000description=HD 1080p 30 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080p_50000066400000000000000000000003061362234133600162310ustar00rootroot00000000000000description=HD 1080p 50 fps frame_rate_num=50 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080p_5994000066400000000000000000000003171362234133600164210ustar00rootroot00000000000000description=HD 1080p 59.94 fps frame_rate_num=60000 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 colorspace=709 mlt-6.20.0/profiles/atsc_1080p_60000066400000000000000000000003061362234133600162320ustar00rootroot00000000000000description=HD 1080p 60 fps frame_rate_num=60 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 colorspace=709 mlt-6.20.0/profiles/atsc_720p_2398000066400000000000000000000003151362234133600163320ustar00rootroot00000000000000description=HD 720p 23.98 fps frame_rate_num=24000 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 colorspace=709 mlt-6.20.0/profiles/atsc_720p_24000066400000000000000000000003041362234133600161500ustar00rootroot00000000000000description=HD 720p 24 fps frame_rate_num=24 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 colorspace=709 mlt-6.20.0/profiles/atsc_720p_25000066400000000000000000000003041362234133600161510ustar00rootroot00000000000000description=HD 720p 25 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_720p_2997000066400000000000000000000003151362234133600163370ustar00rootroot00000000000000description=HD 720p 29.97 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_720p_30000066400000000000000000000003041362234133600161450ustar00rootroot00000000000000description=HD 720p 30 fps frame_rate_num=30 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 colorspace=709 mlt-6.20.0/profiles/atsc_720p_50000066400000000000000000000003041362234133600161470ustar00rootroot00000000000000description=HD 720p 50 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_720p_5994000066400000000000000000000003151362234133600163370ustar00rootroot00000000000000description=HD 720p 59.94 fps 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 colorspace=709 mlt-6.20.0/profiles/atsc_720p_60000066400000000000000000000003041362234133600161500ustar00rootroot00000000000000description=HD 720p 60 fps frame_rate_num=60 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 colorspace=709 mlt-6.20.0/profiles/cif_15000066400000000000000000000003001362234133600152030ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/cif_ntsc000066400000000000000000000003041362234133600157310ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/cif_pal000066400000000000000000000002751362234133600155450ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/cvd_ntsc000066400000000000000000000003041362234133600157440ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/cvd_pal000066400000000000000000000002751362234133600155600ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/dv_ntsc000066400000000000000000000003051362234133600156020ustar00rootroot00000000000000description=DV/DVD 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 colorspace=601 mlt-6.20.0/profiles/dv_ntsc_wide000066400000000000000000000003231362234133600166120ustar00rootroot00000000000000description=DV/DVD Widescreen NTSC 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 colorspace=601 mlt-6.20.0/profiles/dv_pal000066400000000000000000000003001362234133600154020ustar00rootroot00000000000000description=DV/DVD 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 colorspace=601 mlt-6.20.0/profiles/dv_pal_wide000066400000000000000000000003141362234133600164170ustar00rootroot00000000000000description=DV/DVD Widescreen PAL 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 colorspace=601 mlt-6.20.0/profiles/hdv_1080_25p000066400000000000000000000003141362234133600160610ustar00rootroot00000000000000description=HDV 1440x1080p 25 fps 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 colorspace=709 mlt-6.20.0/profiles/hdv_1080_30p000066400000000000000000000003251362234133600160570ustar00rootroot00000000000000description=HDV 1440x1080p 29.97 fps 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 colorspace=709 mlt-6.20.0/profiles/hdv_1080_50i000066400000000000000000000003141362234133600160500ustar00rootroot00000000000000description=HDV 1440x1080i 25 fps 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 colorspace=709 mlt-6.20.0/profiles/hdv_1080_60i000066400000000000000000000003251362234133600160530ustar00rootroot00000000000000description=HDV 1440x1080i 29.97 fps 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 colorspace=709 mlt-6.20.0/profiles/hdv_720_25p000066400000000000000000000003041362234133600160000ustar00rootroot00000000000000description=HD 720p 25 fps 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 colorspace=709 mlt-6.20.0/profiles/hdv_720_30p000066400000000000000000000003151362234133600157760ustar00rootroot00000000000000description=HD 720p 29.97 fps 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 colorspace=709 mlt-6.20.0/profiles/hdv_720_50p000066400000000000000000000003041362234133600157760ustar00rootroot00000000000000description=HD 720p 50 fps 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 colorspace=709 mlt-6.20.0/profiles/hdv_720_60p000066400000000000000000000003151362234133600160010ustar00rootroot00000000000000description=HD 720p 59.94 fps 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 colorspace=709 mlt-6.20.0/profiles/qcif_15000066400000000000000000000003011362234133600153650ustar00rootroot00000000000000description=QCIF 15 fps 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 colorspace=601 mlt-6.20.0/profiles/qcif_ntsc000066400000000000000000000003051362234133600161130ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/qcif_pal000066400000000000000000000002761362234133600157270ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/qhd_1440p_2398000066400000000000000000000003251362234133600162350ustar00rootroot00000000000000description=2.5K QHD 1440p 23.98 fps frame_rate_num=24000 frame_rate_den=1001 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/qhd_1440p_24000066400000000000000000000003141362234133600160530ustar00rootroot00000000000000description=2.5K QHD 1440p 24 fps frame_rate_num=24 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/qhd_1440p_25000066400000000000000000000003141362234133600160540ustar00rootroot00000000000000description=2.5K QHD 1440p 25 fps frame_rate_num=25 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/qhd_1440p_2997000066400000000000000000000003251362234133600162420ustar00rootroot00000000000000description=2.5K QHD 1440p 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/qhd_1440p_30000066400000000000000000000003141362234133600160500ustar00rootroot00000000000000description=2.5K QHD 1440p 30 fps frame_rate_num=30 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/qhd_1440p_50000066400000000000000000000003141362234133600160520ustar00rootroot00000000000000description=2.5K QHD 1440p 50 fps frame_rate_num=50 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/qhd_1440p_5994000066400000000000000000000003251362234133600162420ustar00rootroot00000000000000description=2.5K QHD 1440p 59.94 fps frame_rate_num=60000 frame_rate_den=1001 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/qhd_1440p_60000066400000000000000000000003141362234133600160530ustar00rootroot00000000000000description=2.5K QHD 1440p 60 fps frame_rate_num=60 frame_rate_den=1 width=2560 height=1440 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/quarter_15000066400000000000000000000002771362234133600161420ustar00rootroot00000000000000description=QVGA 15 fps 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 colorspace=601 mlt-6.20.0/profiles/quarter_ntsc000066400000000000000000000003101362234133600166500ustar00rootroot00000000000000description=QVGA 29.97 fps 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 colorspace=601 mlt-6.20.0/profiles/quarter_ntsc_wide000066400000000000000000000003241362234133600176650ustar00rootroot00000000000000description=QVGA Widescreen 29.97 fps 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 colorspace=601 mlt-6.20.0/profiles/quarter_pal000066400000000000000000000003031362234133600164570ustar00rootroot00000000000000description=384x288 4:3 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 colorspace=601 mlt-6.20.0/profiles/quarter_pal_wide000066400000000000000000000003051362234133600174710ustar00rootroot00000000000000description=512x288 16:9 PAL 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 colorspace=601 mlt-6.20.0/profiles/sdi_486i_5994000066400000000000000000000003101362234133600161610ustar00rootroot00000000000000description=NTSC 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=720 height=486 progressive=0 sample_aspect_num=8 sample_aspect_den=9 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-6.20.0/profiles/sdi_486p_2398000066400000000000000000000003101362234133600161630ustar00rootroot00000000000000description=NTSC 23.98 fps frame_rate_num=24000 frame_rate_den=1001 width=720 height=486 progressive=1 sample_aspect_num=8 sample_aspect_den=9 display_aspect_num=4 display_aspect_den=3 colorspace=601 mlt-6.20.0/profiles/square_1080p_30000066400000000000000000000003111362234133600165710ustar00rootroot00000000000000description=Square 1080p 30 fps frame_rate_num=30 frame_rate_den=1 width=1080 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=1 display_aspect_den=1 colorspace=709 mlt-6.20.0/profiles/square_1080p_60000066400000000000000000000003111362234133600165740ustar00rootroot00000000000000description=Square 1080p 60 fps frame_rate_num=60 frame_rate_den=1 width=1080 height=1080 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=1 display_aspect_den=1 colorspace=709 mlt-6.20.0/profiles/square_ntsc000066400000000000000000000003021362234133600164660ustar00rootroot00000000000000description=VGA 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 colorspace=601 mlt-6.20.0/profiles/square_ntsc_wide000066400000000000000000000003161362234133600175030ustar00rootroot00000000000000description=VGA Widescreen NTSC 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 colorspace=601 mlt-6.20.0/profiles/square_pal000066400000000000000000000003031362234133600162740ustar00rootroot00000000000000description=768x576 4:3 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 colorspace=601 mlt-6.20.0/profiles/square_pal_wide000066400000000000000000000003071362234133600173100ustar00rootroot00000000000000description=1024x576 16:9 PAL 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 colorspace=601 mlt-6.20.0/profiles/svcd_ntsc000066400000000000000000000003051362234133600161300ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/svcd_ntsc_wide000066400000000000000000000003211362234133600171360ustar00rootroot00000000000000description=SVCD Widescreen NTSC 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 colorspace=601 mlt-6.20.0/profiles/svcd_pal000066400000000000000000000002761362234133600157440ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/svcd_pal_wide000066400000000000000000000003121362234133600167430ustar00rootroot00000000000000description=SVCD Widescreen PAL 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=16 display_aspect_den=9 colorspace=601 mlt-6.20.0/profiles/uhd_2160p_2398000066400000000000000000000003231362234133600162370ustar00rootroot00000000000000description=4K UHD 2160p 23.98 fps frame_rate_num=24000 frame_rate_den=1001 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/uhd_2160p_24000066400000000000000000000003121362234133600160550ustar00rootroot00000000000000description=4K UHD 2160p 24 fps frame_rate_num=24 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/uhd_2160p_25000066400000000000000000000003121362234133600160560ustar00rootroot00000000000000description=4K UHD 2160p 25 fps frame_rate_num=25 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/uhd_2160p_2997000066400000000000000000000003231362234133600162440ustar00rootroot00000000000000description=4K UHD 2160p 29.97 fps frame_rate_num=30000 frame_rate_den=1001 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/uhd_2160p_30000066400000000000000000000003121362234133600160520ustar00rootroot00000000000000description=4K UHD 2160p 30 fps frame_rate_num=30 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/uhd_2160p_50000066400000000000000000000003121362234133600160540ustar00rootroot00000000000000description=4K UHD 2160p 50 fps frame_rate_num=50 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/uhd_2160p_5994000066400000000000000000000003231362234133600162440ustar00rootroot00000000000000description=4K UHD 2160p 59.94 fps frame_rate_num=60000 frame_rate_den=1001 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/uhd_2160p_60000066400000000000000000000003121362234133600160550ustar00rootroot00000000000000description=4K UHD 2160p 60 fps frame_rate_num=60 frame_rate_den=1 width=3840 height=2160 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=16 display_aspect_den=9 colorspace=709 mlt-6.20.0/profiles/vcd_ntsc000066400000000000000000000003041362234133600157440ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/vcd_pal000066400000000000000000000002751362234133600155600ustar00rootroot00000000000000description=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 colorspace=601 mlt-6.20.0/profiles/vertical_hd_30000066400000000000000000000003111362234133600167250ustar00rootroot00000000000000description=Vertical HD 30 fps frame_rate_num=30 frame_rate_den=1 width=1080 height=1920 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=9 display_aspect_den=16 colorspace=709 mlt-6.20.0/profiles/vertical_hd_60000066400000000000000000000003111362234133600167300ustar00rootroot00000000000000description=Vertical HD 60 fps frame_rate_num=60 frame_rate_den=1 width=1080 height=1920 progressive=1 sample_aspect_num=1 sample_aspect_den=1 display_aspect_num=9 display_aspect_den=16 colorspace=709 mlt-6.20.0/reconfigure000077500000000000000000000002341362234133600146330ustar00rootroot00000000000000#!/bin/sh if [ -s "config.log" ]; then $(tail -1 config.log) $@ else echo The file \"config.log\" does not exist. echo You must run configure first. fi mlt-6.20.0/setenv000066400000000000000000000006321362234133600136260ustar00rootroot00000000000000 # 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 MLT_PRESETS_PATH=`pwd`/presets export LD_LIBRARY_PATH=\ `pwd`/src/framework:\ `pwd`/src/mlt++:\ $LD_LIBRARY_PATH #[ $(uname -s) = Darwin ] && export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH export PATH=`pwd`/src/melt:$PATH mlt-6.20.0/src/000077500000000000000000000000001362234133600131655ustar00rootroot00000000000000mlt-6.20.0/src/examples/000077500000000000000000000000001362234133600150035ustar00rootroot00000000000000mlt-6.20.0/src/examples/Makefile000066400000000000000000000004251362234133600164440ustar00rootroot00000000000000CXXFLAGS += -Wall -g $(shell pkg-config mlt++ --cflags) LDFLAGS += $(shell pkg-config mlt++ --libs-only-L) LDLIBS += $(shell pkg-config mlt++ --libs-only-l) CC=c++ all: play play: play.o play.o: play.cpp clean: $(RM) play play.o distclean: clean install: uninstall: mlt-6.20.0/src/examples/play.cpp000066400000000000000000000010621362234133600164530ustar00rootroot00000000000000#include using namespace Mlt; void play(const char *filename) { Profile profile; // defaults to dv_pal Producer producer(profile, filename); Consumer consumer(profile); // defaults to sdl // Prevent scaling to the profile size. // Let the sdl consumer do all scaling. consumer.set("rescale", "none"); // Automatically exit at end of file. consumer.set("terminate_on_pause", 1); consumer.connect(producer); consumer.run(); consumer.stop(); } int main( int, char **argv ) { Factory::init(); play(argv[1]); Factory::close(); return 0; } mlt-6.20.0/src/framework/000077500000000000000000000000001362234133600151625ustar00rootroot00000000000000mlt-6.20.0/src/framework/CMakeLists.txt000066400000000000000000000031121362234133600177170ustar00rootroot00000000000000file(GLOB mlt_src *.c) if(WIN32) list(APPEND mlt_src ../win32/win32.c ../win32/strptime.c) endif() add_library(mlt SHARED ${mlt_src}) set_target_properties (mlt PROPERTIES SOVERSION ${MLT_VERSION} VERSION 6) if(WIN32) find_package(dlfcn-win32 REQUIRED) find_package(iconv REQUIRED) target_link_libraries(mlt dlfcn-win32::dl Iconv::Iconv) if(NODEPLOY) target_compile_definitions(mlt PRIVATE NODEPLOY) endif() else() target_link_libraries(mlt dl) endif() if(NOT (WIN32 OR APPLE)) target_compile_definitions(mlt PRIVATE PREFIX_DATA="${CMAKE_INSTALL_FULL_DATADIR}/mlt" PREFIX_LIB="${CMAKE_INSTALL_FULL_LIBDIR}/mlt") endif() target_link_libraries(mlt m Threads::Threads) target_include_directories(mlt PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(mlt PUBLIC ${CMAKE_SOURCE_DIR}/src) if(WIN32) install(TARGETS mlt DESTINATION ${CMAKE_INSTALL_BINDIR}) else() install(TARGETS mlt DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() file(GLOB mlt_head *.h) install(FILES ${mlt_head} DESTINATION include/mlt) configure_file(mlt-framework.pc.in mlt-framework.pc @ONLY) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/mlt-framework.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT Development) #configure_file(MLTConfig.cmake.in MLTConfig.cmake @ONLY) #configure_file(MLTConfigVersion.cmake.in MLTConfigVersion.cmake @ONLY) #install (FILES # ${CMAKE_CURRENT_BINARY_DIR}/MLTConfig.cmake # ${CMAKE_CURRENT_BINARY_DIR}/MLTConfigVersion.cmake # DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mlt # COMPONENT Development) mlt-6.20.0/src/framework/Makefile000066400000000000000000000074131362234133600166270ustar00rootroot00000000000000include ../../config.mak include config.mak NAME = libmlt$(LIBSUF) TARGET = $(NAME).$(version) ifeq ($(targetos), Darwin) NAME = libmlt$(LIBSUF) TARGET = libmlt.$(version)$(LIBSUF) SONAME = libmlt.$(soversion)$(LIBSUF) SHFLAGS += -install_name $(libdir)/$(SONAME) -current_version $(version) -compatibility_version $(soversion) else ifeq ($(targetos), MinGW) NAME = libmlt$(LIBSUF) TARGET = libmlt-$(soversion)$(LIBSUF) SHFLAGS += -Wl,--output-def,libmlt.def else NAME = libmlt$(LIBSUF) TARGET = $(NAME).$(version) SONAME = $(NAME).$(soversion) SHFLAGS += -Wl,-soname,$(SONAME) endif ifeq ($(targetos), Linux) SHFLAGS += -Wl,--version-script=mlt.vers endif OBJS = mlt_frame.o \ mlt_version.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 \ mlt_animation.o \ mlt_slices.o \ mlt_luma_map.o INCS = mlt_consumer.h \ mlt_version.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 \ mlt_animation.h \ mlt_slices.h \ mlt_luma_map.h SRCS := $(OBJS:.o=.c) ifeq ($(targetos), MinGW) LDFLAGS += -liconv OBJS += ../win32/win32.o ../win32/strptime.o SRCS += ../win32/win32.c ../win32/strptime.c endif CFLAGS += $(RDYNAMIC) -DPREFIX_DATA="\"$(mltdatadir)\"" -DPREFIX_LIB="\"$(moduledir)\"" LDFLAGS += $(LIBDL) -lpthread -lm all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS) ln -sf $(TARGET) $(NAME) if [ "$(targetos)" != "MinGW" ]; then \ ln -sf $(TARGET) $(SONAME) ; \ fi depend: $(SRCS) $(CC) -MM $(CFLAGS) $^ 1>.depend distclean: clean rm -f .depend clean: rm -f $(OBJS) $(TARGET) $(NAME) $(SONAME) libmlt.def install: install -d $(DESTDIR)$(libdir) if [ "$(targetos)" = "MinGW" ]; then \ if [ "$(windeploy)" = true ]; then \ install -m 755 $(TARGET) "$(DESTDIR)$(prefix)" ; \ install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/libmlt.dll" ; \ else \ install -m 755 $(TARGET) "$(DESTDIR)$(bindir)" ; \ install -m 755 $(TARGET) "$(DESTDIR)$(bindir)/libmlt.dll" ; \ fi; \ install -m 644 libmlt.def "$(DESTDIR)$(libdir)" ; \ else \ install -m 755 $(TARGET) $(DESTDIR)$(libdir) ; \ ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(SONAME) ; \ ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(NAME) ; \ fi install -d "$(DESTDIR)$(prefix)/include/mlt/framework" install -m 644 $(INCS) "$(DESTDIR)$(prefix)/include/mlt/framework" install -d "$(DESTDIR)$(mltdatadir)" install -m 644 metaschema.yaml "$(DESTDIR)$(mltdatadir)" uninstall: rm -f "$(DESTDIR)$(libdir)/$(TARGET)" if [ "$(targetos)" != "MinGW" ]; then \ rm -f "$(DESTDIR)$(libdir)/$(SONAME)" ; \ rm -f "$(DESTDIR)$(libdir)/$(NAME)" ; \ else \ if [ "$(windeploy)" = true ]; then \ rm -f "$(DESTDIR)$(prefix)/$(TARGET)" ; \ rm -f "$(DESTDIR)$(libdir)/libmlt.dll" ; \ else \ rm -f "$(DESTDIR)$(bindir)/$(TARGET)" ; \ rm -f "$(DESTDIR)$(bindir)/libmlt.dll" ; \ fi ; \ rm -f "$(DESTDIR)$(libdir)/libmlt.def" ; \ fi rm -rf "$(DESTDIR)$(prefix)/include/mlt/framework" rm -f "$(DESTDIR)$(mltdatadir)/metaschema.yaml" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/framework/configure000077500000000000000000000002111362234133600170630ustar00rootroot00000000000000#!/bin/sh echo "framework -I$prefix/include -I$prefix/include/mlt -D_REENTRANT -L$libdir -lmlt" >> ../../packages.dat echo > config.mak mlt-6.20.0/src/framework/metaschema.yaml000066400000000000000000000116051362234133600201600ustar00rootroot00000000000000--- # A metadata schema in Kwalify: http://www.kuwata-lab.com/kwalify/ # Version: 0.3 type: map mapping: "schema_version": # This should match the version comment above type: float required: yes "LC_NUMERIC": # If not provided LC_NUMERIC=C is used, not the system's locale. type: str required: no "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: - boolean # 0 or 1; not 'true', 'false', 'yes', or 'no' strings at this time - float - geometry - integer - properties # for passing options to encapsulated services - string - time # time string values (clock, SMPTE) can be accepted in addition to frames - rect # "X Y W H" or "X/Y:WxH" - color # 0xrrggbbaa | #rrggbb | #aarrggbb "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 "argument": # If this is also the service constructor argument. type: bool default: no "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 mlt-6.20.0/src/framework/mlt-framework.pc.in000066400000000000000000000006071362234133600207050ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ datadir=@CMAKE_INSTALL_FULL_DATADIR@ moduledir=${libdir}/mlt mltdatadir=${datadir}/mlt Name: mlt-framework Description: MLT multimedia framework Version: @MLT_VERSION@ Requires: Libs: -L${libdir} -lmlt Cflags: -I${includedir} -I${includedir}/mlt mlt-6.20.0/src/framework/mlt.h000066400000000000000000000035221362234133600161310ustar00rootroot00000000000000/** * \file mlt.h * \brief header file for lazy client and implementation code :-) * * Copyright (C) 2003-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_H #define MLT_H /** \mainpage MLT API Reference Documentation * \par * We recommend that you look in Data Structures * or Files. * \par * Additional documentation about MLT, in general, can be found on the * MLT website. */ #ifdef __cplusplus extern "C" { #endif #include "mlt_animation.h" #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" #include "mlt_cache.h" #include "mlt_version.h" #include "mlt_slices.h" #ifdef __cplusplus } #endif #endif mlt-6.20.0/src/framework/mlt.vers000066400000000000000000000334051362234133600166640ustar00rootroot00000000000000MLT_0.8.8 { global: default_callback; mlt_audio_format_name; mlt_audio_format_size; mlt_cache_close; mlt_cache_get; mlt_cache_get_frame; mlt_cache_get_size; mlt_cache_init; mlt_cache_item_close; mlt_cache_item_data; mlt_cache_purge; mlt_cache_put; mlt_cache_put_frame; mlt_cache_set_size; mlt_consumer_close; mlt_consumer_connect; mlt_consumer_get_frame; mlt_consumer_init; mlt_consumer_is_stopped; mlt_consumer_new; mlt_consumer_position; mlt_consumer_properties; mlt_consumer_purge; mlt_consumer_put_frame; mlt_consumer_rt_frame; mlt_consumer_service; mlt_consumer_start; mlt_consumer_stop; mlt_consumer_stopped; mlt_deque_close; mlt_deque_count; mlt_deque_init; mlt_deque_insert; mlt_deque_peek; mlt_deque_peek_back; mlt_deque_peek_back_double; mlt_deque_peek_back_int; mlt_deque_peek_front; mlt_deque_peek_front_double; mlt_deque_peek_front_int; mlt_deque_pop_back; mlt_deque_pop_back_double; mlt_deque_pop_back_int; mlt_deque_pop_front; mlt_deque_pop_front_double; mlt_deque_pop_front_int; mlt_deque_push_back; mlt_deque_push_back_double; mlt_deque_push_back_int; mlt_deque_push_front; mlt_deque_push_front_double; mlt_deque_push_front_int; mlt_environment; mlt_environment_set; mlt_event_block; mlt_event_close; mlt_event_inc_ref; mlt_events_block; mlt_events_close_wait_for; mlt_events_disconnect; mlt_events_fire; mlt_events_init; mlt_events_listen; mlt_events_register; mlt_events_setup_wait_for; mlt_events_unblock; mlt_events_wait_for; mlt_event_unblock; mlt_factory_close; mlt_factory_consumer; mlt_factory_directory; mlt_factory_event_object; mlt_factory_filter; mlt_factory_init; mlt_factory_producer; mlt_factory_register_for_clean_up; mlt_factory_transition; mlt_field_close; mlt_field_disconnect_service; mlt_field_init; mlt_field_multitrack; mlt_field_new; mlt_field_plant_filter; mlt_field_plant_transition; mlt_field_properties; mlt_field_service; mlt_field_tractor; mlt_filter_close; mlt_filter_connect; mlt_filter_get_in; mlt_filter_get_length; mlt_filter_get_length2; mlt_filter_get_out; mlt_filter_get_position; mlt_filter_get_progress; mlt_filter_get_track; mlt_filter_init; mlt_filter_new; mlt_filter_process; mlt_filter_properties; mlt_filter_service; mlt_filter_set_in_and_out; mlt_frame_clone; mlt_frame_close; mlt_frame_get_alpha_mask; mlt_frame_get_aspect_ratio; mlt_frame_get_audio; mlt_frame_get_image; mlt_frame_get_original_producer; mlt_frame_get_position; mlt_frame_get_waveform; mlt_frame_init; mlt_frame_is_test_audio; mlt_frame_is_test_card; mlt_frame_original_position; mlt_frame_pop_audio; mlt_frame_pop_frame; mlt_frame_pop_get_image; mlt_frame_pop_service; mlt_frame_pop_service_int; mlt_frame_properties; mlt_frame_push_audio; mlt_frame_push_frame; mlt_frame_push_get_image; mlt_frame_push_service; mlt_frame_push_service_int; mlt_frame_replace_image; mlt_frame_service_stack; mlt_frame_set_alpha; mlt_frame_set_aspect_ratio; mlt_frame_set_audio; mlt_frame_set_image; mlt_frame_set_position; mlt_frame_unique_properties; mlt_frame_write_ppm; mlt_geometry_close; mlt_geometry_fetch; mlt_geometry_get_length; mlt_geometry_init; mlt_geometry_insert; mlt_geometry_interpolate; mlt_geometry_next_key; mlt_geometry_parse; mlt_geometry_parse_item; mlt_geometry_prev_key; mlt_geometry_refresh; mlt_geometry_remove; mlt_geometry_serialise; mlt_geometry_serialise_cut; mlt_geometry_set_length; mlt_global_properties; mlt_image_format_name; mlt_image_format_size; mlt_log; mlt_log_get_level; mlt_log_set_callback; mlt_log_set_level; mlt_multitrack_clip; mlt_multitrack_close; mlt_multitrack_connect; mlt_multitrack_count; mlt_multitrack_init; mlt_multitrack_producer; mlt_multitrack_properties; mlt_multitrack_refresh; mlt_multitrack_service; mlt_multitrack_track; mlt_parser_close; mlt_parser_new; mlt_parser_properties; mlt_parser_start; mlt_playlist_append; mlt_playlist_append_io; mlt_playlist_blank; mlt_playlist_blanks_from; mlt_playlist_blank_time; mlt_playlist_clear; mlt_playlist_clip; mlt_playlist_clip_is_mix; mlt_playlist_clip_length; mlt_playlist_clip_start; mlt_playlist_close; mlt_playlist_consolidate_blanks; mlt_playlist_count; mlt_playlist_current; mlt_playlist_current_clip; mlt_playlist_get_clip; mlt_playlist_get_clip_at; mlt_playlist_get_clip_index_at; mlt_playlist_get_clip_info; mlt_playlist_init; mlt_playlist_insert; mlt_playlist_insert_at; mlt_playlist_insert_blank; mlt_playlist_is_blank; mlt_playlist_is_blank_at; mlt_playlist_join; mlt_playlist_mix; mlt_playlist_mix_add; mlt_playlist_move; mlt_playlist_move_region; mlt_playlist_new; mlt_playlist_pad_blanks; mlt_playlist_producer; mlt_playlist_properties; mlt_playlist_remove; mlt_playlist_remove_region; mlt_playlist_repeat_clip; mlt_playlist_replace_with_blank; mlt_playlist_resize_clip; mlt_playlist_service; mlt_playlist_split; mlt_playlist_split_at; mlt_pool_alloc; mlt_pool_close; mlt_pool_init; mlt_pool_purge; mlt_pool_realloc; mlt_pool_release; mlt_producer_attach; mlt_producer_clear; mlt_producer_close; mlt_producer_cut; mlt_producer_cut_parent; mlt_producer_detach; mlt_producer_filter; mlt_producer_frame; mlt_producer_frame_time; mlt_producer_get_fps; mlt_producer_get_in; mlt_producer_get_length; mlt_producer_get_length_time; mlt_producer_get_out; mlt_producer_get_playtime; mlt_producer_get_speed; mlt_producer_init; mlt_producer_is_blank; mlt_producer_is_cut; mlt_producer_is_mix; mlt_producer_new; mlt_producer_optimise; mlt_producer_position; mlt_producer_prepare_next; mlt_producer_properties; mlt_producer_seek; mlt_producer_seek_time; mlt_producer_service; mlt_producer_set_in_and_out; mlt_producer_set_speed; mlt_profile_clone; mlt_profile_close; mlt_profile_dar; mlt_profile_fps; mlt_profile_from_producer; mlt_profile_init; mlt_profile_list; mlt_profile_load_file; mlt_profile_load_properties; mlt_profile_load_string; mlt_profile_sar; mlt_properties_close; mlt_properties_count; mlt_properties_debug; mlt_properties_dec_ref; mlt_properties_dir_list; mlt_properties_dump; mlt_properties_get; mlt_properties_get_data; mlt_properties_get_data_at; mlt_properties_get_double; mlt_properties_get_int; mlt_properties_get_int64; mlt_properties_get_lcnumeric; mlt_properties_get_name; mlt_properties_get_position; mlt_properties_get_time; mlt_properties_get_value; mlt_properties_inc_ref; mlt_properties_inherit; mlt_properties_init; mlt_properties_is_sequence; mlt_properties_load; mlt_properties_lock; mlt_properties_mirror; mlt_properties_new; mlt_properties_parse; mlt_properties_parse_yaml; mlt_properties_pass; mlt_properties_pass_list; mlt_properties_pass_property; mlt_properties_preset; mlt_properties_ref_count; mlt_properties_rename; mlt_properties_save; mlt_properties_serialise_yaml; mlt_properties_set; mlt_properties_set_data; mlt_properties_set_double; mlt_properties_set_int; mlt_properties_set_int64; mlt_properties_set_lcnumeric; mlt_properties_set_or_default; mlt_properties_set_position; mlt_properties_unlock; mlt_property_close; mlt_property_get_data; mlt_property_get_double; mlt_property_get_int; mlt_property_get_int64; mlt_property_get_position; mlt_property_get_string; mlt_property_get_string_l; mlt_property_get_time; mlt_property_init; mlt_property_pass; mlt_property_set_data; mlt_property_set_double; mlt_property_set_int; mlt_property_set_int64; mlt_property_set_position; mlt_property_set_string; mlt_repository_close; mlt_repository_consumers; mlt_repository_create; mlt_repository_filters; mlt_repository_init; mlt_repository_languages; mlt_repository_metadata; mlt_repository_presets; mlt_repository_producers; mlt_repository_register; mlt_repository_register_metadata; mlt_repository_transitions; mlt_sample_calculator; mlt_sample_calculator_to_now; mlt_sdl_mutex; mlt_service_apply_filters; mlt_service_attach; mlt_service_cache_get; mlt_service_cache_get_size; mlt_service_cache_purge; mlt_service_cache_put; mlt_service_cache_set_size; mlt_service_close; mlt_service_connect_producer; mlt_service_consumer; mlt_service_detach; mlt_service_filter; mlt_service_get_frame; mlt_service_get_producer; mlt_service_identify; mlt_service_init; mlt_service_lock; mlt_service_producer; mlt_service_profile; mlt_service_properties; mlt_service_set_profile; mlt_service_unlock; mlt_tokeniser_close; mlt_tokeniser_count; mlt_tokeniser_get_input; mlt_tokeniser_get_string; mlt_tokeniser_init; mlt_tokeniser_parse_new; mlt_tractor_close; mlt_tractor_connect; mlt_tractor_field; mlt_tractor_get_track; mlt_tractor_init; mlt_tractor_multitrack; mlt_tractor_new; mlt_tractor_producer; mlt_tractor_properties; mlt_tractor_refresh; mlt_tractor_service; mlt_tractor_set_track; mlt_transition_close; mlt_transition_connect; mlt_transition_get_a_track; mlt_transition_get_b_track; mlt_transition_get_in; mlt_transition_get_length; mlt_transition_get_out; mlt_transition_get_position; mlt_transition_get_progress; mlt_transition_get_progress_delta; mlt_transition_init; mlt_transition_new; mlt_transition_process; mlt_transition_properties; mlt_transition_service; mlt_transition_set_in_and_out; mlt_version_get_int; mlt_version_get_major; mlt_version_get_minor; mlt_version_get_revision; mlt_version_get_string; mlt_vlog; local: *; }; MLT_0.9.0 { global: mlt_animation_new; mlt_animation_parse; mlt_animation_refresh; mlt_animation_get_length; mlt_animation_set_length; mlt_animation_parse_item; mlt_animation_get_item; mlt_animation_insert; mlt_animation_remove; mlt_animation_interpolate; mlt_animation_next_key; mlt_animation_prev_key; mlt_animation_serialize_cut; mlt_animation_serialize; mlt_animation_close; mlt_properties_get_color; mlt_properties_set_color; mlt_properties_anim_get; mlt_properties_anim_set; mlt_properties_anim_get_int; mlt_properties_anim_set_int; mlt_properties_anim_get_double; mlt_properties_anim_set_double; mlt_properties_get_animation; mlt_properties_set_rect; mlt_properties_get_rect; mlt_properties_anim_set_rect; mlt_properties_anim_get_rect; mlt_property_interpolate; mlt_property_anim_get_double; mlt_property_anim_get_int; mlt_property_anim_get_string; mlt_property_anim_set_double; mlt_property_anim_set_int; mlt_property_anim_set_string; mlt_property_get_animation; mlt_property_set_rect; mlt_property_get_rect; mlt_property_anim_set_rect; mlt_property_anim_get_rect; mlt_service_filter_count; mlt_service_move_filter; } MLT_0.8.8; MLT_0.9.2 { global: mlt_playlist_mix_in; mlt_playlist_mix_out; mlt_properties_frames_to_time; mlt_properties_time_to_frames; mlt_properties_from_utf8; } MLT_0.9.0; MLT_0.9.4 { global: mlt_pool_stat; mlt_frame_get_alpha; } MLT_0.9.2; MLT_0.9.8 { global: mlt_service_disconnect_producer; mlt_multitrack_disconnect; mlt_tractor_remove_track; mlt_service_insert_producer; mlt_multitrack_insert; mlt_tractor_insert_track; mlt_transition_set_tracks; mlt_animation_key_count; mlt_animation_key_get; } MLT_0.9.4; MLT_0.9.10 { global: mlt_factory_repository; mlt_properties_to_utf8; } MLT_0.9.8; MLT_6.4.0 { global: mlt_slices_init; mlt_slices_close; mlt_slices_run; } MLT_0.9.10; MLT_6.6.0 { global: mlt_image_format_planes; mlt_image_format_id; mlt_slices_count_normal; mlt_slices_count_rr; mlt_slices_count_fifo; mlt_slices_run_normal; mlt_slices_run_rr; mlt_slices_run_fifo; mlt_log_timings_now; mlt_service_disconnect_all_producers; } MLT_6.4.0; MLT_6.8.0 { global: mlt_channel_layout_name; mlt_channel_layout_id; mlt_channel_layout_channels; mlt_channel_layout_default; mlt_animation_key_set_type; mlt_animation_key_set_frame; } MLT_6.6.0; MLT_6.10.0 { global: mlt_animation_serialize_cut_tf; mlt_animation_serialize_tf; mlt_property_clear; mlt_property_get_string_tf; mlt_property_get_string_l_tf; mlt_properties_clear; mlt_properties_get_value_tf; } MLT_6.8.0; MLT_6.12.0 { global: mlt_profile_lumas_dir; } MLT_6.10.0; MLT_6.14.0 { global: mlt_frame_get_unique_properties; mlt_playlist_reorder; mlt_producer_set_creation_time; mlt_producer_get_creation_time; } MLT_6.12.0; MLT_6.18.0 { global: mlt_luma_map_init; mlt_luma_map_new; mlt_luma_map_render; mlt_luma_map_from_pgm; mlt_luma_map_from_yuv422; } MLT_6.14.0; MLT_6.20.0 { global: mlt_profile_scale_width; mlt_profile_scale_height; mlt_properties_set_string; } MLT_6.18.0; mlt-6.20.0/src/framework/mlt_animation.c000066400000000000000000000601711362234133600201660ustar00rootroot00000000000000/** * \file mlt_animation.c * \brief Property Animation class definition * \see mlt_animation_s * * Copyright (C) 2004-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_animation.h" #include "mlt_tokeniser.h" #include "mlt_profile.h" #include "mlt_factory.h" #include "mlt_properties.h" #include #include #include /** \brief animation list node pointer */ typedef struct animation_node_s *animation_node; /** \brief private animation list node */ struct animation_node_s { struct mlt_animation_item_s item; animation_node next, prev; }; /** \brief Property Animation class * * This is the animation engine for a Property object. It is dependent upon * the mlt_property API and used by the various mlt_property_anim_* functions. */ struct mlt_animation_s { char *data; /**< the string representing the animation */ int length; /**< the maximum number of frames to use when interpreting negative keyframe positions */ double fps; /**< framerate to use when converting time clock strings to frame units */ locale_t locale; /**< pointer to a locale to use when converting strings to numeric values */ animation_node nodes; /**< a linked list of keyframes (and possibly non-keyframe values) */ }; /** Create a new animation object. * * \public \memberof mlt_animation_s * \return an animation object */ mlt_animation mlt_animation_new( ) { mlt_animation self = calloc( 1, sizeof( *self ) ); return self; } /** Re-interpolate non-keyframe nodes after a series of insertions or removals. * * \public \memberof mlt_animation_s * \param self an animation */ void mlt_animation_interpolate( mlt_animation self ) { // Parse all items to ensure non-keyframes are calculated correctly. if ( self && self->nodes ) { animation_node current = self->nodes; while ( current ) { if ( !current->item.is_key ) { double progress; mlt_property points[4]; animation_node prev = current->prev; animation_node next = current->next; while ( prev && !prev->item.is_key ) prev = prev->prev; while ( next && !next->item.is_key ) next = next->next; if ( !prev ) { current->item.is_key = 1; prev = current; } if ( !next ) { next = current; } points[0] = prev->prev? prev->prev->item.property : prev->item.property; points[1] = prev->item.property; points[2] = next->item.property; points[3] = next->next? next->next->item.property : next->item.property; progress = current->item.frame - prev->item.frame; progress /= next->item.frame - prev->item.frame; mlt_property_interpolate( current->item.property, points, progress, self->fps, self->locale, current->item.keyframe_type ); } // Move to the next item current = current->next; } } } /** Remove a node from the linked list. * * \private \memberof mlt_animation_s * \param self an animation * \param node the node to remove * \return false */ static int mlt_animation_drop( mlt_animation self, animation_node node ) { if ( node == self->nodes ) { self->nodes = node->next; if ( self->nodes ) { self->nodes->prev = NULL; self->nodes->item.is_key = 1; } } else if ( node->next && node->prev ) { node->prev->next = node->next; node->next->prev = node->prev; } else if ( node->next ) { node->next->prev = node->prev; } else if ( node->prev ) { node->prev->next = node->next; } mlt_property_close( node->item.property ); free( node ); return 0; } /** Reset an animation and free all strings and properties. * * \private \memberof mlt_animation_s * \param self an animation */ static void mlt_animation_clean( mlt_animation self ) { if (!self) return; free( self->data ); self->data = NULL; while ( self->nodes ) mlt_animation_drop( self, self->nodes ); } /** Parse a string representing an animation. * * A semicolon is the delimiter between keyframe=value items in the string. * \public \memberof mlt_animation_s * \param self an animation * \param data the string representing an animation * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param fps the framerate to use when evaluating time strings * \param locale the locale to use when converting strings to numbers * \return true if there was an error */ int mlt_animation_parse(mlt_animation self, const char *data, int length, double fps, locale_t locale ) { if (!self) return 1; int error = 0; int i = 0; struct mlt_animation_item_s item; mlt_tokeniser tokens = mlt_tokeniser_init( ); // Clean the existing geometry mlt_animation_clean( self ); // Update the info on the data if ( data ) self->data = strdup( data ); self->length = length; self->fps = fps; self->locale = locale; item.property = mlt_property_init(); item.frame = item.is_key = 0; item.keyframe_type = mlt_keyframe_discrete; // Tokenise if ( data ) mlt_tokeniser_parse_new( tokens, (char*) data, ";" ); // Iterate through each token for ( i = 0; i < mlt_tokeniser_count( tokens ); i++ ) { char *value = mlt_tokeniser_get_string( tokens, i ); // If no data in keyframe, drop it (trailing semicolon) if ( !value || !strcmp( value, "" ) ) continue; // Reset item item.frame = item.is_key = 0; // Do not parse a string enclosed entirely within quotes as animation. // (mlt_tokeniser already skips splitting on delimiter inside quotes). if ( value[0] == '\"' && value[strlen(value) - 1] == '\"' ) { // Remove the quotes. value[strlen(value) - 1] = '\0'; mlt_property_set_string( item.property, &value[1] ); } else { // Now parse the item mlt_animation_parse_item( self, &item, value ); } // Now insert into place mlt_animation_insert( self, &item ); } mlt_animation_interpolate( self ); // Cleanup mlt_tokeniser_close( tokens ); mlt_property_close( item.property ); return error; } /** Conditionally refresh the animation if it is modified. * * \public \memberof mlt_animation_s * \param self an animation * \param data the string representing an animation * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return true if there was an error */ int mlt_animation_refresh( mlt_animation self, const char *data, int length ) { if (!self) return 1; if ( ( length != self->length )|| ( data && ( !self->data || strcmp( data, self->data ) ) ) ) return mlt_animation_parse( self, data, length, self->fps, self->locale ); return 0; } /** Get the length of the animation. * * If the animation was initialized with a zero or negative value, then this * gets the maximum frame number from animation's list of nodes. * \public \memberof mlt_animation_s * \param self an animation * \return the number of frames */ int mlt_animation_get_length( mlt_animation self ) { int length = 0; if ( self ) { if ( self->length > 0 ) { length = self->length; } else if ( self->nodes ) { animation_node node = self->nodes; while ( node ) { if ( node->item.frame > length ) length = node->item.frame; node = node->next; } } } return length; } /** Set the length of the animation. * * The length is used for interpreting negative keyframe positions as relative * to the length. It is also used when serializing an animation as a string. * \public \memberof mlt_animation_s * \param self an animation * \param length the length of the animation in frame units */ void mlt_animation_set_length( mlt_animation self, int length ) { if ( self ) self->length = length; } /** Parse a string representing an animation keyframe=value. * * This function does not affect the animation itself! But it will use some state * of the animation for the parsing (e.g. fps, locale). * It parses into a mlt_animation_item that you provide. * \p item->frame should be specified if the string does not have an equal sign and time field. * If an exclamation point (!) or vertical bar (|) character precedes the equal sign, then * the keyframe interpolation is set to discrete. If a tilde (~) precedes the equal sign, * then the keyframe interpolation is set to smooth (spline). * * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item * \param value the string representing an animation * \return true if there was an error */ int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const char *value ) { int error = 0; if ( self && item && value && strcmp( value, "" ) ) { // Determine if a position has been specified if ( strchr( value, '=' ) ) { // Parse an absolute time value. // Null terminate the string at the equal sign to prevent interpreting // a colon in the part to the right of the equal sign as indicative of a // a time value string. char *s = strdup( value ); char *p = strchr( s, '=' ); p[0] = '\0'; mlt_property_set_string( item->property, s ); item->frame = mlt_property_get_int( item->property, self->fps, self->locale ); free( s ); // The character preceding the equal sign indicates interpolation method. p = strchr( value, '=' ) - 1; if ( p[0] == '|' || p[0] == '!' ) item->keyframe_type = mlt_keyframe_discrete; else if ( p[0] == '~' ) item->keyframe_type = mlt_keyframe_smooth; else item->keyframe_type = mlt_keyframe_linear; value = &p[2]; // Check if the value is quoted. p = &p[2]; if ( p && p[0] == '\"' && p[strlen(p) - 1] == '\"' ) { // Remove the quotes. p[strlen(p) - 1] = '\0'; value = &p[1]; } } // Special case - frame < 0 if ( item->frame < 0 ) item->frame += mlt_animation_get_length( self ); // Set remainder of string as item value. mlt_property_set_string( item->property, value ); item->is_key = 1; } else { error = 1; } return error; } /** Load an animation item for an absolute position. * * This performs interpolation if there is no keyframe at the \p position. * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item that will be filled in * \param position the frame number for the point in time * \return true if there was an error */ int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int position ) { if (!self || !item) return 1; int error = 0; // Need to find the nearest keyframe to the position specified animation_node node = self->nodes; // Iterate through the keyframes until we reach last or have while ( node && node->next && position >= node->next->item.frame ) node = node->next; if ( node ) { item->keyframe_type = node->item.keyframe_type; // Position is before the first keyframe. if ( position < node->item.frame ) { item->is_key = 0; if ( item->property ) mlt_property_pass( item->property, node->item.property ); } // Item exists. else if ( position == node->item.frame ) { item->is_key = node->item.is_key; if ( item->property ) mlt_property_pass( item->property, node->item.property ); } // Position is after the last keyframe. else if ( !node->next ) { item->is_key = 0; if ( item->property ) mlt_property_pass( item->property, node->item.property ); } // Interpolation needed. else { if ( item->property ) { double progress; mlt_property points[4]; points[0] = node->prev? node->prev->item.property : node->item.property; points[1] = node->item.property; points[2] = node->next->item.property; points[3] = node->next->next? node->next->next->item.property : node->next->item.property; progress = position - node->item.frame; progress /= node->next->item.frame - node->item.frame; mlt_property_interpolate( item->property, points, progress, self->fps, self->locale, item->keyframe_type ); } item->is_key = 0; } } else { item->frame = item->is_key = 0; error = 1; } item->frame = position; return error; } /** Insert an animation item. * * \public \memberof mlt_animation_s * \param self an animation * \param item an animation item * \return true if there was an error * \see mlt_animation_parse_item */ int mlt_animation_insert( mlt_animation self, mlt_animation_item item ) { if (!self || !item) return 1; int error = 0; animation_node node = calloc( 1, sizeof( *node ) ); node->item.frame = item->frame; node->item.is_key = 1; node->item.keyframe_type = item->keyframe_type; node->item.property = mlt_property_init(); mlt_property_pass( node->item.property, item->property ); // Determine if we need to insert or append to the list, or if it's a new list if ( self->nodes ) { // Get the first item animation_node current = self->nodes; // Locate an existing nearby item while ( current->next && item->frame > current->item.frame ) current = current->next; if ( item->frame < current->item.frame ) { if ( current == self->nodes ) self->nodes = node; if ( current->prev ) current->prev->next = node; node->next = current; node->prev = current->prev; current->prev = node; } else if ( item->frame > current->item.frame ) { if ( current->next ) current->next->prev = node; node->next = current->next; node->prev = current; current->next = node; } else { // Update matching node. current->item.frame = item->frame; current->item.is_key = 1; current->item.keyframe_type = item->keyframe_type; mlt_property_close( current->item.property ); current->item.property = node->item.property; free( node ); } } else { // Set the first item self->nodes = node; } return error; } /** Remove the keyframe at the specified position. * * \public \memberof mlt_animation_s * \param self an animation * \param position the frame number of the animation node to remove * \return true if there was an error */ int mlt_animation_remove( mlt_animation self, int position ) { if (!self) return 1; int error = 1; animation_node node = self->nodes; while ( node && position != node->item.frame ) node = node->next; if ( node && position == node->item.frame ) error = mlt_animation_drop( self, node ); return error; } /** Get the keyfame at the position or the next following. * * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item which will be updated * \param position the frame number at which to start looking for the next animation node * \return true if there was an error */ int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int position ) { if (!self || !item) return 1; animation_node node = self->nodes; while ( node && position > node->item.frame ) node = node->next; if ( node ) { item->frame = node->item.frame; item->is_key = node->item.is_key; item->keyframe_type = node->item.keyframe_type; if ( item->property ) mlt_property_pass( item->property, node->item.property ); } return ( node == NULL ); } /** Get the keyfame at the position or the next preceding. * * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item which will be updated * \param position the frame number at which to start looking for the previous animation node * \return true if there was an error */ int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int position ) { if (!self || !item) return 1; animation_node node = self->nodes; while ( node && node->next && position >= node->next->item.frame ) node = node->next; if ( node ) { item->frame = node->item.frame; item->is_key = node->item.is_key; item->keyframe_type = node->item.keyframe_type; if ( item->property ) mlt_property_pass( item->property, node->item.property ); } return ( node == NULL ); } /** Serialize a cut of the animation (with time format). * * The caller is responsible for free-ing the returned string. * \public \memberof mlt_animation_s * \param self an animation * \param in the frame at which to start serializing animation nodes * \param out the frame at which to stop serializing nodes * \param time_format the time format to use for the key frames * \return a string representing the animation */ char *mlt_animation_serialize_cut_tf( mlt_animation self, int in, int out, mlt_time_format time_format ) { struct mlt_animation_item_s item; char *ret = calloc( 1, 1000 ); size_t used = 0; size_t size = 1000; mlt_property time_property = mlt_property_init(); item.property = mlt_property_init(); item.frame = item.is_key = 0; item.keyframe_type = mlt_keyframe_discrete; if ( in == -1 ) in = 0; if ( out == -1 ) out = mlt_animation_get_length( self ); if ( self && ret ) { item.frame = in; while ( 1 ) { size_t item_len = 0; // If it's the first frame, then it's not necessarily a key if ( item.frame == in ) { if ( mlt_animation_get_item( self, &item, item.frame ) ) break; // If the first keyframe is larger than the current position // then do nothing here if ( self->nodes->item.frame > item.frame ) { item.frame ++; continue; } // To ensure correct seeding item.is_key = 1; } // Typically, we move from keyframe to keyframe else if ( item.frame <= out ) { if ( mlt_animation_next_key( self, &item, item.frame ) ) break; // Special case - crop at the out point if ( item.frame > out ) mlt_animation_get_item( self, &item, out ); } // We've handled the last keyframe else { break; } // Determine length of string to be appended. item_len += 100; const char* value = mlt_property_get_string_l( item.property, self->locale ); if ( item.is_key && value ) { item_len += strlen( value ); // Check if the value must be quoted. if ( strchr(value, ';') || strchr(value, '=') ) item_len += 2; } // Reallocate return string to be long enough. while ( used + item_len + 2 > size ) // +2 for ';' and NULL { size += 1000; ret = realloc( ret, size ); } // Append item delimiter (;) if needed. if ( ret && used > 0 ) { used ++; strcat( ret, ";" ); } if ( ret ) { // Append keyframe time and keyframe/value delimiter (=). const char *s; switch (item.keyframe_type) { case mlt_keyframe_discrete: s = "|"; break; case mlt_keyframe_smooth: s = "~"; break; default: s = ""; break; } if ( time_property && self->fps > 0.0 ) { mlt_property_set_int( time_property, item.frame - in ); const char *time = mlt_property_get_time( time_property, time_format, self->fps, self->locale ); sprintf( ret + used, "%s%s=", time, s ); } else { sprintf( ret + used, "%d%s=", item.frame - in, s ); } used = strlen( ret ); // Append item value. if ( item.is_key && value ) { // Check if the value must be quoted. if ( strchr(value, ';') || strchr(value, '=') ) sprintf( ret + used, "\"%s\"", value ); else strcat( ret, value ); } used = strlen( ret ); } item.frame ++; } } mlt_property_close( item.property ); mlt_property_close( time_property ); return ret; } static mlt_time_format default_time_format() { const char *e = getenv("MLT_ANIMATION_TIME_FORMAT"); return e? strtol( e, NULL, 10 ) : mlt_time_frames; } /** Serialize a cut of the animation. * * This version outputs the key frames' position as a frame number. * The caller is responsible for free-ing the returned string. * \public \memberof mlt_animation_s * \param self an animation * \param in the frame at which to start serializing animation nodes * \param out the frame at which to stop serializing nodes * \return a string representing the animation */ char *mlt_animation_serialize_cut( mlt_animation self, int in, int out ) { return mlt_animation_serialize_cut_tf( self, in, out, default_time_format() ); } /** Serialize the animation (with time format). * * The caller is responsible for free-ing the returned string. * \public \memberof mlt_animation_s * \param self an animation * \param time_format the time format to use for the key frames * \return a string representing the animation */ char *mlt_animation_serialize_tf( mlt_animation self, mlt_time_format time_format ) { char *ret = mlt_animation_serialize_cut_tf( self, -1, -1, time_format ); if ( self && ret ) { free( self->data ); self->data = ret; ret = strdup( ret ); } return ret; } /** Serialize the animation. * * This version outputs the key frames' position as a frame number. * The caller is responsible for free-ing the returned string. * \public \memberof mlt_animation_s * \param self an animation * \return a string representing the animation */ char *mlt_animation_serialize( mlt_animation self ) { return mlt_animation_serialize_tf( self, default_time_format() ); } /** Get the number of keyframes. * * \public \memberof mlt_animation_s * \param self an animation * \return the number of keyframes or -1 on error */ int mlt_animation_key_count( mlt_animation self ) { int count = -1; if ( self ) { animation_node node = self->nodes; for ( count = 0; node; ++count ) node = node->next; } return count; } /** Get an animation item for the N-th keyframe. * * \public \memberof mlt_animation_s * \param self an animation * \param item an already allocated animation item that will be filled in * \param index the N-th keyframe (0 based) in this animation * \return true if there was an error */ int mlt_animation_key_get( mlt_animation self, mlt_animation_item item, int index ) { if (!self || !item) return 1; int error = 0; animation_node node = self->nodes; // Iterate through the keyframes. int i = index; while ( i-- && node ) node = node->next; if ( node ) { item->is_key = node->item.is_key; item->frame = node->item.frame; item->keyframe_type = node->item.keyframe_type; if ( item->property ) mlt_property_pass( item->property, node->item.property ); } else { item->frame = item->is_key = 0; error = 1; } return error; } /** Close the animation and deallocate all of its resources. * * \public \memberof mlt_animation_s * \param self the animation to destroy */ void mlt_animation_close( mlt_animation self ) { if ( self ) { mlt_animation_clean( self ); free( self ); } } /** Change the interpolation for the N-th keyframe. * * \public \memberof mlt_animation_s * \param self an animation * \param index the N-th keyframe (0 based) in this animation * \param type the method of interpolation for this key frame * \return true if there was an error */ int mlt_animation_key_set_type(mlt_animation self, int index, mlt_keyframe_type type) { if (!self) return 1; int error = 0; animation_node node = self->nodes; // Iterate through the keyframes. int i = index; while ( i-- && node ) node = node->next; if ( node ) { node->item.keyframe_type = type; mlt_animation_interpolate(self); } else { error = 1; } return error; } /** Change the frame number for the N-th keyframe. * * \public \memberof mlt_animation_s * \param self an animation * \param index the N-th keyframe (0 based) in this animation * \param frame the position of this keyframe in frame units * \return true if there was an error */ int mlt_animation_key_set_frame(mlt_animation self, int index, int frame) { if (!self) return 1; int error = 0; animation_node node = self->nodes; // Iterate through the keyframes. int i = index; while ( i-- && node ) node = node->next; if ( node ) { node->item.frame = frame; mlt_animation_interpolate(self); } else { error = 1; } return error; } mlt-6.20.0/src/framework/mlt_animation.h000066400000000000000000000071701362234133600201730ustar00rootroot00000000000000/** * \file mlt_animation.h * \brief Property Animation class declaration * \see mlt_animation_s * * Copyright (C) 2004-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_ANIMATION_H #define MLT_ANIMATION_H #include "mlt_types.h" #include "mlt_property.h" /** \brief Animation class * * Once an animation has been constructed using mlt_properties_s, this interface * provides a to query and manipulate the animation except for values. One must * use mlt_properties_s still to get, set, and change values. * * \envvar \em MLT_ANIMATION_TIME_FORMAT the time value string format to use, * defaults to mlt_time_frames. Use the numeric value of mlt_time_format as * the value of this variable. */ /** \brief An animation item that represents a keyframe-property combination. */ struct mlt_animation_item_s { int is_key; /**< a boolean of whether this is a key frame or an interpolated item */ int frame; /**< the frame number for this instance of the property */ mlt_property property; /**< the property for this point in time */ mlt_keyframe_type keyframe_type; /**< the method of interpolation for this key frame */ }; typedef struct mlt_animation_item_s *mlt_animation_item; /**< pointer to an animation item */ extern mlt_animation mlt_animation_new( ); extern int mlt_animation_parse(mlt_animation self, const char *data, int length, double fps, locale_t locale ); extern int mlt_animation_refresh( mlt_animation self, const char *data, int length ); extern int mlt_animation_get_length( mlt_animation self ); extern void mlt_animation_set_length( mlt_animation self, int length ); extern int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const char *data ); extern int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int position ); extern int mlt_animation_insert( mlt_animation self, mlt_animation_item item ); extern int mlt_animation_remove( mlt_animation self, int position ); extern void mlt_animation_interpolate( mlt_animation self ); extern int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int position ); extern int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int position ); extern char *mlt_animation_serialize_cut_tf( mlt_animation self, int in, int out, mlt_time_format ); extern char *mlt_animation_serialize_cut( mlt_animation self, int in, int out ); extern char *mlt_animation_serialize_tf( mlt_animation self, mlt_time_format ); extern char *mlt_animation_serialize( mlt_animation self ); extern int mlt_animation_key_count( mlt_animation self ); extern int mlt_animation_key_get( mlt_animation self, mlt_animation_item item, int index ); extern void mlt_animation_close( mlt_animation self ); extern int mlt_animation_key_set_type( mlt_animation self, int index, mlt_keyframe_type type ); extern int mlt_animation_key_set_frame( mlt_animation self, int index, int frame ); #endif mlt-6.20.0/src/framework/mlt_cache.c000066400000000000000000000427311362234133600172540ustar00rootroot00000000000000/** * \file mlt_cache.c * \brief least recently used cache * \see mlt_profile_s * * Copyright (C) 2007-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_types.h" #include "mlt_log.h" #include "mlt_properties.h" #include "mlt_cache.h" #include "mlt_frame.h" #include #include /** the maximum number of data objects to cache per line */ #define MAX_CACHE_SIZE (200) /** the default number of data objects to cache per line */ #define DEFAULT_CACHE_SIZE (4) /** \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 */ int size; /**< the maximum number of items permitted in the cache <= \p MAX_CACHE_SIZE */ int is_frames; /**< indicates if this cache is used to cache frames */ void* *current; /**< pointer to the current array of pointers */ void* A[ MAX_CACHE_SIZE ]; void* B[ MAX_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]; if ( cache->is_frames ) { // Frame caches are easy - just close the object as mlt_frame. mlt_frame_close( object ); return; } // Fetch the cache item from the active list by its owner's address sprintf( key, "%p", object ); 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 ); } } } } /** 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 ) { pthread_mutex_lock( &item->cache->mutex ); cache_object_close( item->cache, item->object, item->data ); pthread_mutex_unlock( &item->cache->mutex ); } } /** Create a new cache. * * The default size is \p DEFAULT_CACHE_SIZE. * \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->size = DEFAULT_CACHE_SIZE; result->current = result->A; pthread_mutex_init( &result->mutex, NULL ); result->active = mlt_properties_new(); result->garbage = mlt_properties_new(); } return result; } /** Set the number of items to cache. * * This must be called before using the cache. The size can not be more * than \p MAX_CACHE_SIZE. * \public \memberof mlt_cache_s * \param cache the cache to adjust * \param size the new size of the cache */ void mlt_cache_set_size( mlt_cache cache, int size ) { if ( size <= MAX_CACHE_SIZE ) cache->size = size; } /** Get the number of possible cache items. * * \public \memberof mlt_cache_s * \param cache the cache to check * \return the current maximum size of the cache */ int mlt_cache_get_size( mlt_cache cache ) { return cache->size; } /** Destroy a cache. * * \public \memberof mlt_cache_s * \param cache the cache to destroy */ 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 ) { if (!cache) return; 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 ) { cache_object_close( cache, o, NULL ); } else { alt[ j++ ] = o; } } cache->count = j; cache->current = alt; } 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. * * This function and mlt_cache_get() are not scalable with a large volume * of unique \p object parameter values. Therefore, it does not make sense * to use it for a frame/image cache using the frame position for \p object. * Instead, use mlt_cache_put_frame() for that. * * \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 cache_object_close( cache, *hit, NULL ); // 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 cache_object_close( cache, cache->current[0], NULL ); // 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; } /** Shuffle the cache entries between the two arrays and return the frame for a position. * * \private \memberof mlt_cache_s * \param cache a cache object * \param position the position of the frame that you want * \return a frame if there was a hit or NULL for a miss */ static mlt_frame* shuffle_get_frame( mlt_cache cache, mlt_position position ) { int i = cache->count; int j = cache->count - 1; mlt_frame *hit = NULL; mlt_frame *alt = (mlt_frame*) ( 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 ) { mlt_frame *o = (mlt_frame*) &cache->current[ i ]; if ( mlt_frame_original_position( *o ) == position ) 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-- ) { mlt_frame *o = (mlt_frame*) &cache->current[ i ]; if ( !hit && mlt_frame_original_position( *o ) == position ) { 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 frame in the cache. * * Unlike mlt_cache_put() this version is more suitable for caching frames * and their data - like images. However, this version does not use reference * counting and garbage collection. Rather, frames are cloned with deep copy * to avoid those things. * * \public \memberof mlt_cache_s * \param cache a cache object * \param frame the frame to cache * \see mlt_frame_get_frame */ void mlt_cache_put_frame( mlt_cache cache, mlt_frame frame ) { pthread_mutex_lock( &cache->mutex ); mlt_frame *hit = shuffle_get_frame( cache, mlt_frame_original_position( frame ) ); mlt_frame *alt = (mlt_frame*) ( cache->current == cache->A ? cache->B : cache->A ); // add the frame to the cache if ( hit ) { // release the old data mlt_frame_close( *hit ); // 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 mlt_frame_close( cache->current[0] ); // The MRU end gets the new item hit = &alt[ cache->count - 1 ]; } *hit = mlt_frame_clone( frame, 1 ); mlt_log( NULL, MLT_LOG_DEBUG, "%s: put %d = %p\n", __FUNCTION__, cache->count - 1, frame ); // swap the current array cache->current = (void**) alt; cache->is_frames = 1; pthread_mutex_unlock( &cache->mutex ); } /** Get a frame from the cache. * * You must call mlt_frame_close() on the frame you receive from this. * * \public \memberof mlt_cache_s * \param cache a cache object * \param position the position of the frame that you want * \return a frame if found or NULL if not found or has been flushed from the cache * \see mlt_frame_put_frame */ mlt_frame mlt_cache_get_frame( mlt_cache cache, mlt_position position ) { mlt_frame result = NULL; pthread_mutex_lock( &cache->mutex ); mlt_frame *hit = shuffle_get_frame( cache, position ); mlt_frame *alt = (mlt_frame*) ( 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 ]; result = mlt_frame_clone( *hit, 1 ); mlt_log( NULL, MLT_LOG_DEBUG, "%s: get %d = %p\n", __FUNCTION__, cache->count - 1, *hit ); // swap the current array cache->current = (void**) alt; } pthread_mutex_unlock( &cache->mutex ); return result; } mlt-6.20.0/src/framework/mlt_cache.h000066400000000000000000000031721362234133600172550ustar00rootroot00000000000000/** * \file mlt_cache.h * \brief least recently used cache * \see mlt_cache_s * * Copyright (C) 2007-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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_set_size( mlt_cache cache, int size ); extern int mlt_cache_get_size( mlt_cache cache ); 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 ); extern void mlt_cache_put_frame( mlt_cache cache, mlt_frame frame ); extern mlt_frame mlt_cache_get_frame( mlt_cache cache, mlt_position position ); #endif mlt-6.20.0/src/framework/mlt_consumer.c000066400000000000000000001622411362234133600200430ustar00rootroot00000000000000/** * \file mlt_consumer.c * \brief abstraction for all consumer services * \see mlt_consumer_s * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 #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 /** This is not the ideal place for this, but it is needed by VDPAU as well. */ pthread_mutex_t mlt_sdl_mutex = PTHREAD_MUTEX_INITIALIZER; /** \brief private members of mlt_consumer */ typedef struct { int real_time; atomic_int ahead; int preroll; mlt_image_format image_format; mlt_audio_format audio_format; mlt_deque queue; void *ahead_thread; pthread_mutex_t queue_mutex; pthread_cond_t queue_cond; pthread_mutex_t put_mutex; pthread_cond_t put_cond; mlt_frame put; int put_active; mlt_event event_listener; mlt_position position; pthread_mutex_t position_mutex; int is_purge; int aud_counter; double fps; int channels; int frequency; atomic_int speed; /* additional fields added for the parallel work queue */ mlt_deque worker_threads; pthread_mutex_t done_mutex; pthread_cond_t done_cond; int consecutive_dropped; int consecutive_rendered; int process_head; atomic_int started; pthread_t *threads; /**< used to deallocate all threads */ } consumer_private; typedef void* ( *thread_function_t )( void* ); static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); static void mlt_consumer_property_changed( mlt_properties owner, mlt_consumer self, char *name ); static void apply_profile_properties( mlt_consumer self, mlt_profile profile, mlt_properties properties ); static void on_consumer_frame_show( mlt_properties owner, mlt_consumer self, mlt_frame frame ); static void transmit_thread_create( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); static void mlt_thread_create( mlt_consumer self, thread_function_t function ); static void transmit_thread_join( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); static void mlt_thread_join( mlt_consumer self ); static void consumer_read_ahead_start( mlt_consumer self ); /** Initialize a consumer service. * * \public \memberof mlt_consumer_s * \param self 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 self is NULL) * \return true if there was an error */ int mlt_consumer_init( mlt_consumer self, void *child, mlt_profile profile ) { int error = 0; memset( self, 0, sizeof( struct mlt_consumer_s ) ); self->child = child; consumer_private *priv = self->local = calloc( 1, sizeof( consumer_private ) ); error = mlt_service_init( &self->parent, self ); if ( error == 0 ) { // Get the properties from the service mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->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( self ); mlt_properties_set_data( properties, "_profile", profile, 0, (mlt_destructor)mlt_profile_close, NULL ); } apply_profile_properties( self, profile, properties ); mlt_properties_set( properties, "mlt_type", "consumer" ); // Default rescaler for all consumers mlt_properties_set( properties, "rescale", "bilinear" ); // Default read ahead buffer size mlt_properties_set_int( properties, "buffer", 25 ); mlt_properties_set_int( properties, "drop_max", 5 ); // 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 with s16 :-/ priv->image_format = mlt_image_yuv422; priv->audio_format = mlt_audio_s16; 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-thread-started", NULL ); mlt_events_register( properties, "consumer-thread-stopped", NULL ); mlt_events_register( properties, "consumer-stopping", NULL ); mlt_events_register( properties, "consumer-stopped", NULL ); mlt_events_register( properties, "consumer-thread-create", ( mlt_transmitter )transmit_thread_create ); mlt_events_register( properties, "consumer-thread-join", ( mlt_transmitter )transmit_thread_join ); mlt_events_listen( properties, self, "consumer-frame-show", ( mlt_listener )on_consumer_frame_show ); // Register a property-changed listener to handle the profile property - // subsequent properties can override the profile priv->event_listener = mlt_events_listen( properties, self, "property-changed", ( mlt_listener )mlt_consumer_property_changed ); // Create the push mutex and condition pthread_mutex_init( &priv->put_mutex, NULL ); pthread_cond_init( &priv->put_cond, NULL ); pthread_mutex_init( &priv->position_mutex, NULL ); } return error; } /** Convert the profile into properties on the consumer. * * \private \memberof mlt_consumer_s * \param self a consumer * \param profile a profile * \param properties a properties list (typically, the consumer's) */ static void apply_profile_properties( mlt_consumer self, mlt_profile profile, mlt_properties properties ) { consumer_private *priv = self->local; mlt_event_block( priv->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_den", profile->display_aspect_den ); mlt_properties_set_int( properties, "colorspace", profile->colorspace ); mlt_event_unblock( priv->event_listener ); } /** The property-changed event listener * * \private \memberof mlt_consumer_s * \param owner the events object * \param self the consumer * \param name the name of the property that changed */ static void mlt_consumer_property_changed( mlt_properties owner, mlt_consumer self, char *name ) { if ( !strcmp( name, "mlt_profile" ) ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Get the current profile mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); // 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 ); } else { profile = new_profile; } // Apply to properties apply_profile_properties( self, profile, properties ); mlt_profile_close( new_profile ); } } else if ( !strcmp( name, "frame_rate_num" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); 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( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); 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( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) profile->width = mlt_properties_get_int( properties, "width" ); } else if ( !strcmp( name, "height" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) profile->height = mlt_properties_get_int( properties, "height" ); } else if ( !strcmp( name, "progressive" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) profile->progressive = mlt_properties_get_int( properties, "progressive" ); } else if ( !strcmp( name, "sample_aspect_num" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) { profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); } } else if ( !strcmp( name, "sample_aspect_den" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) { profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); } } else if ( !strcmp( name, "display_aspect_num" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); 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( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); 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 ) ); } } else if ( !strcmp( name, "colorspace" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) ); if ( profile ) profile->colorspace = mlt_properties_get_int( properties, "colorspace" ); } } /** 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 the events object that will be passed to \p listener * \param self a service that will be passed to \p listener * \param args an array of pointers - the first entry is passed as a frame to \p listener */ static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener != NULL ) listener( owner, self, ( 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 the events object that will be passed to \p listener * \param self a service that will be passed to \p listener * \param args an array of pointers - the first entry is passed as a frame to \p listener */ static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener != NULL ) listener( owner, self, ( mlt_frame )args[ 0 ] ); } /** A listener on the consumer-frame-show event * * Saves the position of the frame shown. * * \private \memberof mlt_consumer_s * \param owner the events object * \param consumer the consumer on which this event occurred * \param frame the frame that was shown */ static void on_consumer_frame_show( mlt_properties owner, mlt_consumer consumer, mlt_frame frame ) { if ( frame ) { consumer_private* priv = consumer->local; pthread_mutex_lock( &priv->position_mutex ); priv->position = mlt_frame_get_position( frame ); pthread_mutex_unlock( &priv->position_mutex ); } } /** 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 self = malloc( sizeof( struct mlt_consumer_s ) ); // Initialise it if ( self != NULL && mlt_consumer_init( self, NULL, profile ) == 0 ) { // Return it return self; } else { free(self); return NULL; } } /** Get the parent service object. * * \public \memberof mlt_consumer_s * \param self a consumer * \return the parent service class * \see MLT_CONSUMER_SERVICE */ mlt_service mlt_consumer_service( mlt_consumer self ) { return self != NULL ? &self->parent : NULL; } /** Get the consumer properties. * * \public \memberof mlt_consumer_s * \param self a consumer * \return the consumer's properties list * \see MLT_CONSUMER_PROPERTIES */ mlt_properties mlt_consumer_properties( mlt_consumer self ) { return self != NULL ? MLT_SERVICE_PROPERTIES( &self->parent ) : NULL; } /** Connect the consumer to the producer. * * \public \memberof mlt_consumer_s * \param self 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 self, mlt_service producer ) { return mlt_service_connect_producer( &self->parent, producer, 0 ); } /** Set the audio format to use in the render thread. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void set_audio_format( mlt_consumer self ) { // Get the audio format to use for rendering threads. consumer_private *priv = self->local; mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); const char *format = mlt_properties_get( properties, "mlt_audio_format" ); if ( format ) { if ( !strcmp( format, "none" ) ) priv->audio_format = mlt_audio_none; else if ( !strcmp( format, "s32" ) ) priv->audio_format = mlt_audio_s32; else if ( !strcmp( format, "s32le" ) ) priv->audio_format = mlt_audio_s32le; else if ( !strcmp( format, "float" ) ) priv->audio_format = mlt_audio_float; else if ( !strcmp( format, "f32le" ) ) priv->audio_format = mlt_audio_f32le; else if ( !strcmp( format, "u8" ) ) priv->audio_format = mlt_audio_u8; } } /** Set the image format to use in render threads. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void set_image_format( mlt_consumer self ) { // Get the image format to use for rendering threads. consumer_private *priv = self->local; mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); const char* format = mlt_properties_get( properties, "mlt_image_format" ); if ( format ) { priv->image_format = mlt_image_format_id( format ); if ( mlt_image_invalid == priv->image_format ) priv->image_format = mlt_image_yuv422; // mlt_image_glsl is for internal use only. // Remapping it glsl_texture prevents breaking existing apps // using the legacy "glsl" name. else if ( mlt_image_glsl == priv->image_format ) priv->image_format = mlt_image_glsl_texture; } } /** Start the consumer. * * \public \memberof mlt_consumer_s * \param self a consumer * \return true if there was an error */ int mlt_consumer_start( mlt_consumer self ) { int error = 0; if ( !mlt_consumer_is_stopped( self ) ) return error; consumer_private *priv = self->local; // Stop listening to the property-changed event mlt_event_block( priv->event_listener ); // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // 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... pthread_mutex_lock( &priv->put_mutex ); priv->put = NULL; priv->put_active = 1; pthread_mutex_unlock( &priv->put_mutex ); // 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( self ) ); 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 ); } // The profile could have changed between a stop and a restart. apply_profile_properties( self, mlt_service_profile( MLT_CONSUMER_SERVICE(self) ), properties ); // Set the frame duration in microseconds for the frame-dropping heuristic int frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" ); int frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" ); int frame_duration = 0; if ( frame_rate_num && frame_rate_den ) { frame_duration = 1000000 / frame_rate_num * frame_rate_den; } mlt_properties_set_int( properties, "frame_duration", frame_duration ); mlt_properties_set_int( properties, "drop_count", 0 ); // 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( self ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "ante" ) ); // Set the real_time preference priv->real_time = mlt_properties_get_int( properties, "real_time" ); // For worker threads implementation, buffer must be at least # threads if ( abs( priv->real_time ) > 1 && mlt_properties_get_int( properties, "buffer" ) <= abs( priv->real_time ) ) mlt_properties_set_int( properties, "_buffer", abs( priv->real_time ) + 1 ); // Store the parameters for audio processing. priv->aud_counter = 0; priv->fps = mlt_properties_get_double( properties, "fps" ); priv->channels = mlt_properties_get_int( properties, "channels" ); priv->frequency = mlt_properties_get_int( properties, "frequency" ); priv->preroll = 1; #ifdef _WIN32 if ( priv->real_time == 1 || priv->real_time == -1 ) consumer_read_ahead_start( self ); #endif // Start the service if ( self->start != NULL ) error = self->start( self ); return error; } /** An alternative method to feed frames into the consumer. * * Only valid if the consumer itself is not connected. * * \public \memberof mlt_consumer_s * \param self a consumer * \param frame a frame * \return true (ignore self for now) */ int mlt_consumer_put_frame( mlt_consumer self, mlt_frame frame ) { int error = 1; // Get the service associated to the consumer mlt_service service = MLT_CONSUMER_SERVICE( self ); if ( mlt_service_producer( service ) == NULL ) { struct timeval now; struct timespec tm; consumer_private *priv = self->local; mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(self), "put_pending", 1 ); pthread_mutex_lock( &priv->put_mutex ); while ( priv->put_active && priv->put != NULL ) { gettimeofday( &now, NULL ); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait( &priv->put_cond, &priv->put_mutex, &tm ); } mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(self), "put_pending", 0 ); if ( priv->put_active && priv->put == NULL ) priv->put = frame; else mlt_frame_close( frame ); pthread_cond_broadcast( &priv->put_cond ); pthread_mutex_unlock( &priv->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 self a consumer * \return a frame */ mlt_frame mlt_consumer_get_frame( mlt_consumer self ) { // Frame to return mlt_frame frame = NULL; // Get the service associated to the consumer mlt_service service = MLT_CONSUMER_SERVICE( self ); // Get the consumer properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Get the frame if ( mlt_service_producer( service ) == NULL && mlt_properties_get_int( properties, "put_mode" ) ) { struct timeval now; struct timespec tm; consumer_private *priv = self->local; pthread_mutex_lock( &priv->put_mutex ); while ( priv->put_active && priv->put == NULL ) { gettimeofday( &now, NULL ); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait( &priv->put_cond, &priv->put_mutex, &tm ); } frame = priv->put; priv->put = NULL; pthread_cond_broadcast( &priv->put_cond ); pthread_mutex_unlock( &priv->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 ); // Pass along the interpolation and deinterlace options // TODO: get rid of consumer_deinterlace and use profile.progressive mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale" ) ); 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" ) ); mlt_properties_set_int( frame_properties, "consumer_tff", mlt_properties_get_int( properties, "top_field_first" ) ); mlt_properties_set( frame_properties, "consumer_color_trc", mlt_properties_get( properties, "color_trc" ) ); mlt_properties_set( frame_properties, "consumer_channel_layout", mlt_properties_get( properties, "channel_layout" ) ); } // 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 self = arg; consumer_private *priv = self->local; // Get the properties of the consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // 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" ); // Audio processing variables int samples = 0; void *audio = NULL; // See if audio is turned off int audio_off = mlt_properties_get_int( properties, "audio_off" ); // 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 = 0; int skipped = 0; int64_t time_process = 0; int skip_next = 0; mlt_position pos = 0; mlt_position start_pos = 0; mlt_position last_pos = 0; int frame_duration = mlt_properties_get_int( properties, "frame_duration" ); int drop_max = mlt_properties_get_int( properties, "drop_max" ); if ( preview_off && preview_format != 0 ) priv->image_format = preview_format; set_audio_format( self ); set_image_format( self ); mlt_events_fire( properties, "consumer-thread-started", NULL ); // Get the first frame frame = mlt_consumer_get_frame( self ); priv->speed = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ); if ( frame ) { // Get the audio of the first frame if ( !audio_off ) { samples = mlt_sample_calculator( priv->fps, priv->frequency, priv->aud_counter++ ); mlt_frame_get_audio( frame, &audio, &priv->audio_format, &priv->frequency, &priv->channels, &samples ); } // Get the image of the first frame if ( !video_off ) { mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-frame-render", frame, NULL ); mlt_frame_get_image( frame, &image, &priv->image_format, &width, &height, 0 ); } // Mark as rendered mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); last_pos = start_pos = pos = mlt_frame_get_position( frame ); } // Get the starting time (can ignore the times above) gettimeofday( &ante, NULL ); // Continue to read ahead while ( priv->ahead ) { // Get the maximum size of the buffer int buffer = (priv->speed == 0) ? 1 : MAX(mlt_properties_get_int( properties, "buffer" ), 0) + 1; // Put the current frame into the queue pthread_mutex_lock( &priv->queue_mutex ); while( priv->ahead && mlt_deque_count( priv->queue ) >= buffer ) pthread_cond_wait( &priv->queue_cond, &priv->queue_mutex ); if ( priv->is_purge ) { mlt_frame_close( frame ); priv->is_purge = 0; } else { mlt_deque_push_back( priv->queue, frame ); } pthread_cond_broadcast( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); mlt_log_timings_begin(); // Get the next frame frame = mlt_consumer_get_frame( self ); mlt_log_timings_end( NULL, "mlt_consumer_get_frame" ); // If there's no frame, we're probably stopped... if ( frame == NULL ) continue; pos = mlt_frame_get_position( frame ); priv->speed = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ); // WebVfx uses this to setup a consumer-stopping event handler. mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "consumer", self, 0, NULL, NULL ); // Increment the counter used for averaging processing cost count ++; // Always process audio if ( !audio_off ) { samples = mlt_sample_calculator( priv->fps, priv->frequency, priv->aud_counter++ ); mlt_frame_get_audio( frame, &audio, &priv->audio_format, &priv->frequency, &priv->channels, &samples ); } // All non-normal playback frames should be shown if ( priv->speed != 1 ) { #ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); #endif // Indicate seeking or trick-play start_pos = pos; } // If skip flag not set or frame-dropping disabled if ( !skip_next || priv->real_time == -1 ) { if ( !video_off ) { // Reset width/height - could have been changed by previous mlt_frame_get_image width = mlt_properties_get_int( properties, "width" ); height = mlt_properties_get_int( properties, "height" ); // Get the image mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-frame-render", frame, NULL ); mlt_log_timings_begin(); mlt_frame_get_image( frame, &image, &priv->image_format, &width, &height, 0 ); mlt_log_timings_end( NULL, "mlt_frame_get_image" ); } // Indicate the rendered image is available. mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); // Reset consecutively-skipped counter skipped = 0; } else // Skip image processing { // Increment the number of consecutively-skipped frames skipped++; // If too many (1 sec) consecutively-skipped frames if ( skipped > drop_max ) { // Reset cost tracker time_process = 0; count = 1; mlt_log_verbose( self, "too many frames dropped - forcing next frame\n" ); } } // Get the time to process this frame int64_t time_current = time_difference( &ante ); // If the current time is not suddenly some large amount if ( time_current < time_process / count * 20 || !time_process || count < 5 ) { // Accumulate the cost for processing this frame time_process += time_current; } else { mlt_log_debug( self, "current %"PRId64" threshold %"PRId64" count %d\n", time_current, (int64_t) (time_process / count * 20), count ); // Ignore the cost of this frame's time count--; } // Determine if we started, resumed, or seeked if ( pos != last_pos + 1 ) start_pos = pos; last_pos = pos; // Do not skip the first 20% of buffer at start, resume, or seek if ( pos - start_pos <= buffer / 5 + 1 ) { // Reset cost tracker time_process = 0; count = 1; } // Reset skip flag skip_next = 0; // Only consider skipping if the buffer level is low (or really small) if ( mlt_deque_count( priv->queue ) <= buffer / 5 + 1 && count > 1 ) { // Skip next frame if average cost exceeds frame duration. if ( time_process / count > frame_duration ) skip_next = 1; if ( skip_next ) mlt_log_debug( self, "avg usec %"PRId64" (%"PRId64"/%d) duration %d\n", time_process/count, time_process, count, frame_duration); } } // Remove the last frame mlt_frame_close( frame ); // Wipe the queue pthread_mutex_lock( &priv->queue_mutex ); while ( mlt_deque_count( priv->queue ) ) mlt_frame_close( mlt_deque_pop_back( priv->queue ) ); // Close the queue mlt_deque_close( priv->queue ); priv->queue = NULL; pthread_mutex_unlock( &priv->queue_mutex ); mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-thread-stopped", NULL ); return NULL; } /** Locate the first unprocessed frame in the queue. * * When playing with realtime behavior, we do not use the true head, but * rather an adjusted process_head. The process_head is adjusted based on * the rate of frame-dropping or recovery from frame-dropping. The idea is * that as the level of frame-dropping increases to move the process_head * closer to the tail because the frames are not completing processing prior * to their playout! Then, as frames are not dropped the process_head moves * back closer to the head of the queue so that worker threads can work * ahead of the playout point (queue head). * * \private \memberof mlt_consumer_s * \param self a consumer * \return an index into the queue */ static inline int first_unprocessed_frame( mlt_consumer self ) { consumer_private *priv = self->local; int index = priv->real_time <= 0 ? 0 : priv->process_head; while ( index < mlt_deque_count( priv->queue ) && MLT_FRAME( mlt_deque_peek( priv->queue, index ) )->is_processing ) index++; return index; } /** The worker thread procedure for parallel processing frames. * * \private \memberof mlt_consumer_s * \param arg a consumer */ static void *consumer_worker_thread( void *arg ) { // The argument is the consumer mlt_consumer self = arg; consumer_private *priv = self->local; // Get the properties of the consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); // Get the width and height int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); mlt_image_format format = priv->image_format; // 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" ); // General frame variable mlt_frame frame = NULL; uint8_t *image = NULL; if ( preview_off && preview_format != 0 ) format = preview_format; mlt_events_fire( properties, "consumer-thread-started", NULL ); // Continue to read ahead while ( priv->ahead ) { // Get the next unprocessed frame from the work queue pthread_mutex_lock( &priv->queue_mutex ); int index = first_unprocessed_frame( self ); while ( priv->ahead && index >= mlt_deque_count( priv->queue ) ) { mlt_log_debug( MLT_CONSUMER_SERVICE(self), "waiting in worker index = %d queue count = %d\n", index, mlt_deque_count( priv->queue ) ); pthread_cond_wait( &priv->queue_cond, &priv->queue_mutex ); index = first_unprocessed_frame( self ); } // Mark the frame for processing frame = mlt_deque_peek( priv->queue, index ); if ( frame ) { mlt_log_debug( MLT_CONSUMER_SERVICE(self), "worker processing index = %d frame " MLT_POSITION_FMT " queue count = %d\n", index, mlt_frame_get_position(frame), mlt_deque_count( priv->queue ) ); frame->is_processing = 1; mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( frame ) ); } pthread_mutex_unlock( &priv->queue_mutex ); // If there's no frame, we're probably stopped... if ( frame == NULL ) continue; // WebVfx uses this to setup a consumer-stopping event handler. mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "consumer", self, 0, NULL, NULL ); #ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED // All non normal playback frames should be shown if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 ) mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); #endif // Get the image if ( !video_off ) { // Fetch width/height again width = mlt_properties_get_int( properties, "width" ); height = mlt_properties_get_int( properties, "height" ); mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-frame-render", frame, NULL ); mlt_frame_get_image( frame, &image, &format, &width, &height, 0 ); } mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); mlt_frame_close( frame ); // Tell a waiting thread (non-realtime main consumer thread) that we are done. pthread_mutex_lock( &priv->done_mutex ); pthread_cond_broadcast( &priv->done_cond ); pthread_mutex_unlock( &priv->done_mutex ); } return NULL; } /** Start the read/render thread. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void consumer_read_ahead_start( mlt_consumer self ) { consumer_private *priv = self->local; if ( priv->started ) return; // We're running now priv->ahead = 1; // Create the frame queue priv->queue = mlt_deque_init( ); // Create the queue mutex pthread_mutex_init( &priv->queue_mutex, NULL ); // Create the condition pthread_cond_init( &priv->queue_cond, NULL ); // Create the read ahead mlt_thread_create( self, (thread_function_t) consumer_read_ahead_thread ); priv->started = 1; } /** Start the worker threads. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void consumer_work_start( mlt_consumer self ) { consumer_private *priv = self->local; int n = abs( priv->real_time ); pthread_t *thread; if ( priv->started ) return; thread = calloc( 1, sizeof( pthread_t ) * n ); // We're running now priv->ahead = 1; priv->threads = thread; // These keep track of the acceleration of frame dropping or recovery. priv->consecutive_dropped = 0; priv->consecutive_rendered = 0; // This is the position in the queue from which to look for a frame to process. // If we always start from the head, then we may likely not complete processing // before the frame is played out. priv->process_head = 0; // Create the queues priv->queue = mlt_deque_init(); priv->worker_threads = mlt_deque_init(); // Create the mutexes pthread_mutex_init( &priv->queue_mutex, NULL ); pthread_mutex_init( &priv->done_mutex, NULL ); // Create the conditions pthread_cond_init( &priv->queue_cond, NULL ); pthread_cond_init( &priv->done_cond, NULL ); // Create the read ahead if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES( self ), "priority" ) ) { struct sched_param priority; pthread_attr_t thread_attributes; priority.sched_priority = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self ), "priority" ); 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 ); while ( n-- ) { if ( pthread_create( thread, &thread_attributes, consumer_worker_thread, self ) < 0 ) { if ( pthread_create( thread, NULL, consumer_worker_thread, self ) == 0 ) mlt_deque_push_back( priv->worker_threads, thread ); } else { mlt_deque_push_back( priv->worker_threads, thread ); } thread++; } pthread_attr_destroy( &thread_attributes ); } else { while ( n-- ) { if ( pthread_create( thread, NULL, consumer_worker_thread, self ) == 0 ) mlt_deque_push_back( priv->worker_threads, thread ); thread++; } } priv->started = 1; } /** Stop the read/render thread. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void consumer_read_ahead_stop( mlt_consumer self ) { consumer_private *priv = self->local; // Make sure we're running int expected = 1; if ( atomic_compare_exchange_strong( &priv->started, &expected, 0 ) ) { // Inform thread to stop priv->ahead = 0; mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-stopping", NULL ); // Broadcast to the condition in case it's waiting pthread_mutex_lock( &priv->queue_mutex ); pthread_cond_broadcast( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); // Broadcast to the put condition in case it's waiting pthread_mutex_lock( &priv->put_mutex ); pthread_cond_broadcast( &priv->put_cond ); pthread_mutex_unlock( &priv->put_mutex ); // Join the thread mlt_thread_join( self ); // Destroy the frame queue mutex pthread_mutex_destroy( &priv->queue_mutex ); // Destroy the condition pthread_cond_destroy( &priv->queue_cond ); } } /** Stop the worker threads. * * \private \memberof mlt_consumer_s * \param self a consumer */ static void consumer_work_stop( mlt_consumer self ) { consumer_private *priv = self->local; // Make sure we're running int expected = 1; if ( atomic_compare_exchange_strong( &priv->started, &expected, 0 ) ) { // Inform thread to stop priv->ahead = 0; mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-stopping", NULL ); // Broadcast to the queue condition in case it's waiting pthread_mutex_lock( &priv->queue_mutex ); pthread_cond_broadcast( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); // Broadcast to the put condition in case it's waiting pthread_mutex_lock( &priv->put_mutex ); pthread_cond_broadcast( &priv->put_cond ); pthread_mutex_unlock( &priv->put_mutex ); // Broadcast to the done condition in case it's waiting pthread_mutex_lock( &priv->done_mutex ); pthread_cond_broadcast( &priv->done_cond ); pthread_mutex_unlock( &priv->done_mutex ); // Join the threads pthread_t *thread; while ( ( thread = mlt_deque_pop_back( priv->worker_threads ) ) ) pthread_join( *thread, NULL ); // Deallocate the array of threads free( priv->threads ); // Destroy the mutexes pthread_mutex_destroy( &priv->queue_mutex ); pthread_mutex_destroy( &priv->done_mutex ); // Destroy the conditions pthread_cond_destroy( &priv->queue_cond ); pthread_cond_destroy( &priv->done_cond ); // Wipe the queues while ( mlt_deque_count( priv->queue ) ) mlt_frame_close( mlt_deque_pop_back( priv->queue ) ); // Close the queues mlt_deque_close( priv->queue ); mlt_deque_close( priv->worker_threads ); mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-thread-stopped", NULL ); } } /** Flush the read/render thread's buffer. * * \public \memberof mlt_consumer_s * \param self a consumer */ void mlt_consumer_purge( mlt_consumer self ) { if ( self ) { consumer_private *priv = self->local; pthread_mutex_lock( &priv->put_mutex ); if ( priv->put ) { mlt_frame_close( priv->put ); priv->put = NULL; } pthread_cond_broadcast( &priv->put_cond ); pthread_mutex_unlock( &priv->put_mutex ); if ( self->purge ) self->purge( self ); if ( priv->started && priv->real_time ) pthread_mutex_lock( &priv->queue_mutex ); while ( priv->started && mlt_deque_count( priv->queue ) ) mlt_frame_close( mlt_deque_pop_back( priv->queue ) ); if ( priv->started && priv->real_time ) { priv->is_purge = 1; pthread_cond_broadcast( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); if ( abs( priv->real_time ) > 1 ) { pthread_mutex_lock( &priv->done_mutex ); pthread_cond_broadcast( &priv->done_cond ); pthread_mutex_unlock( &priv->done_mutex ); } } pthread_mutex_lock( &priv->put_mutex ); if ( priv->put ) { mlt_frame_close( priv->put ); priv->put = NULL; } pthread_cond_broadcast( &priv->put_cond ); pthread_mutex_unlock( &priv->put_mutex ); } } /** Use multiple worker threads and a work queue. */ static mlt_frame worker_get_frame( mlt_consumer self, mlt_properties properties ) { // Frame to return mlt_frame frame = NULL; consumer_private *priv = self->local; int threads = abs( priv->real_time ); int audio_off = mlt_properties_get_int( properties, "audio_off" ); int samples = 0; void *audio = NULL; int buffer = mlt_properties_get_int( properties, "_buffer" ); buffer = buffer > 0 ? buffer : mlt_properties_get_int( properties, "buffer" ); // This is a heuristic to determine a suitable minimum buffer size for the number of threads. int headroom = (priv->real_time < 0) ? threads : (2 + threads * threads); buffer = MAX(buffer, headroom); // Start worker threads if not already started. if ( ! priv->ahead ) { int prefill = mlt_properties_get_int( properties, "prefill" ); prefill = prefill > 0 && prefill < buffer ? prefill : buffer; set_audio_format( self ); set_image_format( self ); consumer_work_start( self ); // Fill the work queue. int i = buffer; while ( priv->ahead && i-- ) { frame = mlt_consumer_get_frame( self ); if ( frame ) { // Process the audio if ( !audio_off ) { samples = mlt_sample_calculator( priv->fps, priv->frequency, priv->aud_counter++ ); mlt_frame_get_audio( frame, &audio, &priv->audio_format, &priv->frequency, &priv->channels, &samples ); } pthread_mutex_lock( &priv->queue_mutex ); mlt_deque_push_back( priv->queue, frame ); pthread_cond_signal( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); priv->speed = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ); buffer = (priv->speed == 0) ? 1 : buffer; } } // Wait for prefill while ( priv->ahead && first_unprocessed_frame( self ) < prefill ) { pthread_mutex_lock( &priv->done_mutex ); pthread_cond_wait( &priv->done_cond, &priv->done_mutex ); pthread_mutex_unlock( &priv->done_mutex ); } priv->process_head = threads; } // mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "size %d done count %d work count %d process_head %d\n", // threads, first_unprocessed_frame( self ), mlt_deque_count( priv->queue ), priv->process_head ); // Feed the work queue while ( priv->ahead && mlt_deque_count( priv->queue ) < buffer ) { frame = mlt_consumer_get_frame( self ); if ( frame ) { // Process the audio if ( !audio_off ) { samples = mlt_sample_calculator( priv->fps, priv->frequency, priv->aud_counter++ ); mlt_frame_get_audio( frame, &audio, &priv->audio_format, &priv->frequency, &priv->channels, &samples ); } pthread_mutex_lock( &priv->queue_mutex ); mlt_deque_push_back( priv->queue, frame ); pthread_cond_signal( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); priv->speed = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ); buffer = (priv->speed == 0) ? 1 : buffer; } } // Wait if not realtime. while ( priv->ahead && priv->real_time < 0 && !priv->is_purge && !( mlt_properties_get_int( MLT_FRAME_PROPERTIES( MLT_FRAME( mlt_deque_peek_front( priv->queue ) ) ), "rendered" ) ) ) { pthread_mutex_lock( &priv->done_mutex ); pthread_cond_wait( &priv->done_cond, &priv->done_mutex ); pthread_mutex_unlock( &priv->done_mutex ); } // Get the frame from the queue. pthread_mutex_lock( &priv->queue_mutex ); frame = mlt_deque_pop_front( priv->queue ); pthread_mutex_unlock( &priv->queue_mutex ); if ( ! frame ) { priv->is_purge = 0; return frame; } // Adapt the worker process head to the runtime conditions. if ( priv->real_time > 0 ) { if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "rendered" ) ) { priv->consecutive_dropped = 0; if ( priv->process_head > threads && priv->consecutive_rendered >= priv->process_head ) priv->process_head--; else priv->consecutive_rendered++; } else { priv->consecutive_rendered = 0; if ( priv->process_head < buffer - threads && priv->consecutive_dropped > threads ) priv->process_head++; else priv->consecutive_dropped++; } // mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "dropped %d rendered %d process_head %d\n", // priv->consecutive_dropped, priv->consecutive_rendered, priv->process_head ); // Check for too many consecutively dropped frames if ( priv->consecutive_dropped > mlt_properties_get_int( properties, "drop_max" ) ) { int orig_buffer = mlt_properties_get_int( properties, "buffer" ); int prefill = mlt_properties_get_int( properties, "prefill" ); mlt_log_verbose( self, "too many frames dropped - " ); // If using a default low-latency buffer level (SDL) and below the limit if ( ( orig_buffer == 1 || prefill == 1 ) && buffer < (threads + 1) * 10 ) { // Auto-scale the buffer to compensate mlt_log_verbose( self, "increasing buffer to %d\n", buffer + threads ); mlt_properties_set_int( properties, "_buffer", buffer + threads ); priv->consecutive_dropped = priv->fps / 2; } else { // Tell the consumer to render it mlt_log_verbose( self, "forcing next frame\n" ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); priv->consecutive_dropped = 0; } } if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered") ) { int dropped = mlt_properties_get_int( properties, "drop_count" ); mlt_properties_set_int( properties, "drop_count", ++dropped ); mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "dropped video frame %d\n", dropped ); } } if ( priv->is_purge ) { priv->is_purge = 0; mlt_frame_close( frame ); frame = NULL; } return frame; } /** 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 self a consumer * \return a frame */ mlt_frame mlt_consumer_rt_frame( mlt_consumer self ) { // Frame to return mlt_frame frame = NULL; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); consumer_private *priv = self->local; // Check if the user has requested real time or not if ( priv->real_time > 1 || priv->real_time < -1 ) { // see above return worker_get_frame( self, properties ); } else if ( priv->real_time == 1 || priv->real_time == -1 ) { int size = 1; if ( priv->preroll ) { int buffer = mlt_properties_get_int( properties, "buffer" ); int prefill = mlt_properties_get_int( properties, "prefill" ); #ifndef _WIN32 consumer_read_ahead_start( self ); #endif if ( buffer > 1 && priv->speed ) size = prefill > 0 && prefill < buffer ? prefill : buffer; priv->preroll = 0; } // Get frame from queue pthread_mutex_lock( &priv->queue_mutex ); mlt_log_timings_begin(); while( priv->ahead && mlt_deque_count( priv->queue ) < size ) pthread_cond_wait( &priv->queue_cond, &priv->queue_mutex ); frame = mlt_deque_pop_front( priv->queue ); mlt_log_timings_end( NULL, "wait_for_frame_queue" ); pthread_cond_broadcast( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); if ( priv->real_time == 1 && frame && !mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered" ) ) { int dropped = mlt_properties_get_int( properties, "drop_count" ); mlt_properties_set_int( properties, "drop_count", ++dropped ); mlt_log_verbose( MLT_CONSUMER_SERVICE(self), "dropped video frame %d\n", dropped ); } } else // real_time == 0 { if ( !priv->ahead ) { priv->ahead = 1; mlt_events_fire( properties, "consumer-thread-started", NULL ); } // Get the frame in non real time frame = mlt_consumer_get_frame( self ); // This isn't true, but from the consumers perspective it is if ( frame != NULL ) { mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 ); // WebVfx uses this to setup a consumer-stopping event handler. mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "consumer", self, 0, NULL, NULL ); } } return frame; } /** Callback for the implementation to indicate a stopped condition. * * \public \memberof mlt_consumer_s * \param self a consumer */ void mlt_consumer_stopped( mlt_consumer self ) { mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( self ), "running", 0 ); mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-stopped", NULL ); mlt_event_unblock( ( ( consumer_private* ) self->local )->event_listener ); } /** Stop the consumer. * * \public \memberof mlt_consumer_s * \param self a consumer * \return true if there was an error */ int mlt_consumer_stop( mlt_consumer self ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); consumer_private *priv = self->local; // Just in case... mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping put waiting\n" ); pthread_mutex_lock( &priv->put_mutex ); priv->put_active = 0; pthread_cond_broadcast( &priv->put_cond ); pthread_mutex_unlock( &priv->put_mutex ); // Stop the consumer mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping consumer\n" ); // Cancel the read ahead threads if ( priv->started ) { // Unblock the consumer calling mlt_consumer_rt_frame pthread_mutex_lock( &priv->queue_mutex ); pthread_cond_broadcast( &priv->queue_cond ); pthread_mutex_unlock( &priv->queue_mutex ); } // Invoke the child callback if ( self->stop != NULL ) self->stop( self ); // Check if the user has requested real time or not and stop if necessary mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping read_ahead\n" ); if ( abs( priv->real_time ) == 1 ) consumer_read_ahead_stop( self ); else if ( abs( priv->real_time ) > 1 ) consumer_work_stop( self ); // 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( self ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "post" ) ); mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopped\n" ); return 0; } /** Determine if the consumer is stopped. * * \public \memberof mlt_consumer_s * \param self a consumer * \return true if the consumer is stopped */ int mlt_consumer_is_stopped( mlt_consumer self ) { // Check if the consumer is stopped if ( self && self->is_stopped ) return self->is_stopped( self ); return 0; } /** Close and destroy the consumer. * * \public \memberof mlt_consumer_s * \param self a consumer */ void mlt_consumer_close( mlt_consumer self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_CONSUMER_PROPERTIES( self ) ) <= 0 ) { // Get the childs close function void ( *consumer_close )( ) = self->close; if ( consumer_close ) { // Just in case... //mlt_consumer_stop( self ); self->close = NULL; consumer_close( self ); } else { consumer_private *priv = self->local; // Make sure it only gets called once self->parent.close = NULL; // Destroy the push mutex and condition pthread_mutex_destroy( &priv->put_mutex ); pthread_cond_destroy( &priv->put_cond ); pthread_mutex_destroy( &priv->position_mutex ); mlt_service_close( &self->parent ); free( priv ); } } } /** Get the position of the last frame shown. * * \public \memberof mlt_consumer_s * \param consumer a consumer * \return the position */ mlt_position mlt_consumer_position( mlt_consumer consumer ) { consumer_private* priv = consumer->local; pthread_mutex_lock( &priv->position_mutex ); mlt_position result = priv->position; pthread_mutex_unlock( &priv->position_mutex ); return result; } static void transmit_thread_create( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener ) listener( owner, self, (void**) args[0] /* handle */, (int*) args[1] /* priority */, (thread_function_t) args[2], (void*) args[3] /* data */ ); } static void mlt_thread_create( mlt_consumer self, thread_function_t function ) { consumer_private *priv = self->local; mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES( self ), "priority" ) ) { struct sched_param priority; priority.sched_priority = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self ), "priority" ); if ( mlt_events_fire( properties, "consumer-thread-create", &priv->ahead_thread, &priority.sched_priority, function, self, NULL ) < 1 ) { 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 ); priv->ahead_thread = malloc( sizeof( pthread_t ) ); pthread_t *handle = priv->ahead_thread; if ( pthread_create( ( pthread_t* ) &( *handle ), &thread_attributes, function, self ) < 0 ) pthread_create( ( pthread_t* ) &( *handle ), NULL, function, self ); pthread_attr_destroy( &thread_attributes ); } } else { int priority = -1; if ( mlt_events_fire( properties, "consumer-thread-create", &priv->ahead_thread, &priority, function, self, NULL ) < 1 ) { priv->ahead_thread = malloc( sizeof( pthread_t ) ); pthread_t *handle = priv->ahead_thread; pthread_create( ( pthread_t* ) &( *handle ), NULL, function, self ); } } } static void transmit_thread_join( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener ) listener( owner, self, (void*) args[0] /* handle */ ); } static void mlt_thread_join( mlt_consumer self ) { consumer_private *priv = self->local; if ( mlt_events_fire( MLT_CONSUMER_PROPERTIES(self), "consumer-thread-join", priv->ahead_thread, NULL ) < 1 ) { pthread_t *handle = priv->ahead_thread; pthread_join( *handle, NULL ); free( priv->ahead_thread ); } priv->ahead_thread = NULL; } mlt-6.20.0/src/framework/mlt_consumer.h000066400000000000000000000165561362234133600200570ustar00rootroot00000000000000/** * \file mlt_consumer.h * \brief abstraction for all consumer services * \see mlt_consumer_s * * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 prefill the number of frames to render before commencing * output when real_time <> 0, defaults to the size of buffer * \properties \em drop_max the maximum number of consecutively dropped frames, defaults to 5 * \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 channel_layout the layout of the audio channels, defaults to auto. * other options include: mono, stereo, 5.1, 7.1, etc. * \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 fire this immediately after showing a frame * or when a frame should be shown (if audio-only consumer). * \event \em consumer-frame-render The base class fires this immediately before rendering a frame. * \event \em consumer-thread-create Override the implementation of creating and * starting a thread by listening and responding to this (real_time 1 or -1 only). * \event \em consumer-thread-join Override the implementation of waiting and * joining a terminated thread by listening and responding to this (real_time 1 or -1 only). * \event \em consumer-thread-started The base class fires when beginning execution of a rendering thread. * \event \em consumer-thread-stopped The base class fires when a rendering thread has ended. * \event \em consumer-stopping This is fired when stop was requested, but before render threads are joined. * \event \em consumer-stopped This is fired when the subclass implementation calls mlt_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 * \properties \em priority the OS scheduling priority for the render threads when real_time is not 0. * \properties \em top_field_first when not progressive, whether interlace field order is top-field-first, defaults to 0. * Set this to -1 if the consumer does not care about the field order. * \properties \em mlt_image_format the image format to request in rendering threads, defaults to yuv422 * \properties \em mlt_audio_format the audio format to request in rendering threads, defaults to S16 * \properties \em audio_off set non-zero to disable audio processing * \properties \em video_off set non-zero to disable video processing * \properties \em drop_count the number of video frames not rendered since starting consumer */ 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 ); /** Purge the consumer of buffered data (virtual function). * * \param mlt_consumer a consumer */ void ( *purge )( 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 */ }; #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 ); extern mlt_position mlt_consumer_position( mlt_consumer ); #endif mlt-6.20.0/src/framework/mlt_deque.c000066400000000000000000000215141362234133600173100ustar00rootroot00000000000000/** * \file mlt_deque.c * \brief double ended queue * \see mlt_deque_s * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // Local header files #include "mlt_deque.h" // System header files #include #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; atomic_int count; }; /** Create a deque. * * \public \memberof mlt_deque_s * \return a new deque */ mlt_deque mlt_deque_init( ) { mlt_deque self = calloc( 1, sizeof( struct mlt_deque_s ) ); return self; } /** Return the number of items in the deque. * * \public \memberof mlt_deque_s * \param self a deque * \return the number of items */ int mlt_deque_count( mlt_deque self ) { if ( self ) return self->count; else return 0; } /** Allocate space on the deque. * * \private \memberof mlt_deque_s * \param self a deque * \return true if there was an error */ static int mlt_deque_allocate( mlt_deque self ) { if ( self->count == self->size ) { self->list = realloc( self->list, sizeof( deque_entry ) * ( self->size + 20 ) ); self->size += 20; } return self->list == NULL; } /** Push an item to the end. * * \public \memberof mlt_deque_s * \param self a deque * \param item an opaque pointer * \return true if there was an error */ int mlt_deque_push_back( mlt_deque self, void *item ) { int error = mlt_deque_allocate( self ); if ( error == 0 ) self->list[ self->count ++ ].addr = item; return error; } /** Pop an item. * * \public \memberof mlt_deque_s * \param self a pointer * \return an opaque pointer */ void *mlt_deque_pop_back( mlt_deque self ) { return self->count > 0 ? self->list[ -- self->count ].addr : NULL; } /** Queue an item at the start. * * \public \memberof mlt_deque_s * \param self a deque * \param item an opaque pointer * \return true if there was an error */ int mlt_deque_push_front( mlt_deque self, void *item ) { int error = mlt_deque_allocate( self ); if ( error == 0 ) { memmove( &self->list[ 1 ], self->list, ( self->count ++ ) * sizeof( deque_entry ) ); self->list[ 0 ].addr = item; } return error; } /** Remove an item from the start. * * \public \memberof mlt_deque_s * \param self a pointer * \return an opaque pointer */ void *mlt_deque_pop_front( mlt_deque self ) { void *item = NULL; if ( self->count > 0 ) { item = self->list[ 0 ].addr; memmove( self->list, &self->list[ 1 ], ( -- self->count ) * sizeof( deque_entry ) ); } return item; } /** Inquire on item at back of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return an opaque pointer */ void *mlt_deque_peek_back( mlt_deque self ) { return self->count > 0 ? self->list[ self->count - 1 ].addr : NULL; } /** Inquire on item at front of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return an opaque pointer */ void *mlt_deque_peek_front( mlt_deque self ) { return self->count > 0 ? self->list[ 0 ].addr : NULL; } /** Inquire on item in deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \param index the position in the deque * \return an opaque pointer */ void *mlt_deque_peek( mlt_deque self, int index ) { return self->count > index ? self->list[ index ].addr : NULL; } /** Insert an item in a sorted fashion. * * Optimized for the equivalent of \p mlt_deque_push_back. * * \public \memberof mlt_deque_s * \param self a deque * \param item an opaque pointer * \param cmp a function pointer to the comparison function * \return true if there was an error */ int mlt_deque_insert( mlt_deque self, void *item, mlt_deque_compare cmp ) { int error = mlt_deque_allocate( self ); if ( error == 0 ) { int n = self->count + 1; while ( --n ) if ( cmp( item, self->list[ n - 1 ].addr ) >= 0 ) break; memmove( &self->list[ n + 1 ], &self->list[ n ], ( self->count - n ) * sizeof( deque_entry ) ); self->list[ n ].addr = item; self->count++; } return error; } /** Push an integer to the end. * * \public \memberof mlt_deque_s * \param self a deque * \param item an integer * \return true if there was an error */ int mlt_deque_push_back_int( mlt_deque self, int item ) { int error = mlt_deque_allocate( self ); if ( error == 0 ) self->list[ self->count ++ ].value = item; return error; } /** Pop an integer. * * \public \memberof mlt_deque_s * \param self a deque * \return an integer */ int mlt_deque_pop_back_int( mlt_deque self ) { return self->count > 0 ? self->list[ -- self->count ].value : 0; } /** Queue an integer at the start. * * \public \memberof mlt_deque_s * \param self a deque * \param item an integer * \return true if there was an error */ int mlt_deque_push_front_int( mlt_deque self, int item ) { int error = mlt_deque_allocate( self ); if ( error == 0 ) { memmove( &self->list[ 1 ], self->list, ( self->count ++ ) * sizeof( deque_entry ) ); self->list[ 0 ].value = item; } return error; } /** Remove an integer from the start. * * \public \memberof mlt_deque_s * \param self a deque * \return an integer */ int mlt_deque_pop_front_int( mlt_deque self ) { int item = 0; if ( self->count > 0 ) { item = self->list[ 0 ].value; memmove( self->list, &self->list[ 1 ], ( -- self->count ) * sizeof( deque_entry ) ); } return item; } /** Inquire on an integer at back of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return an integer */ int mlt_deque_peek_back_int( mlt_deque self ) { return self->count > 0 ? self->list[ self->count - 1 ].value : 0; } /** Inquire on an integer at front of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return an integer */ int mlt_deque_peek_front_int( mlt_deque self ) { return self->count > 0 ? self->list[ 0 ].value : 0; } /** Push a double float to the end. * * \public \memberof mlt_deque_s * \param self a deque * \param item a double float * \return true if there was an error */ int mlt_deque_push_back_double( mlt_deque self, double item ) { int error = mlt_deque_allocate( self ); if ( error == 0 ) self->list[ self->count ++ ].floating = item; return error; } /** Pop a double float. * * \public \memberof mlt_deque_s * \param self a deque * \return a double float */ double mlt_deque_pop_back_double( mlt_deque self ) { return self->count > 0 ? self->list[ -- self->count ].floating : 0; } /** Queue a double float at the start. * * \public \memberof mlt_deque_s * \param self a deque * \param item a double float * \return true if there was an error */ int mlt_deque_push_front_double( mlt_deque self, double item ) { int error = mlt_deque_allocate( self ); if ( error == 0 ) { memmove( &self->list[ 1 ], self->list, ( self->count ++ ) * sizeof( deque_entry ) ); self->list[ 0 ].floating = item; } return error; } /** Remove a double float from the start. * * \public \memberof mlt_deque_s * \param self a deque * \return a double float */ double mlt_deque_pop_front_double( mlt_deque self ) { double item = 0; if ( self->count > 0 ) { item = self->list[ 0 ].floating; memmove( self->list, &self->list[ 1 ], ( -- self->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 self a deque * \return a double float */ double mlt_deque_peek_back_double( mlt_deque self ) { return self->count > 0 ? self->list[ self->count - 1 ].floating : 0; } /** Inquire on a double float at front of deque but don't remove. * * \public \memberof mlt_deque_s * \param self a deque * \return a double float */ double mlt_deque_peek_front_double( mlt_deque self ) { return self->count > 0 ? self->list[ 0 ].floating : 0; } /** Destroy the queue. * * \public \memberof mlt_deque_s * \param self a deque */ void mlt_deque_close( mlt_deque self ) { free( self->list ); free( self ); } mlt-6.20.0/src/framework/mlt_deque.h000066400000000000000000000047561362234133600173260ustar00rootroot00000000000000/** * \file mlt_deque.h * \brief double ended queue * \see mlt_deque_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_DEQUE_H #define MLT_DEQUE_H #include "mlt_types.h" /** The callback function used to compare items for insert sort. * * \public \memberof mlt_deque_s * \param a the first object * \param b the second object * \returns 0 if equal, < 0 if a < b, or > 0 if a > b */ typedef int ( *mlt_deque_compare )( void *a, void *b ); 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 void *mlt_deque_peek( mlt_deque self, int index ); extern int mlt_deque_insert( mlt_deque self, void *item, mlt_deque_compare ); 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 mlt-6.20.0/src/framework/mlt_events.c000066400000000000000000000300331362234133600175050ustar00rootroot00000000000000/** * \file mlt_events.c * \brief event handling * \see mlt_events_struct * * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 self event. * * \public \memberof mlt_event_struct * \param self an event */ void mlt_event_inc_ref( mlt_event self ) { if ( self != NULL ) self->ref_count ++; } /** Increment the block count on self event. * * \public \memberof mlt_event_struct * \param self an event */ void mlt_event_block( mlt_event self ) { if ( self != NULL && self->owner != NULL ) self->block_count ++; } /** Decrement the block count on self event. * * \public \memberof mlt_event_struct * \param self an event */ void mlt_event_unblock( mlt_event self ) { if ( self != NULL && self->owner != NULL ) self->block_count --; } /** Close self event. * * \public \memberof mlt_event_struct * \param self an event */ void mlt_event_close( mlt_event self ) { if ( self != NULL ) { if ( -- self->ref_count == 1 ) self->owner = NULL; if ( self->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( self ); } } } /* Forward declaration to private functions. */ static mlt_events mlt_events_fetch( mlt_properties ); static void mlt_events_close( mlt_events ); /** Initialise the events structure. * * \public \memberof mlt_events_struct * \param self a properties list */ void mlt_events_init( mlt_properties self ) { mlt_events events = mlt_events_fetch( self ); if (!events && self) { events = calloc( 1, sizeof( struct mlt_events_struct ) ); if (events) { events->list = mlt_properties_new( ); events->owner = self; mlt_properties_set_data( self, "_events", events, 0, ( mlt_destructor )mlt_events_close, NULL ); } } } /** Register an event and transmitter. * * \public \memberof mlt_events_struct * \param self 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 self, const char *id, mlt_transmitter transmitter ) { int error = 1; mlt_events events = mlt_events_fetch( self ); 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 self a properties list * \param id the name of an event * \return the number of listeners */ int mlt_events_fire( mlt_properties self, const char *id, ... ) { int result = 0; mlt_events events = mlt_events_fetch( self ); 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->owner, event->service, args ); else event->listener( event->owner->owner, event->service ); ++result; } } } } return result; } /** Register a listener. * * \public \memberof mlt_events_struct * \param self 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 self, void *service, const char *id, mlt_listener listener ) { mlt_event event = NULL; mlt_events events = mlt_events_fetch( self ); 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 self a properties list * \param service an opaque pointer */ void mlt_events_block( mlt_properties self, void *service ) { mlt_events events = mlt_events_fetch( self ); 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 self a properties list * \param service an opaque pointer */ void mlt_events_unblock( mlt_properties self, void *service ) { mlt_events events = mlt_events_fetch( self ); 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 self a properties list * \param service an opaque pointer */ void mlt_events_disconnect( mlt_properties self, void *service ) { mlt_events events = mlt_events_fetch( self ); 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 { pthread_cond_t cond; pthread_mutex_t mutex; } condition_pair; /** The event listener callback for the wait functions. * * \private \memberof mlt_events_struct * \param self a properties list * \param pair a condition pair */ static void mlt_events_listen_for( mlt_properties self, condition_pair *pair ) { pthread_mutex_lock( &pair->mutex ); pthread_cond_signal( &pair->cond ); pthread_mutex_unlock( &pair->mutex ); } /** Prepare to wait for an event. * * \public \memberof mlt_events_struct * \param self 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 self, const char *id ) { condition_pair *pair = malloc( sizeof( condition_pair ) ); pthread_cond_init( &pair->cond, NULL ); pthread_mutex_init( &pair->mutex, NULL ); pthread_mutex_lock( &pair->mutex ); return mlt_events_listen( self, pair, id, ( mlt_listener )mlt_events_listen_for ); } /** Wait for an event. * * \public \memberof mlt_events_struct * \param self a properties list * \param event an event */ void mlt_events_wait_for( mlt_properties self, 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 self a properties list * \param event an event */ void mlt_events_close_wait_for( mlt_properties self, mlt_event event ) { if ( event != NULL ) { condition_pair *pair = event->service; event->owner = NULL; pthread_mutex_unlock( &pair->mutex ); pthread_mutex_destroy( &pair->mutex ); pthread_cond_destroy( &pair->cond ); free( pair ); } } /** Fetch the events object. * * \private \memberof mlt_events_struct * \param self a properties list * \return an events object */ static mlt_events mlt_events_fetch( mlt_properties self ) { mlt_events events = NULL; if ( self != NULL ) events = mlt_properties_get_data( self, "_events", NULL ); return events; } /** 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 ); } } mlt-6.20.0/src/framework/mlt_events.h000066400000000000000000000045641362234133600175240ustar00rootroot00000000000000/** * \file mlt_events.h * \brief event handling * \see mlt_events_struct * * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 int 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 mlt-6.20.0/src/framework/mlt_factory.c000066400000000000000000000374341362234133600176640ustar00rootroot00000000000000/** * \file mlt_factory.c * \brief the factory method interfaces * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt.h" #include "mlt_repository.h" #include #include #include #include #include /** the default subdirectory of the datadir for holding presets */ #define PRESETS_DIR "/presets" #ifdef _WIN32 # include # ifdef PREFIX_LIB # undef PREFIX_LIB # endif # ifdef PREFIX_DATA # undef PREFIX_DATA # endif /** the default subdirectory of the libdir for holding modules (plugins) */ # define PREFIX_LIB "\\lib\\mlt" /** the default subdirectory of the install prefix for holding module (plugin) data */ # define PREFIX_DATA "\\share\\mlt" #elif defined(__APPLE__) && defined(RELOCATABLE) # include # ifdef PREFIX_LIB # undef PREFIX_LIB # endif # ifdef PREFIX_DATA # undef PREFIX_DATA # endif /** the default subdirectory of the libdir for holding modules (plugins) */ # define PREFIX_LIB "/PlugIns/mlt" /** the default subdirectory of the install prefix for holding module (plugin) data */ # define PREFIX_DATA "/Resources/mlt" #endif /** 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 self * \param args */ static void mlt_factory_create_request( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener != NULL ) listener( owner, self, ( char * )args[ 0 ], ( char * )args[ 1 ], ( mlt_service * )args[ 2 ] ); } /** the -create-done event transmitter * * \param listener * \param owner * \param self * \param args */ static void mlt_factory_create_done( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener != NULL ) listener( owner, self, ( char * )args[ 0 ], ( char * )args[ 1 ], ( mlt_service )args[ 2 ] ); } #if defined(_WIN32) || (defined(__APPLE__) && defined(RELOCATABLE)) // Replacement for buggy dirname() on some systems. // https://github.com/mltframework/mlt/issues/285 static char* mlt_dirname( char *path ) { if ( path && strlen( path ) ) { char *dirsep = strrchr( path, '/' ); // Handle back slash on Windows. if ( !dirsep ) dirsep = strrchr( path, '\\' ); // Handle trailing slash. if ( dirsep == &path[ strlen( path ) - 1 ] ) { dirsep[0] = '\0'; dirsep = strrchr( path, '/' ); if ( !dirsep ) dirsep = strrchr( path, '\\' ); } // Truncate string at last directory separator. if ( dirsep ) dirsep[0] = '\0'; } return path; } #endif /** Construct the repository and factories. * * \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 ) { // Load the system locales const char* locale = ""; #if defined(_WIN32) if (getenv("LC_ALL")) locale = getenv("LC_ALL"); #endif setlocale( LC_ALL, locale ); if ( ! global_properties ) global_properties = mlt_properties_new( ); // Allow property refresh on a subsequent initialisation if ( global_properties ) { 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" ), "loader" ); mlt_properties_set_or_default( global_properties, "MLT_CONSUMER", getenv( "MLT_CONSUMER" ), "sdl2" ); 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 ); #if defined(_WIN32) char path[1024]; DWORD size = sizeof( path ); GetModuleFileName( NULL, path, size ); #ifndef NODEPLOY char *appdir = mlt_dirname( strdup( path ) ); #else char *appdir = mlt_dirname( mlt_dirname( strdup( path ) ) ); #endif mlt_properties_set( global_properties, "MLT_APPDIR", appdir ); free( appdir ); #elif defined(__APPLE__) && defined(RELOCATABLE) char path[1024]; uint32_t size = sizeof( path ); _NSGetExecutablePath( path, &size ); char *appdir = mlt_dirname( mlt_dirname( strdup( path ) ) ); mlt_properties_set( global_properties, "MLT_APPDIR", appdir ); free( appdir ); #endif } // Only initialise once if ( mlt_directory == NULL ) { #if !defined(_WIN32) && !(defined(__APPLE__) && defined(RELOCATABLE)) // Allow user overrides if ( directory == NULL || !strcmp( directory, "" ) ) directory = getenv( "MLT_REPOSITORY" ); #endif // Store the prefix for later retrieval #if defined(_WIN32) || (defined(__APPLE__) && defined(RELOCATABLE)) if ( directory ) { mlt_directory = strdup( directory ); } else { char *exedir = mlt_environment( "MLT_APPDIR" ); size_t size = strlen( exedir ); if ( global_properties && !getenv( "MLT_DATA" ) ) { mlt_directory = calloc( 1, size + strlen( PREFIX_DATA ) + 1 ); strcpy( mlt_directory, exedir ); strcat( mlt_directory, PREFIX_DATA ); mlt_properties_set( global_properties, "MLT_DATA", mlt_directory ); free( mlt_directory ); } mlt_directory = calloc( 1, size + strlen( PREFIX_LIB ) + 1 ); strcpy( mlt_directory, exedir ); strcat( mlt_directory, PREFIX_LIB ); } #else // If no directory is specified, default to install directory if ( directory == NULL ) directory = PREFIX_LIB; mlt_directory = strdup( directory ); #endif // 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 repository of services repository = mlt_repository_init( mlt_directory ); // Force a clean up when app closes atexit( mlt_factory_close ); } if ( global_properties ) { char *path = getenv( "MLT_PRESETS_PATH" ); if ( path ) { mlt_properties_set( global_properties, "MLT_PRESETS_PATH", path ); } else { path = malloc( strlen( mlt_environment( "MLT_DATA" ) ) + strlen( PRESETS_DIR ) + 1 ); strcpy( path, mlt_environment( "MLT_DATA" ) ); strcat( path, PRESETS_DIR ); mlt_properties_set( global_properties, "MLT_PRESETS_PATH", path ); free( path ); } } return repository; } /** Fetch the repository. * * \return the global repository object */ mlt_repository mlt_factory_repository( ) { 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. * * If you give NULL to \p service, then it will use core module's special * "loader"producer to load \p resource. One can override this default producer * by setting the environment variable MLT_PRODUCER. * * \param profile the \p mlt_profile to use * \param service the name of the producer (optional, defaults to MLT_PRODUCER) * \param resource 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 *resource ) { 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, resource, &obj, NULL ); // Try to instantiate via the specified service if ( obj == NULL ) { obj = mlt_repository_create( repository, profile, producer_type, service, resource ); mlt_events_fire( event_object, "producer-create-done", service, resource, 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 ); } if ( obj == NULL ) { if ( !strcmp( service, "sdl2" ) ) { service = "sdl"; obj = mlt_repository_create( repository, profile, consumer_type, service, input ); } else if ( !strcmp( service, "sdl_audio" ) ) { service = "sdl2_audio"; obj = mlt_repository_create( repository, profile, consumer_type, service, input ); } } if ( obj != NULL ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( obj ); mlt_events_fire( event_object, "consumer-create-done", service, input, obj, NULL ); 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 ); event_object = NULL; #if !defined(_WIN32) // XXX something in here is causing Shotcut/Win32 to not exit completely // under certain conditions: e.g. play a playlist. mlt_properties_close( global_properties ); global_properties = NULL; #endif if ( repository ) { mlt_repository_close( repository ); repository = NULL; } free( mlt_directory ); mlt_directory = NULL; mlt_pool_close( ); } } mlt_properties mlt_global_properties( ) { return global_properties; } mlt-6.20.0/src/framework/mlt_factory.h000066400000000000000000000064571362234133600176720ustar00rootroot00000000000000/** * \file mlt_factory.h * \brief the factory method interfaces * * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_FACTORY_H #define MLT_FACTORY_H #include "mlt_types.h" #include "mlt_profile.h" #include "mlt_repository.h" /** * \envvar \em MLT_PRODUCER the name of a default producer often used by other services, defaults to "loader" * \envvar \em MLT_CONSUMER the name of a default consumer, defaults to "sdl2" followed by "sdl" * \envvar \em MLT_TEST_CARD the name of a producer or file to be played when nothing is available (all tracks blank) * \envvar \em MLT_DATA overrides the default full path to the MLT and module supplemental data files, defaults to \p PREFIX_DATA * \envvar \em MLT_PROFILE selects the default mlt_profile_s, defaults to "dv_pal" * \envvar \em MLT_REPOSITORY overrides the default location of the plugin modules, defaults to \p PREFIX_LIB. * MLT_REPOSITORY is ignored on Windows and OS X relocatable builds. * \envvar \em MLT_PRESETS_PATH overrides the default full path to the properties preset files, defaults to \p MLT_DATA/presets * \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 mlt_repository mlt_factory_repository(); 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 *service, const void *resource ); 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 mlt-6.20.0/src/framework/mlt_field.c000066400000000000000000000147071362234133600172760ustar00rootroot00000000000000/** * \file mlt_field.c * \brief a field for planting multiple transitions and filters * \see mlt_field_s * * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 self = calloc( 1, sizeof( struct mlt_field_s ) ); // Initialise it if ( self != NULL ) { // Construct a multitrack self->multitrack = mlt_multitrack_init( ); // Construct a tractor self->tractor = mlt_tractor_init( ); // The first plant will be connected to the mulitrack self->producer = MLT_MULTITRACK_SERVICE( self->multitrack ); // Connect the tractor to the multitrack mlt_tractor_connect( self->tractor, self->producer ); } // Return self return self; } /** 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 self = calloc( 1, sizeof( struct mlt_field_s ) ); // Initialise it if ( self != NULL ) { // Construct a multitrack self->multitrack = multitrack; // Construct a tractor self->tractor = tractor; // The first plant will be connected to the mulitrack self->producer = MLT_MULTITRACK_SERVICE( self->multitrack ); // Connect the tractor to the multitrack mlt_tractor_connect( self->tractor, self->producer ); } // Return self return self; } /** Get the service associated to this field. * * \public \memberof mlt_field_s * \param self a field * \return the tractor as a service */ mlt_service mlt_field_service( mlt_field self ) { return MLT_TRACTOR_SERVICE( self->tractor ); } /** Get the multitrack. * * \public \memberof mlt_field_s * \param self a field * \return the multitrack */ mlt_multitrack mlt_field_multitrack( mlt_field self ) { return self != NULL ? self->multitrack : NULL; } /** Get the tractor. * * \public \memberof mlt_field_s * \param self a field * \return the tractor */ mlt_tractor mlt_field_tractor( mlt_field self ) { return self != NULL ? self->tractor : NULL; } /** Get the properties associated to this field. * * \public \memberof mlt_field_s * \param self a field * \return a properties list */ mlt_properties mlt_field_properties( mlt_field self ) { return MLT_SERVICE_PROPERTIES( mlt_field_service( self ) ); } /** Plant a filter. * * \public \memberof mlt_field_s * \param self 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 self, mlt_filter that, int track ) { // Connect the filter to the last producer int result = mlt_filter_connect( that, self->producer, track ); // If successful, then we'll use this for connecting in the future if ( result == 0 ) { // This is now the new producer self->producer = MLT_FILTER_SERVICE( that ); // Reconnect tractor to new producer mlt_tractor_connect( self->tractor, self->producer ); // Fire an event mlt_events_fire( mlt_field_properties( self ), "service-changed", NULL ); } return result; } /** Plant a transition. * * \public \memberof mlt_field_s * \param self 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 self, mlt_transition that, int a_track, int b_track ) { // Connect the transition to the last producer int result = mlt_transition_connect( that, self->producer, a_track, b_track ); // If successful, then we'll use self for connecting in the future if ( result == 0 ) { // This is now the new producer self->producer = MLT_TRANSITION_SERVICE( that ); // Reconnect tractor to new producer mlt_tractor_connect( self->tractor, self->producer ); // Fire an event mlt_events_fire( mlt_field_properties( self ), "service-changed", NULL ); } return result; } /** Close the field. * * \public \memberof mlt_field_s * \param self a field */ void mlt_field_close( mlt_field self ) { if ( self != NULL && mlt_properties_dec_ref( mlt_field_properties( self ) ) <= 0 ) { //mlt_tractor_close( self->tractor ); //mlt_multitrack_close( self->multitrack ); free( self ); } } /** 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 ); } mlt-6.20.0/src/framework/mlt_field.h000066400000000000000000000031551362234133600172760ustar00rootroot00000000000000/** * \file mlt_field.h * \brief a field for planting multiple transitions and services * \see mlt_field_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 mlt-6.20.0/src/framework/mlt_filter.c000066400000000000000000000246771362234133600175070ustar00rootroot00000000000000/** * \file mlt_filter.c * \brief abstraction for all filter services * \see mlt_filter_s * * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_filter.h" #include "mlt_frame.h" #include "mlt_producer.h" #include #include #include static int filter_get_frame( mlt_service self, mlt_frame_ptr frame, int index ); /** Initialize a new filter. * * \public \memberof mlt_filter_s * \param self a filter * \param child the object of a subclass * \return true if there was an error */ int mlt_filter_init( mlt_filter self, void *child ) { mlt_service service = &self->parent; memset( self, 0, sizeof( struct mlt_filter_s ) ); self->child = child; if ( mlt_service_init( service, self ) == 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 = self; mlt_properties_set( properties, "mlt_type", "filter" ); mlt_properties_set_position( properties, "in", 0 ); mlt_properties_set_position( properties, "out", 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 self = calloc( 1, sizeof( struct mlt_filter_s ) ); if ( self != NULL && mlt_filter_init( self, NULL ) == 0 ) { return self; } else { free(self); return NULL; } } /** Get the service class interface. * * \public \memberof mlt_filter_s * \param self a filter * \return the service parent class * \see MLT_FILTER_SERVICE */ mlt_service mlt_filter_service( mlt_filter self ) { return self != NULL ? &self->parent : NULL; } /** Get the filter properties. * * \public \memberof mlt_filter_s * \param self a filter * \return the properties list for the filter * \see MLT_FILTER_PROPERTIES */ mlt_properties mlt_filter_properties( mlt_filter self ) { return MLT_SERVICE_PROPERTIES( MLT_FILTER_SERVICE( self ) ); } /** 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 self 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 self, mlt_service producer, int index ) { int ret = mlt_service_connect_producer( &self->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( &self->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 self 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 self, mlt_position in, mlt_position out ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->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 self a filter * \return true on error */ int mlt_filter_get_track( mlt_filter self ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent ); return mlt_properties_get_int( properties, "track" ); } /** Get the in point. * * \public \memberof mlt_filter_s * \param self a filter * \return the start time for the filter relative to the producer */ mlt_position mlt_filter_get_in( mlt_filter self ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent ); return mlt_properties_get_position( properties, "in" ); } /** Get the out point. * * \public \memberof mlt_filter_s * \param self a filter * \return the ending time for the filter relative to the producer */ mlt_position mlt_filter_get_out( mlt_filter self ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent ); return mlt_properties_get_position( properties, "out" ); } /** Get the duration. * * \public \memberof mlt_filter_s * \param self a filter * \return the duration or zero if unlimited */ mlt_position mlt_filter_get_length( mlt_filter self ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent ); mlt_position in = mlt_properties_get_position( properties, "in" ); mlt_position out = mlt_properties_get_position( properties, "out" ); return ( out > 0 ) ? ( out - in + 1 ) : 0; } /** Get the duration. * * This version works with filters with no explicit in and out by getting the * length of the frame's producer. * * \public \memberof mlt_filter_s * \param self a filter * \param frame a frame from which to get its producer * \return the duration or zero if unlimited */ mlt_position mlt_filter_get_length2( mlt_filter self, mlt_frame frame ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent ); mlt_position in = mlt_properties_get_position( properties, "in" ); mlt_position out = mlt_properties_get_position( properties, "out" ); if ( out == 0 && frame ) { // If always active, use the frame's producer mlt_producer producer = mlt_frame_get_original_producer( frame ); if ( producer ) { producer = mlt_producer_cut_parent( producer ); in = mlt_producer_get_in( producer ); out = mlt_producer_get_out( producer ); } } return ( out > 0 ) ? ( out - in + 1 ) : 0; } /** Get the position within the filter. * * The position is relative to the in point. * This will only be valid once mlt_filter_process is called. * * \public \memberof mlt_filter_s * \param self a filter * \param frame a frame * \return the position */ mlt_position mlt_filter_get_position( mlt_filter self, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( self ); mlt_position in = mlt_properties_get_position( properties, "in" ); const char *unique_id = mlt_properties_get( properties, "_unique_id" ); char name[64]; // Make the properties key from unique id snprintf( name, sizeof(name), "pos.%s", unique_id ); return mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), name ) - in; } /** Get the percent complete. * * This will only be valid once mlt_filter_process is called. * * \public \memberof mlt_filter_s * \param self a filter * \param frame a frame * \return the progress in the range 0.0 to 1.0 */ double mlt_filter_get_progress( mlt_filter self, mlt_frame frame ) { double position = mlt_filter_get_position( self, frame ); double length = mlt_filter_get_length2( self, frame ); if (length > 1) return position / (length - 1); else return 1.0; } /** Process the frame. * * When fetching the frame position in a subclass process method, the frame's * position is relative to the filter's producer - not the filter's in point * or timeline. * * \public \memberof mlt_filter_s * \param self a filter * \param frame a frame * \return a frame */ mlt_frame mlt_filter_process( mlt_filter self, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( self ); int disable = mlt_properties_get_int( properties, "disable" ); const char *unique_id = mlt_properties_get( properties, "_unique_id" ); mlt_position position = mlt_frame_get_position( frame ); char name[64]; // Make the properties key from unique id snprintf( name, sizeof(name), "pos.%s", unique_id ); // Save the position on the frame mlt_properties_set_position( MLT_FRAME_PROPERTIES( frame ), name, position ); if ( disable || !self || !self->process ) { return frame; } else { // Add a reference to this filter on the frame mlt_properties_inc_ref( MLT_FILTER_PROPERTIES(self) ); snprintf( name, sizeof(name), "filter.%s", unique_id ); name[sizeof(name) -1] = '\0'; mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), name, self, 0, (mlt_destructor) mlt_filter_close, NULL ); return self->process( self, 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 self = service->child; // Get coords in/out/track int track = mlt_filter_get_track( self ); int in = mlt_filter_get_in( self ); int out = mlt_filter_get_out( self ); // Get the producer this is connected to mlt_service producer = mlt_service_producer( &self->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( self, *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 self a filter */ void mlt_filter_close( mlt_filter self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_FILTER_PROPERTIES( self ) ) <= 0 ) { if ( self->close != NULL ) { self->close( self ); } else { self->parent.close = NULL; mlt_service_close( &self->parent ); } free( self ); } } mlt-6.20.0/src/framework/mlt_filter.h000066400000000000000000000054561362234133600175060ustar00rootroot00000000000000/** * \file mlt_filter.h * \brief abstraction for all filter services * \see mlt_filter_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 * \properties \em service a reference to the service to which this filter is attached. * \properties \em disable Set this to disable the filter while keeping it in the object model. * Currently this is not cleared when the filter is detached. */ 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 mlt_position mlt_filter_get_length( mlt_filter self ); extern mlt_position mlt_filter_get_length2( mlt_filter self, mlt_frame frame ); extern mlt_position mlt_filter_get_position( mlt_filter self, mlt_frame frame ); extern double mlt_filter_get_progress( mlt_filter self, mlt_frame frame ); extern void mlt_filter_close( mlt_filter ); #endif mlt-6.20.0/src/framework/mlt_frame.c000066400000000000000000001244611362234133600173040ustar00rootroot00000000000000/** * \file mlt_frame.c * \brief interface for all frame classes * \see mlt_frame_s * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_frame.h" #include "mlt_producer.h" #include "mlt_factory.h" #include "mlt_profile.h" #include "mlt_log.h" #include #include #include /** Construct a frame object. * * \public \memberof mlt_frame_s * \param service the pointer to any service that can provide access to the profile * \return a frame object on success or NULL if there was an allocation error */ mlt_frame mlt_frame_init( mlt_service service ) { // Allocate a frame mlt_frame self = calloc( 1, sizeof( struct mlt_frame_s ) ); if ( self != NULL ) { mlt_profile profile = mlt_service_profile( service ); // Initialise the properties mlt_properties properties = &self->parent; mlt_properties_init( properties, self ); // 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_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 self->stack_image = mlt_deque_init( ); self->stack_audio = mlt_deque_init( ); self->stack_service = mlt_deque_init( ); } return self; } /** Get a frame's properties. * * \public \memberof mlt_frame_s * \param self a frame * \return the frame's properties or NULL if an invalid frame is supplied */ mlt_properties mlt_frame_properties( mlt_frame self ) { return self != NULL ? &self->parent : NULL; } /** Determine if the frame will produce a test card image. * * \public \memberof mlt_frame_s * \param self a frame * \return true (non-zero) if this will produce from a test card */ int mlt_frame_is_test_card( mlt_frame self ) { mlt_properties properties = MLT_FRAME_PROPERTIES( self ); return ( mlt_deque_count( self->stack_image ) == 0 && !mlt_properties_get_data( properties, "image", NULL ) ) || mlt_properties_get_int( properties, "test_image" ); } /** Determine if the frame will produce audio from a test card. * * \public \memberof mlt_frame_s * \param self a frame * \return true (non-zero) if this will produce from a test card */ int mlt_frame_is_test_audio( mlt_frame self ) { mlt_properties properties = MLT_FRAME_PROPERTIES( self ); return ( mlt_deque_count( self->stack_audio ) == 0 && !mlt_properties_get_data( properties, "audio", NULL ) ) || mlt_properties_get_int( properties, "test_audio" ); } /** Get the sample aspect ratio of the frame. * * \public \memberof mlt_frame_s * \param self a frame * \return the aspect ratio */ double mlt_frame_get_aspect_ratio( mlt_frame self ) { return mlt_properties_get_double( MLT_FRAME_PROPERTIES( self ), "aspect_ratio" ); } /** Set the sample aspect ratio of the frame. * * \public \memberof mlt_frame_s * \param self a frame * \param value the new image sample aspect ratio * \return true if error */ int mlt_frame_set_aspect_ratio( mlt_frame self, double value ) { return mlt_properties_set_double( MLT_FRAME_PROPERTIES( self ), "aspect_ratio", value ); } /** Get the time position of this frame. * * This position is not necessarily the position as the original * producer knows it. It could be the position that the playlist, * multitrack, or tractor producer set. * * \public \memberof mlt_frame_s * \param self a frame * \return the position * \see mlt_frame_original_position */ mlt_position mlt_frame_get_position( mlt_frame self ) { int pos = mlt_properties_get_position( MLT_FRAME_PROPERTIES( self ), "_position" ); return pos < 0 ? 0 : pos; } /** Get the original time position of this frame. * * This is the position that the original producer set on the frame. * * \public \memberof mlt_frame_s * \param self a frame * \return the position */ mlt_position mlt_frame_original_position( mlt_frame self ) { int pos = mlt_properties_get_position( MLT_FRAME_PROPERTIES( self ), "original_position" ); return pos < 0 ? 0 : pos; } /** Set the time position of this frame. * * \public \memberof mlt_frame_s * \param self a frame * \param value the position * \return true if error */ int mlt_frame_set_position( mlt_frame self, mlt_position value ) { // Only set the original_position the first time. if ( ! mlt_properties_get( MLT_FRAME_PROPERTIES( self ), "original_position" ) ) mlt_properties_set_position( MLT_FRAME_PROPERTIES( self ), "original_position", value ); return mlt_properties_set_position( MLT_FRAME_PROPERTIES( self ), "_position", value ); } /** Stack a get_image callback. * * \public \memberof mlt_frame_s * \param self a frame * \param get_image the get_image callback * \return true if error */ int mlt_frame_push_get_image( mlt_frame self, mlt_get_image get_image ) { return mlt_deque_push_back( self->stack_image, get_image ); } /** Pop a get_image callback. * * \public \memberof mlt_frame_s * \param self a frame * \return the get_image callback */ mlt_get_image mlt_frame_pop_get_image( mlt_frame self ) { return mlt_deque_pop_back( self->stack_image ); } /** Push a frame. * * \public \memberof mlt_frame_s * \param self a frame * \param that the frame to push onto \p self * \return true if error */ int mlt_frame_push_frame( mlt_frame self, mlt_frame that ) { return mlt_deque_push_back( self->stack_image, that ); } /** Pop a frame. * * \public \memberof mlt_frame_s * \param self a frame * \return a frame that was previously pushed */ mlt_frame mlt_frame_pop_frame( mlt_frame self ) { return mlt_deque_pop_back( self->stack_image ); } /** Push a service. * * \public \memberof mlt_frame_s * \param self a frame * \param that an opaque pointer * \return true if error */ int mlt_frame_push_service( mlt_frame self, void *that ) { return mlt_deque_push_back( self->stack_image, that ); } /** Pop a service. * * \public \memberof mlt_frame_s * \param self a frame * \return an opaque pointer to something previously pushed */ void *mlt_frame_pop_service( mlt_frame self ) { return mlt_deque_pop_back( self->stack_image ); } /** Push a number. * * \public \memberof mlt_frame_s * \param self a frame * \param that an integer * \return true if error */ int mlt_frame_push_service_int( mlt_frame self, int that ) { return mlt_deque_push_back_int( self->stack_image, that ); } /** Pop a number. * * \public \memberof mlt_frame_s * \param self a frame * \return an integer that was previously pushed */ int mlt_frame_pop_service_int( mlt_frame self ) { return mlt_deque_pop_back_int( self->stack_image ); } /** Push an audio item on the stack. * * \public \memberof mlt_frame_s * \param self a frame * \param that an opaque pointer * \return true if error */ int mlt_frame_push_audio( mlt_frame self, void *that ) { return mlt_deque_push_back( self->stack_audio, that ); } /** Pop an audio item from the stack * * \public \memberof mlt_frame_s * \param self a frame * \return an opaque pointer to something that was pushed onto the frame's audio stack */ void *mlt_frame_pop_audio( mlt_frame self ) { return mlt_deque_pop_back( self->stack_audio ); } /** Return the service stack * * \public \memberof mlt_frame_s * \param self a frame * \return the service stack */ mlt_deque mlt_frame_service_stack( mlt_frame self ) { return self->stack_service; } /** Set a new image on the frame. * * \public \memberof mlt_frame_s * \param self a frame * \param image a pointer to the raw image data * \param size the size of the image data in bytes (optional) * \param destroy a function to deallocate \p image when the frame is closed (optional) * \return true if error */ int mlt_frame_set_image( mlt_frame self, uint8_t *image, int size, mlt_destructor destroy ) { return mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "image", image, size, destroy, NULL ); } /** Set a new alpha channel on the frame. * * \public \memberof mlt_frame_s * \param self a frame * \param alpha a pointer to the alpha channel * \param size the size of the alpha channel in bytes (optional) * \param destroy a function to deallocate \p alpha when the frame is closed (optional) * \return true if error */ int mlt_frame_set_alpha( mlt_frame self, uint8_t *alpha, int size, mlt_destructor destroy ) { self->get_alpha_mask = NULL; return mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "alpha", alpha, size, destroy, NULL ); } /** 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... * * \public \memberof mlt_frame_s * \param self a frame * \param image a new image * \param format the image format * \param width the width of the new image * \param height the height of the new image */ void mlt_frame_replace_image( mlt_frame self, uint8_t *image, mlt_image_format format, int width, int height ) { // Remove all items from the stack while( mlt_deque_pop_back( self->stack_image ) ) ; // Update the information mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "image", image, 0, NULL, NULL ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( self ), "width", width ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( self ), "height", height ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( self ), "format", format ); self->get_alpha_mask = NULL; } /** Get the short name for an image format. * * \public \memberof mlt_frame_s * \param format the image format * \return a string */ const char * mlt_image_format_name( mlt_image_format format ) { switch ( format ) { case mlt_image_none: return "none"; case mlt_image_rgb24: return "rgb24"; case mlt_image_rgb24a: return "rgb24a"; case mlt_image_yuv422: return "yuv422"; case mlt_image_yuv420p: return "yuv420p"; case mlt_image_opengl: return "opengl"; case mlt_image_glsl: return "glsl"; case mlt_image_glsl_texture: return "glsl_texture"; case mlt_image_yuv422p16: return "yuv422p16"; case mlt_image_invalid: return "invalid"; } return "invalid"; } /** Get the id of image format from short name. * * \public \memberof mlt_frame_s * \param name the image format short name * \return a image format */ mlt_image_format mlt_image_format_id( const char * name ) { mlt_image_format f; for( f = mlt_image_none; name && f < mlt_image_invalid; f++ ) { const char * v = mlt_image_format_name( f ); if( !strcmp( v, name ) ) return f; } return mlt_image_invalid; } /** Get the number of bytes needed for an image. * * \public \memberof mlt_frame_s * \param format the image format * \param width width of the image in pixels * \param height height of the image in pixels * \param[out] bpp the number of bytes per pixel (optional) * \return the number of bytes */ int mlt_image_format_size( mlt_image_format format, int width, int height, int *bpp ) { height += 1; switch ( format ) { case mlt_image_rgb24: if ( bpp ) *bpp = 3; return width * height * 3; case mlt_image_opengl: case mlt_image_rgb24a: if ( bpp ) *bpp = 4; return width * height * 4; case mlt_image_yuv422: if ( bpp ) *bpp = 2; return width * height * 2; case mlt_image_yuv420p: if ( bpp ) *bpp = 3 / 2; return width * height * 3 / 2; case mlt_image_glsl: case mlt_image_glsl_texture: if ( bpp ) *bpp = 0; return 4; case mlt_image_yuv422p16: if ( bpp ) *bpp = 0; return 4 * height * width ; default: if ( bpp ) *bpp = 0; return 0; } return 0; } static int generate_test_image( mlt_properties properties, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { mlt_producer producer = mlt_properties_get_data( properties, "test_card_producer", NULL ); mlt_image_format requested_format = *format; int error = 1; if ( producer ) { mlt_frame test_frame = NULL; mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &test_frame, 0 ); if ( test_frame ) { mlt_properties test_properties = MLT_FRAME_PROPERTIES( test_frame ); mlt_properties_set_data( properties, "test_card_frame", test_frame, 0, ( mlt_destructor )mlt_frame_close, NULL ); mlt_properties_set( test_properties, "rescale.interp", mlt_properties_get( properties, "rescale.interp" ) ); error = mlt_frame_get_image( test_frame, buffer, format, width, height, writable ); if ( !error && buffer && *buffer ) { mlt_properties_set_double( properties, "aspect_ratio", mlt_frame_get_aspect_ratio( test_frame ) ); mlt_properties_set_int( properties, "width", *width ); mlt_properties_set_int( properties, "height", *height ); if ( test_frame->convert_image && requested_format != mlt_image_none ) test_frame->convert_image( test_frame, buffer, format, requested_format ); mlt_properties_set_int( properties, "format", *format ); } } else { mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); } } if ( error && buffer ) { 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_double( properties, "aspect_ratio", 1.0 ); switch( *format ) { 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_none: case mlt_image_glsl: case mlt_image_glsl_texture: *format = mlt_image_yuv422; case mlt_image_yuv422: size *= 2; size += *width * 2; *buffer = mlt_pool_alloc( size ); if ( *buffer ) { register uint8_t *p = *buffer; register uint8_t *q = p + size; while ( p != NULL && p != q ) { *p ++ = 235; *p ++ = 128; } } break; case mlt_image_yuv422p16: case mlt_image_yuv420p: size = mlt_image_format_size( *format, *width, *height, NULL ); *buffer = mlt_pool_alloc( size ); if ( *buffer ) { int strides[4]; uint8_t* planes[4]; int h = *height; mlt_image_format_planes( *format, *width, *height, *buffer, planes, strides ); memset(planes[0], 235, h * strides[0]); if ( *format == mlt_image_yuv420p ) h /= 2; memset(planes[1], 128, h * strides[1]); memset(planes[2], 128, h * strides[2]); } break; default: size = 0; break; } mlt_properties_set_data( properties, "image", *buffer, size, ( mlt_destructor )mlt_pool_release, NULL ); mlt_properties_set_int( properties, "test_image", 1 ); error = 0; } return error; } /** Get the image associated to the frame. * * You should express the desired format, width, and height as inputs. As long * as the loader producer was used to generate this or the imageconvert filter * was attached, then you will get the image back in the format you desire. * However, you do not always get the width and height you request depending * on properties and filters. You do not need to supply a pre-allocated * buffer, but you should always supply the desired image format. * * \public \memberof mlt_frame_s * \param self a frame * \param[out] buffer an image buffer * \param[in,out] format the image format * \param[in,out] width the horizontal size in pixels * \param[in,out] height the vertical size in pixels * \param writable whether or not you will need to be able to write to the memory returned in \p buffer * \return true if error * \todo Better describe the width and height as inputs. */ int mlt_frame_get_image( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { mlt_properties properties = MLT_FRAME_PROPERTIES( self ); mlt_get_image get_image = mlt_frame_pop_get_image( self ); mlt_image_format requested_format = *format; int error = 0; if ( get_image ) { mlt_properties_set_int( properties, "image_count", mlt_properties_get_int( properties, "image_count" ) - 1 ); error = get_image( self, buffer, format, width, height, writable ); if ( !error && buffer && *buffer ) { mlt_properties_set_int( properties, "width", *width ); mlt_properties_set_int( properties, "height", *height ); if ( self->convert_image && requested_format != mlt_image_none ) self->convert_image( self, buffer, format, requested_format ); mlt_properties_set_int( properties, "format", *format ); } else { error = generate_test_image( properties, buffer, format, width, height, writable ); } } else if ( mlt_properties_get_data( properties, "image", NULL ) && buffer ) { *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" ); if ( self->convert_image && *buffer && requested_format != mlt_image_none ) { self->convert_image( self, buffer, format, requested_format ); mlt_properties_set_int( properties, "format", *format ); } } else { error = generate_test_image( properties, buffer, format, width, height, writable ); } return error; } /** Get the alpha channel associated to the frame. * * Unlike mlt_frame_get_alpha(), this function WILL create an opaque alpha * channel if one does not already exist. * * \public \memberof mlt_frame_s * \deprecated use mlt_frame_get_alpha() instead * \param self a frame * \return the alpha channel */ uint8_t *mlt_frame_get_alpha_mask( mlt_frame self ) { uint8_t *alpha = NULL; if ( self != NULL ) { if ( self->get_alpha_mask != NULL ) alpha = self->get_alpha_mask( self ); if ( alpha == NULL ) alpha = mlt_properties_get_data( &self->parent, "alpha", NULL ); if ( alpha == NULL ) { int size = mlt_properties_get_int( &self->parent, "width" ) * mlt_properties_get_int( &self->parent, "height" ); alpha = mlt_pool_alloc( size ); memset( alpha, 255, size ); mlt_properties_set_data( &self->parent, "alpha", alpha, size, mlt_pool_release, NULL ); } } return alpha; } /** Get the alpha channel associated to the frame (without creating if it has not). * * Unlike mlt_frame_get_alpha_mask(), this function does NOT create an alpha * channel if one does not already exist. * * \public \memberof mlt_frame_s * \param self a frame * \return the alpha channel or NULL */ uint8_t *mlt_frame_get_alpha( mlt_frame self ) { uint8_t *alpha = NULL; if ( self != NULL ) { if ( self->get_alpha_mask != NULL ) alpha = self->get_alpha_mask( self ); if ( alpha == NULL ) alpha = mlt_properties_get_data( &self->parent, "alpha", NULL ); } return alpha; } /** Get the short name for an audio format. * * You do not need to deallocate the returned string. * \public \memberof mlt_frame_s * \param format an audio format enum * \return a string for the name of the image format */ const char * mlt_audio_format_name( mlt_audio_format format ) { switch ( format ) { case mlt_audio_none: return "none"; case mlt_audio_s16: return "s16"; case mlt_audio_s32: return "s32"; case mlt_audio_s32le: return "s32le"; case mlt_audio_float: return "float"; case mlt_audio_f32le: return "f32le"; case mlt_audio_u8: return "u8"; } return "invalid"; } /** Get the amount of bytes needed for a block of audio. * * \public \memberof mlt_frame_s * \param format an audio format enum * \param samples the number of samples per channel * \param channels the number of channels * \return the number of bytes */ int mlt_audio_format_size( mlt_audio_format format, int samples, int channels ) { switch ( format ) { case mlt_audio_none: return 0; case mlt_audio_s16: return samples * channels * sizeof( int16_t ); case mlt_audio_s32le: case mlt_audio_s32: return samples * channels * sizeof( int32_t ); case mlt_audio_f32le: case mlt_audio_float: return samples * channels * sizeof( float ); case mlt_audio_u8: return samples * channels; } return 0; } /** Get the audio associated to the frame. * * You should express the desired format, frequency, channels, and samples as inputs. As long * as the loader producer was used to generate this or the audioconvert filter * was attached, then you will get the audio back in the format you desire. * However, you do not always get the channels and samples you request depending * on properties and filters. You do not need to supply a pre-allocated * buffer, but you should always supply the desired audio format. * The audio is always in interleaved format. * You should use the \p mlt_sample_calculator to determine the number of samples you want. * * \public \memberof mlt_frame_s * \param self a frame * \param[out] buffer an audio buffer * \param[in,out] format the audio format * \param[in,out] frequency the sample rate * \param[in,out] channels * \param[in,out] samples the number of samples per frame * \return true if error */ int mlt_frame_get_audio( mlt_frame self, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_get_audio get_audio = mlt_frame_pop_audio( self ); mlt_properties properties = MLT_FRAME_PROPERTIES( self ); int hide = mlt_properties_get_int( properties, "test_audio" ); mlt_audio_format requested_format = *format; if ( hide == 0 && get_audio != NULL ) { get_audio( self, buffer, format, frequency, channels, samples ); mlt_properties_set_int( properties, "audio_frequency", *frequency ); mlt_properties_set_int( properties, "audio_channels", *channels ); mlt_properties_set_int( properties, "audio_samples", *samples ); mlt_properties_set_int( properties, "audio_format", *format ); if ( self->convert_audio && *buffer && requested_format != mlt_audio_none ) self->convert_audio( self, buffer, format, requested_format ); } else if ( mlt_properties_get_data( properties, "audio", NULL ) ) { *buffer = mlt_properties_get_data( properties, "audio", NULL ); *format = mlt_properties_get_int( properties, "audio_format" ); *frequency = mlt_properties_get_int( properties, "audio_frequency" ); *channels = mlt_properties_get_int( properties, "audio_channels" ); *samples = mlt_properties_get_int( properties, "audio_samples" ); if ( self->convert_audio && *buffer && requested_format != mlt_audio_none ) self->convert_audio( self, buffer, format, requested_format ); } else { int size = 0; *samples = *samples <= 0 ? 1920 : *samples; *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; mlt_properties_set_int( properties, "audio_frequency", *frequency ); mlt_properties_set_int( properties, "audio_channels", *channels ); mlt_properties_set_int( properties, "audio_samples", *samples ); mlt_properties_set_int( properties, "audio_format", *format ); size = mlt_audio_format_size( *format, *samples, *channels ); if ( size ) *buffer = mlt_pool_alloc( size ); else *buffer = NULL; if ( *buffer ) 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 ); } // TODO: This does not belong here if ( *format == mlt_audio_s16 && mlt_properties_get( properties, "meta.volume" ) && *buffer ) { 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; } /** Set the audio on a frame. * * \public \memberof mlt_frame_s * \param self a frame * \param buffer an buffer containing audio samples * \param format the format of the audio in the \p buffer * \param size the total size of the buffer (optional) * \param destructor a function that releases or deallocates the \p buffer * \return true if error */ int mlt_frame_set_audio( mlt_frame self, void *buffer, mlt_audio_format format, int size, mlt_destructor destructor ) { mlt_properties_set_int( MLT_FRAME_PROPERTIES( self ), "audio_format", format ); return mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "audio", buffer, size, destructor, NULL ); } /** Get audio on a frame as a waveform image. * * This generates an 8-bit grayscale image representation of the audio in a * frame. Currently, this only really works for 2 channels. * This allocates the bitmap using mlt_pool so you should release the return * value with \p mlt_pool_release. * * \public \memberof mlt_frame_s * \param self a frame * \param w the width of the image * \param h the height of the image to create * \return a pointer to a new bitmap */ unsigned char *mlt_frame_get_waveform( mlt_frame self, int w, int h ) { int16_t *pcm = NULL; mlt_properties properties = MLT_FRAME_PROPERTIES( self ); mlt_audio_format format = mlt_audio_s16; int frequency = 16000; int channels = 2; mlt_producer producer = mlt_frame_get_original_producer( self ); double fps = mlt_producer_get_fps( mlt_producer_cut_parent( producer ) ); int samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( self ) ); // Increase audio resolution proportional to requested image size while ( samples < w ) { frequency += 16000; samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( self ) ); } // Get the pcm data mlt_frame_get_audio( self, (void**)&pcm, &format, &frequency, &channels, &samples ); // Make an 8-bit buffer large enough to hold rendering int size = w * h; if ( size <= 0 ) return NULL; unsigned char *bitmap = ( unsigned char* )mlt_pool_alloc( size ); if ( bitmap != NULL ) memset( bitmap, 0, size ); else return NULL; 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; skip = !skip ? 1 : skip; unsigned char gray = 0xFF / skip; int i, j, k; // Iterate sample stream and along x coordinate for ( i = 0; pcm < ubound; i++ ) { // pcm data has channels interleaved for ( j = 0; j < channels; j++, pcm++ ) { // 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 // the vertical resolution of a single channel int height = h * pcm_magnitude / channels / 2 / 32768; // Determine the starting y coordinate - left top, right bottom int displacement = h * (j * 2 + 1) / channels / 2 - ( *pcm < 0 ? 0 : height ); // Position buffer pointer using y coordinate, stride, and x coordinate unsigned char *p = bitmap + i / skip + displacement * w; // Draw vertical line for ( k = 0; k < height + 1; k++ ) if ( *pcm < 0 ) p[ w * k ] = ( k == 0 ) ? 0xFF : p[ w * k ] + gray; else p[ w * k ] = ( k == height ) ? 0xFF : p[ w * k ] + gray; } } return bitmap; } /** Get the end service that produced self frame. * * This fetches the first producer of the frame and not any producers that * encapsulate it. * * \public \memberof mlt_frame_s * \param self a frame * \return a producer */ mlt_producer mlt_frame_get_original_producer( mlt_frame self ) { if ( self != NULL ) return mlt_properties_get_data( MLT_FRAME_PROPERTIES( self ), "_producer", NULL ); return NULL; } /** Destroy the frame. * * \public \memberof mlt_frame_s * \param self a frame */ void mlt_frame_close( mlt_frame self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_FRAME_PROPERTIES( self ) ) <= 0 ) { mlt_deque_close( self->stack_image ); mlt_deque_close( self->stack_audio ); while( mlt_deque_peek_back( self->stack_service ) ) mlt_service_close( mlt_deque_pop_back( self->stack_service ) ); mlt_deque_close( self->stack_service ); mlt_properties_close( &self->parent ); free( self ); } } /***** convenience functions *****/ /** Determine the number of samples that belong in a frame at a time position. * * \public \memberof mlt_frame_s * \param fps the frame rate * \param frequency the sample rate * \param position the time position * \return the number of samples per channel */ int mlt_sample_calculator( float fps, int frequency, int64_t position ) { /* Compute the cumulative number of samples until the start of this frame and the cumulative number of samples until the start of the next frame. Round each to the nearest integer and take the difference to determine the number of samples in this frame. This approach should prevent rounding errors that can accumulate over a large number of frames causing A/V sync problems. */ return mlt_sample_calculator_to_now( fps, frequency, position + 1 ) - mlt_sample_calculator_to_now( fps, frequency, position ); } /** Determine the number of samples that belong before a time position. * * \public \memberof mlt_frame_s * \param fps the frame rate * \param frequency the sample rate * \param position the time position * \return the number of samples per channel * \bug Will this break when mlt_position is converted to double? */ int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t position ) { int64_t samples = 0; if ( fps ) { samples = (int64_t)( (double) position * (double) frequency / (double) fps + ( position < 0 ? -0.5 : 0.5 ) ); } return samples; } void mlt_frame_write_ppm( mlt_frame frame ) { int width = 0; int height = 0; mlt_image_format format = mlt_image_rgb24; uint8_t *image; if ( mlt_frame_get_image( frame, &image, &format, &width, &height, 0 ) == 0 ) { FILE *file; char filename[16]; sprintf( filename, "frame-%05d.ppm", (int)mlt_frame_get_position( frame ) ); file = mlt_fopen( filename, "wb" ); if ( !file ) return; fprintf( file, "P6\n%d %d\n255\n", width, height); fwrite( image, width * height * 3, 1, file ); fclose( file ); } } /** Get or create a properties object unique to this service instance. * * Use this function to hold a service's processing parameters for this * particular frame. Set the parameters in the service's process function. * Then, get the parameters in the function it pushes to the frame's audio * or image stack. This makes the service more parallel by reducing race * conditions and less sensitive to multiple instances (by not setting a * non-unique property on the frame). Creation and destruction of the * properties object is handled automatically. * * \public \memberof mlt_frame_s * \param self a frame * \param service a service * \return a properties object */ mlt_properties mlt_frame_unique_properties( mlt_frame self, mlt_service service ) { mlt_properties frame_props = MLT_FRAME_PROPERTIES( self ); mlt_properties service_props = MLT_SERVICE_PROPERTIES( service ); char *unique = mlt_properties_get( service_props, "_unique_id" ); mlt_properties instance_props = mlt_properties_get_data( frame_props, unique, NULL ); if ( !instance_props ) { instance_props = mlt_properties_new(); mlt_properties_set_data( frame_props, unique, instance_props, 0, (mlt_destructor) mlt_properties_close, NULL ); mlt_properties_set_lcnumeric( instance_props, mlt_properties_get_lcnumeric( service_props ) ); mlt_properties_set_data( instance_props, "_profile", mlt_service_profile( service ), 0, NULL, NULL ); } return instance_props; } /** Get a properties object unique to this service instance. * * Unlike \p mlt_frame_unique_properties, this function does not create the * service-unique properties object if it does not exist. * * \public \memberof mlt_frame_s * \param self a frame * \param service a service * \return a properties object or NULL if it does not exist */ mlt_properties mlt_frame_get_unique_properties( mlt_frame self, mlt_service service ) { char *unique = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" ); return mlt_properties_get_data( MLT_FRAME_PROPERTIES(self), unique, NULL ); } /** Make a copy of a frame. * * This does not copy the get_image/get_audio processing stacks or any * data properties other than the audio and image. * * \public \memberof mlt_frame_s * \param self the frame to clone * \param is_deep a boolean to indicate whether to make a deep copy of the audio * and video data chunks or to make a shallow copy by pointing to the supplied frame * \return a almost-complete copy of the frame * \todo copy the processing deques */ mlt_frame mlt_frame_clone( mlt_frame self, int is_deep ) { mlt_frame new_frame = mlt_frame_init( NULL ); mlt_properties properties = MLT_FRAME_PROPERTIES( self ); mlt_properties new_props = MLT_FRAME_PROPERTIES( new_frame ); void *data, *copy; int size; mlt_properties_inherit( new_props, properties ); // Carry over some special data properties for the multi consumer. mlt_properties_set_data( new_props, "_producer", mlt_frame_get_original_producer( self ), 0, NULL, NULL ); mlt_properties_set_data( new_props, "movit.convert", mlt_properties_get_data( properties, "movit.convert", NULL), 0, NULL, NULL ); if ( is_deep ) { data = mlt_properties_get_data( properties, "audio", &size ); if ( data ) { if ( !size ) size = mlt_audio_format_size( mlt_properties_get_int( properties, "audio_format" ), mlt_properties_get_int( properties, "audio_samples" ), mlt_properties_get_int( properties, "audio_channels" ) ); copy = mlt_pool_alloc( size ); memcpy( copy, data, size ); mlt_properties_set_data( new_props, "audio", copy, size, mlt_pool_release, NULL ); } data = mlt_properties_get_data( properties, "image", &size ); if ( data ) { int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); if ( ! size ) size = mlt_image_format_size( mlt_properties_get_int( properties, "format" ), width, height, NULL ); copy = mlt_pool_alloc( size ); memcpy( copy, data, size ); mlt_properties_set_data( new_props, "image", copy, size, mlt_pool_release, NULL ); data = mlt_properties_get_data( properties, "alpha", &size ); if ( data ) { if ( ! size ) size = width * height; copy = mlt_pool_alloc( size ); memcpy( copy, data, size ); mlt_properties_set_data( new_props, "alpha", copy, size, mlt_pool_release, NULL ); }; } } else { // This frame takes a reference on the original frame since the data is a shallow copy. mlt_properties_inc_ref( properties ); mlt_properties_set_data( new_props, "_cloned_frame", self, 0, (mlt_destructor) mlt_frame_close, NULL ); // Copy properties data = mlt_properties_get_data( properties, "audio", &size ); mlt_properties_set_data( new_props, "audio", data, size, NULL, NULL ); data = mlt_properties_get_data( properties, "image", &size ); mlt_properties_set_data( new_props, "image", data, size, NULL, NULL ); data = mlt_properties_get_data( properties, "alpha", &size ); mlt_properties_set_data( new_props, "alpha", data, size, NULL, NULL ); } return new_frame; } /** Build a planes pointers of image mapping * * For proper and unified planar image processing, planes sizes and planes pointers should * be provides to processing code. * * \public \memberof mlt_frame_s * \param format the image format * \param width width of the image in pixels * \param height height of the image in pixels * \param[in] data pointer to allocated image * \param[out] planes pointers to plane's pointers will be set * \param[out] strides pointers to plane's strides will be set * \return the number of bytes */ int mlt_image_format_planes( mlt_image_format format, int width, int height, void* data, unsigned char *planes[4], int strides[4]) { if ( mlt_image_yuv422p16 == format ) { strides[0] = width * 2; strides[1] = width; strides[2] = width; strides[3] = 0; planes[0] = (unsigned char*)data; planes[1] = planes[0] + height * strides[0]; planes[2] = planes[1] + height * strides[1]; planes[3] = 0; } else if ( mlt_image_yuv420p == format ) { strides[0] = width; strides[1] = width >> 1; strides[2] = width >> 1; strides[3] = 0; planes[0] = (unsigned char*)data; planes[1] = (unsigned char*)data + width * height; planes[2] = (unsigned char*)data + ( 5 * width * height ) / 4; planes[3] = 0; } else { int bpp; mlt_image_format_size( format, width, height, &bpp ); planes[0] = data; planes[1] = 0; planes[2] = 0; planes[3] = 0; strides[0] = bpp * width; strides[1] = 0; strides[2] = 0; strides[3] = 0; }; return 0; } /** Get the short name for a channel configuration. * * You do not need to deallocate the returned string. * \public \member of mlt_frame_s * \param cfg a channel configuration enum * \return a string for the name of the channel configuration */ const char * mlt_channel_layout_name( mlt_channel_layout layout ) { switch ( layout ) { case mlt_channel_auto: return "auto"; case mlt_channel_independent: return "independent"; case mlt_channel_mono: return "mono"; case mlt_channel_stereo: return "stereo"; case mlt_channel_2p1: return "2.1"; case mlt_channel_3p0: return "3.0"; case mlt_channel_3p0_back: return "3.0(back)"; case mlt_channel_4p0: return "4.0"; case mlt_channel_quad_back: return "quad"; case mlt_channel_quad_side: return "quad(side)"; case mlt_channel_3p1: return "3.1"; case mlt_channel_5p0_back: return "5.0"; case mlt_channel_5p0: return "5.0(side)"; case mlt_channel_4p1: return "4.1"; case mlt_channel_5p1_back: return "5.1"; case mlt_channel_5p1: return "5.1(side)"; case mlt_channel_6p0: return "6.0"; case mlt_channel_6p0_front: return "6.0(front)"; case mlt_channel_hexagonal: return "hexagonal"; case mlt_channel_6p1: return "6.1"; case mlt_channel_6p1_back: return "6.1(back)"; case mlt_channel_6p1_front: return "6.1(front)"; case mlt_channel_7p0: return "7.0"; case mlt_channel_7p0_front: return "7.0(front)"; case mlt_channel_7p1: return "7.1"; case mlt_channel_7p1_wide_side: return "7.1(wide-side)"; case mlt_channel_7p1_wide_back: return "7.1(wide)"; } return "invalid"; } /** Get the id of channel configuration from short name. * * \public \memberof mlt_frame_s * \param name the channel configuration short name * \return a channel configuration */ mlt_channel_layout mlt_channel_layout_id( const char * name ) { if( name ) { mlt_channel_layout c; for( c = mlt_channel_auto; c <= mlt_channel_7p1_wide_back; c++ ) { const char * v = mlt_channel_layout_name( c ); if( !strcmp( v, name ) ) return c; } } return mlt_channel_auto; } /** Get the number of channels for a channel configuration. * * \public \memberof mlt_frame_s * \param cfg a channel configuration enum * \return the number of channels for the channel configuration */ int mlt_channel_layout_channels( mlt_channel_layout layout ) { switch ( layout ) { case mlt_channel_auto: return 0; case mlt_channel_independent: return 0; case mlt_channel_mono: return 1; case mlt_channel_stereo: return 2; case mlt_channel_2p1: return 3; case mlt_channel_3p0: return 3; case mlt_channel_3p0_back: return 3; case mlt_channel_4p0: return 4; case mlt_channel_quad_back: return 4; case mlt_channel_quad_side: return 4; case mlt_channel_3p1: return 4; case mlt_channel_5p0_back: return 5; case mlt_channel_5p0: return 5; case mlt_channel_4p1: return 5; case mlt_channel_5p1_back: return 6; case mlt_channel_5p1: return 6; case mlt_channel_6p0: return 6; case mlt_channel_6p0_front: return 6; case mlt_channel_hexagonal: return 6; case mlt_channel_6p1: return 7; case mlt_channel_6p1_back: return 7; case mlt_channel_6p1_front: return 7; case mlt_channel_7p0: return 7; case mlt_channel_7p0_front: return 7; case mlt_channel_7p1: return 8; case mlt_channel_7p1_wide_back: return 8; case mlt_channel_7p1_wide_side: return 8; } return 0; } /** Get a default channel configuration for a given number of channels. * * \public \memberof mlt_frame_s * \param channels the number of channels * \return the default channel configuration */ mlt_channel_layout mlt_channel_layout_default( int channels ) { mlt_channel_layout c; for( c = mlt_channel_mono; c <= mlt_channel_7p1_wide_back; c++ ) { if( mlt_channel_layout_channels( c ) == channels ) return c; } return mlt_channel_independent; } mlt-6.20.0/src/framework/mlt_frame.h000066400000000000000000000222431362234133600173040ustar00rootroot00000000000000/** * \file mlt_frame.h * \brief interface for all frame classes * \see mlt_frame_s * * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); /** \brief Frame class * * The frame is the primary data object that gets passed around to and through services. * * \extends mlt_properties * \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 * \properties \em previous \em frame a reference to the unfiltered preceding frame * (no speed factor applied, only available when \em _need_previous_next is set on the producer) * \properties \em next \em frame a reference to the unfiltered following frame * (no speed factor applied, only available when \em _need_previous_next is set on the producer) * \properties \em colorspace the standard for the YUV coefficients * \properties \em force_full_luma luma range handling, set to -1 for pass-through, 1 for full range, 0 for scaling * \properties \em color_trc the color transfer characteristic (gamma) * \properties \em audio_frequency the sample rate of the audio * \properties \em audio_channels the number of audio channels * \properties \em audio_samples the number of audio samples * \properties \em audio_format the mlt_audio_format for the audio on this frame * \properties \em format the mlt_image_format of the image on this frame * \properties \em width the horizontal resolution of the image * \properties \em height the vertical resolution of the image * \properties \em aspect_ratio the sample aspect ratio of the image */ struct mlt_frame_s { struct mlt_properties_s parent; /**< \private A frame extends properties. */ /** Get the alpha channel (callback function). * \param self a frame * \return the 8-bit alpha channel */ uint8_t * ( *get_alpha_mask )( mlt_frame self ); /** Convert the image format (callback function). * \param self a frame * \param[in,out] image a buffer of image data * \param[in,out] input the image format of supplied image data * \param output the image format to which to convert * \return true if error */ int ( *convert_image )( mlt_frame self, uint8_t **image, mlt_image_format *input, mlt_image_format output ); /** Convert the audio format (callback function). * \param self a frame * \param[in,out] audio a buffer of audio data * \param[in,out] input the audio format of supplied data * \param output the audio format to which to convert * \return true if error */ int ( *convert_audio )( mlt_frame self, void **audio, mlt_audio_format *input, mlt_audio_format output ); mlt_deque stack_image; /**< \private the image processing stack of operations and data */ mlt_deque stack_audio; /**< \private the audio processing stack of operations and data */ mlt_deque stack_service; /**< \private a general purpose data stack */ int is_processing; /**< \private indicates if a frame is or was processed by the parallel consumer */ }; #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 mlt_position mlt_frame_original_position( mlt_frame self ); extern int mlt_frame_set_position( mlt_frame self, mlt_position value ); extern int mlt_frame_set_image( mlt_frame self, uint8_t *image, int size, mlt_destructor destroy ); extern int mlt_frame_set_alpha( mlt_frame self, uint8_t *alpha, int size, mlt_destructor destroy ); 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 uint8_t *mlt_frame_get_alpha( mlt_frame self ); extern int mlt_frame_get_audio( mlt_frame self, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); extern int mlt_frame_set_audio( mlt_frame self, void *buffer, mlt_audio_format, int size, mlt_destructor ); 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 ); extern mlt_properties mlt_frame_unique_properties( mlt_frame self, mlt_service service ); extern mlt_properties mlt_frame_get_unique_properties( mlt_frame self, mlt_service service ); extern mlt_frame mlt_frame_clone( mlt_frame self, int is_deep ); /* convenience functions */ 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 ); extern const char * mlt_image_format_name( mlt_image_format format ); extern int mlt_image_format_size( mlt_image_format format, int width, int height, int *bpp ); extern const char * mlt_audio_format_name( mlt_audio_format format ); extern int mlt_audio_format_size( mlt_audio_format format, int samples, int channels ); extern void mlt_frame_write_ppm( mlt_frame frame ); extern int mlt_image_format_planes( mlt_image_format format, int width, int height, void* data, unsigned char *planes[4], int strides[4]); extern mlt_image_format mlt_image_format_id( const char * name ); extern const char * mlt_channel_layout_name( mlt_channel_layout layout ); extern mlt_channel_layout mlt_channel_layout_id( const char * name ); extern int mlt_channel_layout_channels( mlt_channel_layout layout ); extern mlt_channel_layout mlt_channel_layout_default( int channels ); /** This macro scales RGB into the YUV gamut - y is scaled by 219/255 and uv by 224/255. */ #define RGB2YUV_601_SCALED(r, g, b, y, u, v)\ y = ((263*r + 516*g + 100*b) >> 10) + 16;\ u = ((-152*r - 300*g + 450*b) >> 10) + 128;\ v = ((450*r - 377*g - 73*b) >> 10) + 128; /** This macro scales RGB into the YUV gamut - uv is scaled by 224/255 (y unused). */ #define RGB2UV_601_SCALED(r, g, b, u, v)\ u = ((-152*r - 300*g + 450*b) >> 10) + 128;\ v = ((450*r - 377*g - 73*b) >> 10) + 128; /** This macro scales YUV up into the full gamut of the RGB color space. */ #define YUV2RGB_601_SCALED( y, u, v, r, g, b ) \ r = ((1192 * ( y - 16 ) + 1634 * ( v - 128 ) ) >> 10 ); \ g = ((1192 * ( y - 16 ) - 832 * ( v - 128 ) - 401 * ( 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 mlt-6.20.0/src/framework/mlt_geometry.c000066400000000000000000000410351362234133600200400ustar00rootroot00000000000000/** * \file mlt_geometry.c * \brief geometry animation API (deprecated) * \deprecated use mlt_animation_s instead * * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_geometry.h" #include "mlt_tokeniser.h" #include "mlt_factory.h" #include "mlt_profile.h" #include #include #include /** private part of geometry animation item (deprecated) * \deprecated use mlt_animation_s instead */ typedef struct geometry_item_s { struct mlt_geometry_item_s data; struct geometry_item_s *next, *prev; } *geometry_item; /** private part of geometry object (deprecated) * \deprecated use mlt_animation_s instead */ 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 self = calloc( 1, sizeof( struct mlt_geometry_s ) ); if ( self != NULL ) { self->local = calloc( 1, sizeof( geometry_s ) ); if ( self->local != NULL ) { geometry g = self->local; g->nw = 720; g->nh = 576; } else { free( self ); self = NULL; } } return self; } /** A linear step */ static inline double linearstep( double start, double end, double position, int length ) { double o = ( end - start ) / length; return start + position * o; } void mlt_geometry_interpolate( mlt_geometry self ) { geometry g = self->local; // Parse of all items to ensure unspecified keys are calculated correctly if ( g->item != NULL ) { int i = 0; for ( i = 0; i < 5; i ++ ) { geometry_item current = g->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 self, geometry_item item ) { geometry g = self->local; if ( item == g->item ) { g->item = item->next; if ( g->item != NULL ) g->item->prev = NULL; // To ensure correct seeding, ensure all values are fixed if ( g->item != NULL ) { g->item->data.f[0] = 1; g->item->data.f[1] = 1; g->item->data.f[2] = 1; g->item->data.f[3] = 1; g->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 self ) { geometry g = self->local; free( g->data ); g->data = NULL; while( g->item ) mlt_geometry_drop( self, g->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 // Append a pair's value with ! to enable distort. int mlt_geometry_parse( mlt_geometry self, 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 g = self->local; // Clean the existing geometry mlt_geometry_clean( self ); // Update the info on the data if ( length != -1 ) g->length = length; if ( nw != -1 ) g->nw = nw; if ( nh != -1 ) g->nh = nh; if ( data != NULL ) g->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 ); // If no data in keyframe, drop it (trailing semicolon) if ( value == NULL || !strcmp( value, "" ) ) continue; // Set item to 0 memset( &item, 0, sizeof( struct mlt_geometry_item_s ) ); // Now parse the item mlt_geometry_parse_item( self, &item, value ); // Now insert into place mlt_geometry_insert( self, &item ); } mlt_geometry_interpolate( self ); // Remove the tokeniser mlt_tokeniser_close( tokens ); // ??? return 0; } // Conditionally refresh in case of a change int mlt_geometry_refresh( mlt_geometry self, char *data, int length, int nw, int nh ) { geometry g = self->local; int changed = ( length != -1 && length != g->length ); changed = changed || ( nw != -1 && nw != g->nw ); changed = changed || ( nh != -1 && nh != g->nh ); changed = changed || ( data != NULL && ( g->data == NULL || strcmp( data, g->data ) ) ); if ( changed ) return mlt_geometry_parse( self, data, length, nw, nh ); return -1; } int mlt_geometry_get_length( mlt_geometry self ) { // Get the local/private structure geometry g = self->local; // return the length return g->length; } void mlt_geometry_set_length( mlt_geometry self, int length ) { // Get the local/private structure geometry g = self->local; // set the length g->length = length; } int mlt_geometry_parse_item( mlt_geometry self, mlt_geometry_item item, char *value ) { int ret = 0; // Get the local/private structure geometry g = self->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 * g->length; else item->frame = temp; value = p + 1; } // Special case - frame < 0 if ( item->frame < 0 ) item->frame += g->length; // Obtain the current value at this position - self allows new // frames to be created which don't specify all values mlt_geometry_fetch( self, 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 *= g->nw / 100.0; else if ( count == 1 || count == 3 ) temp *= g->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 self, mlt_geometry_item item, float position ) { // Get the local geometry geometry g = self->local; // Need to find the nearest key to the position specified geometry_item key = g->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 self, mlt_geometry_item item ) { // Get the local/private geometry structure geometry g = self->local; // Create a new local item (this may be removed if a key already exists at self position) geometry_item gi = calloc( 1, sizeof( struct geometry_item_s ) ); memcpy( &gi->data, item, sizeof( struct mlt_geometry_item_s ) ); gi->data.key = 1; // Determine if we need to insert or append to the list, or if it's a new list if ( g->item != NULL ) { // Get the first item geometry_item place = g->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 == g->item ) g->item = gi; if ( place->prev ) place->prev->next = gi; gi->next = place; gi->prev = place->prev; place->prev = gi; } else if ( item->frame > place->data.frame ) { if ( place->next ) place->next->prev = gi; gi->next = place->next; gi->prev = place; place->next = gi; } else { memcpy( &place->data, &gi->data, sizeof( struct mlt_geometry_item_s ) ); free( gi ); } } else { // Set the first item g->item = gi; // To ensure correct seeding, ensure all values are fixed g->item->data.f[0] = 1; g->item->data.f[1] = 1; g->item->data.f[2] = 1; g->item->data.f[3] = 1; g->item->data.f[4] = 1; } // TODO: Error checking return 0; } // Remove the key at the specified position int mlt_geometry_remove( mlt_geometry self, int position ) { int ret = 1; // Get the local/private geometry structure geometry g = self->local; // Get the first item geometry_item place = g->item; while( place != NULL && position != place->data.frame ) place = place->next; if ( place != NULL && position == place->data.frame ) ret = mlt_geometry_drop( self, place ); return ret; } // Get the key at the position or the next following int mlt_geometry_next_key( mlt_geometry self, mlt_geometry_item item, int position ) { // Get the local/private geometry structure geometry g = self->local; // Get the first item geometry_item place = g->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 self, mlt_geometry_item item, int position ) { // Get the local/private geometry structure geometry g = self->local; // Get the first item geometry_item place = g->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 self, int in, int out ) { geometry g = self->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( self ); 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( self, &item, item.frame ) ) break; // If the first key is larger than the current position // then do nothing here if ( g->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( self, &item, item.frame ) ) break; // Special case - crop at the out point if ( item.frame > out ) mlt_geometry_fetch( self, &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 ), "%g", item.x ); if ( item.f[1] ) { strcat( temp, "/" ); sprintf( temp + strlen( temp ), "%g", item.y ); } if ( item.f[2] ) { strcat( temp, ":" ); sprintf( temp + strlen( temp ), "%g", item.w ); } if ( item.f[3] ) { strcat( temp, "x" ); sprintf( temp + strlen( temp ), "%g", item.h ); } if ( item.f[4] ) { strcat( temp, ":" ); sprintf( temp + strlen( temp ), "%g", item.mix ); } if ( used + strlen( temp ) + 2 > size ) // +2 for ';' and NULL { 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 self ) { geometry g = self->local; char *ret = mlt_geometry_serialise_cut( self, 0, g->length ); if ( ret ) { free( g->data ); g->data = ret; } return strdup( ret ); } // Close the geometry void mlt_geometry_close( mlt_geometry self ) { if ( self != NULL ) { mlt_geometry_clean( self ); free( self->local ); free( self ); } } mlt-6.20.0/src/framework/mlt_geometry.h000066400000000000000000000064211362234133600200450ustar00rootroot00000000000000/** * \file mlt_geometry.h * \brief geometry animation API (deprecated) * \deprecated use mlt_animation_s instead * * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_GEOMETRY_H #define MLT_GEOMETRY_H #include "mlt_types.h" /** geometry animation item (deprecated) * \deprecated use mlt_animation_s instead */ 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 ]; }; /** geometry object (deprecated) * \deprecated use mlt_animation_s instead */ 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 ); /* Typically, re-interpolate after a series of insertions or removals. */ extern void mlt_geometry_interpolate( mlt_geometry self ); /* 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 mlt-6.20.0/src/framework/mlt_log.c000066400000000000000000000060071362234133600167660ustar00rootroot00000000000000/** * \file mlt_log.c * \brief logging functions * * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_log.h" #include "mlt_service.h" #include #ifndef NDEBUG #include #include #endif static int log_level = MLT_LOG_WARNING; 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; #ifndef NDEBUG if ( print_prefix && level >= MLT_LOG_TIMINGS ) { struct timeval tv; time_t ltime; struct tm *rtime; char buf[32]; gettimeofday(&tv, NULL); ltime = tv.tv_sec; rtime = localtime( <ime ); strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S" , rtime ); fprintf( stderr, "| %s.%.3d | ", buf, (int)(tv.tv_usec / 1000) ); } #endif if ( print_prefix && properties ) { char *mlt_type = mlt_properties_get( properties, "mlt_type" ); char *mlt_service = mlt_properties_get( properties, "mlt_service" ); char *resource = mlt_properties_get( properties, "resource" ); if ( !( resource && *resource && resource[0] == '<' && resource[ strlen(resource) - 1 ] == '>' ) ) mlt_type = mlt_properties_get( properties, "mlt_type" ); if ( mlt_service ) fprintf( stderr, "[%s %s] ", mlt_type, mlt_service ); else fprintf( stderr, "[%s %p] ", mlt_type, ptr ); if ( resource ) fprintf( stderr, "%s\n ", resource ); } 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; } int64_t mlt_log_timings_now( void ) { int64_t r = 0; #ifndef NDEBUG struct timeval tv; gettimeofday(&tv, NULL); r = tv.tv_sec; r *= 1000000LL; r += tv.tv_usec; #endif return r; } mlt-6.20.0/src/framework/mlt_log.h000066400000000000000000000074551362234133600170030ustar00rootroot00000000000000/** * \file mlt_log.h * \brief logging functions * * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_LOG_H #define MLT_LOG_H #include #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 #define MLT_LOG_TIMINGS 44 /** * 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_timings(service, format, args...) mlt_log((service), MLT_LOG_TIMINGS, (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 ) ); #define mlt_log_timings_begin() \ { \ int64_t _mlt_log_timings_begin = mlt_log_timings_now(), _mlt_log_timings_end; #define mlt_log_timings_end(service, msg) \ _mlt_log_timings_end = mlt_log_timings_now(); \ mlt_log_timings( service, "%s:%d: T(%s)=%" PRId64 " us\n", \ __FILE__, __LINE__, msg, _mlt_log_timings_end - _mlt_log_timings_begin ); \ } int64_t mlt_log_timings_now( void ); #endif /* MLT_LOG_H */ mlt-6.20.0/src/framework/mlt_luma_map.c000066400000000000000000000265441362234133600200100ustar00rootroot00000000000000/** * \file mlt_luma_map.c * \brief functions to generate and read luma-wipe transition maps * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_luma_map.h" #include "mlt_pool.h" #include "mlt_types.h" #include #include #include #define HALF_USHRT_MAX (1 << 15) void mlt_luma_map_init(mlt_luma_map self) { memset( self, 0, sizeof(struct mlt_luma_map_s) ); self->type = 0; self->w = 720; self->h = 576; self->bands = 1; self->rband = 0; self->vmirror = 0; self->hmirror = 0; self->dmirror = 0; self->invert = 0; self->offset = 0; self->flip = 0; self->flop = 0; self->quart = 0; self->pflop = 0; self->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 *mlt_luma_map_render(mlt_luma_map self) { int i = 0; int j = 0; int k = 0; if ( self->quart ) { self->w *= 2; self->h *= 2; } if ( self->rotate ) { int t = self->w; self->w = self->h; self->h = t; } if (self->bands < 0) self->bands = self->h; int max = ( 1 << 16 ) - 1; uint16_t *image = mlt_pool_alloc( self->w * self->h * sizeof( uint16_t ) ); uint16_t *end = image + self->w * self->h; uint16_t *p = image; uint16_t *r = image; int lower = 0; int lpb = self->h / self->bands; int rpb = max / self->bands; int direction = 1; int half_w = self->w / 2; int half_h = self->h / 2; if ( !self->dmirror && ( self->hmirror || self->vmirror ) ) rpb *= 2; for ( i = 0; i < self->bands; i ++ ) { lower = i * rpb; direction = 1; if ( self->rband && i % 2 == 1 ) { direction = -1; lower += rpb; } switch( self->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 < self->w; k ++ ) { x = k - half_w; value = sqrti( x * x + y * y ); *p ++ = lower + ( direction * rpb * ( ( max * value ) / length ) / max ) + ( j * self->offset * 2 / lpb ) + ( j * self->offset / lpb ); } } } break; case 2: { for ( j = 0; j < lpb; j ++ ) { int value = ( ( j * self->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 * self->offset * 2 / lpb ) + ( j * self->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 < self->w; k ++ ) *p ++ = lower + ( direction * ( rpb * ( ( k * max ) / self->w ) / max ) ) + ( j * self->offset * 2 / lpb ); break; } } if ( self->quart ) { self->w /= 2; self->h /= 2; for ( i = 1; i < self->h; i ++ ) { p = image + i * self->w; r = image + i * 2 * self->w; j = self->w; while ( j -- > 0 ) *p ++ = *r ++; } } if ( self->dmirror ) { for ( i = 0; i < self->h; i ++ ) { p = image + i * self->w; r = end - i * self->w; j = ( self->w * ( self->h - i ) ) / self->h; while ( j -- ) *( -- r ) = *p ++; } } if ( self->flip ) { uint16_t t; for ( i = 0; i < self->h; i ++ ) { p = image + i * self->w; r = p + self->w; while( p != r ) { t = *p; *p ++ = *( -- r ); *r = t; } } } if ( self->flop ) { uint16_t t; r = end; for ( i = 1; i < self->h / 2; i ++ ) { p = image + i * self->w; j = self->w; while( j -- ) { t = *( -- p ); *p = *( -- r ); *r = t; } } } if ( self->hmirror ) { p = image; while ( p < end ) { r = p + self->w; while ( p != r ) *( -- r ) = *p ++; p += self->w / 2; } } if ( self->vmirror ) { p = image; r = end; while ( p != r ) *( -- r ) = *p ++; } if ( self->invert ) { p = image; r = image; while ( p < end ) *p ++ = max - *r ++; } if ( self->pflip ) { uint16_t t; for ( i = 0; i < self->h; i ++ ) { p = image + i * self->w; r = p + self->w; while( p != r ) { t = *p; *p ++ = *( -- r ); *r = t; } } } if ( self->pflop ) { uint16_t t; end = image + self->w * self->h; r = end; for ( i = 1; i < self->h / 2; i ++ ) { p = image + i * self->w; j = self->w; while( j -- ) { t = *( -- p ); *p = *( -- r ); *r = t; } } } if ( self->rotate ) { uint16_t *image2 = mlt_pool_alloc( self->w * self->h * sizeof( uint16_t ) ); for ( i = 0; i < self->h; i ++ ) { p = image + i * self->w; r = image2 + self->h - i - 1; for ( j = 0; j < self->w; j ++ ) { *r = *( p ++ ); r += self->h; } } i = self->w; self->w = self->h; self->h = i; mlt_pool_release( image ); image = image2; } return image; } /** Load the luma map from PGM stream. */ int mlt_luma_map_from_pgm(const char *filename, uint16_t **map, int *width, int *height) { uint8_t *data = NULL; FILE *f = mlt_fopen( filename, "rb" ); int error = f == NULL; while (!error) { 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) { error = 1; 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) { error = 1; break; } // process 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 (f) fclose(f); mlt_pool_release( data ); return error; } mlt_luma_map mlt_luma_map_new(const char *path) { mlt_luma_map self = malloc(sizeof(struct mlt_luma_map_s)); if (self) { mlt_luma_map_init(self); if (strstr(path, "luma02.pgm")) { self->bands = -1; // use height } else if (strstr(path, "luma03.pgm")) { self->hmirror = 1; } else if (strstr(path, "luma04.pgm")) { self->bands = -1; // use height self->vmirror = 1; } else if (strstr(path, "luma05.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; } else if (strstr(path, "luma06.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; self->flip = 1; } else if (strstr(path, "luma07.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; self->quart = 1; } else if (strstr(path, "luma08.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; self->quart = 1; self->flip = 1; } else if (strstr(path, "luma09.pgm")) { self->bands = 12; } else if (strstr(path, "luma10.pgm")) { self->bands = 12; self->rotate = 1; } else if (strstr(path, "luma11.pgm")) { self->bands = 12; self->rband = 1; } else if (strstr(path, "luma12.pgm")) { self->bands = 12; self->rband = 1; self->vmirror = 1; } else if (strstr(path, "luma13.pgm")) { self->bands = 12; self->rband = 1; self->rotate = 1; self->flop = 1; } else if (strstr(path, "luma14.pgm")) { self->bands = 12; self->rband = 1; self->rotate = 1; self->vmirror = 1; } else if (strstr(path, "luma15.pgm")) { self->offset = HALF_USHRT_MAX; self->dmirror = 1; self->hmirror = 1; } else if (strstr(path, "luma16.pgm")) { self->type = 1; } else if (strstr(path, "luma17.pgm")) { self->type = 1; self->bands = 2; self->rband = 1; } else if (strstr(path, "luma18.pgm")) { self->type = 2; } else if (strstr(path, "luma19.pgm")) { self->type = 2; self->quart = 1; } else if (strstr(path, "luma20.pgm")) { self->type = 2; self->quart = 1; self->flip = 1; } else if (strstr(path, "luma21.pgm")) { self->type = 2; self->quart = 1; self->bands = 2; } else if (strstr(path, "luma22.pgm")) { self->type = 3; } } return self; } /** Generate a 16-bit luma map from an 8-bit image. */ void mlt_luma_map_from_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; // process the image data into the luma bitmap for ( i = 0; i < size; i += 2 ) *p++ = ( image[ i ] - 16 ) * 299; // 299 = 65535 / 219 } mlt-6.20.0/src/framework/mlt_luma_map.h000066400000000000000000000032171362234133600200050ustar00rootroot00000000000000/* * \file mlt_luma_map.h * \brief functions to generate and read luma-wipe transition maps * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_LUMA_MAP_H #define MLT_LUMA_MAP_H #include #include #ifdef __cplusplus extern "C" { #endif struct mlt_luma_map_s { 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; }; typedef struct mlt_luma_map_s *mlt_luma_map; extern void mlt_luma_map_init( mlt_luma_map self ); extern mlt_luma_map mlt_luma_map_new( const char *path ); extern uint16_t *mlt_luma_map_render( mlt_luma_map self ); extern int mlt_luma_map_from_pgm( const char *filename, uint16_t **map, int *width, int *height ); extern void mlt_luma_map_from_yuv422( uint8_t *image, uint16_t **map, int width, int height ); #ifdef __cplusplus } #endif #endif mlt-6.20.0/src/framework/mlt_multitrack.c000066400000000000000000000452541362234133600203730ustar00rootroot00000000000000/** * \file mlt_multitrack.c * \brief multitrack service class * \see mlt_multitrack_s * * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_multitrack.h" #include "mlt_playlist.h" #include "mlt_frame.h" #include "mlt_factory.h" #include "mlt_cache.h" #include #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 self = calloc( 1, sizeof( struct mlt_multitrack_s ) ); if ( self != NULL ) { mlt_producer producer = &self->parent; if ( mlt_producer_init( producer, self ) == 0 ) { mlt_properties properties = MLT_MULTITRACK_PROPERTIES( self ); producer->get_frame = producer_get_frame; mlt_properties_set_data( properties, "multitrack", self, 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( self ); self = NULL; } } return self; } /** Get the producer associated to this multitrack. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \return the producer object * \see MLT_MULTITRACK_PRODUCER */ mlt_producer mlt_multitrack_producer( mlt_multitrack self ) { return self != NULL ? &self->parent : NULL; } /** Get the service associated this multitrack. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \return the service object * \see MLT_MULTITRACK_SERVICE */ mlt_service mlt_multitrack_service( mlt_multitrack self ) { return MLT_MULTITRACK_SERVICE( self ); } /** Get the properties associated this multitrack. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \return the multitrack's property list * \see MLT_MULTITRACK_PROPERTIES */ mlt_properties mlt_multitrack_properties( mlt_multitrack self ) { return MLT_MULTITRACK_PROPERTIES( self ); } /** Initialize position related information. * * \public \memberof mlt_multitrack_s * \param self a multitrack */ void mlt_multitrack_refresh( mlt_multitrack self ) { int i = 0; // Obtain the properties of this multitrack mlt_properties properties = MLT_MULTITRACK_PROPERTIES( self ); // 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 < self->count; i ++ ) { // Get the producer from this index mlt_track track = self->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 ( self->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 self a multitrack */ static void mlt_multitrack_listener( mlt_producer producer, mlt_multitrack self ) { mlt_multitrack_refresh( self ); } static void resize_service_caches( mlt_multitrack self ) { mlt_properties caches = mlt_properties_get_data( mlt_global_properties(), "caches", NULL ); if ( caches ) { int i; for ( i = 0; i < mlt_properties_count(caches); ++i ) { mlt_cache cache = mlt_properties_get_data_at( caches, i, NULL ); if ( self->count * 3 > mlt_cache_get_size(cache) ) mlt_cache_set_size( cache, self->count * 3 ); } } } /** 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 self 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 self, mlt_producer producer, int track ) { // Connect to the producer to ourselves at the specified track int result = mlt_service_connect_producer( MLT_MULTITRACK_SERVICE( self ), MLT_PRODUCER_SERVICE( producer ), track ); if ( result == 0 ) { mlt_track current_track = ( track < self->count )? self->list[ track ] : NULL; // Resize the producer list if need be if ( track >= self->size ) { int i; self->list = realloc( self->list, ( track + 10 ) * sizeof( mlt_track ) ); for ( i = self->size; i < track + 10; i ++ ) self->list[ i ] = NULL; self->size = track + 10; } if ( current_track ) { mlt_event_close( current_track->event ); mlt_producer_close( current_track->producer ); } else { self->list[ track ] = malloc( sizeof( struct mlt_track_s ) ); } // Assign the track in our list here self->list[ track ]->producer = producer; self->list[ track ]->event = mlt_events_listen( MLT_PRODUCER_PROPERTIES( producer ), self, "producer-changed", ( mlt_listener )mlt_multitrack_listener ); mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) ); mlt_event_inc_ref( self->list[ track ]->event ); // Increment the track count if need be if ( track >= self->count ) { self->count = track + 1; resize_service_caches( self ); } // Refresh our stats mlt_multitrack_refresh( self ); } return result; } /** Insert a producer to a given track. * * \public \memberof mlt_multitrack_s * \param self 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_insert( mlt_multitrack self, mlt_producer producer, int track ) { if ( track >= self->count ) return mlt_multitrack_connect( self, producer, track ); // Connect to the producer to ourselves at the specified track int result = mlt_service_insert_producer( MLT_MULTITRACK_SERVICE( self ), MLT_PRODUCER_SERVICE( producer ), track ); if ( result == 0 ) { // Resize the producer list if needed. if ( self->count + 1 > self->size ) { int new_size = self->size + 10; self->list = realloc( self->list, new_size * sizeof( mlt_track ) ); if ( self->list ) { memset( &self->list[ self->size ], 0, new_size - self->size ); self->size = new_size; } } if ( self->list ) { // Move all of the list elements following track N down by 1. memmove( &self->list[ track + 1 ], &self->list[ track ], ( self->count - track ) * sizeof ( mlt_track ) ); self->count ++; resize_service_caches( self ); // Assign the track in our list. self->list[ track ] = malloc( sizeof( struct mlt_track_s ) ); self->list[ track ]->producer = producer; self->list[ track ]->event = mlt_events_listen( MLT_PRODUCER_PROPERTIES( producer ), self, "producer-changed", ( mlt_listener )mlt_multitrack_listener ); mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) ); mlt_event_inc_ref( self->list[ track ]->event ); // Refresh our stats mlt_multitrack_refresh( self ); } else { result = -1; } } return result; } /** Remove the N-th track. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \param track the index of the track to remove * \return true if there was an error */ int mlt_multitrack_disconnect( mlt_multitrack self, int track ) { int error = -1; if ( self && self->list && track >= 0 && track < self->count ) { // Disconnect the track producer. error = mlt_service_disconnect_producer( MLT_MULTITRACK_SERVICE(self), track ); if ( !error ) { // Release references on track. if ( self->list[ track ] ) { mlt_producer_close( self->list[ track ]->producer ); mlt_event_close( self->list[ track ]->event ); } // Contract the list of tracks. for ( ; track + 1 < self->count; track ++ ) { if ( self->list[ track ] && self->list[ track + 1 ] ) { self->list[ track ]->producer = self->list[ track + 1 ]->producer; self->list[ track ]->event = self->list[ track + 1 ]->event; } } if ( self->list[ self->count - 1 ] ) { free( self->list[ self->count - 1 ] ); self->list[ self->count - 1 ] = NULL; } self->count --; // Recalculate the duration. mlt_multitrack_refresh( self ); } } return error; } /** Get the number of tracks. * * \public \memberof mlt_multitrack_s * \param self a multitrack * \return the number of tracks */ int mlt_multitrack_count( mlt_multitrack self ) { if ( self == NULL ) return 0; else return self->count; } /** Get an individual track as a producer. * * \public \memberof mlt_multitrack_s * \param self 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 self, int track ) { mlt_producer producer = NULL; if ( self->list != NULL && track >= 0 && track < self->count ) producer = self->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; } /** Increase the capacity of a set of mlt_position. * * \private \memberof mlt_multitrack_s * \param array an array of positions (the set) * \param count the current number of elements in the array (not the capacity) * \param[out] size the current capacity of the array * \return the new address of the array */ static mlt_position* resize_set( mlt_position *map, int count, int *size ) { mlt_position* result = map; // Resize only if needed. if ( count + 1 >= *size ) { result = realloc( map, (*size + 1000) * sizeof(*map) ); memset( map + *size, 0, 1000 * sizeof(*map) ); *size += 1000; } return result; } /** 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 self 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 self, mlt_whence whence, int index ) { mlt_position position = 0; int i = 0; int j = 0; int size = 1000; mlt_position *map = calloc( size, sizeof(*map) ); int count = 0; for ( i = 0; i < self->count; i ++ ) { // Get the producer for this track mlt_producer producer = self->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 ); map = resize_set( map, count, &size ); // 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 ) ); map = resize_set( map, count, &size ); } 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( self ) ); 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 self = parent->child; // Check if we have a track for this index if ( index >= 0 && index < self->count && self->list[ index ] != NULL ) { // Get the producer for this track mlt_producer producer = self->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_frame_set_position( *frame, 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 >= self->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 self a multitrack */ void mlt_multitrack_close( mlt_multitrack self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_MULTITRACK_PROPERTIES( self ) ) <= 0 ) { int i = 0; for ( i = 0; i < self->count; i ++ ) { if ( self->list[ i ] != NULL ) { mlt_event_close( self->list[ i ]->event ); mlt_producer_close( self->list[ i ]->producer ); free( self->list[ i ] ); } } // Close the producer self->parent.close = NULL; mlt_producer_close( &self->parent ); // Free the list free( self->list ); // Free the object free( self ); } } mlt-6.20.0/src/framework/mlt_multitrack.h000066400000000000000000000051101362234133600203630ustar00rootroot00000000000000/** * \file mlt_multitrack.h * \brief multitrack service class * \see mlt_multitrack_s * * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 int mlt_multitrack_insert( mlt_multitrack self, mlt_producer producer, int track ); extern int mlt_multitrack_disconnect( mlt_multitrack self, 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 mlt-6.20.0/src/framework/mlt_parser.c000066400000000000000000000156111362234133600175020ustar00rootroot00000000000000/** * \file mlt_parser.c * \brief service parsing functionality * \see mlt_parser_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt.h" #include static int on_invalid( mlt_parser self, mlt_service object ) { return 0; } static int on_unknown( mlt_parser self, mlt_service object ) { return 0; } static int on_start_producer( mlt_parser self, mlt_producer object ) { return 0; } static int on_end_producer( mlt_parser self, mlt_producer object ) { return 0; } static int on_start_playlist( mlt_parser self, mlt_playlist object ) { return 0; } static int on_end_playlist( mlt_parser self, mlt_playlist object ) { return 0; } static int on_start_tractor( mlt_parser self, mlt_tractor object ) { return 0; } static int on_end_tractor( mlt_parser self, mlt_tractor object ) { return 0; } static int on_start_multitrack( mlt_parser self, mlt_multitrack object ) { return 0; } static int on_end_multitrack( mlt_parser self, mlt_multitrack object ) { return 0; } static int on_start_track( mlt_parser self ) { return 0; } static int on_end_track( mlt_parser self ) { return 0; } static int on_start_filter( mlt_parser self, mlt_filter object ) { return 0; } static int on_end_filter( mlt_parser self, mlt_filter object ) { return 0; } static int on_start_transition( mlt_parser self, mlt_transition object ) { return 0; } static int on_end_transition( mlt_parser self, mlt_transition object ) { return 0; } mlt_parser mlt_parser_new( ) { mlt_parser self = calloc( 1, sizeof( struct mlt_parser_s ) ); if ( self != NULL && mlt_properties_init( &self->parent, self ) == 0 ) { self->on_invalid = on_invalid; self->on_unknown = on_unknown; self->on_start_producer = on_start_producer; self->on_end_producer = on_end_producer; self->on_start_playlist = on_start_playlist; self->on_end_playlist = on_end_playlist; self->on_start_tractor = on_start_tractor; self->on_end_tractor = on_end_tractor; self->on_start_multitrack = on_start_multitrack; self->on_end_multitrack = on_end_multitrack; self->on_start_track = on_start_track; self->on_end_track = on_end_track; self->on_start_filter = on_start_filter; self->on_end_filter = on_end_filter; self->on_start_transition = on_start_transition; self->on_end_transition = on_end_transition; } return self; } mlt_properties mlt_parser_properties( mlt_parser self ) { return &self->parent; } int mlt_parser_start( mlt_parser self, mlt_service object ) { int error = 0; mlt_service_type type = mlt_service_identify( object ); switch( type ) { case invalid_type: error = self->on_invalid( self, object ); break; case unknown_type: error = self->on_unknown( self, object ); break; case producer_type: if ( mlt_producer_is_cut( ( mlt_producer )object ) ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_cut_parent( ( mlt_producer )object ) ); error = self->on_start_producer( self, ( mlt_producer )object ); if ( error == 0 ) { int i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_producer( self, ( mlt_producer )object ); break; case playlist_type: error = self->on_start_playlist( self, ( mlt_playlist )object ); if ( error == 0 ) { int i = 0; while ( error == 0 && i < mlt_playlist_count( ( mlt_playlist )object ) ) mlt_parser_start( self, ( 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( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_playlist( self, ( mlt_playlist )object ); break; case tractor_type: error = self->on_start_tractor( self, ( mlt_tractor )object ); if ( error == 0 ) { int i = 0; mlt_service next = mlt_service_producer( object ); mlt_parser_start( self, ( mlt_service )mlt_tractor_multitrack( ( mlt_tractor )object ) ); while ( next != ( mlt_service )mlt_tractor_multitrack( ( mlt_tractor )object ) ) { mlt_parser_start( self, next ); next = mlt_service_producer( next ); } while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_tractor( self, ( mlt_tractor )object ); break; case multitrack_type: error = self->on_start_multitrack( self, ( mlt_multitrack )object ); if ( error == 0 ) { int i = 0; while ( i < mlt_multitrack_count( ( mlt_multitrack )object ) ) { self->on_start_track( self ); mlt_parser_start( self, ( mlt_service )mlt_multitrack_track( ( mlt_multitrack )object , i ++ ) ); self->on_end_track( self ); } i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_multitrack( self, ( mlt_multitrack )object ); break; case filter_type: error = self->on_start_filter( self, ( mlt_filter )object ); if ( error == 0 ) { int i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_filter( self, ( mlt_filter )object ); break; case transition_type: error = self->on_start_transition( self, ( mlt_transition )object ); if ( error == 0 ) { int i = 0; while ( error == 0 && mlt_producer_filter( ( mlt_producer )object, i ) != NULL ) error = mlt_parser_start( self, ( mlt_service )mlt_producer_filter( ( mlt_producer )object, i ++ ) ); } error = self->on_end_transition( self, ( mlt_transition )object ); break; case field_type: break; case consumer_type: break; } return error; } void mlt_parser_close( mlt_parser self ) { if ( self != NULL ) { mlt_properties_close( &self->parent ); free( self ); } } mlt-6.20.0/src/framework/mlt_parser.h000066400000000000000000000044241362234133600175070ustar00rootroot00000000000000/** * \file mlt_parser.h * \brief service parsing functionality * \see mlt_parser_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 mlt-6.20.0/src/framework/mlt_playlist.c000066400000000000000000002061731362234133600200540ustar00rootroot00000000000000/** * \file mlt_playlist.c * \brief playlist service class * \see mlt_playlist_s * * Copyright (C) 2003-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 self, int clip ); static int mlt_playlist_resize_mix( mlt_playlist self, int clip, int in, int out ); static void mlt_playlist_next( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); /** 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 self = calloc( 1, sizeof( struct mlt_playlist_s ) ); if ( self != NULL ) { mlt_producer producer = &self->parent; // Construct the producer if ( mlt_producer_init( producer, self ) != 0 ) goto error1; // Override the producer get_frame producer->get_frame = producer_get_frame; // Define the destructor producer->close = ( mlt_destructor )mlt_playlist_close; producer->close_object = self; // Initialise blank if ( mlt_producer_init( &self->blank, NULL ) != 0 ) goto error1; mlt_properties_set( MLT_PRODUCER_PROPERTIES( &self->blank ), "mlt_service", "blank" ); mlt_properties_set( MLT_PRODUCER_PROPERTIES( &self->blank ), "resource", "blank" ); // Indicate that this producer is a playlist mlt_properties_set_data( MLT_PLAYLIST_PROPERTIES( self ), "playlist", self, 0, NULL, NULL ); // Specify the eof condition mlt_properties_set( MLT_PLAYLIST_PROPERTIES( self ), "eof", "pause" ); mlt_properties_set( MLT_PLAYLIST_PROPERTIES( self ), "resource", "" ); mlt_properties_set( MLT_PLAYLIST_PROPERTIES( self ), "mlt_type", "mlt_producer" ); mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( self ), "in", 0 ); mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( self ), "out", -1 ); mlt_properties_set_position( MLT_PLAYLIST_PROPERTIES( self ), "length", 0 ); self->size = 10; self->list = calloc( self->size, sizeof( playlist_entry * ) ); if ( self->list == NULL ) goto error2; mlt_events_register( MLT_PLAYLIST_PROPERTIES( self ), "playlist-next", (mlt_transmitter) mlt_playlist_next ); } return self; error2: free( self->list ); error1: free( self ); return NULL; } /** Construct a playlist with a profile. * * Sets the resource property to "". * Set the mlt_type to property to "mlt_producer". * \public \memberof mlt_playlist_s * \param profile the profile to use with the profile * \return a new playlist */ mlt_playlist mlt_playlist_new( mlt_profile profile ) { mlt_playlist self = mlt_playlist_init(); if ( self ) mlt_properties_set_data( MLT_PLAYLIST_PROPERTIES( self ), "_profile", profile, 0, NULL, NULL ); return self; } /** Get the producer associated to this playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the producer interface * \see MLT_PLAYLIST_PRODUCER */ mlt_producer mlt_playlist_producer( mlt_playlist self ) { return self != NULL ? &self->parent : NULL; } /** Get the service associated to this playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the service interface * \see MLT_PLAYLIST_SERVICE */ mlt_service mlt_playlist_service( mlt_playlist self ) { return MLT_PRODUCER_SERVICE( &self->parent ); } /** Get the properties associated to this playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the playlist's properties list * \see MLT_PLAYLIST_PROPERTIES */ mlt_properties mlt_playlist_properties( mlt_playlist self ) { return MLT_PRODUCER_PROPERTIES( &self->parent ); } /** Refresh the playlist after a clip has been changed. * * \private \memberof mlt_playlist_s * \param self a playlist * \return false */ static int mlt_playlist_virtual_refresh( mlt_playlist self ) { // Obtain the properties mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); int i = 0; mlt_position frame_count = 0; for ( i = 0; i < self->count; i ++ ) { // Get the producer mlt_producer producer = self->list[ i ]->producer; if ( producer ) { int current_length = mlt_producer_get_playtime( producer ); // Check if the length of the producer has changed if ( self->list[ i ]->frame_in != mlt_producer_get_in( producer ) || self->list[ i ]->frame_out != mlt_producer_get_out( producer ) ) { // This clip should be removed... if ( current_length < 1 ) { self->list[ i ]->frame_in = 0; self->list[ i ]->frame_out = -1; self->list[ i ]->frame_count = 0; } else { self->list[ i ]->frame_in = mlt_producer_get_in( producer ); self->list[ i ]->frame_out = mlt_producer_get_out( producer ); self->list[ i ]->frame_count = current_length; } // Update the producer_length self->list[ i ]->producer_length = current_length; } } // Calculate the frame_count self->list[ i ]->frame_count = ( self->list[ i ]->frame_out - self->list[ i ]->frame_in + 1 ) * self->list[ i ]->repeat; // Update the frame_count for self clip frame_count += self->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 self a playlist */ static void mlt_playlist_listener( mlt_producer producer, mlt_playlist self ) { mlt_playlist_virtual_refresh( self ); } /** Append to the virtual playlist. * * \private \memberof mlt_playlist_s * \param self 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 self, 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 ) ) { mlt_position length = out - in + 1; // Make sure the blank is long enough to accommodate the length specified if ( length > mlt_producer_get_length( &self->blank ) ) { mlt_properties blank_props = MLT_PRODUCER_PROPERTIES( &self->blank ); mlt_events_block( blank_props, blank_props ); mlt_producer_set_in_and_out( &self->blank, in, out ); mlt_events_unblock( blank_props, blank_props ); } // Now make sure the cut comes from this self->blank if ( source == NULL ) { producer = mlt_producer_cut( &self->blank, in, out ); } else if ( !mlt_producer_is_cut( source ) || mlt_producer_cut_parent( source ) != &self->blank ) { producer = mlt_producer_cut( &self->blank, in, out ); } else { producer = source; mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) ); } properties = MLT_PRODUCER_PROPERTIES( producer ); // Make sure this cut of blank is long enough if ( length > mlt_producer_get_length( producer ) ) mlt_properties_set_int( properties, "length", length ); } else if ( mlt_producer_is_cut( source ) ) { producer = source; if ( in < 0 ) in = mlt_producer_get_in( producer ); if ( out < 0 || 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 < 0 || in < mlt_producer_get_in( producer ) ) in = mlt_producer_get_in( producer ); if ( out < 0 || 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 loader 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 ), "_loader" ) ) { 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 ( self->count >= self->size ) { int i; self->list = realloc( self->list, ( self->size + 10 ) * sizeof( playlist_entry * ) ); for ( i = self->size; i < self->size + 10; i ++ ) self->list[ i ] = NULL; self->size += 10; } // Create the entry self->list[ self->count ] = calloc( 1, sizeof( playlist_entry ) ); if ( self->list[ self->count ] != NULL ) { self->list[ self->count ]->producer = producer; self->list[ self->count ]->frame_in = in; self->list[ self->count ]->frame_out = out; self->list[ self->count ]->frame_count = out - in + 1; self->list[ self->count ]->repeat = 1; self->list[ self->count ]->producer_length = mlt_producer_get_playtime( producer ); self->list[ self->count ]->event = mlt_events_listen( parent, self, "producer-changed", ( mlt_listener )mlt_playlist_listener ); mlt_event_inc_ref( self->list[ self->count ]->event ); mlt_properties_set( properties, "eof", "pause" ); mlt_producer_set_speed( producer, 0 ); self->count ++; } return mlt_playlist_virtual_refresh( self ); } /** Locate a producer by index. * * \private \memberof mlt_playlist_s * \param self 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 self, 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 < self->count; *clip += 1 ) { // Increment the total *total += self->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 < self->list[ *clip ]->frame_count ) { // Found it, now break producer = self->list[ *clip ]->producer; break; } else { // Decrement position by length of self entry *position -= self->list[ *clip ]->frame_count; } } return producer; } /** The transmitter for the producer-next event * * Invokes the listener. * * \private \memberof mlt_playlist_s * \param listener a function pointer that will be invoked * \param owner the events object that will be passed to \p listener * \param self a service that will be passed to \p listener * \param args an array of pointers. */ static void mlt_playlist_next( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener ) listener( owner, self, args[ 0 ] ); } /** 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 self 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 self, int *progressive ) { // Map playlist position to real producer in virtual playlist mlt_position position = mlt_producer_frame( &self->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( self, &position, &i, &total ); // Get the properties mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); // 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( self->list[ j ]->producer ) ); mlt_producer p = self->list[ j ]->producer; if ( p ) { self->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 = self->list[ i ]->frame_count / self->list[ i ]->repeat; *progressive = count == 1; mlt_producer_seek( producer, (int)position % count ); } else if ( !strcmp( eof, "pause" ) && total > 0 ) { playlist_entry *entry = self->list[ self->count - 1 ]; int count = entry->frame_count / entry->repeat; mlt_producer self_producer = MLT_PLAYLIST_PRODUCER( self ); mlt_producer_seek( self_producer, original - 1 ); producer = entry->producer; mlt_producer_seek( producer, (int)entry->frame_out % count ); mlt_producer_set_speed( self_producer, 0 ); mlt_producer_set_speed( producer, 0 ); *progressive = count == 1; } else if ( !strcmp( eof, "loop" ) && total > 0 ) { playlist_entry *entry = self->list[ 0 ]; mlt_producer self_producer = MLT_PLAYLIST_PRODUCER( self ); mlt_producer_seek( self_producer, 0 ); producer = entry->producer; mlt_producer_seek( producer, 0 ); } else { producer = &self->blank; } // Determine if we have moved to the next entry in the playlist. if ( original == total - 2 ) mlt_events_fire( properties, "playlist-next", i, NULL ); return MLT_PRODUCER_SERVICE( producer ); } /** Invoked when a producer indicates that it has prematurely reached its end. * * \private \memberof mlt_playlist_s * \param self a playlist * \return a producer * \see producer_get_frame */ static mlt_producer mlt_playlist_virtual_set_out( mlt_playlist self ) { // Default producer to blank mlt_producer producer = &self->blank; // Map playlist position to real producer in virtual playlist mlt_position position = mlt_producer_frame( &self->parent ); // Loop through the virtual playlist int i = 0; for ( i = 0; i < self->count; i ++ ) { if ( position < self->list[ i ]->frame_count ) { // Found it, now break producer = self->list[ i ]->producer; break; } else { // Decrement position by length of this entry position -= self->list[ i ]->frame_count; } } // Seek in real producer to relative position if ( i < self->count && self->list[ i ]->frame_out != position ) { // Update the frame_count for the changed clip (hmmm) self->list[ i ]->frame_out = position; self->list[ i ]->frame_count = self->list[ i ]->frame_out - self->list[ i ]->frame_in + 1; // Refresh the playlist mlt_playlist_virtual_refresh( self ); } return producer; } /** Obtain the current clips index. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the index of the playlist entry at the current position */ int mlt_playlist_current_clip( mlt_playlist self ) { // Map playlist position to real producer in virtual playlist mlt_position position = mlt_producer_frame( &self->parent ); // Loop through the virtual playlist int i = 0; for ( i = 0; i < self->count; i ++ ) { if ( position < self->list[ i ]->frame_count ) { // Found it, now break break; } else { // Decrement position by length of this entry position -= self->list[ i ]->frame_count; } } return i; } /** Obtain the current clips producer. * * \public \memberof mlt_playlist_s * \param self a playlist * \return the producer at the current position */ mlt_producer mlt_playlist_current( mlt_playlist self ) { int i = mlt_playlist_current_clip( self ); if ( i < self->count ) return self->list[ i ]->producer; else return &self->blank; } /** Get the position which corresponds to the start of the next clip. * * \public \memberof mlt_playlist_s * \param self 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 self, 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( self ) + index; break; case mlt_whence_relative_end: absolute_clip = self->count - index; break; } // Check that we're in a valid range if ( absolute_clip < 0 ) absolute_clip = 0; else if ( absolute_clip > self->count ) absolute_clip = self->count; // Now determine the position for ( i = 0; i < absolute_clip; i ++ ) position += self->list[ i ]->frame_count; return position; } /** Get all the info about the clip specified. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_playlist_clip_info *info, int index ) { int error = index < 0 || index >= self->count || self->list[ index ]->producer == NULL; memset( info, 0, sizeof( mlt_playlist_clip_info ) ); if ( !error ) { mlt_producer producer = mlt_producer_cut_parent( self->list[ index ]->producer ); mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); info->clip = index; info->producer = producer; info->cut = self->list[ index ]->producer; info->start = mlt_playlist_clip( self, mlt_whence_relative_start, index ); info->resource = mlt_properties_get( properties, "resource" ); info->frame_in = self->list[ index ]->frame_in; info->frame_out = self->list[ index ]->frame_out; info->frame_count = self->list[ index ]->frame_count; info->repeat = self->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 self a playlist * \return the number of playlist entries */ int mlt_playlist_count( mlt_playlist self ) { return self->count; } /** Clear the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \return true if there was an error */ int mlt_playlist_clear( mlt_playlist self ) { int i; for ( i = 0; i < self->count; i ++ ) { mlt_event_close( self->list[ i ]->event ); mlt_producer_close( self->list[ i ]->producer ); } self->count = 0; return mlt_playlist_virtual_refresh( self ); } /** Append a producer to the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param producer the producer to append * \return true if there was an error */ int mlt_playlist_append( mlt_playlist self, mlt_producer producer ) { // Append to virtual list return mlt_playlist_virtual_append( self, producer, 0, mlt_producer_get_playtime( producer ) - 1 ); } /** Append a producer to the playlist with in/out points. * * \public \memberof mlt_playlist_s * \param self a playlist * \param producer the producer to append * \param in the starting point on the producer; a negative value is the same as 0 * \param out the ending point on the producer; a negative value is the same as producer length - 1 * \return true if there was an error */ int mlt_playlist_append_io( mlt_playlist self, mlt_producer producer, mlt_position in, mlt_position out ) { // Append to virtual list if ( in < 0 && out < 0 ) return mlt_playlist_append( self, producer ); else return mlt_playlist_virtual_append( self, producer, in, out ); } /** Append a blank to the playlist of a given length. * * \public \memberof mlt_playlist_s * \param self a playlist * \param out the ending time of the blank entry, not its duration * \return true if there was an error */ int mlt_playlist_blank( mlt_playlist self, mlt_position out ) { // Append to the virtual list if ( out >= 0 ) return mlt_playlist_virtual_append( self, &self->blank, 0, out ); else return 1; } /** Append a blank item to the playlist with duration as a time string. * * \public \memberof mlt_playlist_s * \param self a playlist * \param length the duration of the blank entry as a time string * \return true if there was an error */ int mlt_playlist_blank_time( mlt_playlist self, const char* length ) { if ( self && length ) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); mlt_properties_set( properties , "_blank_time", length ); mlt_position duration = mlt_properties_get_position( properties, "_blank_time" ); return mlt_playlist_blank( self, duration - 1 ); } else return 1; } /** Insert a producer into the playlist. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_producer producer, int where, mlt_position in, mlt_position out ) { // Append to end mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); mlt_playlist_append_io( self, producer, in, out ); // Move to the position specified mlt_playlist_move( self, self->count - 1, where ); mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); return mlt_playlist_virtual_refresh( self ); } /** Remove an entry in the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param where the playlist entry index * \return true if there was an error */ int mlt_playlist_remove( mlt_playlist self, int where ) { int error = where < 0 || where >= self->count; if ( error == 0 && mlt_playlist_unmix( self, where ) != 0 ) { // We need to know the current clip and the position within the playlist int current = mlt_playlist_current_clip( self ); mlt_position position = mlt_producer_position( MLT_PLAYLIST_PRODUCER( self ) ); // We need all the details about the clip we're removing mlt_playlist_clip_info where_info; playlist_entry *entry = self->list[ where ]; mlt_properties properties = MLT_PRODUCER_PROPERTIES( entry->producer ); // Loop variable int i = 0; // Get the clip info mlt_playlist_get_clip_info( self, &where_info, where ); // Reorganise the list for ( i = where + 1; i < self->count; i ++ ) self->list[ i - 1 ] = self->list[ i ]; self->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( self ), where_info.start ); else if ( where < current && self->count > 0 ) mlt_producer_seek( MLT_PLAYLIST_PRODUCER( self ), position - where_info.frame_count ); else if ( self->count == 0 ) mlt_producer_seek( MLT_PLAYLIST_PRODUCER( self ), 0 ); // Free the entry free( entry ); // Refresh the playlist mlt_playlist_virtual_refresh( self ); } return error; } /** Move an entry in the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param src an entry index * \param dest an entry index * \return false */ int mlt_playlist_move( mlt_playlist self, 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 >= self->count ) src = self->count - 1; if ( dest < 0 ) dest = 0; if ( dest >= self->count ) dest = self->count - 1; if ( src != dest && self->count > 1 ) { int current = mlt_playlist_current_clip( self ); mlt_position position = mlt_producer_position( MLT_PLAYLIST_PRODUCER( self ) ); 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( self, ¤t_info, current ); position -= current_info.start; if ( current == src ) current = dest; else if ( src < current && current < dest ) current --; else if ( dest < current && current < src ) current ++; else if ( current == dest ) current = src; src_entry = self->list[ src ]; if ( src > dest ) { for ( i = src; i > dest; i -- ) self->list[ i ] = self->list[ i - 1 ]; } else { for ( i = src; i < dest; i ++ ) self->list[ i ] = self->list[ i + 1 ]; } self->list[ dest ] = src_entry; mlt_playlist_get_clip_info( self, ¤t_info, current ); mlt_producer_seek( MLT_PLAYLIST_PRODUCER( self ), current_info.start + position ); mlt_playlist_virtual_refresh( self ); } return 0; } /** Reorder the entries in the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param indices a list of current indices mapped to the new desired index * \return true if there was an error */ int mlt_playlist_reorder( mlt_playlist self, const int *indices ) { // Check that the playlist is sortable. if ( self->count < 2 ) return 1; // Sanity check the indices. The values must be in range and unique. int i, j; for ( i = 0; i < self->count - 1; i++ ) for ( j = i + 1; j < self->count; j++ ) if ( indices[i] < 0 || indices[i] >= self->count || indices[j] < 0 || indices[j] >= self->count || indices[i] == indices[j] ) return 1; // Create a new list to copy entries in a new order. playlist_entry **new_list = calloc( self->size, sizeof( playlist_entry * ) ); if ( new_list == NULL ) return 1; // Copy entries according to the new indices int new_index; for ( new_index = 0; new_index < self->count; new_index++ ) { int old_index = indices[new_index]; new_list[new_index] = self->list[old_index]; } // Delete the old list and save the new list free( self->list ); self->list = new_list; mlt_playlist_virtual_refresh( self ); return 0; } /** Repeat the specified clip n times. * * \public \memberof mlt_playlist_s * \param self 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 self, int clip, int repeat ) { int error = repeat < 1 || clip < 0 || clip >= self->count; if ( error == 0 ) { playlist_entry *entry = self->list[ clip ]; entry->repeat = repeat; mlt_playlist_virtual_refresh( self ); } return error; } /** Resize the specified clip. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param in the new starting time on the clip's producer; a negative value is the same as 0 * \param out the new ending time on the clip's producer; a negative value is the same as length - 1 * \return true if there was an error */ int mlt_playlist_resize_clip( mlt_playlist self, int clip, mlt_position in, mlt_position out ) { int error = clip < 0 || clip >= self->count; if ( error == 0 && mlt_playlist_resize_mix( self, clip, in, out ) != 0 ) { playlist_entry *entry = self->list[ clip ]; mlt_producer producer = entry->producer; mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); mlt_events_block( properties, properties ); if ( mlt_producer_is_blank( producer ) ) { mlt_position length = out - in + 1; // Make sure the parent blank is long enough to accommodate the length specified if ( length > mlt_producer_get_length( &self->blank ) ) { mlt_properties blank_props = MLT_PRODUCER_PROPERTIES( &self->blank ); mlt_properties_set_int( blank_props, "length", length ); mlt_producer_set_in_and_out( &self->blank, 0, out - in ); } // Make sure this cut of blank is long enough if ( length > mlt_producer_get_length( producer ) ) mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "length", length ); } if ( in < 0 ) in = 0; if ( out < 0 || 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( self ); } return error; } /** Split a clip on the playlist at the given position. * * This splits after the specified frame. * \public \memberof mlt_playlist_s * \param self 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 self, int clip, mlt_position position ) { int error = clip < 0 || clip >= self->count; if ( error == 0 ) { playlist_entry *entry = self->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( self ), self ); mlt_playlist_resize_clip( self, 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( self, split, clip + 1, 0, -1 ); mlt_properties_lock( entry_properties ); 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_properties_unlock( entry_properties ); mlt_producer_close( split ); } else { mlt_playlist_insert( self, &self->blank, clip + 1, 0, out - position - 1 ); } mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); mlt_playlist_virtual_refresh( self ); } else { error = 1; } } return error; } /** Split the playlist at the absolute position. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_position position, int left ) { int result = self == NULL ? -1 : 0; if ( !result ) { if ( position >= 0 && position < mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( self ) ) ) { int clip = mlt_playlist_get_clip_index_at( self, position ); mlt_playlist_clip_info info; mlt_playlist_get_clip_info( self, &info, clip ); if ( left && position != info.start ) mlt_playlist_split( self, clip, position - info.start - 1 ); else if ( !left ) mlt_playlist_split( self, clip, position - info.start ); result = position; } else if ( position <= 0 ) { result = 0; } else { result = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( self ) ); } } return result; } /** Join 1 or more consecutive clips. * * \public \memberof mlt_playlist_s * \param self 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 self, int clip, int count, int merge ) { int error = clip < 0 || clip >= self->count; if ( error == 0 ) { int i = clip; mlt_playlist new_clip = mlt_playlist_new( mlt_service_profile( MLT_PLAYLIST_SERVICE(self) ) ); mlt_properties_set_lcnumeric( MLT_PLAYLIST_PROPERTIES( new_clip ), mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); if ( clip + count >= self->count ) count = self->count - clip - 1; for ( i = 0; i <= count; i ++ ) { playlist_entry *entry = self->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( self, clip ); } mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); mlt_playlist_insert( self, 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. * * This version of the mix function does not utilize any frames beyond the out of * clip A or before the in point of clip B. It takes the frames needed for the length * of the transition by adjusting the duration of both clips - the out point for clip A * and the in point for clip B. * \public \memberof mlt_playlist_s * \param self 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 self, int clip, int length, mlt_transition transition ) { int error = ( clip < 0 || clip + 1 >= self->count ); if ( error == 0 ) { playlist_entry *clip_a = self->list[ clip ]; playlist_entry *clip_b = self->list[ clip + 1 ]; mlt_producer track_a = NULL; mlt_producer track_b = NULL; mlt_tractor tractor = mlt_tractor_new( ); mlt_service_set_profile( MLT_TRACTOR_SERVICE( tractor ), mlt_service_profile( MLT_PLAYLIST_SERVICE( self ) ) ); mlt_properties_set_lcnumeric( MLT_TRACTOR_PROPERTIES( tractor ), mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); // 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( self, 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( self, clip + 2 ); } else if ( clip_b->frame_out - clip_b->frame_in >= length ) { mlt_playlist_resize_clip( self, 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( self, 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( self, clip ); } else if ( clip_a->frame_out - clip_a->frame_in >= length ) { mlt_playlist_resize_clip( self, 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( self, clip ); } // Unblock and force a fire off of change events to listeners mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); mlt_playlist_virtual_refresh( self ); mlt_tractor_close( tractor ); } return error; } /** Mix consecutive clips for a specified length. * * This version of the mix function maintains the out point of the clip A by occupying the * beginning of clip B before its current in point. Therefore, it ends up adjusting the in * point and duration of clip B without affecting the duration of clip A. * Also, therefore, there must be enough frames after the out point of clip A. * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param length the number of frames over which to create the mix * \return true if there was an error */ int mlt_playlist_mix_in( mlt_playlist self, int clip, int length ) { int error = ( clip < 0 || clip + 1 >= self->count ); if ( error == 0 ) { playlist_entry *clip_a = self->list[ clip ]; playlist_entry *clip_b = self->list[ clip + 1 ]; mlt_producer track_a = NULL; mlt_producer track_b = NULL; mlt_tractor tractor = mlt_tractor_new( ); mlt_service_set_profile( MLT_TRACTOR_SERVICE( tractor ), mlt_service_profile( MLT_PLAYLIST_SERVICE( self ) ) ); mlt_properties_set_lcnumeric( MLT_TRACTOR_PROPERTIES( tractor ), mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); // Check length is valid for both clips and resize if necessary. int max_size = ( clip_a->frame_out + 1 ) > clip_b->frame_count ? ( clip_a->frame_out + 1 ) : 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_out + 1 ) track_a = mlt_producer_cut( clip_a->producer, clip_a->frame_out + 1, clip_a->frame_out + length ); 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( self, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 ); mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL ); // 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( self, clip + 2 ); } else if ( clip_b->frame_out - clip_b->frame_in >= length ) { mlt_playlist_resize_clip( self, 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( self, 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( self, clip ); } else if ( clip_a->frame_out - clip_a->frame_in > 0 ) { 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( self, clip ); } // Unblock and force a fire off of change events to listeners mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); mlt_playlist_virtual_refresh( self ); mlt_tractor_close( tractor ); } return error; } /** Mix consecutive clips for a specified length. * * This version of the mix function maintains the in point of the B clip by occupying the * end of clip A before its current out point. Therefore, it ends up adjusting the out * point and duration of clip A without affecting the duration or starting frame of clip B. * Also, therefore, there must be enough frames before the in point of clip B. * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \param length the number of frames over which to create the mix * \return true if there was an error */ int mlt_playlist_mix_out( mlt_playlist self, int clip, int length ) { int error = ( clip < 0 || clip + 1 >= self->count ); if ( error == 0 ) { playlist_entry *clip_a = self->list[ clip ]; playlist_entry *clip_b = self->list[ clip + 1 ]; mlt_producer track_a = NULL; mlt_producer track_b = NULL; mlt_tractor tractor = mlt_tractor_new( ); mlt_service_set_profile( MLT_TRACTOR_SERVICE( tractor ), mlt_service_profile( MLT_PLAYLIST_SERVICE( self ) ) ); mlt_properties_set_lcnumeric( MLT_TRACTOR_PROPERTIES( tractor ), mlt_properties_get_lcnumeric( MLT_PLAYLIST_PROPERTIES( self ) ) ); mlt_events_block( MLT_PLAYLIST_PROPERTIES( self ), self ); // Check length is valid for both clips and resize if necessary. int max_size = clip_a->frame_count > clip_b->frame_in ? clip_a->frame_count : clip_b->frame_in; 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_in ) track_b = mlt_producer_cut( clip_b->producer, clip_b->frame_in - length, clip_b->frame_in - 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( self, MLT_TRACTOR_PRODUCER( tractor ), clip + 1, -1, -1 ); mlt_properties_set_data( MLT_TRACTOR_PROPERTIES( tractor ), "mlt_mix", tractor, 0, NULL, NULL ); // 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( self, clip + 2 ); } else if ( clip_b->frame_out - clip_b->frame_in > 0 ) { 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( self, 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( self, clip ); } else if ( clip_a->frame_out - clip_a->frame_in >= length ) { mlt_playlist_resize_clip( self, 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( self, clip ); } // Unblock and force a fire off of change events to listeners mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); mlt_playlist_virtual_refresh( self ); mlt_tractor_close( tractor ); } return error; } /** Add a transition to an existing mix. * * \public \memberof mlt_playlist_s * \param self 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 self, int clip, mlt_transition transition ) { mlt_producer producer = mlt_producer_cut_parent( mlt_playlist_get_clip( self, 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, self->list[ clip ]->frame_count - 1 ); } return error; } /** Return the clip at the clip index. * * \public \memberof mlt_playlist_s * \param self 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 self, int clip ) { if ( clip >= 0 && clip < self->count ) return self->list[ clip ]->producer; return NULL; } /** Return the clip at the specified position. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_position position ) { int index = 0, total = 0; return mlt_playlist_locate( self, &position, &index, &total ); } /** Return the clip index of the specified position. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_position position ) { int index = 0, total = 0; mlt_playlist_locate( self, &position, &index, &total ); return index; } /** Determine if the clip is a mix. * * \public \memberof mlt_playlist_s * \param self 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 self, int clip ) { mlt_producer producer = mlt_producer_cut_parent( mlt_playlist_get_clip( self, 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 self a playlist * \param clip the index of the playlist entry * \return true if there was an error */ static int mlt_playlist_unmix( mlt_playlist self, int clip ) { int error = ( clip < 0 || clip >= self->count ); // Ensure that the clip request is actually a mix if ( error == 0 ) { mlt_producer producer = mlt_producer_cut_parent( self->list[ clip ]->producer ); mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); error = mlt_properties_get_data( properties, "mlt_mix", NULL ) == NULL || self->list[ clip ]->preservation_hack; } if ( error == 0 ) { playlist_entry *mix = self->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( self ), self ); 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( self, 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( self, cut, clip + 1, -1, -1 ); } mlt_properties_set_data( properties, "mlt_mix", NULL, 0, NULL, NULL ); mlt_playlist_remove( self, clip ); mlt_events_unblock( MLT_PLAYLIST_PROPERTIES( self ), self ); mlt_playlist_virtual_refresh( self ); } return error; } /** Resize a mix clip. * * \private \memberof mlt_playlist_s * \param self 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 self, int clip, int in, int out ) { int error = ( clip < 0 || clip >= self->count ); // Ensure that the clip request is actually a mix if ( error == 0 ) { mlt_producer producer = mlt_producer_cut_parent( self->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 = self->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( self ), self ); 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( self ), self ); mlt_playlist_virtual_refresh( self ); } return error; } /** Consolidate adjacent blank producers. * * \public \memberof mlt_playlist_s * \param self a playlist * \param keep_length set false to remove the last entry if it is blank */ void mlt_playlist_consolidate_blanks( mlt_playlist self, int keep_length ) { if ( self != NULL ) { int i = 0; mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); mlt_events_block( properties, properties ); for ( i = 1; i < self->count; i ++ ) { playlist_entry *left = self->list[ i - 1 ]; playlist_entry *right = self->list[ i ]; if ( mlt_producer_is_blank( left->producer ) && mlt_producer_is_blank( right->producer ) ) { mlt_playlist_resize_clip( self, i - 1, 0, left->frame_count + right->frame_count - 1 ); mlt_playlist_remove( self, i -- ); } } if ( !keep_length && self->count > 0 ) { playlist_entry *last = self->list[ self->count - 1 ]; if ( mlt_producer_is_blank( last->producer ) ) mlt_playlist_remove( self, self->count - 1 ); } mlt_events_unblock( properties, properties ); mlt_playlist_virtual_refresh( self ); } } /** Determine if the specified clip index is a blank. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return true if \p clip is a "blank" producer */ int mlt_playlist_is_blank( mlt_playlist self, int clip ) { return self == NULL || mlt_producer_is_blank( mlt_playlist_get_clip( self, clip ) ); } /** Determine if the specified position is a blank. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_position position ) { return self == NULL || mlt_producer_is_blank( mlt_playlist_get_clip_at( self, position ) ); } /** Replace the specified clip with a blank and return the clip. * * \public \memberof mlt_playlist_s * \param self 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 self, int clip ) { mlt_producer producer = NULL; if ( !mlt_playlist_is_blank( self, clip ) ) { playlist_entry *entry = self->list[ clip ]; int in = entry->frame_in; int out = entry->frame_out; mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); producer = entry->producer; mlt_properties_inc_ref( MLT_PRODUCER_PROPERTIES( producer ) ); mlt_events_block( properties, properties ); mlt_playlist_remove( self, clip ); mlt_playlist_blank( self, out - in ); mlt_playlist_move( self, self->count - 1, clip ); mlt_events_unblock( properties, properties ); mlt_playlist_virtual_refresh( self ); mlt_producer_set_in_and_out( producer, in, out ); } return producer; } /** Insert blank space. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the new blank section * \param out the ending time of the new blank section (duration - 1) */ void mlt_playlist_insert_blank( mlt_playlist self, int clip, int out ) { if ( self != NULL && out >= 0 ) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); mlt_events_block( properties, properties ); mlt_playlist_blank( self, out ); mlt_playlist_move( self, self->count - 1, clip ); mlt_events_unblock( properties, properties ); mlt_playlist_virtual_refresh( self ); } } /** Resize a blank entry. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_position position, int length, int find ) { if ( self != NULL && length != 0 ) { int clip = mlt_playlist_get_clip_index_at( self, position ); mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); mlt_events_block( properties, properties ); if ( find && clip < self->count && !mlt_playlist_is_blank( self, clip ) ) clip ++; if ( clip < self->count && mlt_playlist_is_blank( self, clip ) ) { mlt_playlist_clip_info info; mlt_playlist_get_clip_info( self, &info, clip ); if ( info.frame_out + length > info.frame_in ) mlt_playlist_resize_clip( self, clip, info.frame_in, info.frame_out + length ); else mlt_playlist_remove( self, clip ); } else if ( find && clip < self->count && length > 0 ) { mlt_playlist_insert_blank( self, clip, length ); } mlt_events_unblock( properties, properties ); mlt_playlist_virtual_refresh( self ); } } /** Insert a clip at a specific time. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_position position, mlt_producer producer, int mode ) { int ret = self == NULL || position < 0 || producer == NULL; if ( ret == 0 ) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); int length = mlt_producer_get_playtime( producer ); int clip = mlt_playlist_get_clip_index_at( self, position ); mlt_playlist_clip_info info; mlt_playlist_get_clip_info( self, &info, clip ); mlt_events_block( properties, self ); if ( clip < self->count && mlt_playlist_is_blank( self, clip ) ) { // Split and move to new clip if need be if ( position != info.start && mlt_playlist_split( self, clip, position - info.start - 1 ) == 0 ) mlt_playlist_get_clip_info( self, &info, ++ clip ); // Split again if need be if ( length < info.frame_count ) mlt_playlist_split( self, clip, length - 1 ); // Remove mlt_playlist_remove( self, clip ); // Insert mlt_playlist_insert( self, producer, clip, -1, -1 ); ret = clip; } else if ( clip < self->count ) { if ( position > info.start + info.frame_count / 2 ) clip ++; if ( mode == 1 && clip < self->count && mlt_playlist_is_blank( self, clip ) ) { mlt_playlist_get_clip_info( self, &info, clip ); if ( length < info.frame_count ) mlt_playlist_split( self, clip, length ); mlt_playlist_remove( self, clip ); } mlt_playlist_insert( self, producer, clip, -1, -1 ); ret = clip; } else { if ( mode == 1 ) { if ( position == info.start ) mlt_playlist_remove( self, clip ); else mlt_playlist_blank( self, position - mlt_properties_get_int( properties, "length" ) - 1 ); } mlt_playlist_append( self, producer ); ret = self->count - 1; } mlt_events_unblock( properties, self ); mlt_playlist_virtual_refresh( self ); } else { ret = -1; } return ret; } /** Get the time at which the clip starts relative to the playlist. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return the starting time */ int mlt_playlist_clip_start( mlt_playlist self, int clip ) { mlt_playlist_clip_info info; if ( mlt_playlist_get_clip_info( self, &info, clip ) == 0 ) return info.start; return clip < 0 ? 0 : mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( self ) ); } /** Get the playable duration of the clip. * * \public \memberof mlt_playlist_s * \param self a playlist * \param clip the index of the playlist entry * \return the duration of the playlist entry */ int mlt_playlist_clip_length( mlt_playlist self, int clip ) { mlt_playlist_clip_info info; if ( mlt_playlist_get_clip_info( self, &info, clip ) == 0 ) return info.frame_count; return 0; } /** Get the duration of a blank space. * * \public \memberof mlt_playlist_s * \param self 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 self, int clip, int bounded ) { int count = 0; mlt_playlist_clip_info info; if ( self != NULL && clip < self->count ) { mlt_playlist_get_clip_info( self, &info, clip ); if ( mlt_playlist_is_blank( self, clip ) ) count += info.frame_count; if ( bounded == 0 ) bounded = self->count; for ( clip ++; clip < self->count && bounded >= 0; clip ++ ) { mlt_playlist_get_clip_info( self, &info, clip ); if ( mlt_playlist_is_blank( self, clip ) ) count += info.frame_count; else bounded --; } } return count; } /** Remove a portion of the playlist by time. * * \public \memberof mlt_playlist_s * \param self 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 self, mlt_position position, int length ) { int index = mlt_playlist_get_clip_index_at( self, position ); if ( index >= 0 && index < self->count ) { mlt_properties properties = MLT_PLAYLIST_PROPERTIES( self ); int clip_start = mlt_playlist_clip_start( self, index ); int list_length = mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( self ) ); mlt_events_block( properties, self ); if ( position + length > list_length ) length -= ( position + length - list_length ); if ( clip_start < position ) { mlt_playlist_split( self, index ++, position - clip_start - 1 ); } while( length > 0 ) { if ( mlt_playlist_clip_length( self, index ) > length ) mlt_playlist_split( self, index, length - 1 ); length -= mlt_playlist_clip_length( self, index ); mlt_playlist_remove( self, index ); } mlt_playlist_consolidate_blanks( self, 0 ); mlt_events_unblock( properties, self ); mlt_playlist_virtual_refresh( self ); // Just to be sure, we'll get the clip index again... index = mlt_playlist_get_clip_index_at( self, position ); } return index; } /** Not implemented * * \deprecated not implemented * \public \memberof mlt_playlist_s * \param self * \param position * \param length * \param new_position * \return */ int mlt_playlist_move_region( mlt_playlist self, mlt_position position, int length, int new_position ) { if ( self != 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 self = 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( self, &progressive ); // Check that we have a producer if ( real == NULL ) { *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); return 0; } // Get the frame mlt_properties_inc_ref(MLT_SERVICE_PROPERTIES(real)); 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 ) ); } mlt_properties_dec_ref(MLT_SERVICE_PROPERTIES(real)); // 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( self ); // 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 self a playlist */ void mlt_playlist_close( mlt_playlist self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_PLAYLIST_PROPERTIES( self ) ) <= 0 ) { int i = 0; self->parent.close = NULL; for ( i = 0; i < self->count; i ++ ) { mlt_event_close( self->list[ i ]->event ); mlt_producer_close( self->list[ i ]->producer ); free( self->list[ i ] ); } mlt_producer_close( &self->blank ); mlt_producer_close( &self->parent ); free( self->list ); free( self ); } } mlt-6.20.0/src/framework/mlt_playlist.h000066400000000000000000000155621362234133600200610ustar00rootroot00000000000000/** * \file mlt_playlist.h * \brief playlist service class * \see mlt_playlist_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 * \properties \em hide Set to 1 to hide the video (make it an audio-only track), * 2 to hide the audio (make it a video-only track), or 3 to hide audio and video (hidden track). * This property only applies when using a multitrack or transition. * \event \em playlist-next The playlist fires this when it moves to the next item in the list. * The listener receives one argument that is the index of the entry that just completed. */ 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_playlist mlt_playlist_new( mlt_profile profile ); 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 out ); extern int mlt_playlist_blank_time( mlt_playlist self, const char *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_reorder( mlt_playlist self, const int *indices ); 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_in( mlt_playlist self, int clip, int length ); extern int mlt_playlist_mix_out( mlt_playlist self, int clip, int length ); 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 out ); 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 mlt-6.20.0/src/framework/mlt_pool.c000066400000000000000000000230511362234133600171540ustar00rootroot00000000000000/** * \file mlt_pool.c * \brief memory pooling functionality * \see mlt_pool_s * * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_properties.h" #include "mlt_deque.h" #include "mlt_log.h" #include #include #include // Not nice - memalign is defined here apparently? #ifdef linux #include #endif // Macros to re-assign system functions. #ifdef _WIN32 # define mlt_free _aligned_free # define mlt_alloc(X) _aligned_malloc( (X), 16 ) # define mlt_realloc(X, Y) _aligned_realloc( (X), (Y), 16 ) #else # define mlt_free free # ifdef linux # define mlt_alloc(X) memalign( 16, (X) ) # else # define mlt_alloc(X) malloc( (X) ) # endif # define mlt_realloc realloc #endif // We now require a compile-time define to use mlt_pool. #ifndef USE_MLT_POOL #define USE_MLT_POOL 1 #endif #if !USE_MLT_POOL void mlt_pool_init() {} void *mlt_pool_alloc( int size ) { return mlt_alloc( size ); } void *mlt_pool_realloc( void *ptr, int size ) { return mlt_realloc( ptr, size ); } void mlt_pool_release( void *release ) { return mlt_free( release ); } void mlt_pool_purge() {} void mlt_pool_close() {} void mlt_pool_stat() {} #else /** 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 * * Aligned to 16 byte in case we toss buffers to external assembly * optimized libraries (sse/altivec). */ typedef struct __attribute__ ((aligned (16))) 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 self = calloc( 1, sizeof( struct mlt_pool_s ) ); // Initialise it if ( self != NULL ) { // Initialise the mutex pthread_mutex_init( &self->lock, NULL ); // Create the stack self->stack = mlt_deque_init( ); // Assign the size self->size = size; } // Return it return self; } /** Get an item from the pool. * * \private \memberof mlt_pool_s * \param self a pool * \return an opaque pointer */ static void *pool_fetch( mlt_pool self ) { // We will generate a release object void *ptr = NULL; // Sanity check if ( self != NULL ) { // Lock the pool pthread_mutex_lock( &self->lock ); // Check if the stack is empty if ( mlt_deque_count( self->stack ) != 0 ) { // Pop the top of the stack ptr = mlt_deque_pop_back( self->stack ); // Assign the reference ( ( mlt_release )ptr )->references = 1; } else { // We need to generate a release item mlt_release release = mlt_alloc( self->size ); // If out of memory, log it, reclaim memory, and try again. if ( !release && self->size > 0 ) { mlt_log_fatal( NULL, "[mlt_pool] out of memory\n" ); mlt_pool_purge(); release = mlt_alloc( self->size ); } // Initialise it if ( release != NULL ) { // Increment the number of items allocated to this pool self->count ++; // Assign the pool release->pool = self; // Assign the reference release->references = 1; // Determine the ptr ptr = ( char * )release + sizeof( struct mlt_release_s ); } } // Unlock the pool pthread_mutex_unlock( &self->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 self = that->pool; if ( self != NULL ) { // Lock the pool pthread_mutex_lock( &self->lock ); // Push the that back back on to the stack mlt_deque_push_back( self->stack, ptr ); // Unlock the pool pthread_mutex_unlock( &self->lock ); return; } // Free the release itself mlt_free( ( char * )ptr - sizeof( struct mlt_release_s ) ); } } /** Destroy a pool. * * \private \memberof mlt_pool_s * \param self a pool */ static void pool_close( mlt_pool self ) { if ( self != 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( self->stack ) ) != NULL ) { // We'll free this item now mlt_free( ( char * )release - sizeof( struct mlt_release_s ) ); } // We can now close the stack mlt_deque_close( self->stack ); // Destroy the mutex pthread_mutex_destroy( &self->lock ); // Close the pool free( self ); } } /** 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 += sizeof( struct mlt_release_s ); 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 self = mlt_properties_get_data_at( pools, i, NULL ); // Pointer to unused memory void *release = NULL; // Lock the pool pthread_mutex_lock( &self->lock ); // We'll free all unused items now while ( ( release = mlt_deque_pop_back( self->stack ) ) != NULL ) { mlt_free( ( char * )release - sizeof( struct mlt_release_s ) ); self->count--; } // Unlock the pool pthread_mutex_unlock( &self->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_ mlt_pool_stat( ); #endif // Close the properties mlt_properties_close( pools ); } void mlt_pool_stat( ) { // Stats dump uint64_t allocated = 0, used = 0, s; int i = 0, c = mlt_properties_count( pools ); mlt_log( NULL, MLT_LOG_VERBOSE, "%s: count %d\n", __FUNCTION__, c); for ( i = 0; i < c; i ++ ) { mlt_pool pool = mlt_properties_get_data_at( pools, i, NULL ); if ( pool->count ) mlt_log_verbose( NULL, "%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 ) ? '*' : ' ' ); s = pool->size; s *= pool->count; allocated += s; s = pool->count - mlt_deque_count( pool->stack ); s *= pool->size; used += s; } mlt_log_verbose( NULL, "%s: allocated %"PRIu64" bytes, used %"PRIu64" bytes \n", __FUNCTION__, allocated, used ); } #endif // NO_MLT_POOL mlt-6.20.0/src/framework/mlt_pool.h000066400000000000000000000022431362234133600171610ustar00rootroot00000000000000/** * \file mlt_pool.h * \brief memory pooling functionality * \see mlt_pool_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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( ); extern void mlt_pool_stat( ); #endif mlt-6.20.0/src/framework/mlt_producer.c000066400000000000000000001135601362234133600200330ustar00rootroot00000000000000/** * \file mlt_producer.c * \brief abstraction for all producer services * \see mlt_producer_s * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 // for stat() #include // for stat() #include // for strftime() and gtime() #include // for stat() /* Forward references. */ static int producer_get_frame( mlt_service self, mlt_frame_ptr frame, int index ); static void mlt_producer_property_changed( mlt_service owner, mlt_producer self, char *name ); static void mlt_producer_service_changed( mlt_service owner, mlt_producer self ); /* 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 self 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 self, void *child ) { // Check that we haven't received NULL int error = self == NULL; // Continue if no error if ( error == 0 ) { #ifdef _MLT_PRODUCER_CHECKS_ producers_created ++; #endif // Initialise the producer memset( self, 0, sizeof( struct mlt_producer_s ) ); // Associate with the child self->child = child; // Initialise the service if ( mlt_service_init( &self->parent, self ) == 0 ) { // The parent is the service mlt_service parent = &self->parent; // Define the parent close parent->close = ( mlt_destructor )mlt_producer_close; parent->close_object = self; // For convenience, we'll assume the close_object is self self->close_object = self; // 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, "_speed", 1.0 ); mlt_properties_set_position( properties, "in", 0 ); char *e = getenv( "MLT_DEFAULT_PRODUCER_LENGTH" ); int p = e ? atoi( e ) : 15000; mlt_properties_set_position( properties, "out", MAX(0, p - 1) ); mlt_properties_set_position( properties, "length", p ); 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, self, "service-changed", ( mlt_listener )mlt_producer_service_changed ); mlt_events_listen( properties, self, "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 self the producer * \param name the property that changed */ static void mlt_producer_property_changed( mlt_service owner, mlt_producer self, char *name ) { if ( !strcmp( name, "in" ) || !strcmp( name, "out" ) || !strcmp( name, "length" ) ) mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), "producer-changed", NULL ); } /** Listener for service changes. * * Fires the "producer-changed" event. * * \private \memberof mlt_producer_s * \param owner a service (ignored) * \param self the producer */ static void mlt_producer_service_changed( mlt_service owner, mlt_producer self ) { mlt_events_fire( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), "producer-changed", NULL ); } /** Create and initialize a new producer. * * \public \memberof mlt_producer_s * \return the new producer */ mlt_producer mlt_producer_new( mlt_profile profile ) { mlt_producer self = malloc( sizeof( struct mlt_producer_s ) ); if ( self ) { if ( mlt_producer_init( self, NULL ) == 0 ) { mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( self ), "_profile", profile, 0, NULL, NULL ); mlt_properties_set_double( MLT_PRODUCER_PROPERTIES( self ), "aspect_ratio", mlt_profile_sar( profile ) ); } else { free( self ); return NULL; } } return self; } /** Determine if producer is a cut. * * \public \memberof mlt_producer_s * \param self a producer * \return true if \p self is a "cut" producer * \see mlt_producer_cut */ int mlt_producer_is_cut( mlt_producer self ) { return mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( self ), "_cut" ); } /** Determine if producer is a mix. * * \public \memberof mlt_producer_s * \param self a producer * \return true if \p self is a "mix" producer * \todo Define a mix producer. */ int mlt_producer_is_mix( mlt_producer self ) { mlt_properties properties = self != NULL ? MLT_PRODUCER_PROPERTIES( self ) : 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 self a producer * \return true if \p self is a "blank" producer * \see mlt_playlist_insert_blank */ int mlt_producer_is_blank( mlt_producer self ) { if ( self ) { const char *resource = mlt_properties_get( MLT_PRODUCER_PROPERTIES( mlt_producer_cut_parent( self ) ), "resource" ); return ( resource && !strcmp( "blank", resource ) ); } return ( self == NULL ); } /** Obtain the parent producer. * * \public \memberof mlt_producer_s * \param self a producer * \return either the parent producer if \p self is a "cut" producer or \p self otherwise. */ mlt_producer mlt_producer_cut_parent( mlt_producer self ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); if ( mlt_producer_is_cut( self ) ) return mlt_properties_get_data( properties, "_cut_parent", NULL ); else return self; } /** Create a cut of this producer. * * A "cut" is a portion of another (parent) producer. * * \public \memberof mlt_producer_s * \param self 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 self, int in, int out ) { mlt_producer result = mlt_producer_new( mlt_service_profile( MLT_PRODUCER_SERVICE( self ) ) ); mlt_producer parent = mlt_producer_cut_parent( self ); mlt_properties properties = MLT_PRODUCER_PROPERTIES( result ); mlt_properties parent_props = MLT_PRODUCER_PROPERTIES( parent ); mlt_properties_set_lcnumeric( properties, mlt_properties_get_lcnumeric( MLT_PRODUCER_PROPERTIES( self ) ) ); 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( self ) ) out = MAX(0, 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 self a producer * \return the service parent class * \see MLT_PRODUCER_SERVICE */ mlt_service mlt_producer_service( mlt_producer self ) { return self != NULL ? &self->parent : NULL; } /** Get the producer properties. * * \public \memberof mlt_producer_s * \param self a producer * \return the producer's property list * \see MLT_PRODUCER_PROPERTIES */ mlt_properties mlt_producer_properties( mlt_producer self ) { return MLT_SERVICE_PROPERTIES( &self->parent ); } /** Seek to a specified position. * * \public \memberof mlt_producer_s * \param self a producer * \param position set the "play head" position of the producer * \return false * \todo Document how the properties affect behavior. * \see mlt_producer_seek_time */ int mlt_producer_seek( mlt_producer self, mlt_position position ) { // Determine eof handling mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); 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( self ) ) mlt_producer_seek( mlt_producer_cut_parent( self ), position + mlt_producer_get_in( self ) ); // Check bounds if ( position < 0 || mlt_producer_get_playtime( self ) == 0 ) { position = 0; } else if ( use_points && ( eof == NULL || !strcmp( eof, "pause" ) ) && position >= mlt_producer_get_playtime( self ) ) { mlt_producer_set_speed( self, 0 ); position = mlt_producer_get_playtime( self ) - 1; } else if ( use_points && eof && !strcmp( eof, "loop" ) && position >= mlt_producer_get_playtime( self ) ) { position = (int)position % (int)mlt_producer_get_playtime( self ); } // Set the position mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( self ), "_position", position ); // Calculate the absolute frame mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( self ), "_frame", use_points * mlt_producer_get_in( self ) + position ); return 0; } /** Seek to a specified time string. * * \public \memberof mlt_producer_s * \param self a producer * \param time set the "play head" position of the producer to the time string * \return false * \see mlt_producer_seek */ int mlt_producer_seek_time( mlt_producer self, const char* time ) { mlt_properties_set( MLT_PRODUCER_PROPERTIES(self), "_seek_time", time ); mlt_position position = mlt_properties_get_position( MLT_PRODUCER_PROPERTIES(self), "_seek_time" ); return mlt_producer_seek( self, position ); } /** Get the current position (relative to in point). * * \public \memberof mlt_producer_s * \param self a producer * \return the position of the "play head" relative to its beginning */ mlt_position mlt_producer_position( mlt_producer self ) { return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( self ), "_position" ); } /** Get the current position (relative to start of producer). * * \public \memberof mlt_producer_s * \param self a producer * \return the position of the "play head" regardless of the in point */ mlt_position mlt_producer_frame( mlt_producer self ) { return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( self ), "_frame" ); } /** Get the current position (relative to start of producer) as a time string. * * \public \memberof mlt_producer_s * \param self a producer * \param format the time value format * \return the position of the "play head" regardless of the in point */ char* mlt_producer_frame_time( mlt_producer self, mlt_time_format format ) { return mlt_properties_get_time( MLT_PRODUCER_PROPERTIES( self ), "_frame", format ); } /** Set the playing speed. * * \public \memberof mlt_producer_s * \param self a producer * \param speed the new speed as a relative factor (1.0 = normal) * \return true if error */ int mlt_producer_set_speed( mlt_producer self, double speed ) { return mlt_properties_set_double( MLT_PRODUCER_PROPERTIES( self ), "_speed", speed ); } /** Get the playing speed. * * \public \memberof mlt_producer_s * \param self a producer * \return the speed as a relative factor (1.0 = normal) */ double mlt_producer_get_speed( mlt_producer self ) { return mlt_properties_get_double( MLT_PRODUCER_PROPERTIES( self ), "_speed" ); } /** Get the frames per second. * * This is determined by the producer's profile. * * \public \memberof mlt_producer_s * \param self a producer * \return the video refresh rate */ double mlt_producer_get_fps( mlt_producer self ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self ) ); 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 self a producer * \param in the relative starting time; a negative value is the same as 0 * \param out the relative ending time; a negative value is the same as length - 1 * \return false */ int mlt_producer_set_in_and_out( mlt_producer self, mlt_position in, mlt_position out ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); // Correct ins and outs if necessary if ( in < 0 ) in = 0; else if ( in >= mlt_producer_get_length( self ) ) in = MAX(0, mlt_producer_get_length( self ) - 1); if ( mlt_producer_is_blank( self ) && out >= mlt_producer_get_length( self ) ) // Extend the blank producer if needed. mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( self ), "length", out + 1 ); else if ( out < 0 || out >= mlt_producer_get_length( self ) ) // Get the out point from the length. out = MAX(0, mlt_producer_get_length( self ) - 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 self a producer * \return false */ int mlt_producer_clear( mlt_producer self ) { if ( self != NULL ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); 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 self a producer * \return the in point */ mlt_position mlt_producer_get_in( mlt_producer self ) { return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( self ), "in" ); } /** Get the out point. * * \public \memberof mlt_producer_s * \param self a producer * \return the out point */ mlt_position mlt_producer_get_out( mlt_producer self ) { return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( self ), "out" ); } /** Get the total play time. * * \public \memberof mlt_producer_s * \param self a producer * \return the playable (based on in and out points) duration */ mlt_position mlt_producer_get_playtime( mlt_producer self ) { return mlt_producer_get_out( self ) - mlt_producer_get_in( self ) + 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 self a producer * \return the duration of the producer regardless of in and out points */ mlt_position mlt_producer_get_length( mlt_producer self ) { return mlt_properties_get_position( MLT_PRODUCER_PROPERTIES( self ), "length" ); } /** Get the total, unedited length of the producer as a time string. * * The value returned by a live streaming producer is unknown. * * \public \memberof mlt_producer_s * \param self a producer * \param format the time value format * \return the duration of the producer regardless of in and out points */ char* mlt_producer_get_length_time( mlt_producer self, mlt_time_format format ) { return mlt_properties_get_time( MLT_PRODUCER_PROPERTIES( self ), "length", format ); } /** 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 self a producer */ void mlt_producer_prepare_next( mlt_producer self ) { if ( mlt_producer_get_speed( self ) != 0 ) mlt_producer_seek( self, mlt_producer_position( self ) + mlt_producer_get_speed( self ) ); } /** 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 self = service != NULL ? service->child : NULL; if ( self != NULL && !mlt_producer_is_cut( self ) ) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); // Determine eof handling char *eof = mlt_properties_get( MLT_PRODUCER_PROPERTIES( self ), "eof" ); // Get the speed of the producer double speed = mlt_producer_get_speed( self ); // 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 self clone = clone == NULL ? self : clone; // A properly instatiated producer will have a get_frame method... if ( self->get_frame == NULL || ( eof && !strcmp( eof, "continue" ) && mlt_producer_position( self ) > mlt_producer_get_out( self ) ) ) { // Generate a test frame *frame = mlt_frame_init( service ); // Set the position result = mlt_frame_set_position( *frame, mlt_producer_position( self ) ); // 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( self ); } else { // Get the frame from the implementation result = self->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 ( self != NULL ) { // Get the speed of the cut double speed = mlt_producer_get_speed( self ); // Get the parent of the cut mlt_producer parent = mlt_producer_cut_parent( self ); // 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( self ); // Determine the clone index int clone_index = mlt_properties_get_int( properties, "_clone" ); // Determine the clone to use mlt_producer clone = self; 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( self ) ), key, NULL ); if ( clone == NULL ) mlt_log( service, MLT_LOG_ERROR, "requested clone doesn't exist %d\n", clone_index ); clone = clone == NULL ? self : clone; } else { clone = parent; } // We need to seek to the correct position in the clone mlt_producer_seek( clone, mlt_producer_get_in( self ) + 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", self, 0, NULL, NULL ); mlt_properties_set_double( MLT_FRAME_PROPERTIES( *frame ), "_speed", speed ); mlt_producer_prepare_next( self ); } else { *frame = mlt_frame_init( service ); result = 0; } // Pass on all meta properties from the producer/cut on to the frame if ( *frame != NULL && self != NULL ) { int i = 0; mlt_properties p_props = MLT_PRODUCER_PROPERTIES( self ); mlt_properties f_props = MLT_FRAME_PROPERTIES( *frame ); mlt_properties_lock( p_props ); 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_value( p_props, i ) ); else if ( !strncmp( name, "set.", 4 ) ) mlt_properties_set( f_props, name + 4, mlt_properties_get_value( p_props, i ) ); } mlt_properties_unlock( p_props ); } return result; } /** Attach a filter. * * \public \memberof mlt_producer_s * \param self a producer * \param filter the filter to attach * \return true if there was an error */ int mlt_producer_attach( mlt_producer self, mlt_filter filter ) { return mlt_service_attach( MLT_PRODUCER_SERVICE( self ), filter ); } /** Detach a filter. * * \public \memberof mlt_producer_s * \param self a service * \param filter the filter to detach * \return true if there was an error */ int mlt_producer_detach( mlt_producer self, mlt_filter filter ) { return mlt_service_detach( MLT_PRODUCER_SERVICE( self ), filter ); } /** Retrieve a filter. * * \public \memberof mlt_producer_s * \param self 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 self, int index ) { return mlt_service_filter( MLT_PRODUCER_SERVICE( self ), index ); } /** Clone a producer. * * \private \memberof mlt_producer_s * \param self a producer * \return a new producer that is a copy of \p self * \see mlt_producer_set_clones */ static mlt_producer mlt_producer_clone( mlt_producer self ) { mlt_producer clone = NULL; mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); char *resource = mlt_properties_get( properties, "resource" ); char *service = mlt_properties_get( properties, "mlt_service" ); mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self ) ); 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, NULL, 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 self a producer * \param clones the number of copies to make * \see mlt_producer_optimise */ static void mlt_producer_set_clones( mlt_producer self, int clones ) { mlt_producer parent = mlt_producer_cut_parent( self ); 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 self, int multitrack, int track, int position ) { mlt_properties properties = mlt_parser_properties( self ); 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 self ) { mlt_properties properties = mlt_parser_properties( self ); mlt_deque stack = mlt_properties_get_data( properties, "stack", NULL ); return mlt_deque_pop_back( stack ); } static track_info *peek( mlt_parser self ) { mlt_properties properties = mlt_parser_properties( self ); mlt_deque stack = mlt_properties_get_data( properties, "stack", NULL ); return mlt_deque_peek_back( stack ); } static int on_start_multitrack( mlt_parser self, mlt_multitrack object ) { track_info *info = peek( self ); return push( self, info->multitrack ++, info->track, info->position ); } static int on_start_track( mlt_parser self ) { track_info *info = peek( self ); info->position -= info->offset; info->length -= info->offset; return push( self, info->multitrack, info->track ++, info->position ); } static int on_start_producer( mlt_parser self, mlt_producer object ) { mlt_properties properties = mlt_parser_properties( self ); 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( self ); 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 self ) { track_info *track = pop( self ); track_info *multi = peek( self ); multi->length += track->length; multi->position += track->length; multi->offset = track->length; free( track ); return 0; } static int on_end_multitrack( mlt_parser self, mlt_multitrack object ) { track_info *multi = pop( self ); track_info *track = peek( self ); 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 self a producer * \return true if there was an error */ int mlt_producer_optimise( mlt_producer self ) { 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( self ) ); 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_at( producers, k, &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 self a producer */ void mlt_producer_close( mlt_producer self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_PRODUCER_PROPERTIES( self ) ) <= 0 ) { self->parent.close = NULL; if ( self->close != NULL ) { self->close( self->close_object ); } else { int destroy = mlt_producer_is_cut( self ); #if _MLT_PRODUCER_CHECKS_ == 1 // Show debug info mlt_properties_debug( MLT_PRODUCER_PROPERTIES( self ), "Producer closing", stderr ); #endif #ifdef _MLT_PRODUCER_CHECKS_ // Show current stats - these should match when the app is closed mlt_log( MLT_PRODUCER_SERVICE( self ), MLT_LOG_DEBUG, "Producers created %d, destroyed %d\n", producers_created, ++producers_destroyed ); #endif mlt_service_close( &self->parent ); if ( destroy ) free( self ); } } } /* * Boost implementation of timegm() * (C) Copyright Howard Hinnant * (C) Copyright 2010-2011 Vicente J. Botet Escriba */ static inline int32_t is_leap(int32_t year) { if(year % 400 == 0) return 1; if(year % 100 == 0) return 0; if(year % 4 == 0) return 1; return 0; } static inline int32_t days_from_0(int32_t year) { year--; return 365 * year + (year / 400) - (year/100) + (year / 4); } static inline int32_t days_from_1970(int32_t year) { const int days_from_0_to_1970 = days_from_0(1970); return days_from_0(year) - days_from_0_to_1970; } static inline int32_t days_from_1jan(int32_t year,int32_t month,int32_t day) { static const int32_t days[2][12] = { { 0,31,59,90,120,151,181,212,243,273,304,334}, { 0,31,60,91,121,152,182,213,244,274,305,335} }; return days[is_leap(year)][month-1] + day - 1; } static inline time_t internal_timegm(struct tm const *t) { int year = t->tm_year + 1900; int month = t->tm_mon; if(month > 11) { year += month/12; month %= 12; } else if(month < 0) { int years_diff = (-month + 11)/12; year -= years_diff; month+=12 * years_diff; } month++; int day = t->tm_mday; int day_of_year = days_from_1jan(year,month,day); int days_since_epoch = days_from_1970(year) + day_of_year; time_t seconds_in_day = 3600 * 24; time_t result = seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec; return result; } /* End of Boost implementation of timegm(). */ /** Get the creation time for the producer. * * The creation_time value is searched in the following order: * - A "creation_time" property in ISO 8601 format (yyyy-mm-ddThh:mm:ss) * - A "meta.attr.com.apple.quicktime.creationdate.markup" property in ISO 8601 format * - A "meta.attr.creation_time.markup" property in ISO 8601 format * - If the producer has a resource that is a file, the mtime of the file * * \public \memberof mlt_producer_s * \param self a producer * \return the creation time of the producer in seconds since the epoch */ int64_t mlt_producer_get_creation_time( mlt_producer self ) { mlt_producer producer = mlt_producer_cut_parent( self ); // Prefer creation_time producer property if present char* datestr = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "creation_time"); if (!datestr) { // Fall back to quicktime creationdate metadata (common for .mov files) // creationdate is preferred over creation_time metadata because // creation_time may be recalculated if the device re-encodes the file. datestr = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "meta.attr.com.apple.quicktime.creationdate.markup"); } if (!datestr) { // Fall back to creation_time metadata (common for most media handled by ffmpeg) datestr = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "meta.attr.creation_time.markup"); } if (datestr) { struct tm time_info = {0}; double seconds; char offset_indicator = 0; int hour_offset = 0; int min_offset = 0; int ret = sscanf(datestr, "%04d-%02d-%02dT%02d:%02d:%lf%c%02d%02d", &time_info.tm_year, &time_info.tm_mon, &time_info.tm_mday, &time_info.tm_hour, &time_info.tm_min, &seconds, &offset_indicator, &hour_offset, &min_offset); if (ret >= 6) { time_info.tm_sec = (int) seconds; time_info.tm_mon -= 1; time_info.tm_year -= 1900; time_info.tm_isdst =-1; int64_t milliseconds = (int64_t) internal_timegm(&time_info) * 1000; milliseconds += (seconds - (double)time_info.tm_sec) * 1000.0; // Apply time zone offset if present. if (ret == 9 && offset_indicator == '-') { milliseconds += ((hour_offset * 60) + min_offset) * 60000; } else if (ret == 9 && offset_indicator == '+') { milliseconds -= ((hour_offset * 60) + min_offset) * 60000; } return milliseconds; } } // Fall back to file modification time. char* resource = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "resource"); if (!resource) { resource = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "warp_resource"); } if (resource) { struct stat file_info; if ( !stat( resource, &file_info ) ) { return (int64_t)file_info.st_mtime * 1000; } } return 0; } /** Set the creation time for the producer. * * A "creation_time" property in ISO 8601 format (yyyy-mm-ddThh:mm:ss) will be * applied to the producer. * * \public \memberof mlt_producer_s * \param self a producer * \param creation_time the creation time of the producer in seconds since the epoch */ void mlt_producer_set_creation_time( mlt_producer self, int64_t creation_time ) { time_t time = creation_time / 1000; mlt_producer parent = mlt_producer_cut_parent( self ); char* datestr = calloc( 1, 20 ); strftime( datestr, 20, "%Y-%m-%dT%H:%M:%S", gmtime( &time ) ); mlt_properties_set( MLT_PRODUCER_PROPERTIES( parent ), "creation_time", datestr); free( datestr ); } mlt-6.20.0/src/framework/mlt_producer.h000066400000000000000000000150161362234133600200350ustar00rootroot00000000000000/** * \file mlt_producer.h * \brief abstraction for all producer services * \see mlt_producer_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PRODUCER_H #define MLT_PRODUCER_H #include "mlt_service.h" #include "mlt_filter.h" #include "mlt_profile.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 either service-changed was fired or the timing of the 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 * \envvar \em MLT_DEFAULT_PRODUCER_LENGTH - the default duration of the producer in frames, defaults to 15000. * Most producers will set the producer length to something appropriate * like the real duration of an audio or video clip. However, some other things * like still images and generators do not have an intrinsic length besides one * or infinity. Those producers tend to not override the default length and one * expect the app or user to set the length. The default value of 15000 was chosen * to provide something useful - not too long or short and convenient to simply * set an out point without necessarily nedding to extend the length. * \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( mlt_profile ); 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 int mlt_producer_seek_time( mlt_producer self, const char* time ); extern mlt_position mlt_producer_position( mlt_producer self ); extern mlt_position mlt_producer_frame( mlt_producer self ); char* mlt_producer_frame_time( mlt_producer self, mlt_time_format ); 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 char* mlt_producer_get_length_time( mlt_producer self, mlt_time_format ); 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 ); int64_t mlt_producer_get_creation_time( mlt_producer self ); void mlt_producer_set_creation_time( mlt_producer self, int64_t creation_time ); #endif mlt-6.20.0/src/framework/mlt_profile.c000066400000000000000000000366621362234133600176570ustar00rootroot00000000000000/** * \file mlt_profile.c * \brief video output definition * \see mlt_profile_s * * Copyright (C) 2007-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt.h" #include #include #include #include /** the default subdirectory of the datadir for holding profiles */ #define PROFILES_DIR "/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 $datadir/mlt/profiles else if ( !prefix && mlt_environment( "MLT_DATA" ) ) { prefix = mlt_environment( "MLT_DATA" ); filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + strlen( name ) + 1 ); strcpy( filename, prefix ); strcat( filename, PROFILES_DIR ); } // Use environment variable instead else if ( prefix ) { filename = calloc( 1, strlen( prefix ) + strlen( name ) + 2 ); strcpy( filename, prefix ); if ( filename[ strlen( filename ) - 1 ] != '/' ) filename[ strlen( filename ) ] = '/'; } else { mlt_properties_close( properties ); return profile; } // 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; profile->colorspace = 601; } } } return profile; } static void set_mlt_normalisation( const char *profile_name ) { if ( profile_name ) { if ( strstr( profile_name, "_ntsc" ) || strstr( profile_name, "_60" ) || strstr( profile_name, "_5994" ) || strstr( profile_name, "_2997" ) || 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" ); } } } /** 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 ) ); set_mlt_normalisation( basename( filename ) ); free( filename ); } mlt_properties_close( properties ); } // Set MLT_NORMALISATION to appease legacy modules char *profile_name = mlt_environment( "MLT_PROFILE" ); set_mlt_normalisation( profile_name ); 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" ); profile->colorspace = mlt_properties_get_int( properties, "colorspace" ); } 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(); mlt_profile profile = NULL; 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++; } profile = mlt_profile_load_properties( properties ); mlt_properties_close( properties ); } return profile; } /** Get the video frame rate as a floating point value. * * \public \memberof mlt_profile_s * @param profile a profile * @return the frame rate */ double mlt_profile_fps( mlt_profile profile ) { if ( profile ) return ( double ) profile->frame_rate_num / profile->frame_rate_den; else return 0; } /** Get the sample aspect ratio as a floating point value. * * \public \memberof mlt_profile_s * \param profile a profile * \return the pixel aspect ratio */ double mlt_profile_sar( mlt_profile profile ) { if ( profile ) return ( double ) profile->sample_aspect_num / profile->sample_aspect_den; else return 0; } /** Get the display aspect ratio as floating point value. * * \public \memberof mlt_profile_s * \param profile a profile * \return the image aspect ratio */ double mlt_profile_dar( mlt_profile profile ) { if ( profile ) return ( double ) profile->display_aspect_num / profile->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 ) { free( profile->description ); profile->description = NULL; free( profile ); profile = NULL; } } /** Make a copy of a profile. * * \public \memberof mlt_profile_s * \param profile the profile to clone * \return a copy of the profile */ mlt_profile mlt_profile_clone( mlt_profile profile ) { mlt_profile clone = NULL; if ( profile ) { clone = calloc( 1, sizeof( *profile ) ); if ( clone ) { memcpy( clone, profile, sizeof( *profile ) ); clone->description = strdup( profile->description ); } } return clone; } /** Get the list of profiles. * * The caller MUST close the returned properties object! * Each entry in the list is keyed on its name, and its value is another * properties object that contains the attributes of the profile. * \public \memberof mlt_profile_s * \return a list of profiles */ mlt_properties mlt_profile_list( ) { char *filename = NULL; const char *prefix = getenv( "MLT_PROFILES_PATH" ); mlt_properties properties = mlt_properties_new(); mlt_properties dir = mlt_properties_new(); int sort = 1; const char *wildcard = NULL; int i; // Load from $datadir/mlt/profiles if no env var if ( prefix == NULL ) { prefix = mlt_environment( "MLT_DATA" ); filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + 1 ); strcpy( filename, prefix ); strcat( filename, PROFILES_DIR ); prefix = filename; } mlt_properties_dir_list( dir, prefix, wildcard, sort ); for ( i = 0; i < mlt_properties_count( dir ); i++ ) { char *filename = mlt_properties_get_value( dir, i ); char *profile_name = basename( filename ); if ( profile_name[0] != '.' && strcmp( profile_name, "Makefile" ) && profile_name[ strlen( profile_name ) - 1 ] != '~' ) { mlt_properties profile = mlt_properties_load( filename ); if ( profile ) { mlt_properties_set_data( properties, profile_name, profile, 0, (mlt_destructor) mlt_properties_close, NULL ); } } } mlt_properties_close( dir ); free( filename ); return properties; } /** Update the profile using the attributes of a producer. * * Use this to make an "auto-profile." Typically, you need to re-open the producer * after you use this because some producers (e.g. avformat) adjust their framerate * to that of the profile used when you created it. * \public \memberof mlt_profile_s * \param profile the profile to update * \param producer the producer to inspect */ void mlt_profile_from_producer( mlt_profile profile, mlt_producer producer ) { mlt_frame fr = NULL; uint8_t *buffer = NULL; mlt_image_format fmt = mlt_image_none; mlt_properties p; int w = profile->width; int h = profile->height; if ( ! mlt_service_get_frame( MLT_PRODUCER_SERVICE(producer), &fr, 0 ) && fr ) { // Skip scaling and padding since not needed and we request mlt_image_none. mlt_properties_set( MLT_FRAME_PROPERTIES(fr), "rescale.interp", "none" ); if ( ! mlt_frame_get_image( fr, &buffer, &fmt, &w, &h, 0 ) ) { // Some source properties are not exposed until after the first get_image call. mlt_frame_close( fr ); mlt_service_get_frame( MLT_PRODUCER_SERVICE(producer), &fr, 0 ); p = MLT_FRAME_PROPERTIES( fr ); // mlt_properties_dump(p, stderr); if ( mlt_properties_get_int( p, "meta.media.frame_rate_den" ) && mlt_properties_get_int( p, "meta.media.sample_aspect_den" ) ) { profile->width = mlt_properties_get_int( p, "meta.media.width" ); profile->height = mlt_properties_get_int( p, "meta.media.height" ); profile->progressive = mlt_properties_get_int( p, "meta.media.progressive" ); if ( 1000 > mlt_properties_get_double( p, "meta.media.frame_rate_num" ) / mlt_properties_get_double( p, "meta.media.frame_rate_den" ) ) { profile->frame_rate_num = mlt_properties_get_int( p, "meta.media.frame_rate_num" ); profile->frame_rate_den = mlt_properties_get_int( p, "meta.media.frame_rate_den" ); } else { profile->frame_rate_num = 60; profile->frame_rate_den = 1; } // AVCHD is mis-reported as double frame rate. if ( profile->progressive == 0 && ( profile->frame_rate_num / profile->frame_rate_den == 50 || profile->frame_rate_num / profile->frame_rate_den == 59 ) ) profile->frame_rate_num /= 2; profile->sample_aspect_num = mlt_properties_get_int( p, "meta.media.sample_aspect_num" ); profile->sample_aspect_den = mlt_properties_get_int( p, "meta.media.sample_aspect_den" ); profile->colorspace = mlt_properties_get_int( p, "meta.media.colorspace" ); profile->display_aspect_num = lrint( (double) profile->sample_aspect_num * profile->width / profile->sample_aspect_den ); profile->display_aspect_den = profile->height; free( profile->description ); profile->description = strdup( "automatic" ); profile->is_explicit = 0; } } } mlt_frame_close( fr ); mlt_producer_seek( producer, 0 ); } /** Get the lumas subdirectory to use for the aspect ratio. * * \public \memberof mlt_profile_s * \param profile the profile to update * \return the name of a subdirectory generated by the lumas module */ char *mlt_profile_lumas_dir( mlt_profile profile ) { if ( profile ) { if (profile->display_aspect_num == profile->display_aspect_den) { mlt_environment_set( "MLT_LUMAS_DIR", "square" ); // [-inf, 0.8) => 9:16 } else if ( mlt_profile_dar( profile ) < 0.8 ) { mlt_environment_set( "MLT_LUMAS_DIR", "9_16" ); // 0.5625 // [0.8, 1.3) => 1:1 } else if ( mlt_profile_dar( profile ) < 1.3 ) { mlt_environment_set( "MLT_LUMAS_DIR", "square" ); // [1.3, 1.5) => 4:3 } else if ( mlt_profile_dar( profile ) < 1.5 ) { if (profile->frame_rate_num == 30000 && profile->frame_rate_den == 1001) mlt_environment_set( "MLT_LUMAS_DIR", "NTSC" ); // 1.5 else mlt_environment_set( "MLT_LUMAS_DIR", "PAL" ); // 1.25 // [1.5, inf] => 16:9 } else { mlt_environment_set( "MLT_LUMAS_DIR", "16_9" ); } } else { mlt_environment_set("MLT_LUMAS_DIR", "16_9"); } return mlt_environment( "MLT_LUMAS_DIR" ); } /** Get the width scale factor. * * \public \memberof mlt_profile_s * \param profile the profile to reference * \param width the number of pixels the consumer requested * \return the scale factor for the width */ double mlt_profile_scale_width(mlt_profile profile, int width) { return (profile && width && profile->width)? (double) width / profile->width : 1.0; } /** Get the height scale factor. * * \public \memberof mlt_profile_s * \param profile the profile to reference * \param height the number of pixels the consumer requested * \return the scale factor for the height */ double mlt_profile_scale_height(mlt_profile profile, int height) { return (profile && profile->width)? (double) height / profile->height : 1.0; } mlt-6.20.0/src/framework/mlt_profile.h000066400000000000000000000062271362234133600176560ustar00rootroot00000000000000/** * \file mlt_profile.h * \brief video output definition * \see mlt_profile_s * * Copyright (C) 2007-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PROFILE_H #define MLT_PROFILE_H #include "mlt_types.h" /** \brief Profile class * * \envvar \em MLT_PROFILES_PATH overrides the default full path to the profile preset files, defaults to \p MLT_DATA/profiles * \envvar \em MLT_PROFILE the profile preset to use, defaults to "dv_pal" */ 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) */ int colorspace; /**< the Y'CbCr colorspace standard: =601 for ITU-R 601, =709 for ITU-R 709, or =240 for SMPTE240M */ int is_explicit; /**< used internally to indicate if the profile was requested explicitly or computed or defaulted */ }; 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 ); extern mlt_profile mlt_profile_clone( mlt_profile profile ); extern mlt_properties mlt_profile_list( ); extern void mlt_profile_from_producer( mlt_profile profile, mlt_producer producer ); extern char *mlt_profile_lumas_dir( mlt_profile profile ); extern double mlt_profile_scale_width( mlt_profile profile, int width ); extern double mlt_profile_scale_height( mlt_profile profile, int height ); #endif mlt-6.20.0/src/framework/mlt_properties.c000066400000000000000000002170551362234133600204100ustar00rootroot00000000000000/** * \file mlt_properties.c * \brief Properties class definition * \see mlt_properties_s * * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // For strtod_l #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "mlt_properties.h" #include "mlt_property.h" #include "mlt_deque.h" #include "mlt_log.h" #include "mlt_factory.h" #include #include #include #include #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; locale_t locale; } 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 self the properties structure to initialize * \param child an opaque pointer to a subclass object * \return true if failed */ int mlt_properties_init( mlt_properties self, void *child ) { if ( self != NULL ) { #ifdef _MLT_PROPERTY_CHECKS_ // Increment number of properties created properties_created ++; #endif // NULL all methods memset( self, 0, sizeof( struct mlt_properties_s ) ); // Assign the child of the object self->child = child; // Allocate the local structure self->local = calloc( 1, sizeof( property_list ) ); // Increment the ref count ( ( property_list * )self->local )->ref_count = 1; pthread_mutex_init( &( ( property_list * )self->local )->mutex, NULL );; } // Check that initialisation was successful return self != NULL && self->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 self = calloc( 1, sizeof( struct mlt_properties_s ) ); // Initialise self mlt_properties_init( self, NULL ); // Return the pointer return self; } /** Set the numeric locale used for string/double conversions. * * \public \memberof mlt_properties_s * \param self a properties list * \param locale the locale name * \return true if error */ int mlt_properties_set_lcnumeric( mlt_properties self, const char *locale ) { int error = 0; #if !defined(_WIN32) if ( self && locale ) { property_list *list = self->local; #if defined(__GLIBC__) || defined(__APPLE__) if ( list->locale ) freelocale( list->locale ); list->locale = newlocale( LC_NUMERIC_MASK, locale, NULL ); #else free( list->locale ); list->locale = strdup( locale ); #endif } else error = 1; #endif // _WIN32 return error; } /** Get the numeric locale for this properties object. * * Do not free the result. * \public \memberof mlt_properties_s * \param self a properties list * \return the locale name if this properties has a specific locale it is using, NULL otherwise */ const char* mlt_properties_get_lcnumeric( mlt_properties self ) { if ( !self ) return NULL; const char *result = NULL; #if !defined(_WIN32) property_list *list = self->local; if ( list->locale ) { #if defined(__APPLE__) result = querylocale( LC_NUMERIC_MASK, list->locale ); #elif defined(__GLIBC__) result = list->locale->__names[ LC_NUMERIC ]; #else result = list->locale; #endif #if defined(_WIN32) if ( result ) { // Convert the string from ANSI code page to UTF-8. mlt_properties_set( self, "_lcnumeric_in", result ); mlt_properties_to_utf8( self, "_lcnumeric_in", "_lcnumeric_out" ); result = mlt_properties_get( self, "_lcnumeric_out" ); } #endif } #endif // _WIN32 return result; } static int load_properties( mlt_properties self, const char *filename ) { // Open the file FILE *file = mlt_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 new line character from the string int x = strlen( temp ) - 1; if ( temp[x] == '\n' || temp[x] == '\r' ) temp[x] = '\0'; // Check if the line starts with a . if ( temp[ 0 ] == '.' ) { char temp2[ 1024 ]; strcpy( temp2, last ); strncat( temp2, temp, sizeof(temp2) - strlen(temp2) - 1 ); 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( self, temp ); } // Close the file fclose( file ); } return file? 0 : errno; } /** 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 the absolute file name * \return a new properties object */ mlt_properties mlt_properties_load( const char *filename ) { // Construct a standalone properties object mlt_properties self = mlt_properties_new( ); if ( self != NULL ) load_properties( self, filename ); // Return the pointer return self; } /** Set properties from a preset. * * Presets are typically installed to $prefix/share/mlt/presets/{type}/{service}/[{profile}/]{name}. * For example, "/usr/share/mlt/presets/consumer/avformat/dv_ntsc_wide/DVD" * could be an encoding preset for a widescreen NTSC DVD Video. * Do not specify the type and service in the preset name parameter; these are * inferred automatically from the service to which you are applying the preset. * Using the example above and assuming you are calling this function on the * avformat consumer, the name passed to the function should simply be DVD. * Note that the profile portion of the path is optional, but a profile-specific * preset with the same name as a more generic one is given a higher priority. * \todo Look in a user-specific location - somewhere in the home directory. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the name of a preset in a well-known location or the explicit path * \return true if error */ int mlt_properties_preset( mlt_properties self, const char *name ) { struct stat stat_buff; // validate input if ( !( self && name && strlen( name ) ) ) return 1; // See if name is an explicit file if ( ! stat( name, &stat_buff ) ) { return load_properties( self, name ); } else { // Look for profile-specific preset before a generic one. const char *data = mlt_environment( "MLT_PRESETS_PATH" ); const char *type = mlt_properties_get( self, "mlt_type" ); const char *service = mlt_properties_get( self, "mlt_service" ); const char *profile = mlt_environment( "MLT_PROFILE" ); int error = 0; if ( data && type && service ) { char *path = malloc( 5 + strlen(name) + strlen(data) + strlen(type) + strlen(service) + ( profile? strlen(profile) : 0 ) ); sprintf( path, "%s/%s/%s/%s/%s", data, type, service, profile, name ); if ( load_properties( self, path ) ) { sprintf( path, "%s/%s/%s/%s", data, type, service, name ); error = load_properties( self, path ); } free( path ); } else { error = 1; } return error; } } /** Generate a hash key. * * \private \memberof mlt_properties_s * \param name a string * \return an integer */ static inline int generate_hash( const char *name ) { unsigned int hash = 5381; while ( *name ) hash = hash * 33 + (unsigned int) ( *name ++ ); return hash % 199; } /** Copy a serializable property to a properties list that is mirroring this one. * * Special case - when a container (such as loader) is protecting another * producer, we need to ensure that properties are passed through to the * real producer. * \private \memberof mlt_properties_s * \param self a properties list * \param name the name of the property to copy */ static inline void mlt_properties_do_mirror( mlt_properties self, const char *name ) { if ( !self ) return; property_list *list = self->local; if ( list->mirror != NULL ) { char *value = mlt_properties_get( self, name ); if ( value != NULL ) mlt_properties_set( list->mirror, name, value ); } } /** Increment the reference count. * * \public \memberof mlt_properties_s * \param self a properties list * \return the new reference count */ int mlt_properties_inc_ref( mlt_properties self ) { int result = 0; if ( self != NULL ) { property_list *list = self->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 self a properties list * \return the new reference count */ int mlt_properties_dec_ref( mlt_properties self ) { int result = 0; if ( self != NULL ) { property_list *list = self->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 self a properties list * \return the current reference count */ int mlt_properties_ref_count( mlt_properties self ) { if ( self != NULL ) { property_list *list = self->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 self the properties to mirror */ void mlt_properties_mirror( mlt_properties self, mlt_properties that ) { if ( !self ) return; property_list *list = self->local; list->mirror = that; } /** Copy all serializable properties to another properties list. * * \public \memberof mlt_properties_s * \param self The properties to copy to * \param that The properties to copy from * \return true if error */ int mlt_properties_inherit( mlt_properties self, mlt_properties that ) { if ( !self || !that ) return 1; // Set "properties" first so preset overrides are reliable. char *value = mlt_properties_get(that, "properties"); if (value) mlt_properties_set(self, "properties", value); mlt_properties_lock( 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 ); if (name && strcmp("properties", name)) mlt_properties_set( self, name, value ); } } mlt_properties_unlock( that ); return 0; } /** Pass all serializable properties that match a prefix to another properties object * * \warning The prefix is stripped from the name when it is set on the \p self properties list! * For example a property named "foo.bar" will match prefix "foo.", but the property * will be named simply "bar" on the receiving properties object. * \public \memberof mlt_properties_s * \param self the properties to copy to * \param that The properties to copy from * \param prefix the property names to match (required) * \return true if error */ int mlt_properties_pass( mlt_properties self, mlt_properties that, const char *prefix ) { if ( !self || !that ) return 1; 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( self, name + length, value ); } } return 0; } /** Locate a property by name. * * \private \memberof mlt_properties_s * \param self 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 self, const char *name ) { if ( !self || !name ) return NULL; property_list *list = self->local; mlt_property value = NULL; int key = generate_hash( name ); mlt_properties_lock( self ); int i = list->hash[ key ] - 1; if ( i >= 0 ) { // Check if we're hashed if ( list->count > 0 && list->name[ i ] && !strcmp( list->name[ i ], name ) ) value = list->value[ i ]; // Locate the item for ( i = list->count - 1; value == NULL && i >= 0; i -- ) if ( list->name[ i ] && !strcmp( list->name[ i ], name ) ) value = list->value[ i ]; } mlt_properties_unlock( self ); return value; } /** Add a new property. * * \private \memberof mlt_properties_s * \param self a properties list * \param name the name of the new property * \return the new property */ static mlt_property mlt_properties_add( mlt_properties self, const char *name ) { property_list *list = self->local; int key = generate_hash( name ); mlt_property result; mlt_properties_lock( self ); // 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 result = list->value[ list->count ++ ]; mlt_properties_unlock( self ); return result; } /** Fetch a property by name and add one if not found. * * \private \memberof mlt_properties_s * \param self a properties list * \param name the property to lookup or add * \return the property */ static mlt_property mlt_properties_fetch( mlt_properties self, const char *name ) { // Try to find an existing property first mlt_property property = mlt_properties_find( self, name ); // If it wasn't found, create one if ( property == NULL ) property = mlt_properties_add( self, name ); // Return the property return property; } /** Copy a property to another properties list. * * \public \memberof mlt_properties_s * \author Zach * \param self 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 self, 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( self, 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 self the properties to copy to * \param that the properties to copy from * \param list a delimited list of property names * \return true if error */ int mlt_properties_pass_list( mlt_properties self, mlt_properties that, const char *list ) { if ( !self || !that || !list ) return 1; 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( self, that, ptr ); ptr += count + 1; if ( !done ) ptr += strspn( ptr, delim ); } free( props ); return 0; } static int is_valid_expression(mlt_properties self, const char* value) { int result = *value != '\0'; char id[255]; while (*value != '\0') { size_t length = strcspn(value, "+-*/"); // Get the identifier length = MIN(sizeof(id) - 1, length); strncpy(id, value, length); id[length] = '\0'; value += length; // Determine if the property exists if (!isdigit(id[0]) && !mlt_properties_get(self, id)) { result = 0; break; } // Get the next op if (value[0] != '\0') ++value; } return result; } /** Set a property to a string. * * The property name "properties" is reserved to load the preset in \p value. * When the value begins with '@' then it is interpreted as a very simple math * expression containing only the +, -, *, and / operators. * The event "property-changed" is fired after the property has been set. * * This makes a copy of the string value you supply. * \public \memberof mlt_properties_s * \param self 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 self, const char *name, const char *value ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, 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( self, name ); } else if ( value[ 0 ] == '@' && is_valid_expression(self, &value[1]) ) { double total = 0; double current = 0; char id[ 255 ]; char op = '+'; value ++; while ( *value != '\0' ) { size_t length = strcspn( value, "+-*/" ); // Get the identifier length = MIN(sizeof(id) - 1, length); strncpy( id, value, length ); id[ length ] = '\0'; value += length; // Determine the value if ( isdigit( id[ 0 ] ) ) { #if defined(__GLIBC__) || defined(__APPLE__) || HAVE_STRTOD_L property_list *list = self->local; if ( list->locale ) current = strtod_l( id, NULL, list->locale ); else #endif current = strtod( id, NULL ); } else { current = mlt_properties_get_double( self, 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( self, name ); } else { error = mlt_property_set_string( property, value ); mlt_properties_do_mirror( self, name ); if ( !strcmp( name, "properties" ) ) mlt_properties_preset( self, value ); } mlt_events_fire( self, "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 self 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 self, const char *name, const char *value, const char *def ) { return mlt_properties_set( self, name, value == NULL ? def : value ); } /** Set a property to a string. * * Unlike \mlt_properties_set this function does not attempt to interpret an expression. * The property name "properties" is reserved to load the preset in \p value. * The event "property-changed" is fired after the property has been set. * * This makes a copy of the string value you supply. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the property's new value * \return true if error */ int mlt_properties_set_string( mlt_properties self, const char *name, const char *value ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, 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( self, name ); } else { error = mlt_property_set_string( property, value ); mlt_properties_do_mirror( self, name ); if ( !strcmp( name, "properties" ) ) mlt_properties_preset( self, value ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** 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 self 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 self, const char *name ) { char *result = NULL; mlt_property value = mlt_properties_find( self, name ); if ( value ) { property_list *list = self->local; result = mlt_property_get_string_l( value, list->locale ); } return result; } /** Get a property name by index. * * Do not free the returned string. * \public \memberof mlt_properties_s * \param self 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 self, int index ) { if ( !self ) return NULL; property_list *list = self->local; if ( index >= 0 && index < list->count ) return list->name[ index ]; return NULL; } /** Get a property's string value by index (with time format). * * Do not free the returned string. * \public \memberof mlt_properties_s * \param self a properties list * \param index the numeric index of the property * \param time_format the time format to use for animation * \return the property value as a string or NULL if the index is out of range */ char *mlt_properties_get_value_tf( mlt_properties self, int index, mlt_time_format time_format ) { if ( !self ) return NULL; property_list *list = self->local; if ( index >= 0 && index < list->count ) return mlt_property_get_string_l_tf( list->value[ index ], list->locale, time_format ); return NULL; } /** Get a property's string value by index. * * Do not free the returned string. * \public \memberof mlt_properties_s * \param self 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 self, int index ) { return mlt_properties_get_value_tf( self, index, mlt_time_frames ); } /** 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 self 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 self, int index, int *size ) { if ( !self ) return NULL; property_list *list = self->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 self a properties list * \return the number of property objects or -1 if error */ int mlt_properties_count( mlt_properties self ) { if ( !self ) return -1; property_list *list = self->local; return list->count; } /** Set a value by parsing a name=value string. * * \public \memberof mlt_properties_s * \param self 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 self, const char *namevalue ) { if ( !self ) return 1; 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( self, name, value ); free( name ); free( value ); return error; } /** Get an integer associated to the name. * * \public \memberof mlt_properties_s * \param self 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 self, const char *name ) { int result = 0; mlt_property value = mlt_properties_find( self, name ); if ( value ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; result = mlt_property_get_int( value, fps, list->locale ); } return result; } /** Set a property to an integer value. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the integer * \return true if error */ int mlt_properties_set_int( mlt_properties self, const char *name, int value ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { error = mlt_property_set_int( property, value ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get a 64-bit integer associated to the name. * * \public \memberof mlt_properties_s * \param self 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 self, const char *name ) { mlt_property value = mlt_properties_find( self, 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 self a properties list * \param name the property to set * \param value the integer * \return true if error */ int mlt_properties_set_int64( mlt_properties self, const char *name, int64_t value ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { error = mlt_property_set_int64( property, value ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get a floating point value associated to the name. * * \public \memberof mlt_properties_s * \param self 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 self, const char *name ) { double result = 0; mlt_property value = mlt_properties_find( self, name ); if ( value ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; result = mlt_property_get_double( value, fps, list->locale ); } return result; } /** Set a property to a floating point value. * * \public \memberof mlt_properties_s * \param self 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 self, const char *name, double value ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { error = mlt_property_set_double( property, value ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get a position value associated to the name. * * \public \memberof mlt_properties_s * \param self 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 self, const char *name ) { mlt_position result = 0; mlt_property value = mlt_properties_find( self, name ); if ( value ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; result = mlt_property_get_position( value, fps, list->locale ); } return result; } /** Set a property to a position value. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param value the position * \return true if error */ int mlt_properties_set_position( mlt_properties self, const char *name, mlt_position value ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { error = mlt_property_set_position( property, value ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "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 self 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 self, const char *name, int *length ) { mlt_property value = mlt_properties_find( self, name ); return value == NULL ? NULL : mlt_property_get_data( value, length ); } /** Store binary data as a property. * * \public \memberof mlt_properties_s * \param self 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 deallocate 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 self, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) error = mlt_property_set_data( property, value, length, destroy, serialise ); mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Rename a property. * * \public \memberof mlt_properties_s * \param self 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 self, const char *source, const char *dest ) { mlt_property value = mlt_properties_find( self, dest ); if ( value == NULL ) { property_list *list = self->local; int i = 0; // Locate the item mlt_properties_lock( self ); for ( i = 0; i < list->count; i ++ ) { if ( list->name[ i ] && !strcmp( list->name[ i ], source ) ) { free( list->name[ i ] ); list->name[ i ] = strdup( dest ); list->hash[ generate_hash( dest ) ] = i + 1; break; } } mlt_properties_unlock( self ); } return value != NULL; } /** Dump the properties to a file handle. * * \public \memberof mlt_properties_s * \param self a properties list * \param output a file handle */ void mlt_properties_dump( mlt_properties self, FILE *output ) { if ( !self || !output ) return; property_list *list = self->local; int i = 0; for ( i = 0; i < list->count; i ++ ) if ( mlt_properties_get( self, list->name[ i ] ) != NULL ) fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( self, 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 self a properties pointer * \param title a string to preface the output * \param output a file handle */ void mlt_properties_debug( mlt_properties self, const char *title, FILE *output ) { if ( !self || !output ) return; if ( output == NULL ) output = stderr; fprintf( output, "%s: ", title ); if ( self != NULL ) { property_list *list = self->local; int i = 0; fprintf( output, "[ ref=%d", list->ref_count ); for ( i = 0; i < list->count; i ++ ) if ( mlt_properties_get( self, list->name[ i ] ) != NULL ) fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( self, list->name[ i ] ) ); else fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( self, 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 self 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 self, const char *filename ) { int error = 1; if ( !self || !filename ) return error; FILE *f = mlt_fopen( filename, "w" ); if ( f != NULL ) { mlt_properties_dump( self, 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 self a property * \param that a property * \return < 0 if \p self less than \p that, 0 if equal, or > 0 if \p self is greater than \p that */ static int mlt_compare( const void *self, const void *that ) { return strcmp( mlt_property_get_string( *( const mlt_property * )self ), 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 self 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 self, 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( self ) ); snprintf( fullname, 1024, "%s/%s", dirname, de->d_name ); if ( pattern == NULL ) mlt_properties_set( self, key, fullname ); else if ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) ) mlt_properties_set( self, key, fullname ); de = readdir( dir ); } closedir( dir ); } if ( sort && mlt_properties_count( self ) ) { property_list *list = self->local; mlt_properties_lock( self ); qsort( list->value, mlt_properties_count( self ), sizeof( mlt_property ), mlt_compare ); mlt_properties_unlock( self ); } return mlt_properties_count( self ); } /** Close a properties object. * * Deallocates the properties object and everything it contains. * \public \memberof mlt_properties_s * \param self a properties object */ void mlt_properties_close( mlt_properties self ) { if ( self != NULL && mlt_properties_dec_ref( self ) <= 0 ) { if ( self->close != NULL ) { self->close( self->close_object ); } else { property_list *list = self->local; int index = 0; #if _MLT_PROPERTY_CHECKS_ == 1 // Show debug info mlt_properties_debug( self, "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 -- ) { mlt_property_close( list->value[ index ] ); free( list->name[ index ] ); } #if defined(__GLIBC__) || defined(__APPLE__) // Cleanup locale if ( list->locale ) freelocale( list->locale ); #else free( list->locale ); #endif // Clear up the list pthread_mutex_destroy( &list->mutex ); free( list->name ); free( list->value ); free( list ); // Free self now if self has no child if ( self->child == NULL ) free( self ); } } } /** 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; int index; mlt_deque index_stack; 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_back( context->stack ); // If name is quoted if ( ptr && name[0] == '\"' ) { // Look for ending quote. ptr = strchr( ptr + 1, '\"'); if ( ptr ) // Locate delimiter after ending quote. ptr = strchr( ptr + 1, ':' ); else // No ending quote! ptr = strchr( name, ':' ); } // 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_back( context->stack ); context->index = mlt_deque_pop_back_int( context->index_stack ); } properties = mlt_deque_peek_back( 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 ) { const char* quote = strchr( ptr, '\"' ); if ( !quote || quote > 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_lcnumeric( child, mlt_properties_get_lcnumeric( properties ) ); mlt_properties_set_data( properties, name, child, 0, ( mlt_destructor )mlt_properties_close, NULL ); mlt_deque_push_back( context->stack, child ); mlt_deque_push_back_int( context->index_stack, context->index ); 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]; mlt_properties_set_lcnumeric( child, mlt_properties_get_lcnumeric( properties ) ); snprintf( key, sizeof(key), "%d", context->index++ ); mlt_properties_set_data( properties, key, child, 0, ( mlt_destructor )mlt_properties_close, NULL ); mlt_deque_push_back( context->stack, child ); mlt_deque_push_back_int( context->index_stack, context->index ); 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( "" ); } // Remove quotes around the name. if ( name && name[0] == '"' && name[strlen(name) - 1] == '"' ) { name ++; name[strlen(name) - 1] = '\0'; } error = mlt_properties_set( properties, name, value ); if ( !strcmp( name, "LC_NUMERIC" ) ) mlt_properties_set_lcnumeric( properties, 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 self = mlt_properties_new( ); if ( self ) { // Open the file FILE *file = mlt_fopen( filename, "r" ); // Load contents of file if ( file ) { // Temp string char temp[ 1024 ]; char *ptemp = &temp[ 0 ]; // Default to LC_NUMERIC = C mlt_properties_set_lcnumeric( self, "C" ); // Parser context yaml_parser context = calloc( 1, sizeof( struct yaml_parser_context ) ); context->stack = mlt_deque_init(); context->index_stack = mlt_deque_init(); mlt_deque_push_back( context->stack, self ); mlt_deque_push_back_int( context->index_stack, 0 ); // 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 ); mlt_deque_close( context->index_stack ); free( context->block_name ); free( context ); } } // Return the pointer return self; } /* * 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. 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, " " ); } static void strbuf_escape( strbuf output, const char *value, char c ) { char *v = strdup( value ); char *s = v; char *found = strchr( s, c ); while ( found ) { *found = '\0'; strbuf_printf( output, "%s\\%c", s, c ); s = found + 1; found = strchr( s, c ); } strbuf_printf( output, "%s", s ); free( v ); } static inline int has_reserved_char( const char* string ) { return strchr( string, ':' ) || strchr( string, '[' ) || strchr( string, '\'' ) || strchr( string, '#' ); } /** 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 ); free( v ); } /** Recursively serialize a properties list into a string buffer as YAML Tiny. * * \private \memberof mlt_properties_s * \param self 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 self, strbuf output, int indent, int is_parent_sequence ) { property_list *list = self->local; int i = 0; int is_sequence = mlt_properties_is_sequence( self ); 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 ); const char *name = list->name[i]; const char *value = mlt_properties_get( self, name ); if ( is_sequence ) { // Ignore hidden/non-serialisable items if ( name[ 0 ] != '_' ) { // Indicate a sequence item indent_yaml( output, indent ); strbuf_printf( output, "- " ); // If the value can be represented as a string 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( name ) + strlen( "|" ) ); } else if ( has_reserved_char( value ) ) { strbuf_printf( output, "\"" ); strbuf_escape( output, value, '"' ); strbuf_printf( output, "\"\n", value ); } else if ( strchr( value, '"') ) { strbuf_printf( output, "'%s'\n", value ); } else { strbuf_printf( output, "%s\n", value ); } } } // Recurse on child if ( child && child->local ) serialise_yaml( child, output, indent + 2, 1 ); } else { // Assume this is a normal map-oriented properties list // Ignore hidden/non-serialisable items // If the value can be represented as a string if ( name[ 0 ] != '_' && value && strcmp( value, "" ) ) { if ( is_parent_sequence == 0 ) indent_yaml( output, indent ); else is_parent_sequence = 0; // Output the name. if ( has_reserved_char( name) ) { strbuf_printf( output, "\"" ); strbuf_escape( output, name, '"' ); strbuf_printf( output, "\": " ); } else { strbuf_printf( output, "%s: ", name ); } // Determine if this is an unfolded block literal if ( strchr( value, '\n' ) ) { strbuf_printf( output, "|\n" ); output_yaml_block_literal( output, value, indent + strlen( name ) + strlen( ": " ) ); } else if ( has_reserved_char( value ) ) { strbuf_printf( output, "\"" ); strbuf_escape( output, value, '"' ); strbuf_printf( output, "\"\n" ); } else if ( strchr( value, '"') ) { strbuf_printf( output, "'%s'\n", value ); } else { strbuf_printf( output, "%s\n", value ); } } // Output a child as a map item if ( child && child->local ) { indent_yaml( output, indent ); if ( has_reserved_char( name ) ) { strbuf_printf( output, "\"" ); strbuf_escape( output, name, '"' ); strbuf_printf( output, "\":\n" ); } else { strbuf_printf( output, "%s:\n", name ); } // 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 self a properties list * \return a string containing YAML Tiny that represents the properties list */ char *mlt_properties_serialise_yaml( mlt_properties self ) { if ( !self ) return NULL; const char *lc_numeric = mlt_properties_get_lcnumeric( self ); strbuf b = strbuf_new(); strbuf_printf( b, "---\n" ); mlt_properties_set_lcnumeric( self, "C" ); serialise_yaml( self, b, 0, 0 ); mlt_properties_set_lcnumeric( self, lc_numeric ); strbuf_printf( b, "...\n" ); char *ret = b->string; strbuf_close( b ); return ret; } /** Protect a properties list against concurrent access. * * \public \memberof mlt_properties_s * \param self a properties list */ void mlt_properties_lock( mlt_properties self ) { if ( self ) pthread_mutex_lock( &( ( property_list* )( self->local ) )->mutex ); } /** End protecting a properties list against concurrent access. * * \public \memberof mlt_properties_s * \param self a properties list */ void mlt_properties_unlock( mlt_properties self ) { if ( self ) pthread_mutex_unlock( &( ( property_list* )( self->local ) )->mutex ); } /** Remove the value for a property. * * This initializes the value to zero and removes any string, data, or animation. * This is especially useful when you want to reset an animation. * \public \memberof mlt_properties_s * \param self a properties list * \param name the name of the property to clear */ void mlt_properties_clear( mlt_properties self, const char *name ) { if ( !self || !name ) return; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property ) mlt_property_clear( property ); mlt_events_fire( self, "property-changed", name, NULL ); } /** Get a time string associated to the name. * * Do not free the returned string. It's lifetime is controlled by the property. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param format the time format that you want * \return the property's time value or NULL if \p name does not exist or there is no profile */ char *mlt_properties_get_time( mlt_properties self, const char* name, mlt_time_format format ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); if ( profile ) { double fps = mlt_profile_fps( profile ); mlt_property value = mlt_properties_find( self, name ); property_list *list = self->local; return value == NULL ? NULL : mlt_property_get_time( value, format, fps, list->locale ); } return NULL; } /** Convert a frame count to a time string. * * Do not free the returned string. It's lifetime is controlled by the property. * \public \memberof mlt_properties_s * \param self a properties list * \param frames the frame count to convert * \param format the time format that you want * \return the time string or NULL if error, e.g. there is no profile */ char *mlt_properties_frames_to_time( mlt_properties self, mlt_position frames, mlt_time_format format ) { const char *name = "_mlt_properties_time"; mlt_properties_set_position( self, name, frames ); return mlt_properties_get_time( self, name, format ); } /** Convert a time string to a frame count. * * \public \memberof mlt_properties_s * \param self a properties list * \param time the time string to convert * \return a frame count or a negative value if error, e.g. there is no profile */ mlt_position mlt_properties_time_to_frames( mlt_properties self, const char *time ) { const char *name = "_mlt_properties_time"; mlt_properties_set( self, name, time ); return mlt_properties_get_position( self, name ); } /** Convert a numeric property to a tuple of color components. * * If the property's string is red, green, blue, white, or black, then it * is converted to the corresponding opaque color tuple. Otherwise, the property * is fetched as an integer and then converted. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return a color structure */ mlt_color mlt_properties_get_color( mlt_properties self, const char* name ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; mlt_property value = mlt_properties_find( self, name ); mlt_color result = { 0xff, 0xff, 0xff, 0xff }; if ( value ) { const char *color = mlt_property_get_string_l( value, list->locale ); unsigned int color_int = mlt_property_get_int( value, fps, list->locale ); 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, "black" ) ) { result.r = 0x00; result.g = 0x00; result.b = 0x00; } 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; } /** Set a property to an integer value by color. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param color the color * \return true if error */ int mlt_properties_set_color( mlt_properties self, const char *name, mlt_color color ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { uint32_t value = ( color.r << 24 ) | ( color.g << 16 ) | ( color.b << 8 ) | color.a; error = mlt_property_set_int( property, value ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get a string value by name at a frame position. * * Do not free the returned string. It's lifetime is controlled by the property * and this properties object. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the property's string value or NULL if it does not exist */ char* mlt_properties_anim_get( mlt_properties self, const char *name, int position, int length ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); mlt_property value = mlt_properties_find( self, name ); property_list *list = self->local; return value == NULL ? NULL : mlt_property_anim_get_string( value, fps, list->locale, position, length ); } /** Set a property to a string at a frame position. * * The event "property-changed" is fired after the property has been set. * * This makes a copy of the string value you supply. * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the property's new value * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return true if error */ int mlt_properties_anim_set( mlt_properties self, const char *name, const char *value, int position, int length ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; error = mlt_property_anim_set_string( property, value, fps, list->locale, position, length ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get an integer associated to the name at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the integer value, 0 if not found (which may also be a legitimate value) */ int mlt_properties_anim_get_int( mlt_properties self, const char *name, int position, int length ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; mlt_property value = mlt_properties_find( self, name ); return value == NULL ? 0 : mlt_property_anim_get_int( value, fps, list->locale, position, length ); } /** Set a property to an integer value at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the integer * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return true if error */ int mlt_properties_anim_set_int( mlt_properties self, const char *name, int value, int position, int length, mlt_keyframe_type keyframe_type ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; error = mlt_property_anim_set_int( property, value, fps, list->locale, position, length, keyframe_type ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get a real number associated to the name at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the real number, 0 if not found (which may also be a legitimate value) */ double mlt_properties_anim_get_double( mlt_properties self, const char *name, int position, int length ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; mlt_property value = mlt_properties_find( self, name ); return value == NULL ? 0.0 : mlt_property_anim_get_double( value, fps, list->locale, position, length ); } /** Set a property to a real number at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return true if error */ int mlt_properties_anim_set_double( mlt_properties self, const char *name, double value, int position, int length, mlt_keyframe_type keyframe_type ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; error = mlt_property_anim_set_double( property, value, fps, list->locale, position, length, keyframe_type ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get the animation associated to the name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return The animation object or NULL if the property has no animation */ mlt_animation mlt_properties_get_animation( mlt_properties self, const char *name ) { mlt_property value = mlt_properties_find( self, name ); return value == NULL ? NULL : mlt_property_get_animation( value ); } /** Set a property to a rectangle value. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the rectangle * \return true if error */ extern int mlt_properties_set_rect( mlt_properties self, const char *name, mlt_rect value ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { error = mlt_property_set_rect( property, value ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get a rectangle associated to the name. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \return the rectangle value, the rectangle fields will be DBL_MIN if not found */ extern mlt_rect mlt_properties_get_rect( mlt_properties self, const char* name ) { property_list *list = self->local; mlt_property value = mlt_properties_find( self, name ); mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN }; return value == NULL ? rect : mlt_property_get_rect( value, list->locale ); } /** Set a property to a rectangle value at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to set * \param value the rectangle * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return true if error */ extern int mlt_properties_anim_set_rect( mlt_properties self, const char *name, mlt_rect value, int position, int length , mlt_keyframe_type keyframe_type ) { int error = 1; if ( !self || !name ) return error; // Fetch the property to work with mlt_property property = mlt_properties_fetch( self, name ); // Set it if not NULL if ( property != NULL ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; error = mlt_property_anim_set_rect( property, value, fps, list->locale, position, length, keyframe_type ); mlt_properties_do_mirror( self, name ); } mlt_events_fire( self, "property-changed", name, NULL ); return error; } /** Get a rectangle associated to the name at a frame position. * * \public \memberof mlt_properties_s * \param self a properties list * \param name the property to get * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the rectangle value, the rectangle fields will be DBL_MIN if not found */ extern mlt_rect mlt_properties_anim_get_rect( mlt_properties self, const char *name, int position, int length ) { mlt_profile profile = mlt_properties_get_data( self, "_profile", NULL ); double fps = mlt_profile_fps( profile ); property_list *list = self->local; mlt_property value = mlt_properties_find( self, name ); mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN }; return value == NULL ? rect : mlt_property_anim_get_rect( value, fps, list->locale, position, length ); } #ifndef _WIN32 // See win32/win32.c for win32 implementation. /** Convert UTF-8 property to the locale-defined encoding. * * MLT uses UTF-8 for strings, but Windows cannot accept UTF-8 for a filename. * Windows uses code pages for the locale encoding. * \public \memberof mlt_properties_s * \param self a properties list * \param name_from the property to read whose value is a UTF-8 string * \param name_to the name of the new property that will contain converted string * \return true if error */ int mlt_properties_from_utf8( mlt_properties properties, const char *name_from, const char *name_to ) { // On non-Windows platforms, assume UTF-8 will always work and does not need conversion. // This function just becomes a pass-through operation. // This was largely chosen to prevent adding a libiconv dependency to the framework per policy. // However, for file open operations on Windows, especially when processing XML, a text codec // dependency is hardly avoidable. return mlt_properties_set( properties, name_to, mlt_properties_get( properties, name_from ) ); } #endif mlt-6.20.0/src/framework/mlt_properties.h000066400000000000000000000156371362234133600204170ustar00rootroot00000000000000/** * \file mlt_properties.h * \brief Properties class declaration * \see mlt_properties_s * * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 int mlt_properties_set_lcnumeric( mlt_properties, const char *locale ); extern const char* mlt_properties_get_lcnumeric( mlt_properties self ); extern mlt_properties mlt_properties_load( const char *file ); extern int mlt_properties_preset( mlt_properties self, const char *name ); 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_set_string( mlt_properties self, const char *name, const char *value ); 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_tf( mlt_properties self, int index, mlt_time_format ); 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 ); extern void mlt_properties_lock( mlt_properties self ); extern void mlt_properties_unlock( mlt_properties self ); extern void mlt_properties_clear( mlt_properties self, const char *name ); extern char *mlt_properties_get_time( mlt_properties, const char* name, mlt_time_format ); extern char *mlt_properties_frames_to_time( mlt_properties, mlt_position, mlt_time_format ); extern mlt_position mlt_properties_time_to_frames( mlt_properties, const char* time ); extern mlt_color mlt_properties_get_color( mlt_properties, const char* name ); extern int mlt_properties_set_color( mlt_properties, const char* name, mlt_color value ); extern char* mlt_properties_anim_get( mlt_properties self, const char *name, int position, int length ); extern int mlt_properties_anim_set( mlt_properties self, const char *name, const char *value, int position, int length ); extern int mlt_properties_anim_get_int( mlt_properties self, const char *name, int position, int length ); extern int mlt_properties_anim_set_int( mlt_properties self, const char *name, int value, int position, int length, mlt_keyframe_type keyframe_type ); extern double mlt_properties_anim_get_double( mlt_properties self, const char *name, int position, int length ); extern int mlt_properties_anim_set_double( mlt_properties self, const char *name, double value, int position, int length, mlt_keyframe_type keyframe_type ); extern mlt_animation mlt_properties_get_animation( mlt_properties self, const char *name ); extern int mlt_properties_set_rect( mlt_properties self, const char *name, mlt_rect value ); extern mlt_rect mlt_properties_get_rect( mlt_properties self, const char *name ); extern int mlt_properties_anim_set_rect( mlt_properties self, const char *name, mlt_rect value, int position, int length, mlt_keyframe_type keyframe_type ); extern mlt_rect mlt_properties_anim_get_rect( mlt_properties self, const char *name, int position, int length ); extern int mlt_properties_from_utf8( mlt_properties properties, const char *name_from, const char *name_to ); extern int mlt_properties_to_utf8( mlt_properties properties, const char *name_from, const char *name_to ); #endif mlt-6.20.0/src/framework/mlt_property.c000066400000000000000000001503531362234133600200750ustar00rootroot00000000000000/** * \file mlt_property.c * \brief Property class definition * \see mlt_property_s * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // For strtod_l #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "mlt_property.h" #include "mlt_animation.h" #include #include #include #include #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_prop_rect = 64 //!< set as a mlt_rect } 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; pthread_mutex_t mutex; mlt_animation animation; }; /** Construct a property and initialize it * \public \memberof mlt_property_s */ mlt_property mlt_property_init( ) { mlt_property self = calloc( 1, sizeof( *self ) ); if ( self ) pthread_mutex_init( &self->mutex, NULL ); return self; } /** Clear (0/null) a property. * * Frees up any associated resources in the process. * \private \memberof mlt_property_s * \param self a property */ static void clear_property( mlt_property self ) { // Special case data handling if ( self->types & mlt_prop_data && self->destructor != NULL ) self->destructor( self->data ); // Special case string handling if ( self->prop_string ) free( self->prop_string ); mlt_animation_close( self->animation ); // Wipe stuff self->types = 0; self->prop_int = 0; self->prop_position = 0; self->prop_double = 0; self->prop_int64 = 0; self->prop_string = NULL; self->data = NULL; self->length = 0; self->destructor = NULL; self->serialiser = NULL; self->animation = NULL; } /** Clear (0/null) a property. * * Frees up any associated resources in the process. * \public \memberof mlt_property_s * \param self a property */ void mlt_property_clear( mlt_property self ) { pthread_mutex_lock( &self->mutex ); clear_property( self ); pthread_mutex_unlock( &self->mutex ); } /** Set the property to an integer value. * * \public \memberof mlt_property_s * \param self a property * \param value an integer * \return false */ int mlt_property_set_int( mlt_property self, int value ) { pthread_mutex_lock( &self->mutex ); clear_property( self ); self->types = mlt_prop_int; self->prop_int = value; pthread_mutex_unlock( &self->mutex ); return 0; } /** Set the property to a floating point value. * * \public \memberof mlt_property_s * \param self a property * \param value a double precision floating point value * \return false */ int mlt_property_set_double( mlt_property self, double value ) { pthread_mutex_lock( &self->mutex ); clear_property( self ); self->types = mlt_prop_double; self->prop_double = value; pthread_mutex_unlock( &self->mutex ); return 0; } /** Set the property to a position value. * * Position is a relative time value in frame units. * \public \memberof mlt_property_s * \param self a property * \param value a position value * \return false */ int mlt_property_set_position( mlt_property self, mlt_position value ) { pthread_mutex_lock( &self->mutex ); clear_property( self ); self->types = mlt_prop_position; self->prop_position = value; pthread_mutex_unlock( &self->mutex ); 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 self a property * \param value the string to copy to the property * \return true if it failed */ int mlt_property_set_string( mlt_property self, const char *value ) { pthread_mutex_lock( &self->mutex ); if ( value != self->prop_string ) { clear_property( self ); self->types = mlt_prop_string; if ( value != NULL ) self->prop_string = strdup( value ); } else { self->types = mlt_prop_string; } pthread_mutex_unlock( &self->mutex ); return self->prop_string == NULL; } /** Set the property to a 64-bit integer value. * * \public \memberof mlt_property_s * \param self a property * \param value a 64-bit integer * \return false */ int mlt_property_set_int64( mlt_property self, int64_t value ) { pthread_mutex_lock( &self->mutex ); clear_property( self ); self->types = mlt_prop_int64; self->prop_int64 = value; pthread_mutex_unlock( &self->mutex ); 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 self 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 self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser ) { pthread_mutex_lock( &self->mutex ); if ( self->data == value ) self->destructor = NULL; clear_property( self ); self->types = mlt_prop_data; self->data = value; self->length = length; self->destructor = destructor; self->serialiser = serialiser; pthread_mutex_unlock( &self->mutex ); return 0; } /** Parse a SMIL clock value. * * \private \memberof mlt_property_s * \param self a property * \param s the string to parse * \param fps frames per second * \param locale the locale to use for parsing a real number value * \return position in frames */ static int time_clock_to_frames( mlt_property self, const char *s, double fps, locale_t locale ) { char *pos, *copy = strdup( s ); int hours = 0, minutes = 0; double seconds; s = copy; pos = strrchr( s, ':' ); #if !defined(__GLIBC__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(HAVE_STRTOD_L) char *orig_localename = NULL; if ( locale ) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock( &self->mutex ); // Get the current locale orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); // Set the new locale setlocale( LC_NUMERIC, locale ); } #endif if ( pos ) { #if defined(__GLIBC__) || defined(__APPLE__) || defined(HAVE_STRTOD_L) if ( locale ) seconds = strtod_l( pos + 1, NULL, locale ); else #endif seconds = strtod( pos + 1, NULL ); *pos = 0; pos = strrchr( s, ':' ); if ( pos ) { minutes = atoi( pos + 1 ); *pos = 0; hours = atoi( s ); } else { minutes = atoi( s ); } } else { #if defined(__GLIBC__) || defined(__APPLE__) || defined(HAVE_STRTOD_L) if ( locale ) seconds = strtod_l( s, NULL, locale ); else #endif seconds = strtod( s, NULL ); } #if !defined(__GLIBC__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(HAVE_STRTOD_L) if ( locale ) { // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); free( orig_localename ); pthread_mutex_unlock( &self->mutex ); } #endif free( copy ); return floor( fps * hours * 3600 ) + floor( fps * minutes * 60 ) + lrint( fps * seconds ); } /** Parse a SMPTE timecode string. * * \private \memberof mlt_property_s * \param self a property * \param s the string to parse * \param fps frames per second * \return position in frames */ static int time_code_to_frames( mlt_property self, const char *s, double fps ) { char *pos, *copy = strdup( s ); int hours = 0, minutes = 0, seconds = 0, frames; s = copy; pos = strrchr( s, ';' ); if ( !pos ) pos = strrchr( s, ':' ); if ( pos ) { frames = atoi( pos + 1 ); *pos = 0; pos = strrchr( s, ':' ); if ( pos ) { seconds = atoi( pos + 1 ); *pos = 0; pos = strrchr( s, ':' ); if ( pos ) { minutes = atoi( pos + 1 ); *pos = 0; hours = atoi( s ); } else { minutes = atoi( s ); } } else { seconds = atoi( s ); } } else { frames = atoi( s ); } free( copy ); return floor( fps * hours * 3600 ) + floor( fps * minutes * 60 ) + ceil( fps * seconds ) + frames; } /** Convert a 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. * * If the string contains a colon it is interpreted as a time value. If it also * contains a period or comma character, the string is parsed as a clock value: * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF. * \private \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return the resultant integer */ static int mlt_property_atoi( mlt_property self, double fps, locale_t locale ) { const char *value = self->prop_string; // 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 if ( fps > 0 && strchr( value, ':' ) ) { if ( strchr( value, '.' ) || strchr( value, ',' ) ) return time_clock_to_frames( self, value, fps, locale ); else return time_code_to_frames( self, value, fps ); } else { return strtol( value, NULL, 10 ); } } /** Get the property as an integer. * * \public \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return an integer value */ int mlt_property_get_int( mlt_property self, double fps, locale_t locale ) { pthread_mutex_lock( &self->mutex ); int result = 0; if ( self->types & mlt_prop_int ) result = self->prop_int; else if ( self->types & mlt_prop_double ) result = ( int )self->prop_double; else if ( self->types & mlt_prop_position ) result = ( int )self->prop_position; else if ( self->types & mlt_prop_int64 ) result = ( int )self->prop_int64; else if ( self->types & mlt_prop_rect && self->data ) result = ( int ) ( (mlt_rect*) self->data )->x; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) result = mlt_property_atoi( self, fps, locale ); pthread_mutex_unlock( &self->mutex ); return result; } /** Convert a string to a floating point number. * * If the string contains a colon it is interpreted as a time value. If it also * contains a period or comma character, the string is parsed as a clock value: * HH:MM:SS. Otherwise, the time value is parsed as a SMPTE timecode: HH:MM:SS:FF. * If the numeric string ends with '%' then the value is divided by 100 to convert * it into a ratio. * \private \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return the resultant real number */ static double mlt_property_atof( mlt_property self, double fps, locale_t locale ) { const char *value = self->prop_string; if ( fps > 0 && strchr( value, ':' ) ) { if ( strchr( value, '.' ) || strchr( value, ',' ) ) return time_clock_to_frames( self, value, fps, locale ); else return time_code_to_frames( self, value, fps ); } else { char *end = NULL; double result; #if defined(__GLIBC__) || defined(__APPLE__) || defined(HAVE_STRTOD_L) if ( locale ) result = strtod_l( value, &end, locale ); else #elif !defined(_WIN32) char *orig_localename = NULL; if ( locale ) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock( &self->mutex ); // Get the current locale orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); // Set the new locale setlocale( LC_NUMERIC, locale ); } #endif result = strtod( value, &end ); if ( end && end[0] == '%' ) result /= 100.0; #if !defined(__GLIBC__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(HAVE_STRTOD_L) if ( locale ) { // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); free( orig_localename ); pthread_mutex_unlock( &self->mutex ); } #endif return result; } } /** Get the property as a floating point. * * \public \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use for this conversion * \return a floating point value */ double mlt_property_get_double( mlt_property self, double fps, locale_t locale ) { double result = 0.0; pthread_mutex_lock( &self->mutex ); if ( self->types & mlt_prop_double ) result = self->prop_double; else if ( self->types & mlt_prop_int ) result = ( double )self->prop_int; else if ( self->types & mlt_prop_position ) result = ( double )self->prop_position; else if ( self->types & mlt_prop_int64 ) result = ( double )self->prop_int64; else if ( self->types & mlt_prop_rect && self->data ) result = ( (mlt_rect*) self->data )->x; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) result = mlt_property_atof( self, fps, locale ); pthread_mutex_unlock( &self->mutex ); return result; } /** Get the property as a position. * * A position is an offset time in terms of frame units. * \public \memberof mlt_property_s * \param self a property * \param fps frames per second, used when converting from time value * \param locale the locale to use when converting from time clock value * \return the position in frames */ mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t locale ) { mlt_position result = 0; pthread_mutex_lock( &self->mutex ); if ( self->types & mlt_prop_position ) result = self->prop_position; else if ( self->types & mlt_prop_int ) result = ( mlt_position )self->prop_int; else if ( self->types & mlt_prop_double ) result = ( mlt_position )self->prop_double; else if ( self->types & mlt_prop_int64 ) result = ( mlt_position )self->prop_int64; else if ( self->types & mlt_prop_rect && self->data ) result = ( mlt_position ) ( (mlt_rect*) self->data )->x; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) result = ( mlt_position )mlt_property_atoi( self, fps, locale ); pthread_mutex_unlock( &self->mutex ); return result; } /** 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 self a property * \return a 64-bit integer */ int64_t mlt_property_get_int64( mlt_property self ) { int64_t result = 0; pthread_mutex_lock( &self->mutex ); if ( self->types & mlt_prop_int64 ) result = self->prop_int64; else if ( self->types & mlt_prop_int ) result = ( int64_t )self->prop_int; else if ( self->types & mlt_prop_double ) result = ( int64_t )self->prop_double; else if ( self->types & mlt_prop_position ) result = ( int64_t )self->prop_position; else if ( self->types & mlt_prop_rect && self->data ) result = ( int64_t ) ( (mlt_rect*) self->data )->x; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) result = mlt_property_atoll( self->prop_string ); pthread_mutex_unlock( &self->mutex ); return result; } /** Get the property as a string (with time format). * * 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 self a property * \param time_format the time format to use for animation * \return a string representation of the property or NULL if failed */ char *mlt_property_get_string_tf( mlt_property self, mlt_time_format time_format ) { // Construct a string if need be pthread_mutex_lock( &self->mutex ); if ( self->animation && self->serialiser ) { if ( self->prop_string ) free( self->prop_string ); self->prop_string = self->serialiser( self->animation, time_format ); } else if ( ! ( self->types & mlt_prop_string ) ) { if ( self->types & mlt_prop_int ) { self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); sprintf( self->prop_string, "%d", self->prop_int ); } else if ( self->types & mlt_prop_double ) { self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); sprintf( self->prop_string, "%g", self->prop_double ); } else if ( self->types & mlt_prop_position ) { self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); sprintf( self->prop_string, "%d", (int)self->prop_position ); } else if ( self->types & mlt_prop_int64 ) { self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); sprintf( self->prop_string, "%"PRId64, self->prop_int64 ); } else if ( self->types & mlt_prop_data && self->data && self->serialiser ) { self->types |= mlt_prop_string; self->prop_string = self->serialiser( self->data, self->length ); } } pthread_mutex_unlock( &self->mutex ); // Return the string (may be NULL) return self->prop_string; } static mlt_time_format default_time_format() { const char *e = getenv("MLT_ANIMATION_TIME_FORMAT"); return e? strtol( e, NULL, 10 ) : mlt_time_frames; } /** 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 self a property * \return a string representation of the property or NULL if failed */ char *mlt_property_get_string( mlt_property self ) { return mlt_property_get_string_tf( self, default_time_format() ); } /** Get the property as a string (with locale and time format). * * 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 self a property * \param locale the locale to use for this conversion * \param time_format the time format to use for animation * \return a string representation of the property or NULL if failed */ char *mlt_property_get_string_l_tf( mlt_property self, locale_t locale, mlt_time_format time_format ) { // Optimization for no locale if ( !locale ) return mlt_property_get_string_tf( self, time_format ); // Construct a string if need be pthread_mutex_lock( &self->mutex ); if ( self->animation && self->serialiser ) { if ( self->prop_string ) free( self->prop_string ); self->prop_string = self->serialiser( self->animation, time_format ); } else if ( ! ( self->types & mlt_prop_string ) ) { #if !defined(_WIN32) // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale. // Save the current locale #if defined(__APPLE__) const char *localename = querylocale( LC_NUMERIC_MASK, locale ); #elif defined(__GLIBC__) const char *localename = locale->__names[ LC_NUMERIC ]; #else const char *localename = locale; #endif // Get the current locale char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); // Set the new locale setlocale( LC_NUMERIC, localename ); #endif // _WIN32 if ( self->types & mlt_prop_int ) { self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); sprintf( self->prop_string, "%d", self->prop_int ); } else if ( self->types & mlt_prop_double ) { self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); sprintf( self->prop_string, "%g", self->prop_double ); } else if ( self->types & mlt_prop_position ) { self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); sprintf( self->prop_string, "%d", (int)self->prop_position ); } else if ( self->types & mlt_prop_int64 ) { self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); sprintf( self->prop_string, "%"PRId64, self->prop_int64 ); } else if ( self->types & mlt_prop_data && self->data && self->serialiser ) { self->types |= mlt_prop_string; self->prop_string = self->serialiser( self->data, self->length ); } #if !defined(_WIN32) // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); free( orig_localename ); #endif } pthread_mutex_unlock( &self->mutex ); // Return the string (may be NULL) return self->prop_string; } /** Get the property as a string (with locale). * * 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 self a property * \param locale the locale to use for this conversion * \return a string representation of the property or NULL if failed */ char *mlt_property_get_string_l( mlt_property self, locale_t locale ) { return mlt_property_get_string_l_tf( self, locale, default_time_format() ); } /** 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 self 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 self, int *length ) { // Assign length if not NULL if ( length != NULL ) *length = self->length; // Return the data (note: there is no conversion here) pthread_mutex_lock( &self->mutex ); void* result = self->data; pthread_mutex_unlock( &self->mutex ); return result; } /** Destroy a property and free all related resources. * * \public \memberof mlt_property_s * \param self a property */ void mlt_property_close( mlt_property self ) { clear_property( self ); pthread_mutex_destroy( &self->mutex ); free( self ); } /** 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 self a property * \param that another property */ void mlt_property_pass( mlt_property self, mlt_property that ) { pthread_mutex_lock( &self->mutex ); clear_property( self ); self->types = that->types; if ( self->types & mlt_prop_int64 ) self->prop_int64 = that->prop_int64; else if ( self->types & mlt_prop_int ) self->prop_int = that->prop_int; else if ( self->types & mlt_prop_double ) self->prop_double = that->prop_double; else if ( self->types & mlt_prop_position ) self->prop_position = that->prop_position; if ( self->types & mlt_prop_string ) { if ( that->prop_string != NULL ) self->prop_string = strdup( that->prop_string ); } else if ( that->types & mlt_prop_rect ) { clear_property( self ); self->types = mlt_prop_rect | mlt_prop_data; self->length = that->length; self->data = calloc( 1, self->length ); memcpy( self->data, that->data, self->length ); self->destructor = free; self->serialiser = that->serialiser; } else if ( that->animation && that->serialiser ) { self->types = mlt_prop_string; self->prop_string = that->serialiser( that->animation, default_time_format() ); } else if ( that->types & mlt_prop_data && that->serialiser ) { self->types = mlt_prop_string; self->prop_string = that->serialiser( that->data, that->length ); } pthread_mutex_unlock( &self->mutex ); } /** Convert frame count to a SMPTE timecode string. * * \private \memberof mlt_property_s * \param frames a frame count * \param fps frames per second * \param[out] s the string to write into - must have enough space to hold largest time string */ static void time_smpte_from_frames( int frames, double fps, char *s, int drop ) { int hours, mins, secs; char frame_sep = ':'; if ( fps == 30000.0/1001.0 ) { fps = 30.0; if ( drop ) { int i; for ( i = 1800; i <= frames; i += 1800 ) { if ( i % 18000 ) frames += 2; } frame_sep = ';'; } } hours = frames / ( fps * 3600 ); frames -= floor( hours * 3600 * fps ); mins = frames / ( fps * 60 ); frames -= floor( mins * 60 * fps ); secs = frames / fps; frames -= ceil( secs * fps ); sprintf( s, "%02d:%02d:%02d%c%0*d", hours, mins, secs, frame_sep, ( fps > 999? 4 : fps > 99? 3 : 2 ), frames ); } /** Convert frame count to a SMIL clock value string. * * \private \memberof mlt_property_s * \param frames a frame count * \param fps frames per second * \param[out] s the string to write into - must have enough space to hold largest time string */ static void time_clock_from_frames( int frames, double fps, char *s ) { int hours, mins; double secs; hours = frames / ( fps * 3600 ); frames -= floor( hours * 3600 * fps ); mins = frames / ( fps * 60 ); frames -= floor( mins * 60 * fps ); secs = frames / fps; sprintf( s, "%02d:%02d:%06.3f", hours, mins, secs ); } /** Get the property as a time string. * * The time value can be either a SMPTE timecode or SMIL clock value. * The caller is not responsible for deallocating the returned string! * The string is deallocated when the property is closed. * \public \memberof mlt_property_s * \param self a property * \param format the time format that you want * \param fps frames per second * \param locale the locale to use for this conversion * \return a string representation of the property or NULL if failed */ char *mlt_property_get_time( mlt_property self, mlt_time_format format, double fps, locale_t locale ) { #if !defined(_WIN32) char *orig_localename = NULL; #endif int frames = 0; // Remove existing string if ( self->prop_string ) mlt_property_set_int( self, mlt_property_get_int( self, fps, locale ) ); // Optimization for mlt_time_frames if ( format == mlt_time_frames ) return mlt_property_get_string_l( self, locale ); #if !defined(_WIN32) // Use the specified locale if ( locale ) { // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale. // Save the current locale #if defined(__APPLE__) const char *localename = querylocale( LC_NUMERIC_MASK, locale ); #elif defined(__GLIBC__) const char *localename = locale->__names[ LC_NUMERIC ]; #else // TODO: not yet sure what to do on other platforms const char *localename = locale; #endif // _WIN32 // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock( &self->mutex ); // Get the current locale orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); // Set the new locale setlocale( LC_NUMERIC, localename ); } else #endif // _WIN32 { // Make sure we have a lock before accessing self->types pthread_mutex_lock( &self->mutex ); } // Convert number to string if ( self->types & mlt_prop_int ) { frames = self->prop_int; } else if ( self->types & mlt_prop_position ) { frames = (int) self->prop_position; } else if ( self->types & mlt_prop_double ) { frames = self->prop_double; } else if ( self->types & mlt_prop_int64 ) { frames = (int) self->prop_int64; } self->types |= mlt_prop_string; self->prop_string = malloc( 32 ); if ( format == mlt_time_clock ) time_clock_from_frames( frames, fps, self->prop_string ); else if ( format == mlt_time_smpte_ndf ) time_smpte_from_frames( frames, fps, self->prop_string, 0 ); else // Use smpte drop frame by default time_smpte_from_frames( frames, fps, self->prop_string, 1 ); #if !defined(_WIN32) // Restore the current locale if ( locale ) { setlocale( LC_NUMERIC, orig_localename ); free( orig_localename ); pthread_mutex_unlock( &self->mutex ); } else #endif // _WIN32 { // Make sure we have a lock before accessing self->types pthread_mutex_unlock( &self->mutex ); } // Return the string (may be NULL) return self->prop_string; } /** Determine if the property holds a numeric or numeric string value. * * \private \memberof mlt_property_s * \param self a property * \param locale the locale to use for string evaluation * \return true if it is numeric */ static int is_property_numeric( mlt_property self, locale_t locale ) { int result = ( self->types & mlt_prop_int ) || ( self->types & mlt_prop_int64 ) || ( self->types & mlt_prop_double ) || ( self->types & mlt_prop_position ) || ( self->types & mlt_prop_rect ); // If not already numeric but string is numeric. if ( ( !result && self->types & mlt_prop_string ) && self->prop_string ) { char *p = NULL; #if defined(__GLIBC__) || defined(__APPLE__) || defined(HAVE_STRTOD_L) if ( locale ) strtod_l( self->prop_string, &p, locale ); else #elif !defined(_WIN32) char *orig_localename = NULL; if ( locale ) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock( &self->mutex ); // Get the current locale orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); // Set the new locale setlocale( LC_NUMERIC, locale ); } #endif strtod( self->prop_string, &p ); #if !defined(__GLIBC__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(HAVE_STRTOD_L) if ( locale ) { // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); free( orig_localename ); pthread_mutex_unlock( &self->mutex ); } #endif result = ( p != self->prop_string ); } return result; } /** A linear interpolation function for animation. * * \private \memberof mlt_property_s */ static inline double linear_interpolate( double y1, double y2, double t ) { return y1 + ( y2 - y1 ) * t; } /** A smooth spline interpolation for animation. * * For non-closed curves, you need to also supply the tangent vector at the first and last control point. * This is commonly done: T(P[0]) = P[1] - P[0] and T(P[n]) = P[n] - P[n-1]. * \private \memberof mlt_property_s */ static inline double catmull_rom_interpolate( double y0, double y1, double y2, double y3, double t ) { double t2 = t * t; double a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3; double a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3; double a2 = -0.5 * y0 + 0.5 * y2; double a3 = y1; return a0 * t * t2 + a1 * t2 + a2 * t + a3; } /** Interpolate a new property value given a set of other properties. * * \public \memberof mlt_property_s * \param self the property onto which to set the computed value * \param p an array of at least 1 value in p[1] if \p interp is discrete, * 2 values in p[1] and p[2] if \p interp is linear, or * 4 values in p[0] - p[3] if \p interp is smooth * \param progress a ratio in the range [0, 1] to indicate how far between p[1] and p[2] * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param interp the interpolation method to use * \return true if there was an error */ int mlt_property_interpolate( mlt_property self, mlt_property p[], double progress, double fps, locale_t locale, mlt_keyframe_type interp ) { int error = 0; if ( interp != mlt_keyframe_discrete && is_property_numeric( p[1], locale ) && is_property_numeric( p[2], locale ) ) { if ( self->types & mlt_prop_rect ) { mlt_rect value = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN }; if ( interp == mlt_keyframe_linear ) { mlt_rect points[2]; mlt_rect zero = {0, 0, 0, 0, 0}; points[0] = p[1]? mlt_property_get_rect( p[1], locale ) : zero; if ( p[2] ) { points[1] = mlt_property_get_rect( p[2], locale ); value.x = linear_interpolate( points[0].x, points[1].x, progress ); value.y = linear_interpolate( points[0].y, points[1].y, progress ); value.w = linear_interpolate( points[0].w, points[1].w, progress ); value.h = linear_interpolate( points[0].h, points[1].h, progress ); value.o = linear_interpolate( points[0].o, points[1].o, progress ); } else { value = points[0]; } } else if ( interp == mlt_keyframe_smooth ) { mlt_rect points[4]; mlt_rect zero = {0, 0, 0, 0, 0}; points[1] = p[1]? mlt_property_get_rect( p[1], locale ) : zero; if ( p[2] ) { points[0] = p[0]? mlt_property_get_rect( p[0], locale ) : zero; points[2] = p[2]? mlt_property_get_rect( p[2], locale ) : zero; points[3] = p[3]? mlt_property_get_rect( p[3], locale ) : zero; value.x = catmull_rom_interpolate( points[0].x, points[1].x, points[2].x, points[3].x, progress ); value.y = catmull_rom_interpolate( points[0].y, points[1].y, points[2].y, points[3].y, progress ); value.w = catmull_rom_interpolate( points[0].w, points[1].w, points[2].w, points[3].w, progress ); value.h = catmull_rom_interpolate( points[0].h, points[1].h, points[2].h, points[3].h, progress ); value.o = catmull_rom_interpolate( points[0].o, points[1].o, points[2].o, points[3].o, progress ); } else { value = points[1]; } } error = mlt_property_set_rect( self, value ); } else { double value = 0.0; if ( interp == mlt_keyframe_linear ) { double points[2]; points[0] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0; points[1] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0; value = p[2]? linear_interpolate( points[0], points[1], progress ) : points[0]; } else if ( interp == mlt_keyframe_smooth ) { double points[4]; points[0] = p[0]? mlt_property_get_double( p[0], fps, locale ) : 0; points[1] = p[1]? mlt_property_get_double( p[1], fps, locale ) : 0; points[2] = p[2]? mlt_property_get_double( p[2], fps, locale ) : 0; points[3] = p[3]? mlt_property_get_double( p[3], fps, locale ) : 0; value = p[2]? catmull_rom_interpolate( points[0], points[1], points[2], points[3], progress ) : points[1]; } error = mlt_property_set_double( self, value ); } } else { mlt_property_pass( self, p[1] ); } return error; } /** Create a new animation or refresh an existing one. * * \private \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that */ static void refresh_animation( mlt_property self, double fps, locale_t locale, int length ) { if ( !self->animation ) { self->animation = mlt_animation_new(); self->serialiser = (mlt_serialiser) mlt_animation_serialize_tf; mlt_animation_parse( self->animation, self->prop_string, length, fps, locale ); } else if ( ( self->types & mlt_prop_string ) && self->prop_string ) { mlt_animation_refresh( self->animation, self->prop_string, length ); } else if ( length >= 0 ) { mlt_animation_set_length( self->animation, length ); } } /** Get the real number at a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the real number */ double mlt_property_anim_get_double( mlt_property self, double fps, locale_t locale, int position, int length ) { double result; pthread_mutex_lock( &self->mutex ); if ( self->animation || ( self->prop_string && strchr( self->prop_string, '=' ) ) ) { struct mlt_animation_item_s item; item.property = mlt_property_init(); refresh_animation( self, fps, locale, length ); mlt_animation_get_item( self->animation, &item, position ); pthread_mutex_unlock( &self->mutex ); result = mlt_property_get_double( item.property, fps, locale ); mlt_property_close( item.property ); } else { pthread_mutex_unlock( &self->mutex ); result = mlt_property_get_double( self, fps, locale ); } return result; } /** Get the property as an integer number at a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return an integer value */ int mlt_property_anim_get_int( mlt_property self, double fps, locale_t locale, int position, int length ) { int result; pthread_mutex_lock( &self->mutex ); if ( self->animation || ( self->prop_string && strchr( self->prop_string, '=' ) ) ) { struct mlt_animation_item_s item; item.property = mlt_property_init(); refresh_animation( self, fps, locale, length ); mlt_animation_get_item( self->animation, &item, position ); pthread_mutex_unlock( &self->mutex ); result = mlt_property_get_int( item.property, fps, locale ); mlt_property_close( item.property ); } else { pthread_mutex_unlock( &self->mutex ); result = mlt_property_get_int( self, fps, locale ); } return result; } /** Get the string at certain a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the string representation of the property or NULL if failed */ char* mlt_property_anim_get_string( mlt_property self, double fps, locale_t locale, int position, int length ) { char *result; pthread_mutex_lock( &self->mutex ); if ( self->animation || ( self->prop_string && strchr( self->prop_string, '=' ) ) ) { struct mlt_animation_item_s item; item.property = mlt_property_init(); if ( !self->animation ) refresh_animation( self, fps, locale, length ); mlt_animation_get_item( self->animation, &item, position ); free( self->prop_string ); pthread_mutex_unlock( &self->mutex ); self->prop_string = mlt_property_get_string_l( item.property, locale ); pthread_mutex_lock( &self->mutex ); if ( self->prop_string ) self->prop_string = strdup( self->prop_string ); self->types |= mlt_prop_string; result = self->prop_string; mlt_property_close( item.property ); pthread_mutex_unlock( &self->mutex ); } else { pthread_mutex_unlock( &self->mutex ); result = mlt_property_get_string_l( self, locale ); } return result; } /** Set a property animation keyframe to a real number. * * \public \memberof mlt_property_s * \param self a property * \param value a double precision floating point value * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return false if successful, true to indicate error */ int mlt_property_anim_set_double( mlt_property self, double value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type ) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = keyframe_type; mlt_property_set_double( item.property, value ); pthread_mutex_lock( &self->mutex ); refresh_animation( self, fps, locale, length ); result = mlt_animation_insert( self->animation, &item ); mlt_animation_interpolate( self->animation ); pthread_mutex_unlock( &self->mutex ); mlt_property_close( item.property ); return result; } /** Set a property animation keyframe to an integer value. * * \public \memberof mlt_property_s * \param self a property * \param value an integer * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return false if successful, true to indicate error */ int mlt_property_anim_set_int( mlt_property self, int value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type ) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = keyframe_type; mlt_property_set_int( item.property, value ); pthread_mutex_lock( &self->mutex ); refresh_animation( self, fps, locale, length ); result = mlt_animation_insert( self->animation, &item ); mlt_animation_interpolate( self->animation ); pthread_mutex_unlock( &self->mutex ); mlt_property_close( item.property ); return result; } /** Set a property animation keyframe to a string. * * Strings only support discrete animation. Do not use this to set a property's * animation string that contains a semicolon-delimited set of values; use * mlt_property_set() for that. * \public \memberof mlt_property_s * \param self a property * \param value a string * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return false if successful, true to indicate error */ int mlt_property_anim_set_string( mlt_property self, const char *value, double fps, locale_t locale, int position, int length ) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = mlt_keyframe_discrete; mlt_property_set_string( item.property, value ); pthread_mutex_lock( &self->mutex ); refresh_animation( self, fps, locale, length ); result = mlt_animation_insert( self->animation, &item ); mlt_animation_interpolate( self->animation ); pthread_mutex_unlock( &self->mutex ); mlt_property_close( item.property ); return result; } /** Get an object's animation object. * * You might need to call another mlt_property_anim_ function to actually construct * the animation, as this is a simple accessor function. * \public \memberof mlt_property_s * \param self a property * \return the animation object or NULL if there is no animation */ mlt_animation mlt_property_get_animation( mlt_property self ) { pthread_mutex_lock( &self->mutex ); mlt_animation result = self->animation; pthread_mutex_unlock( &self->mutex ); return result; } /** Convert a rectangle value into a string. * * Unlike the deprecated mlt_geometry API, the canonical form of a mlt_rect * is a space delimited "x y w h o" even though many kinds of field delimiters * may be used to convert a string to a rectangle. * \private \memberof mlt_property_s * \param rect the rectangle to convert * \param length not used * \return the string representation of a rectangle */ static char* serialise_mlt_rect( mlt_rect *rect, int length ) { char* result = calloc( 1, 100 ); if ( rect->x != DBL_MIN ) sprintf( result + strlen( result ), "%g", rect->x ); if ( rect->y != DBL_MIN ) sprintf( result + strlen( result ), " %g", rect->y ); if ( rect->w != DBL_MIN ) sprintf( result + strlen( result ), " %g", rect->w ); if ( rect->h != DBL_MIN ) sprintf( result + strlen( result ), " %g", rect->h ); if ( rect->o != DBL_MIN ) sprintf( result + strlen( result ), " %g", rect->o ); return result; } /** Set a property to a mlt_rect rectangle. * * \public \memberof mlt_property_s * \param self a property * \param value a rectangle * \return false */ int mlt_property_set_rect( mlt_property self, mlt_rect value ) { pthread_mutex_lock( &self->mutex ); clear_property( self ); self->types = mlt_prop_rect | mlt_prop_data; self->length = sizeof(value); self->data = calloc( 1, self->length ); memcpy( self->data, &value, self->length ); self->destructor = free; self->serialiser = (mlt_serialiser) serialise_mlt_rect; pthread_mutex_unlock( &self->mutex ); return 0; } /** Get the property as a rectangle. * * You can use any non-numeric character(s) as a field delimiter. * If the number has a '%' immediately following it, the number is divided by * 100 to convert it into a real number. * \public \memberof mlt_property_s * \param self a property * \param locale the locale to use for when converting from a string * \return a rectangle value */ mlt_rect mlt_property_get_rect( mlt_property self, locale_t locale ) { mlt_rect rect = { DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN, DBL_MIN }; if ( ( self->types & mlt_prop_rect ) && self->data ) rect = *( (mlt_rect*) self->data ); else if ( self->types & mlt_prop_double ) rect.x = self->prop_double; else if ( self->types & mlt_prop_int ) rect.x = ( double )self->prop_int; else if ( self->types & mlt_prop_position ) rect.x = ( double )self->prop_position; else if ( self->types & mlt_prop_int64 ) rect.x = ( double )self->prop_int64; else if ( ( self->types & mlt_prop_string ) && self->prop_string ) { char *value = self->prop_string; char *p = NULL; int count = 0; #if !defined(__GLIBC__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(HAVE_STRTOD_L) char *orig_localename = NULL; if ( locale ) { // Protect damaging the global locale from a temporary locale on another thread. pthread_mutex_lock( &self->mutex ); // Get the current locale orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); // Set the new locale setlocale( LC_NUMERIC, locale ); } #endif while ( *value ) { double temp; #if defined(__GLIBC__) || defined(__APPLE__) || defined(HAVE_STRTOD_L) if ( locale ) temp = strtod_l( value, &p, locale ); else #endif temp = strtod( value, &p ); if ( p != value ) { if ( p[0] == '%' ) { temp /= 100.0; p ++; } // Chomp the delimiter. if ( *p ) p ++; // Assign the value to appropriate field. switch( count ) { case 0: rect.x = temp; break; case 1: rect.y = temp; break; case 2: rect.w = temp; break; case 3: rect.h = temp; break; case 4: rect.o = temp; break; } } else { p++; } value = p; count ++; } #if !defined(__GLIBC__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(HAVE_STRTOD_L) if ( locale ) { // Restore the current locale setlocale( LC_NUMERIC, orig_localename ); free( orig_localename ); pthread_mutex_unlock( &self->mutex ); } #endif } return rect; } /** Set a property animation keyframe to a rectangle. * * \public \memberof mlt_property_s * \param self a property * \param value a rectangle * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \param keyframe_type the interpolation method for this keyframe * \return false if successful, true to indicate error */ int mlt_property_anim_set_rect( mlt_property self, mlt_rect value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type ) { int result; struct mlt_animation_item_s item; item.property = mlt_property_init(); item.frame = position; item.keyframe_type = keyframe_type; mlt_property_set_rect( item.property, value ); pthread_mutex_lock( &self->mutex ); refresh_animation( self, fps, locale, length ); result = mlt_animation_insert( self->animation, &item ); mlt_animation_interpolate( self->animation ); pthread_mutex_unlock( &self->mutex ); mlt_property_close( item.property ); return result; } /** Get a rectangle at a frame position. * * \public \memberof mlt_property_s * \param self a property * \param fps the frame rate, which may be needed for converting a time string to frame units * \param locale the locale, which may be needed for converting a string to a real number * \param position the frame number * \param length the maximum number of frames when interpreting negative keyframe times, * <=0 if you don't care or need that * \return the rectangle */ mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, locale_t locale, int position, int length ) { mlt_rect result; pthread_mutex_lock( &self->mutex ); if ( self->animation || ( self->prop_string && strchr( self->prop_string, '=' ) ) ) { struct mlt_animation_item_s item; item.property = mlt_property_init(); item.property->types = mlt_prop_rect; refresh_animation( self, fps, locale, length ); mlt_animation_get_item( self->animation, &item, position ); pthread_mutex_unlock( &self->mutex ); result = mlt_property_get_rect( item.property, locale ); mlt_property_close( item.property ); } else { pthread_mutex_unlock( &self->mutex ); result = mlt_property_get_rect( self, locale ); } return result; } mlt-6.20.0/src/framework/mlt_property.h000066400000000000000000000104521362234133600200750ustar00rootroot00000000000000/** * \file mlt_property.h * \brief Property class declaration * \see mlt_property_s * * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_PROPERTY_H #define MLT_PROPERTY_H #include "mlt_types.h" #if defined(__FreeBSD__) /* This header has existed since 1994 and defines __FreeBSD_version below. */ #include #endif #if (defined(__GLIBC__) && !defined(__APPLE__)) || defined(HAVE_LOCALE_H) # include #elif defined(__APPLE__) || (defined(__FreeBSD_version) && __FreeBSD_version >= 900506) # include #elif defined(__OpenBSD__) /* XXX matches __nop_locale glue in libc++ */ typedef void* locale_t; #else typedef char* locale_t; #endif extern mlt_property mlt_property_init( ); extern void mlt_property_clear( mlt_property self ); 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, double fps, locale_t ); extern double mlt_property_get_double( mlt_property self, double fps, locale_t ); extern mlt_position mlt_property_get_position( mlt_property self, double fps, locale_t ); extern int64_t mlt_property_get_int64( mlt_property self ); extern char *mlt_property_get_string_tf( mlt_property self, mlt_time_format ); extern char *mlt_property_get_string( mlt_property self ); extern char *mlt_property_get_string_l_tf( mlt_property self, locale_t, mlt_time_format ); extern char *mlt_property_get_string_l( mlt_property self, locale_t ); 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 self, mlt_property that ); extern char *mlt_property_get_time( mlt_property self, mlt_time_format, double fps, locale_t ); extern int mlt_property_interpolate( mlt_property self, mlt_property points[], double progress, double fps, locale_t locale, mlt_keyframe_type interp ); extern double mlt_property_anim_get_double( mlt_property self, double fps, locale_t locale, int position, int length ); extern int mlt_property_anim_get_int( mlt_property self, double fps, locale_t locale, int position, int length ); extern char* mlt_property_anim_get_string( mlt_property self, double fps, locale_t locale, int position, int length ); extern int mlt_property_anim_set_double( mlt_property self, double value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type ); extern int mlt_property_anim_set_int( mlt_property self, int value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type ); extern int mlt_property_anim_set_string( mlt_property self, const char *value, double fps, locale_t locale, int position, int length ); extern mlt_animation mlt_property_get_animation( mlt_property self ); extern int mlt_property_set_rect( mlt_property self, mlt_rect value ); extern mlt_rect mlt_property_get_rect( mlt_property self, locale_t locale ); extern int mlt_property_anim_set_rect( mlt_property self, mlt_rect value, double fps, locale_t locale, int position, int length, mlt_keyframe_type keyframe_type ); extern mlt_rect mlt_property_anim_get_rect( mlt_property self, double fps, locale_t locale, int position, int length ); #endif mlt-6.20.0/src/framework/mlt_repository.c000066400000000000000000000352521362234133600204300ustar00rootroot00000000000000/** * \file mlt_repository.c * \brief provides a map between service and shared objects * \see mlt_repository_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_repository.h" #include "mlt_properties.h" #include "mlt_tokeniser.h" #include "mlt_log.h" #include "mlt_factory.h" #include #include #include #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 self = calloc( 1, sizeof( struct mlt_repository_s )); mlt_properties_init( &self->parent, self ); self->consumers = mlt_properties_new(); self->filters = mlt_properties_new(); self->producers = mlt_properties_new(); self->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; int plugin_count = 0; #ifdef _WIN32 char *syspath = getenv("PATH"); char *exedir = mlt_environment( "MLT_APPDIR" ); #ifdef NODEPLOY char *sep = "\\bin;"; #else char *sep = ";"; #endif char *newpath; newpath = calloc( 1, 5 + strlen( exedir ) + strlen( sep ) + strlen( syspath ) + 1 ); strcat( newpath, "PATH=" ); // len=5 strcat( newpath, exedir ); strcat( newpath, sep ); strcat( newpath, syspath ); putenv( newpath ); free(newpath); #endif // 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( self ); // Register the object file for closure mlt_properties_set_data( &self->parent, object_name, object, 0, ( mlt_destructor )dlclose, NULL ); ++plugin_count; } else { dlclose( object ); } } else if ( strstr( object_name, "libmlt" ) ) { mlt_log_warning( NULL, "%s: failed to dlopen %s\n (%s)\n", __FUNCTION__, object_name, dlerror() ); } } if ( !plugin_count ) mlt_log_error( NULL, "%s: no plugins found in \"%s\"\n", __FUNCTION__, directory ); mlt_properties_close( dir ); return self; } /** 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 self 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 self, 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( self->consumers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL ); break; case filter_type: mlt_properties_set_data( self->filters, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL ); break; case producer_type: mlt_properties_set_data( self->producers, service, new_service( symbol ), 0, ( mlt_destructor )mlt_properties_close, NULL ); break; case transition_type: mlt_properties_set_data( self->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 self 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 self, 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( self->consumers, service, NULL ); break; case filter_type: service_properties = mlt_properties_get_data( self->filters, service, NULL ); break; case producer_type: service_properties = mlt_properties_get_data( self->producers, service, NULL ); break; case transition_type: service_properties = mlt_properties_get_data( self->transitions, service, NULL ); break; default: break; } return service_properties; } /** Construct a new instance of a service. * * \public \memberof mlt_repository_s * \param self 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 self, mlt_profile profile, mlt_service_type type, const char *service, const void *input ) { mlt_properties properties = get_service_properties( self, 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 self a repository */ void mlt_repository_close( mlt_repository self ) { mlt_properties_close( self->consumers ); mlt_properties_close( self->filters ); mlt_properties_close( self->producers ); mlt_properties_close( self->transitions ); mlt_properties_close( &self->parent ); free( self ); } /** 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; } static void list_presets( mlt_properties properties, const char *path, const char *dirname ) { DIR *dir = opendir( dirname ); if ( dir ) { struct dirent *de = readdir( dir ); char fullname[ PATH_MAX ]; while ( de != NULL ) { if ( de->d_name[0] != '.' && de->d_name[strlen( de->d_name ) - 1] != '~' ) { struct stat info; snprintf( fullname, sizeof(fullname), "%s/%s", dirname, de->d_name ); stat( fullname, &info ); if ( S_ISDIR( info.st_mode ) ) { // recurse into subdirectories char sub[ PATH_MAX ]; if ( path ) snprintf( sub, sizeof(sub), "%s/%s", path, de->d_name ); else strncpy( sub, de->d_name, sizeof(sub) ); list_presets( properties, sub, fullname ); } else { // load the preset mlt_properties preset = mlt_properties_load( fullname ); if ( preset && mlt_properties_count( preset ) ) { snprintf( fullname, 1024, "%s/%s", path, de->d_name ); mlt_properties_set_data( properties, fullname, preset, 0, (mlt_destructor) mlt_properties_close, NULL ); } } } de = readdir( dir ); } closedir( dir ); } } /** Get the list of presets. * * \public \memberof mlt_repository_s * \return a properties list of all the presets */ mlt_properties mlt_repository_presets( ) { mlt_properties result = mlt_properties_new(); list_presets( result, NULL, mlt_environment( "MLT_PRESETS_PATH" ) ); return result; } mlt-6.20.0/src/framework/mlt_repository.h000066400000000000000000000065441362234133600204370ustar00rootroot00000000000000/** * \file mlt_repository.h * \brief provides a map between service and shared objects * \see mlt_repository_s * * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 ); extern mlt_properties mlt_repository_presets( ); #endif mlt-6.20.0/src/framework/mlt_service.c000066400000000000000000000662351362234133600176560ustar00rootroot00000000000000/** * \file mlt_service.c * \brief interface definition for all service classes * \see mlt_service_s * * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "mlt_producer.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 self ); static void mlt_service_connect( mlt_service self, mlt_service that ); static int service_get_frame( mlt_service self, mlt_frame_ptr frame, int index ); static void mlt_service_property_changed( mlt_listener, mlt_properties owner, mlt_service self, void **args ); /** Initialize a service. * * \public \memberof mlt_service_s * \param self 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 self, void *child ) { int error = 0; // Initialise everything to NULL memset( self, 0, sizeof( struct mlt_service_s ) ); // Assign the child self->child = child; // Generate local space self->local = calloc( 1, sizeof( mlt_service_base ) ); // Associate the methods self->get_frame = service_get_frame; // Initialise the properties error = mlt_properties_init( &self->parent, self ); if ( error == 0 ) { self->parent.close = ( mlt_destructor )mlt_service_close; self->parent.close_object = self; mlt_events_init( &self->parent ); mlt_events_register( &self->parent, "service-changed", NULL ); mlt_events_register( &self->parent, "property-changed", ( mlt_transmitter )mlt_service_property_changed ); pthread_mutex_init( &( ( mlt_service_base * )self->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 self 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 self, void **args ) { if ( listener != NULL ) listener( owner, self, ( char * )args[ 0 ] ); } /** Acquire a mutual exclusion lock on this service. * * \public \memberof mlt_service_s * \param self the service to lock */ void mlt_service_lock( mlt_service self ) { if ( self != NULL ) pthread_mutex_lock( &( ( mlt_service_base * )self->local )->mutex ); } /** Release a mutual exclusion lock on this service. * * \public \memberof mlt_service_s * \param self the service to unlock */ void mlt_service_unlock( mlt_service self ) { if ( self != NULL ) pthread_mutex_unlock( &( ( mlt_service_base * )self->local )->mutex ); } /** Identify the subclass of the service. * * \public \memberof mlt_service_s * \param self a service * \return the subclass */ mlt_service_type mlt_service_identify( mlt_service self ) { mlt_service_type type = invalid_type; if ( self != NULL ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( self ); 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, "mlt_producer" ) ) type = producer_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 self a service * \param producer a producer * \param index which of potentially multiple producers to this service (0 based) * \return 0 for success, -1 for error, or 3 if \p producer is already connected to \p self */ int mlt_service_connect_producer( mlt_service self, mlt_service producer, int index ) { int i = 0; // Get the service base mlt_service_base *base = self->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 = ( index < base->count )? base->in[ index ] : NULL; // 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, self ); // Close the current service mlt_service_close( current ); // Inform caller that all went well return 0; } else { return -1; } } /** Insert a producer connected to the service. * * mlt_service_connect_producer() appends or overwrites a producer at input * \p index whereas this function inserts pushing all of the inputs down by * 1 in the list of inputs. * * \public \memberof mlt_service_s * \param self a service * \param producer a producer * \param index which of potentially multiple producers to this service (0 based) * \return 0 for success, -1 for error, or 3 if \p producer is already connected to \p self */ int mlt_service_insert_producer( mlt_service self, mlt_service producer, int index ) { // Get the service base mlt_service_base *base = self->local; if ( index >= base->count ) return mlt_service_connect_producer( self, producer, index ); int i = 0; // 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 needed. if ( base->count + 1 > base->size ) { int new_size = base->size + 10; base->in = realloc( base->in, new_size * sizeof( mlt_service ) ); if ( base->in != NULL ) { memset( &base->in[ base->size ], 0, new_size - base->size ); base->size = new_size; } } // If we have space, assign the input if ( base->in && index >= 0 && index < base->size ) { // Increment the reference count on this producer. if ( producer != NULL ) mlt_properties_inc_ref( MLT_SERVICE_PROPERTIES( producer ) ); // Disconnect the producer from its consumer. mlt_service_disconnect( producer ); // Make room in the list for the producer. memmove( &base->in[ index + 1 ], &base->in[ index ], ( base->count - index ) * sizeof( mlt_service ) ); // Add the service to index specified. base->in[ index ] = producer; // Increase the number of active tracks. base->count ++; // Connect the producer to its connected consumer. mlt_service_connect( producer, self ); // Inform caller that all went well return 0; } else { return -1; } } /** Remove the N-th producer. * * \public \memberof mlt_service_s * \param self a service * \param index which producer to remove (0-based) * \return true if there was an error */ int mlt_service_disconnect_producer( mlt_service self, int index ) { mlt_service_base *base = self->local; if ( base->in && index >= 0 && index < base->count ) { mlt_service current = base->in[ index ]; if ( current ) { // Close the current producer. mlt_service_disconnect( current ); mlt_service_close( current ); base->in[ index ] = NULL; // Contract the list of producers. for ( ; index + 1 < base->count; index ++ ) base->in[ index ] = base->in[ index + 1 ]; base->count --; return 0; } } return -1; } /** Remove the all the attached producers * * \public \memberof mlt_service_s * \param self a service * \return the number of successfully disconnected producers */ int mlt_service_disconnect_all_producers( mlt_service self) { int disconnected = 0, i = 0; mlt_service_base *base = self->local; if ( base->in ) { for ( i = 0 ; i < base->count ; i ++ ) { mlt_service current = base->in[ i ]; if ( current ) { mlt_service_close( current ); disconnected ++; } base->in[ i ] = NULL; } base->count = 0; } return disconnected; } /** Disconnect a service from its consumer. * * \private \memberof mlt_service_s * \param self a service */ static void mlt_service_disconnect( mlt_service self ) { if ( self != NULL ) { // Get the service base mlt_service_base *base = self->local; // Disconnect base->out = NULL; } } /** Obtain the consumer a service is connected to. * * \public \memberof mlt_service_s * \param self a service * \return the consumer */ mlt_service mlt_service_consumer( mlt_service self ) { // Get the service base mlt_service_base *base = self->local; // Return the connected consumer return base->out; } /** Obtain the producer a service is connected to. * * \public \memberof mlt_service_s * \param self a service * \return the last-most producer */ mlt_service mlt_service_producer( mlt_service self ) { // Get the service base mlt_service_base *base = self->local; // Return the connected producer return base->count > 0 ? base->in[ base->count - 1 ] : NULL; } /** Associate a service to a consumer. * * Overwrites connection to any existing consumer. * \private \memberof mlt_service_s * \param self a service * \param that a consumer */ static void mlt_service_connect( mlt_service self, mlt_service that ) { if ( self != NULL ) { // Get the service base mlt_service_base *base = self->local; // There's a bit more required here... base->out = that; } } /** Get the first connected producer. * * \public \memberof mlt_service_s * \param self a service * \return the first producer */ mlt_service mlt_service_get_producer( mlt_service self ) { mlt_service producer = NULL; // Get the service base mlt_service_base *base = self->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 self 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 self, mlt_frame_ptr frame, int index ) { mlt_service_base *base = self->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( self ); return 0; } /** Return the properties object. * * \public \memberof mlt_service_s * \param self a service * \return the properties */ mlt_properties mlt_service_properties( mlt_service self ) { return self != NULL ? &self->parent : NULL; } /** Recursively apply attached filters. * * \public \memberof mlt_service_s * \param self 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 self, mlt_frame frame, int index ) { int i; mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties service_properties = MLT_SERVICE_PROPERTIES( self ); mlt_service_base *base = self->local; mlt_position position = mlt_frame_get_position( frame ); mlt_position self_in = mlt_properties_get_position( service_properties, "in" ); mlt_position self_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 ? self_in : in ); mlt_properties_set_position( frame_properties, "out", out == 0 ? self_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 self 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 self, mlt_frame_ptr frame, int index ) { int result = 0; // Lock the service mlt_service_lock( self ); // Ensure that the frame is NULL *frame = NULL; // Only process if we have a valid service if ( self != NULL && self->get_frame != NULL ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( self ); mlt_position in = mlt_properties_get_position( properties, "in" ); mlt_position out = mlt_properties_get_position( properties, "out" ); mlt_position position = mlt_service_identify( self ) == producer_type ? mlt_producer_position( MLT_PRODUCER( self ) ) : -1; result = self->get_frame( self, 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( self, *frame, 1 ); mlt_deque_push_back( MLT_FRAME_SERVICE_STACK( *frame ), self ); if ( mlt_service_identify( self ) == producer_type && mlt_properties_get_int( MLT_SERVICE_PROPERTIES( self ), "_need_previous_next" ) ) { // Save the new position from self->get_frame mlt_position new_position = mlt_producer_position( MLT_PRODUCER( self ) ); // Get the preceding frame, unfiltered mlt_frame previous_frame; mlt_producer_seek( MLT_PRODUCER(self), position - 1 ); result = self->get_frame( self, &previous_frame, index ); if ( !result ) mlt_properties_set_data( properties, "previous frame", previous_frame, 0, ( mlt_destructor ) mlt_frame_close, NULL ); // Get the following frame, unfiltered mlt_frame next_frame; mlt_producer_seek( MLT_PRODUCER(self), position + 1 ); result = self->get_frame( self, &next_frame, index ); if ( !result ) { mlt_properties_set_data( properties, "next frame", next_frame, 0, ( mlt_destructor ) mlt_frame_close, NULL ); } // Restore the new position mlt_producer_seek( MLT_PRODUCER(self), new_position ); } } } // Make sure we return a frame if ( *frame == NULL ) *frame = mlt_frame_init( self ); // Unlock the service mlt_service_unlock( self ); return result; } /** The service-changed event handler. * * \private \memberof mlt_service_s * \param owner ignored * \param self the service on which the "service-changed" event is fired */ static void mlt_service_filter_changed( mlt_service owner, mlt_service self ) { mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "service-changed", NULL ); } /** The property-changed event handler. * * \private \memberof mlt_service_s * \param owner ignored * \param self the service on which the "property-changed" event is fired * \param name the name of the property that changed */ static void mlt_service_filter_property_changed( mlt_service owner, mlt_service self, char *name ) { mlt_events_fire( MLT_SERVICE_PROPERTIES( self ), "property-changed", name, NULL ); } /** Attach a filter. * * \public \memberof mlt_service_s * \param self a service * \param filter the filter to attach * \return true if there was an error */ int mlt_service_attach( mlt_service self, mlt_filter filter ) { int error = self == NULL || filter == NULL; if ( error == 0 ) { int i = 0; mlt_properties properties = MLT_SERVICE_PROPERTIES( self ); mlt_service_base *base = self->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_properties_set_data( props, "service", self, 0, NULL, NULL ); mlt_events_fire( properties, "service-changed", NULL ); mlt_events_fire( props, "service-changed", NULL ); mlt_service cp = mlt_properties_get_data( properties, "_cut_parent", NULL ); if ( cp ) mlt_events_fire( MLT_SERVICE_PROPERTIES(cp), "service-changed", NULL ); mlt_events_listen( props, self, "service-changed", ( mlt_listener )mlt_service_filter_changed ); mlt_events_listen( props, self, "property-changed", ( mlt_listener )mlt_service_filter_property_changed ); } else { error = 2; } } } return error; } /** Detach a filter. * * \public \memberof mlt_service_s * \param self a service * \param filter the filter to detach * \return true if there was an error */ int mlt_service_detach( mlt_service self, mlt_filter filter ) { int error = self == NULL || filter == NULL; if ( error == 0 ) { int i = 0; mlt_service_base *base = self->local; mlt_properties properties = MLT_SERVICE_PROPERTIES( self ); 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 ), self ); mlt_filter_close( filter ); mlt_events_fire( properties, "service-changed", NULL ); } } return error; } /** Get the number of filters attached. * * \public \memberof mlt_service_s * \param self a service * \return the number of attached filters or -1 if there was an error */ int mlt_service_filter_count( mlt_service self ) { int result = -1; if ( self ) { mlt_service_base *base = self->local; result = base->filter_count; } return result; } /** Reorder the attached filters. * * \public \memberof mlt_service_s * \param self a service * \param from the current index value of the filter to move * \param to the new index value for the filter specified in \p from * \return true if there was an error */ int mlt_service_move_filter( mlt_service self, int from, int to ) { int error = -1; if ( self ) { mlt_service_base *base = self->local; if ( from < 0 ) from = 0; if ( from >= base->filter_count ) from = base->filter_count - 1; if ( to < 0 ) to = 0; if ( to >= base->filter_count ) to = base->filter_count - 1; if ( from != to && base->filter_count > 1 ) { mlt_filter filter = base->filters[from]; int i; if ( from > to ) { for ( i = from; i > to; i-- ) base->filters[i] = base->filters[i - 1]; } else { for ( i = from; i < to; i++ ) base->filters[i] = base->filters[i + 1]; } base->filters[to] = filter; mlt_events_fire( MLT_SERVICE_PROPERTIES(self), "service-changed", NULL ); error = 0; } } return error; } /** Retrieve an attached filter. * * \public \memberof mlt_service_s * \param self 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 self, int index ) { mlt_filter filter = NULL; if ( self != NULL ) { mlt_service_base *base = self->local; if ( index >= 0 && index < base->filter_count ) filter = base->filters[ index ]; } return filter; } /** Retrieve the profile. * * \public \memberof mlt_service_s * \param self a service * \return the profile */ mlt_profile mlt_service_profile( mlt_service self ) { return self? mlt_properties_get_data( MLT_SERVICE_PROPERTIES( self ), "_profile", NULL ) : NULL; } /** Set the profile for a service. * * \public \memberof mlt_service_s * \param self a service * \param profile the profile to set onto the service */ void mlt_service_set_profile( mlt_service self, mlt_profile profile ) { mlt_properties_set_data( MLT_SERVICE_PROPERTIES( self ), "_profile", profile, 0, NULL, NULL ); } /** Destroy a service. * * \public \memberof mlt_service_s * \param self the service to destroy */ void mlt_service_close( mlt_service self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_SERVICE_PROPERTIES( self ) ) <= 0 ) { if ( self->close != NULL ) { self->close( self->close_object ); } else { mlt_service_base *base = self->local; int i = 0; int count = base->filter_count; mlt_events_block( MLT_SERVICE_PROPERTIES( self ), self ); while( count -- ) mlt_service_detach( self, base->filters[ 0 ] ); free( base->filters ); for ( i = 0; i < base->count; i ++ ) if ( base->in[ i ] != NULL ) mlt_service_close( base->in[ i ] ); self->parent.close = NULL; free( base->in ); pthread_mutex_destroy( &base->mutex ); free( base ); mlt_properties_close( &self->parent ); } } } /** Release a service's cache items. * * \private \memberof mlt_service_s * \param self a service */ void mlt_service_cache_purge( 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; } /** Set the number of items to cache for the named 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 size the number of items to cache */ void mlt_service_cache_set_size( mlt_service self, const char *name, int size ) { mlt_cache cache = get_cache( self, name ); if ( cache ) mlt_cache_set_size( cache, size ); } /** Get the current maximum size of the named 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 the current maximum number of items to cache or zero if there is an error */ int mlt_service_cache_get_size( mlt_service self, const char *name ) { mlt_cache cache = get_cache( self, name ); if ( cache ) return mlt_cache_get_size( cache ); else return 0; } mlt-6.20.0/src/framework/mlt_service.h000066400000000000000000000123221362234133600176470ustar00rootroot00000000000000/** * \file mlt_service.h * \brief interface declaration for all service classes * \see mlt_service_s * * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 a filter was attached or detached or a transition was connected or disconnected * \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 _profile stores the mlt_profile for a service * \properties \em _unique_id is a unique identifier * \properties \em _need_previous_next boolean that instructs producers to get * preceding and following frames inside of \p mlt_service_get_frame */ struct mlt_service_s { struct mlt_properties_s parent; /**< \private A service extends properties. */ /** 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 int mlt_service_insert_producer( mlt_service self, mlt_service producer, int index ); extern int mlt_service_disconnect_producer( mlt_service self, int index ); extern int mlt_service_disconnect_all_producers( mlt_service self ); 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 int mlt_service_filter_count( mlt_service self ); extern int mlt_service_move_filter( mlt_service self, int from, int to ); extern mlt_filter mlt_service_filter( mlt_service self, int index ); extern mlt_profile mlt_service_profile( mlt_service self ); extern void mlt_service_set_profile( mlt_service self, mlt_profile profile ); 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 ); extern void mlt_service_cache_set_size( mlt_service self, const char *name, int size ); extern int mlt_service_cache_get_size( mlt_service self, const char *name ); extern void mlt_service_cache_purge( mlt_service self ); #endif mlt-6.20.0/src/framework/mlt_slices.c000066400000000000000000000231561362234133600174730ustar00rootroot00000000000000/** * \file mlt_slices.c * \brief sliced threading processing helper * \see mlt_slices_s * * Copyright (C) 2016-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_slices.h" #include "mlt_properties.h" #include "mlt_log.h" #include "mlt_factory.h" #include #include #include #include #ifdef _WIN32 #include #endif #define MAX_SLICES 32 #define ENV_SLICES "MLT_SLICES_COUNT" typedef enum { mlt_policy_normal, mlt_policy_rr, mlt_policy_fifo, mlt_policy_nb } mlt_schedule_policy; static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; static mlt_slices globals[mlt_policy_nb] = {NULL, NULL, NULL}; struct mlt_slices_runtime_s { int jobs, done, curr; mlt_slices_proc proc; void* cookie; struct mlt_slices_runtime_s* next; }; struct mlt_slices_s { int f_exit; int count; int readys; int ref; pthread_mutex_t cond_mutex; pthread_cond_t cond_var_job; pthread_cond_t cond_var_ready; pthread_t threads[MAX_SLICES]; struct mlt_slices_runtime_s *head, *tail; const char* name; }; static void* mlt_slices_worker( void* p ) { int id, idx; struct mlt_slices_runtime_s* r; mlt_slices ctx = (mlt_slices)p; mlt_log_debug( NULL, "%s:%d: ctx=[%p][%s] entering\n", __FUNCTION__, __LINE__ , ctx, ctx->name ); pthread_mutex_lock( &ctx->cond_mutex ); id = ctx->readys; ctx->readys++; while ( 1 ) { mlt_log_debug( NULL, "%s:%d: ctx=[%p][%s] waiting\n", __FUNCTION__, __LINE__ , ctx, ctx->name ); /* wait for new jobs */ while( !ctx->f_exit && !( r = ctx->head ) ) pthread_cond_wait( &ctx->cond_var_job, &ctx->cond_mutex ); if ( ctx->f_exit ) break; if ( !r ) continue; /* check if no new job */ if ( r->curr == r->jobs ) { ctx->head = ctx->head->next; if ( !ctx->head ) ctx->tail = NULL; mlt_log_debug( NULL, "%s:%d: new ctx->head=%p\n", __FUNCTION__, __LINE__, ctx->head ); continue; }; /* new job id */ idx = r->curr; r->curr++; /* run job */ pthread_mutex_unlock( &ctx->cond_mutex ); mlt_log_debug( NULL, "%s:%d: running job: id=%d, idx=%d/%d, pool=[%s]\n", __FUNCTION__, __LINE__, id, idx, r->jobs, ctx->name ); r->proc( id, idx, r->jobs, r->cookie ); pthread_mutex_lock( &ctx->cond_mutex ); /* increase done jobs counter */ r->done++; /* notify we fininished last job */ if ( r->done == r->jobs ) { mlt_log_debug( NULL, "%s:%d: pthread_cond_signal( &ctx->cond_var_ready )\n", __FUNCTION__, __LINE__ ); pthread_cond_broadcast( &ctx->cond_var_ready ); } } pthread_mutex_unlock( &ctx->cond_mutex ); return NULL; } /** Initialize a sliced threading context * * \public \memberof mlt_slices_s * \deprecated * \param threads number of threads to use for job list, 0 for #cpus * \param policy scheduling policy of processing threads, -1 for normal * \param priority priority value that can be used with the scheduling algorithm, -1 for maximum * \return the context pointer */ mlt_slices mlt_slices_init( int threads, int policy, int priority ) { pthread_attr_t tattr; struct sched_param param; mlt_slices ctx = (mlt_slices)calloc( 1, sizeof( struct mlt_slices_s ) ); char *env = getenv( ENV_SLICES ); #ifdef _WIN32 #if _WIN32_WINNT >= 0x0601 int cpus = GetActiveProcessorCount( ALL_PROCESSOR_GROUPS ); #else SYSTEM_INFO info; GetSystemInfo(&info); int cpus = info.dwNumberOfProcessors; #endif #else int cpus = sysconf( _SC_NPROCESSORS_ONLN ); #endif int i, env_val = env ? atoi(env) : 0; /* check given threads count */ if ( !env || !env_val ) { if ( threads < 0 ) threads = -threads * cpus; else if ( !threads ) threads = cpus; } else if ( env_val < 0 ) { if ( threads < 0 ) threads = env_val * threads * cpus; else if ( !threads ) threads = -env_val * cpus; else threads = -env_val * threads; } else // env_val > 0 { if ( threads < 0 ) threads = env_val * threads; else if ( !threads ) threads = env_val; } if ( threads > MAX_SLICES ) threads = MAX_SLICES; ctx->count = threads; /* init attributes */ pthread_mutex_init ( &ctx->cond_mutex, NULL ); pthread_cond_init ( &ctx->cond_var_job, NULL ); pthread_cond_init ( &ctx->cond_var_ready, NULL ); pthread_attr_init( &tattr ); if ( policy < 0 ) policy = SCHED_OTHER; if ( priority < 0 ) priority = sched_get_priority_max( policy ); pthread_attr_setschedpolicy( &tattr, policy ); param.sched_priority = priority; pthread_attr_setschedparam( &tattr, ¶m ); /* run worker threads */ for ( i = 0; i < ctx->count; i++ ) { pthread_create( &ctx->threads[i], &tattr, mlt_slices_worker, ctx ); pthread_setschedparam( ctx->threads[i], policy, ¶m); } pthread_attr_destroy( &tattr ); /* return context */ return ctx; } /** Destroy sliced threading context * * \public \memberof mlt_slices_s * \deprecated * \param ctx context pointer */ void mlt_slices_close( mlt_slices ctx ) { int j; pthread_mutex_lock( &g_lock ); mlt_log_debug( NULL, "%s:%d: ctx=[%p][%s] closing\n", __FUNCTION__, __LINE__, ctx, ctx->name ); /* check reference count */ if ( ctx->ref ) { ctx->ref--; mlt_log_debug( NULL, "%s:%d: ctx=[%p][%s] new ref=%d\n", __FUNCTION__, __LINE__, ctx, ctx->name, ctx->ref ); pthread_mutex_unlock( &g_lock ); return; } pthread_mutex_unlock( &g_lock ); /* notify to exit */ ctx->f_exit = 1; pthread_mutex_lock( &ctx->cond_mutex ); pthread_cond_broadcast( &ctx->cond_var_job); pthread_cond_broadcast( &ctx->cond_var_ready); pthread_mutex_unlock( &ctx->cond_mutex ); /* wait for threads exit */ for ( j = 0; j < ctx->count; j++ ) pthread_join ( ctx->threads[j], NULL ); /* destroy vars */ pthread_cond_destroy ( &ctx->cond_var_ready ); pthread_cond_destroy ( &ctx->cond_var_job ); pthread_mutex_destroy ( &ctx->cond_mutex ); /* free context */ free ( ctx ); } /** Run sliced execution * * \public \memberof mlt_slices_s * \deprecated * \param ctx context pointer * \param jobs number of jobs to process * \param proc number of jobs to process */ void mlt_slices_run( mlt_slices ctx, int jobs, mlt_slices_proc proc, void* cookie ) { struct mlt_slices_runtime_s runtime, *r = &runtime; /* lock */ pthread_mutex_lock( &ctx->cond_mutex); /* check jobs count */ if ( jobs < 0 ) jobs = (-jobs) * ctx->count; if ( !jobs ) jobs = ctx->count; /* setup runtime args */ r->jobs = jobs; r->done = 0; r->curr = 0; r->proc = proc; r->cookie = cookie; r->next = NULL; /* attach job */ if ( ctx->tail ) { ctx->tail->next = r; ctx->tail = r; } else { ctx->head = ctx->tail = r; } /* notify workers */ pthread_cond_broadcast( &ctx->cond_var_job ); /* wait for end of task */ while( !ctx->f_exit && ( r->done < r->jobs ) ) { pthread_cond_wait( &ctx->cond_var_ready, &ctx->cond_mutex ); mlt_log_debug( NULL, "%s:%d: ctx=[%p][%s] signalled\n", __FUNCTION__, __LINE__ , ctx, ctx->name ); } pthread_mutex_unlock( &ctx->cond_mutex); } /** Get a global shared sliced threading context. * * There are separate contexts for each scheduling policy. * * \private \memberof mlt_slices_s * \param policy the thread scheduling policy needed * \return the context pointer */ static mlt_slices mlt_slices_get_global( mlt_schedule_policy policy ) { pthread_mutex_lock( &g_lock ); if ( !globals[policy] ) { int posix_policy; switch (policy) { case mlt_policy_rr: posix_policy = SCHED_RR; break; case mlt_policy_fifo: posix_policy = SCHED_FIFO; break; default: posix_policy = SCHED_OTHER; } globals[policy] = mlt_slices_init( 0, posix_policy, -1 ); mlt_factory_register_for_clean_up( globals[policy], (mlt_destructor) mlt_slices_close ); } pthread_mutex_unlock( &g_lock ); return globals[policy]; } /** Get the number of slices for the normal scheduling policy. * * \public \memberof mlt_slices_s * \return the number of slices */ int mlt_slices_count_normal() { mlt_slices slices = mlt_slices_get_global( mlt_policy_normal ); if (slices) return slices->count; else return 0; } /** Get the number of slices for the round robin scheduling policy. * * \public \memberof mlt_slices_s * \return the number of slices */ int mlt_slices_count_rr() { mlt_slices slices = mlt_slices_get_global( mlt_policy_rr ); if (slices) return slices->count; else return 0; } /** Get the number of slices for the fifo scheduling policy. * * \public \memberof mlt_slices_s * \return the number of slices */ int mlt_slices_count_fifo() { mlt_slices slices = mlt_slices_get_global( mlt_policy_fifo ); if (slices) return slices->count; else return 0; } void mlt_slices_run_normal(int jobs, mlt_slices_proc proc, void *cookie) { return mlt_slices_run( mlt_slices_get_global( mlt_policy_normal ), jobs, proc, cookie ); } void mlt_slices_run_rr(int jobs, mlt_slices_proc proc, void *cookie) { return mlt_slices_run( mlt_slices_get_global( mlt_policy_rr ), jobs, proc, cookie ); } void mlt_slices_run_fifo(int jobs, mlt_slices_proc proc, void *cookie) { return mlt_slices_run( mlt_slices_get_global( mlt_policy_fifo ), jobs, proc, cookie ); } mlt-6.20.0/src/framework/mlt_slices.h000066400000000000000000000033271362234133600174760ustar00rootroot00000000000000/** * \file mlt_slices.h * \brief sliced threading processing helper * \see mlt_slices_s * * Copyright (C) 2016-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_SLICES_H #define MLT_SLICES_H #include "mlt_types.h" /** * \envvar \em MLT_SLICES_COUNT Set the number of slices to use, which * defaults to number of CPUs found. */ struct mlt_slices_s; typedef int (*mlt_slices_proc)( int id, int idx, int jobs, void* cookie ); extern mlt_slices mlt_slices_init( int threads, int policy, int priority ); extern void mlt_slices_close( mlt_slices ctx ); extern void mlt_slices_run( mlt_slices ctx, int jobs, mlt_slices_proc proc, void* cookie ); extern int mlt_slices_count_normal(); extern int mlt_slices_count_rr(); extern int mlt_slices_count_fifo(); extern void mlt_slices_run_normal( int jobs, mlt_slices_proc proc, void* cookie ); extern void mlt_slices_run_rr( int jobs, mlt_slices_proc proc, void* cookie ); extern void mlt_slices_run_fifo( int jobs, mlt_slices_proc proc, void* cookie ); #endif mlt-6.20.0/src/framework/mlt_tokeniser.c000066400000000000000000000075741362234133600202220ustar00rootroot00000000000000/** * \file mlt_tokeniser.c * \brief string tokeniser * \see mlt_tokeniser_s * * Copyright (C) 2002-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* System header files */ #include #include /* Application header files */ #include "mlt_tokeniser.h" /** Initialise a tokeniser. */ mlt_tokeniser mlt_tokeniser_init( ) { mlt_tokeniser tokeniser = calloc( 1, sizeof( mlt_tokeniser_t ) ); tokeniser->input = NULL; tokeniser->tokens = NULL; tokeniser->count = 0; tokeniser->size = 0; return tokeniser; } /** 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 ) { if ( !string || !delimiter ) return 0; 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 += delimiter_size; } } /* 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 ); } mlt-6.20.0/src/framework/mlt_tokeniser.h000066400000000000000000000027351362234133600202210ustar00rootroot00000000000000/** * \file mlt_tokeniser.h * \brief string tokeniser * \see mlt_tokeniser_s * * Copyright (C) 2002-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 tokeniser, char *text, const char *delimiter ); extern char *mlt_tokeniser_get_input( mlt_tokeniser tokeniser ); extern int mlt_tokeniser_count( mlt_tokeniser tokeniser ); extern char *mlt_tokeniser_get_string( mlt_tokeniser tokeniser, int index ); extern void mlt_tokeniser_close( mlt_tokeniser tokeniser ); #endif mlt-6.20.0/src/framework/mlt_tractor.c000066400000000000000000000573411362234133600176720ustar00rootroot00000000000000/** * \file mlt_tractor.c * \brief tractor service class * \see mlt_tractor_s * * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_tractor.h" #include "mlt_frame.h" #include "mlt_multitrack.h" #include "mlt_field.h" #include "mlt_log.h" #include "mlt_transition.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 self ); /** 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 self = calloc( 1, sizeof( struct mlt_tractor_s ) ); if ( self != NULL ) { mlt_producer producer = &self->parent; if ( mlt_producer_init( producer, self ) == 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 = self; } else { free( self ); self = NULL; } } return self; } /** 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 self = calloc( 1, sizeof( struct mlt_tractor_s ) ); if ( self != NULL ) { mlt_producer producer = &self->parent; if ( mlt_producer_init( producer, self ) == 0 ) { mlt_multitrack multitrack = mlt_multitrack_init( ); mlt_field field = mlt_field_new( multitrack, self ); 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 ), self, "producer-changed", ( mlt_listener )mlt_tractor_listener ); producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )mlt_tractor_close; producer->close_object = self; } else { free( self ); self = NULL; } } return self; } /** Get the service object associated to the tractor. * * \public \memberof mlt_tractor_s * \param self a tractor * \return the parent service object * \see MLT_TRACTOR_SERVICE */ mlt_service mlt_tractor_service( mlt_tractor self ) { return MLT_PRODUCER_SERVICE( &self->parent ); } /** Get the producer object associated to the tractor. * * \public \memberof mlt_tractor_s * \param self a tractor * \return the parent producer object * \see MLT_TRACTOR_PRODUCER */ mlt_producer mlt_tractor_producer( mlt_tractor self ) { return self != NULL ? &self->parent : NULL; } /** Get the properties object associated to the tractor. * * \public \memberof mlt_tractor_s * \param self a tractor * \return the tractor's property list * \see MLT_TRACTOR_PROPERTIES */ mlt_properties mlt_tractor_properties( mlt_tractor self ) { return MLT_PRODUCER_PROPERTIES( &self->parent ); } /** Get the field self tractor is harvesting. * * \public \memberof mlt_tractor_s * \param self a tractor * \return a field or NULL if there is no field for this tractor */ mlt_field mlt_tractor_field( mlt_tractor self ) { return mlt_properties_get_data( MLT_TRACTOR_PROPERTIES( self ), "field", NULL ); } /** Get the multitrack a tractor is pulling. * * \public \memberof mlt_tractor_s * \param self a tractor * \return a multitrack or NULL if there is none */ mlt_multitrack mlt_tractor_multitrack( mlt_tractor self ) { return mlt_properties_get_data( MLT_TRACTOR_PROPERTIES( self ), "multitrack", NULL ); } /** Ensure the tractors in/out points match the multitrack. * * \public \memberof mlt_tractor_s * \param self a tractor */ void mlt_tractor_refresh( mlt_tractor self ) { mlt_multitrack multitrack = mlt_tractor_multitrack( self ); mlt_properties multitrack_props = MLT_MULTITRACK_PROPERTIES( multitrack ); mlt_properties properties = MLT_TRACTOR_PROPERTIES( self ); mlt_events_block( multitrack_props, properties ); mlt_events_block( properties, properties ); mlt_multitrack_refresh( multitrack ); mlt_properties_set_position( properties, "in", 0 ); mlt_properties_set_position( properties, "out", mlt_properties_get_position( multitrack_props, "out" ) ); mlt_events_unblock( properties, properties ); mlt_events_unblock( multitrack_props, properties ); mlt_properties_set_position( properties, "length", mlt_properties_get_position( multitrack_props, "length" ) ); } static void mlt_tractor_listener( mlt_multitrack tracks, mlt_tractor self ) { mlt_tractor_refresh( self ); } /** Connect the tractor. * * \public \memberof mlt_tractor_s * \param self a tractor * \param producer a producer * \return true on error */ int mlt_tractor_connect( mlt_tractor self, mlt_service producer ) { int ret = mlt_service_connect_producer( MLT_TRACTOR_SERVICE( self ), producer, 0 ); // This is the producer we're going to connect to if ( ret == 0 ) self->producer = producer; return ret; } /** Set the producer for a specific track. * * \public \memberof mlt_tractor_s * \param self a tractor * \param producer a producer * \param index the 0-based track index * \return true on error */ int mlt_tractor_set_track( mlt_tractor self, mlt_producer producer, int index ) { return mlt_multitrack_connect( mlt_tractor_multitrack( self ), producer, index ); } /** Insert a producer before a specific track. * * This also adjusts the track indices on mlt_transition_s and mlt_filter_s, * * \public \memberof mlt_tractor_s * \param self a tractor * \param producer a producer * \param index the 0-based track index * \return true on error */ int mlt_tractor_insert_track( mlt_tractor self, mlt_producer producer, int index ) { int error = mlt_multitrack_insert( mlt_tractor_multitrack( self ), producer, index ); if ( !error ) { // Update the track indices of transitions and track filters. mlt_service service = mlt_service_producer( MLT_TRACTOR_SERVICE( self ) ); while ( service ) { mlt_service_type type = mlt_service_identify( service ); mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); if ( type == transition_type ) { mlt_transition transition = MLT_TRANSITION( service ); int a_track = mlt_transition_get_a_track( transition ); int b_track = mlt_transition_get_b_track( transition ); if ( a_track >= index || b_track >= index ) { a_track = a_track >= index ? a_track + 1 : a_track; b_track = b_track >= index ? b_track + 1 : b_track; mlt_transition_set_tracks( transition, a_track, b_track ); } } else if ( type == filter_type ) { int current_track = mlt_properties_get_int( properties, "track" ); if ( current_track >= index ) mlt_properties_set_int( properties, "track", current_track + 1 ); } service = mlt_service_producer( service ); } } return error; } /** Remove a track by its index. * * \public \memberof mlt_tractor_s * \param self a tractor * \param index the 0-based track index * \return true on error */ int mlt_tractor_remove_track( mlt_tractor self, int index ) { int error = mlt_multitrack_disconnect( mlt_tractor_multitrack( self ), index ); if ( !error ) { // Update the track indices of transitions and track filters. mlt_service service = mlt_service_producer( MLT_TRACTOR_SERVICE( self ) ); while ( service ) { mlt_service_type type = mlt_service_identify( service ); mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); int track_max = MAX( mlt_multitrack_count( mlt_tractor_multitrack( self ) ) - 1, 0 ); if ( type == transition_type ) { mlt_transition transition = MLT_TRANSITION( service ); int a_track = mlt_transition_get_a_track( transition ); int b_track = mlt_transition_get_b_track( transition ); if ( a_track > index || b_track >= index ) { a_track = CLAMP( a_track > index ? a_track - 1 : a_track, 0, track_max ); b_track = CLAMP( b_track >= index ? b_track - 1 : b_track, 0, track_max ); mlt_transition_set_tracks( transition, a_track, b_track ); } } else if ( type == filter_type ) { int current_track = mlt_properties_get_int( properties, "track" ); if ( current_track >= index ) mlt_properties_set_int( properties, "track", CLAMP( current_track - 1, 0, track_max ) ); } service = mlt_service_producer( service ); } } return error; } /** Get the producer for a specific track. * * \public \memberof mlt_tractor_s * \param self a tractor * \param index the 0-based track index * \return the producer for track \p index */ mlt_producer mlt_tractor_get_track( mlt_tractor self, int index ) { return mlt_multitrack_track( mlt_tractor_multitrack( self ), index ); } static int producer_get_image( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { uint8_t *data = NULL; int size = 0; mlt_properties properties = MLT_FRAME_PROPERTIES( self ); mlt_frame frame = mlt_frame_pop_service( self ); 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_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, "consumer_tff", mlt_properties_get_int( properties, "consumer_tff" ) ); mlt_properties_set( frame_properties, "consumer_color_trc", mlt_properties_get( properties, "consumer_color_trc" ) ); // WebVfx uses this to setup a consumer-stopping event handler. mlt_properties_set_data( frame_properties, "consumer", mlt_properties_get_data( properties, "consumer", NULL ), 0, NULL, NULL ); mlt_frame_get_image( frame, buffer, format, width, height, writable ); mlt_frame_set_image( self, *buffer, 0, 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" ) ); mlt_properties_set_int( properties, "colorspace", mlt_properties_get_int( frame_properties, "colorspace" ) ); mlt_properties_set_int( properties, "force_full_luma", mlt_properties_get_int( frame_properties, "force_full_luma" ) ); mlt_properties_set_int( properties, "top_field_first", mlt_properties_get_int( frame_properties, "top_field_first" ) ); mlt_properties_set( properties, "color_trc", mlt_properties_get( frame_properties, "color_trc" ) ); mlt_properties_set_data( properties, "movit.convert.fence", mlt_properties_get_data( frame_properties, "movit.convert.fence", NULL ), 0, NULL, NULL ); mlt_properties_set_data( properties, "movit.convert.texture", mlt_properties_get_data( frame_properties, "movit.convert.texture", NULL ), 0, NULL, NULL ); mlt_properties_set_int( properties, "movit.convert.use_texture", mlt_properties_get_int( frame_properties, "movit.convert.use_texture" ) ); int i; for ( i = 0; i < mlt_properties_count( frame_properties ); i++ ) { char *name = mlt_properties_get_name( frame_properties, i ); if ( name && !strncmp( name, "_movit ", 7 ) ) { mlt_properties_set_data( properties, name, mlt_properties_get_data_at( frame_properties, i, NULL ), 0, NULL, NULL ); } } data = mlt_frame_get_alpha( frame ); if ( data ) { mlt_properties_get_data( frame_properties, "alpha", &size ); mlt_frame_set_alpha( self, data, size, NULL ); }; self->convert_image = frame->convert_image; self->convert_audio = frame->convert_audio; return 0; } static int producer_get_audio( mlt_frame self, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_properties properties = MLT_FRAME_PROPERTIES( self ); mlt_frame frame = mlt_frame_pop_audio( self ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set( frame_properties, "consumer_channel_layout", mlt_properties_get( properties, "consumer_channel_layout" ) ); mlt_properties_set( frame_properties, "producer_consumer_fps", mlt_properties_get( properties, "producer_consumer_fps" ) ); mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); mlt_frame_set_audio( self, *buffer, *format, mlt_audio_format_size( *format, *samples, *channels ), NULL ); mlt_properties_set_int( properties, "audio_frequency", *frequency ); mlt_properties_set_int( properties, "audio_channels", *channels ); mlt_properties_set_int( properties, "audio_samples", *samples ); 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 self = parent->child; // We only respond to the first track requests if ( track == 0 && self->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 ); // 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 ) { // The output frame will hold the 'global' data feeds (ie: those which are targeted for the final frame) mlt_deque data_queue = mlt_deque_init( ); // Used to garbage collect all frames char label[64]; // Get the id of the tractor char *id = mlt_properties_get( properties, "_unique_id" ); if ( !id ) { mlt_properties_set_int64( properties, "_unique_id", (int64_t) properties ); 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( self->producer, &temp, i ); // Get the temporary properties temp_properties = MLT_FRAME_PROPERTIES( temp ); // Pass all unique meta properties from the producer's frame to the new frame mlt_properties_lock( temp_properties ); int props_count = mlt_properties_count( temp_properties ); int j; for ( j = 0; j < props_count; j ++ ) { char *name = mlt_properties_get_name( temp_properties, j ); if ( !strncmp( name, "meta.", 5 ) && !mlt_properties_get( frame_properties, name ) ) mlt_properties_set( frame_properties, name, mlt_properties_get_value( temp_properties, j ) ); } mlt_properties_unlock( temp_properties ); // Copy the format conversion virtual functions if ( ! (*frame)->convert_image && temp->convert_image ) (*frame)->convert_image = temp->convert_image; if ( ! (*frame)->convert_audio && temp->convert_audio ) (*frame)->convert_audio = temp->convert_audio; // 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 snprintf( label, sizeof(label), "mlt_tractor %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; 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_pass_list( frame_properties, video_properties, "meta.media.width, meta.media.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 ); mlt_properties_set_data( frame_properties, "_producer", mlt_frame_get_original_producer( first_video ), 0, NULL, NULL ); } 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 ); } 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( self->producer, frame, track ); } else { mlt_log( MLT_PRODUCER_SERVICE( parent ), MLT_LOG_ERROR, "tractor without a multitrack!!\n" ); mlt_service_get_frame( self->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 self a tractor */ void mlt_tractor_close( mlt_tractor self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_TRACTOR_PROPERTIES( self ) ) <= 0 ) { self->parent.close = NULL; mlt_producer_close( &self->parent ); free( self ); } } mlt-6.20.0/src/framework/mlt_tractor.h000066400000000000000000000055611362234133600176740ustar00rootroot00000000000000/** * \file mlt_tractor.h * \brief tractor service class * \see mlt_tractor_s * * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 int mlt_tractor_insert_track( mlt_tractor self, mlt_producer producer, int index ); extern int mlt_tractor_remove_track( mlt_tractor self, int index ); extern mlt_producer mlt_tractor_get_track( mlt_tractor self, int index ); extern void mlt_tractor_close( mlt_tractor self ); #endif mlt-6.20.0/src/framework/mlt_transition.c000066400000000000000000000424451362234133600204050ustar00rootroot00000000000000/** * \file mlt_transition.c * \brief abstraction for all transition services * \see mlt_transition_s * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_transition.h" #include "mlt_frame.h" #include "mlt_log.h" #include "mlt_producer.h" #include #include #include /* Forward references */ static int transition_get_frame( mlt_service self, mlt_frame_ptr frame, int index ); /** Initialize a new transition. * * \public \memberof mlt_transition_s * \param self a transition * \param child the object of a subclass * \return true on error */ int mlt_transition_init( mlt_transition self, void *child ) { mlt_service service = &self->parent; memset( self, 0, sizeof( struct mlt_transition_s ) ); self->child = child; if ( mlt_service_init( service, self ) == 0 ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( self ); service->get_frame = transition_get_frame; service->close = ( mlt_destructor )mlt_transition_close; service->close_object = self; mlt_properties_set( properties, "mlt_type", "transition" ); 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 self = calloc( 1, sizeof( struct mlt_transition_s ) ); if ( self != NULL ) mlt_transition_init( self, NULL ); return self; } /** Get the service class interface. * * \public \memberof mlt_transition_s * \param self a transition * \return the service class * \see MLT_TRANSITION_SERVICE */ mlt_service mlt_transition_service( mlt_transition self ) { return self != NULL ? &self->parent : NULL; } /** Get the properties interface. * * \public \memberof mlt_transition_s * \param self a transition * \return the transition's properties * \see MLT_TRANSITION_PROPERTIES */ mlt_properties mlt_transition_properties( mlt_transition self ) { return MLT_TRANSITION_PROPERTIES( self ); } /** Connect a transition with a producer's a and b tracks. * * \public \memberof mlt_transition_s * \param self 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 self, mlt_service producer, int a_track, int b_track ) { int ret = mlt_service_connect_producer( &self->parent, producer, a_track ); if ( ret == 0 ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( self ); self->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 self a transition * \param in the starting time * \param out the ending time */ void mlt_transition_set_in_and_out( mlt_transition self, mlt_position in, mlt_position out ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( self ); mlt_properties_set_position( properties, "in", in ); mlt_properties_set_position( properties, "out", out ); } /** Change the track indices of a transition. * * \public \memberof mlt_transition_s * \param a_track the track index of the first input * \param b_track the track index of the second index */ void mlt_transition_set_tracks( mlt_transition self, int a_track, int b_track ) { mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( self ), "a_track", a_track ); mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( self ), "b_track", b_track ); free( self->frames ); self->frames = NULL; } /** Get the index of the a track. * * \public \memberof mlt_transition_s * \param self a transition * \return the 0-based index of the track of the first producer */ int mlt_transition_get_a_track( mlt_transition self ) { return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "a_track" ); } /** Get the index of the b track. * * \public \memberof mlt_transition_s * \param self a transition * \return the 0-based index of the track of the second producer */ int mlt_transition_get_b_track( mlt_transition self ) { return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "b_track" ); } /** Get the in point. * * \public \memberof mlt_transition_s * \param self a transition * \return the starting time */ mlt_position mlt_transition_get_in( mlt_transition self ) { return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( self ), "in" ); } /** Get the out point. * * \public \memberof mlt_transition_s * \param self a transition * \return the ending time */ mlt_position mlt_transition_get_out( mlt_transition self ) { return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( self ), "out" ); } /** Get the duration. * * \public \memberof mlt_transition_s * \param self a transition * \return the duration or zero if unlimited */ mlt_position mlt_transition_get_length( mlt_transition self ) { mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent ); mlt_position in = mlt_properties_get_position( properties, "in" ); mlt_position out = mlt_properties_get_position( properties, "out" ); return ( out > 0 ) ? ( out - in + 1 ) : 0; } /** Get the position within the transition. * * The position is relative to the in point. * * \public \memberof mlt_transition_s * \param self a transition * \param frame a frame * \return the position */ mlt_position mlt_transition_get_position( mlt_transition self, mlt_frame frame ) { mlt_position in = mlt_transition_get_in( self ); mlt_position position = mlt_frame_get_position( frame ); return position - in; } /** Get the percent complete. * * \public \memberof mlt_transition_s * \param self a transition * \param frame a frame * \return the progress in the range 0.0 to 1.0 */ double mlt_transition_get_progress( mlt_transition self, mlt_frame frame ) { double progress = 0; mlt_position in = mlt_transition_get_in( self ); mlt_position out = mlt_transition_get_out( self ); if ( out == 0 ) { // If always active, use the frame's producer mlt_producer producer = mlt_frame_get_original_producer( frame ); if ( producer ) { in = mlt_producer_get_in( producer ); out = mlt_producer_get_out( producer ); } } if ( out != 0 ) { mlt_position position = mlt_frame_get_position( frame ); progress = ( double ) ( position - in ) / ( double ) ( out - in + 1 ); } return progress; } /** Get the second field incremental progress. * * \public \memberof mlt_transition_s * \param self a transition * \param frame a frame * \return the progress increment in the range 0.0 to 1.0 */ double mlt_transition_get_progress_delta( mlt_transition self, mlt_frame frame ) { double progress = 0; mlt_position in = mlt_transition_get_in( self ); mlt_position out = mlt_transition_get_out( self ); if ( out == 0 ) { // If always active, use the frame's producer mlt_producer producer = mlt_frame_get_original_producer( frame ); if ( producer ) { in = mlt_producer_get_in( producer ); out = mlt_producer_get_out( producer ); } } if ( out != 0 ) { mlt_position position = mlt_frame_get_position( frame ); double length = out - in + 1; double x = ( double ) ( position - in ) / length; double y = ( double ) ( position + 1 - in ) / length; progress = ( y - x ) / 2.0; } return progress; } /** Process the frame. * * If we have no process method (unlikely), we simply return the a_frame unmolested. * * \public \memberof mlt_transition_s * \param self 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 self, mlt_frame a_frame, mlt_frame b_frame ) { if ( self->process == NULL ) return a_frame; else return self->process( self, a_frame, b_frame ); } static int get_image_a( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_transition self = mlt_frame_pop_service( a_frame ); mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame ); // All transitions get scaling const char *rescale = mlt_properties_get( a_props, "rescale.interp" ); if ( !rescale || !strcmp( rescale, "none" ) ) mlt_properties_set( a_props, "rescale.interp", "nearest" ); // Ensure sane aspect ratio if ( mlt_frame_get_aspect_ratio( a_frame ) == 0.0 ) mlt_frame_set_aspect_ratio( a_frame, mlt_profile_sar( mlt_service_profile( MLT_TRANSITION_SERVICE(self) ) ) ); return mlt_frame_get_image( a_frame, image, format, width, height, writable ); } static int get_image_b( mlt_frame b_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_transition self = mlt_frame_pop_service( b_frame ); mlt_frame a_frame = mlt_frame_pop_frame( b_frame ); mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame ); mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); // Set scaling from A frame if not already provided. if ( !mlt_properties_get( b_props, "rescale.interp" ) ) { const char *rescale = mlt_properties_get( a_props, "rescale.interp" ); if ( !rescale || !strcmp( rescale, "none" ) ) rescale = "nearest"; mlt_properties_set( b_props, "rescale.interp", rescale ); } // Ensure sane aspect ratio if ( mlt_frame_get_aspect_ratio( b_frame ) == 0.0 ) mlt_frame_set_aspect_ratio( b_frame, mlt_profile_sar( mlt_service_profile( MLT_TRANSITION_SERVICE(self) ) ) ); mlt_properties_pass_list( b_props, a_props, "consumer_deinterlace, deinterlace_method, consumer_tff, consumer_color_trc, consumer_channel_layout" ); return mlt_frame_get_image( b_frame, image, format, width, height, writable ); } /** Get a frame from a 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 something 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 self = service->child; mlt_properties properties = MLT_TRANSITION_PROPERTIES( self ); 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" ); } a_track = a_track < 0 ? 0 : a_track; b_track = b_track < 0 ? 0 : b_track; // Only act on this operation once per multitrack iteration from the tractor if ( !self->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 ( self->frames == NULL ) self->frames = calloc( b_track + 1, sizeof( mlt_frame ) ); // Get all frames between a and b for( i = a_track; i <= b_track; i ++ ) mlt_service_get_frame( self->producer, &self->frames[ i ], i ); // We're holding these frames until the last_track frame property is received self->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( self->frames[ a_frame ] ) ) a_frame ++; // Determine if we're active now active = a_frame != b_frame && !invalid( self->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 && a_frame <= b_track ) { // For non-always-active transitions, we need the current position of the a frame position = mlt_frame_get_position( self->frames[ a_frame ] ); // If a is in range, we're active active = position >= in && ( out == 0 || position <= out ); } // Finally, process the a and b frames if ( active && !mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "disable" ) ) { int frame_nb = ( !reverse_order && a_frame <= b_track )? a_frame : b_frame; mlt_frame a_frame_ptr = self->frames[ frame_nb ]; frame_nb = ( !reverse_order || a_frame > b_track )? b_frame : a_frame; mlt_frame b_frame_ptr = self->frames[ frame_nb ]; if ( a_frame_ptr && MLT_FRAME_PROPERTIES(a_frame_ptr)->local && b_frame_ptr && MLT_FRAME_PROPERTIES(b_frame_ptr)->local ) { 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 ) ) { // Add hooks for pre-processing frames mlt_frame_push_service( a_frame_ptr, self ); mlt_frame_push_get_image( a_frame_ptr, get_image_a ); mlt_frame_push_frame( b_frame_ptr, a_frame_ptr ); mlt_frame_push_service( b_frame_ptr, self ); mlt_frame_push_get_image( b_frame_ptr, get_image_b ); // Process the transition *frame = mlt_transition_process( self, 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 = self->frames[ index ]; else error = mlt_service_get_frame( self->producer, frame, index ); // Determine if that was the last track self->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" ); return error; } /** Close and destroy the transition. * * \public \memberof mlt_transition_s * \param self a transition */ void mlt_transition_close( mlt_transition self ) { if ( self != NULL && mlt_properties_dec_ref( MLT_TRANSITION_PROPERTIES( self ) ) <= 0 ) { self->parent.close = NULL; if ( self->close != NULL ) { self->close( self ); } else { mlt_service_close( &self->parent ); free( self->frames ); free( self ); } } } mlt-6.20.0/src/framework/mlt_transition.h000066400000000000000000000070211362234133600204010ustar00rootroot00000000000000/** * \file mlt_transition.h * \brief abstraction for all transition services * \see mlt_transition_s * * Copyright (C) 2003-2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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, 3 for both audio and video * \properties \em disable Set this to disable the transition while keeping it in the object model. */ 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 void mlt_transition_set_tracks( mlt_transition self, int a_track, int b_track ); 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_position mlt_transition_get_length( mlt_transition self ); extern mlt_position mlt_transition_get_position( mlt_transition self, mlt_frame frame ); extern double mlt_transition_get_progress( mlt_transition self, mlt_frame frame ); extern double mlt_transition_get_progress_delta( mlt_transition self, mlt_frame frame ); 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 mlt-6.20.0/src/framework/mlt_types.h000066400000000000000000000225751362234133600173660ustar00rootroot00000000000000/** * \file mlt_types.h * \brief Provides forward definitions of all public types * * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_TYPES_H #define MLT_TYPES_H #ifndef GCC_VERSION #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) #endif #ifdef __cplusplus extern "C" { #endif #include #include #include #include "mlt_pool.h" #ifndef PATH_MAX #define PATH_MAX 4096 #endif /** 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, /**< (deprecated) suitable for OpenGL texture */ mlt_image_glsl, /**< for opengl module internal use only */ mlt_image_glsl_texture, /**< an OpenGL texture name */ mlt_image_yuv422p16, /**< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian */ mlt_image_invalid } mlt_image_format; /** The set of supported audio formats */ typedef enum { mlt_audio_none = 0,/**< audio not available */ mlt_audio_pcm = 1, /**< \deprecated signed 16-bit interleaved PCM */ mlt_audio_s16 = 1, /**< signed 16-bit interleaved PCM */ mlt_audio_s32, /**< signed 32-bit non-interleaved PCM */ mlt_audio_float, /**< 32-bit non-interleaved floating point */ mlt_audio_s32le, /**< signed 32-bit interleaved PCM */ mlt_audio_f32le, /**< 32-bit interleaved floating point */ mlt_audio_u8 /**< unsigned 8-bit interleaved PCM */ } mlt_audio_format; typedef enum { mlt_channel_auto = 0, /**< MLT will determine the default configuration based on channel number */ mlt_channel_independent, /**< channels are not related */ mlt_channel_mono, mlt_channel_stereo, mlt_channel_2p1, mlt_channel_3p0, mlt_channel_3p0_back, mlt_channel_4p0, mlt_channel_quad_back, mlt_channel_quad_side, mlt_channel_3p1, mlt_channel_5p0_back, mlt_channel_5p0, mlt_channel_4p1, mlt_channel_5p1_back, mlt_channel_5p1, mlt_channel_6p0, mlt_channel_6p0_front, mlt_channel_hexagonal, mlt_channel_6p1, mlt_channel_6p1_back, mlt_channel_6p1_front, mlt_channel_7p0, mlt_channel_7p0_front, mlt_channel_7p1, mlt_channel_7p1_wide_side, mlt_channel_7p1_wide_back, } mlt_channel_layout; /** The time string formats */ typedef enum { mlt_time_frames = 0, /**< frame count */ mlt_time_clock, /**< SMIL clock-value as [[hh:]mm:]ss[.fraction] */ mlt_time_smpte_df, /**< SMPTE timecode as [[[hh:]mm:]ss{:|;}]frames */ mlt_time_smpte = mlt_time_smpte_df, /**< Deprecated */ mlt_time_smpte_ndf /**< SMPTE NDF timecode as [[[hh:]mm:]ss:]frames */ } mlt_time_format; /** Interpolation methods for animation keyframes */ typedef enum { mlt_keyframe_discrete = 0, /**< non-interpolated; value changes instantaneously at the key frame */ mlt_keyframe_linear, /**< simple, constant pace from this key frame to the next */ mlt_keyframe_smooth /**< eased pacing from this keyframe to the next using a Catmull-Rom spline */ } mlt_keyframe_type; /** The relative time qualifiers */ typedef enum { mlt_whence_relative_start = 0, /**< 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 = 0, /**< 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 */ #ifdef DOUBLE_MLT_POSITION #define MLT_POSITION_FMT "%f" #define MLT_POSITION_MOD(A, B) (A - B * ((int)(A / B))) typedef double mlt_position; #else #define MLT_POSITION_MOD(A, B) A % B #define MLT_POSITION_FMT "%d" typedef int32_t mlt_position; #endif /** A rectangle type with coordinates, size, and opacity */ typedef struct { double x; /**< X coordinate */ double y; /**< Y coordinate */ double w; /**< width */ double h; /**< height */ double o; /**< opacity / mix-level */ } mlt_rect; /** A tuple of color components */ typedef struct { uint8_t r; /**< red */ uint8_t g; /**< green */ uint8_t b; /**< blue */ uint8_t a; /**< alpha */ } mlt_color; 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 struct mlt_animation_s *mlt_animation; /**< pointer to Property Animation object */ typedef struct mlt_slices_s *mlt_slices; /**< pointer to Sliced processing context 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 */ #define MLT_CONSUMER(x) ( ( mlt_consumer )( x ) ) /**< Cast to a Consumer pointer */ #define MLT_FRAME(x) ( ( mlt_frame )( x ) ) /**< Cast to a Frame pointer */ #ifndef MIN #define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif #ifndef CLAMP #define CLAMP(x, min, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) #endif #ifdef _WIN32 #include /* Win32 compatibility function declarations */ #if !defined(__MINGW32__) extern int usleep(unsigned int useconds); #endif #ifndef WIN_PTHREADS_TIME_H extern int nanosleep( const struct timespec * rqtp, struct timespec * rmtp ); #endif extern int setenv(const char *name, const char *value, int overwrite); extern char* getlocale(); extern FILE* win32_fopen(const char *filename_utf8, const char *mode_utf8); #include extern char* strptime(const char *buf, const char *fmt, struct tm *tm); #define mlt_fopen win32_fopen #define MLT_DIRLIST_DELIMITER ";" #else #define mlt_fopen fopen #define MLT_DIRLIST_DELIMITER ":" #endif /* ifdef _WIN32 */ #ifdef __cplusplus } #endif #endif mlt-6.20.0/src/framework/mlt_version.c000066400000000000000000000022611362234133600176700ustar00rootroot00000000000000/** * \file mlt_version.c * \brief contains version information * * Copyright (C) 2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mlt_version.h" int mlt_version_get_int( ) { return LIBMLT_VERSION_INT; } char *mlt_version_get_string( ) { return LIBMLT_VERSION; } int mlt_version_get_major( ) { return LIBMLT_VERSION_MAJOR; } int mlt_version_get_minor( ) { return LIBMLT_VERSION_MINOR; } int mlt_version_get_revision( ) { return LIBMLT_VERSION_REVISION; } mlt-6.20.0/src/framework/mlt_version.h000066400000000000000000000030371362234133600176770ustar00rootroot00000000000000/** * \file mlt_version.h * \brief contains version information * * Copyright (C) 2010-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_VERSION_H #define MLT_VERSION_H // Add quotes around any #define variables #define MLT_STRINGIZE2(s) #s #define MLT_STRINGIZE(s) MLT_STRINGIZE2(s) #define LIBMLT_VERSION_MAJOR 6 #define LIBMLT_VERSION_MINOR 20 #define LIBMLT_VERSION_REVISION 0 #define LIBMLT_VERSION_INT ((LIBMLT_VERSION_MAJOR<<16)+(LIBMLT_VERSION_MINOR<<8)+LIBMLT_VERSION_REVISION) #define LIBMLT_VERSION MLT_STRINGIZE(LIBMLT_VERSION_MAJOR.LIBMLT_VERSION_MINOR.LIBMLT_VERSION_REVISION) extern int mlt_version_get_int( ); extern int mlt_version_get_major( ); extern int mlt_version_get_minor( ); extern int mlt_version_get_revision( ); extern char *mlt_version_get_string( ); #endif mlt-6.20.0/src/melt/000077500000000000000000000000001362234133600141265ustar00rootroot00000000000000mlt-6.20.0/src/melt/CMakeLists.txt000066400000000000000000000013701362234133600166670ustar00rootroot00000000000000set(melt_lib mlt Threads::Threads) find_package(SDL2 QUIET) if(SDL2_FOUND) add_library(sdl2 SHARED IMPORTED GLOBAL) set_target_properties(sdl2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIRS} IMPORTED_LOCATION ${libdir}/libSDL2.so ) else() pkg_check_modules(sdl2 IMPORTED_TARGET GLOBAL sdl2) if(TARGET PkgConfig::sdl2) add_library(sdl2 ALIAS PkgConfig::sdl2) endif() endif() if(TARGET sdl2) if(MINGW) list(APPEND melt_lib mingw32) endif() list(APPEND melt_lib sdl2) endif() add_executable(melt melt.c io.c) target_compile_definitions(melt PRIVATE VERSION="${MLT_VERSION}") target_link_libraries(melt ${melt_lib}) install(TARGETS melt RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) mlt-6.20.0/src/melt/Makefile000066400000000000000000000025301362234133600155660ustar00rootroot00000000000000include ../../config.mak OBJS = melt.o \ io.o CFLAGS += -I.. $(RDYNAMIC) -DVERSION=\"$(version)\" LDFLAGS += -L../framework -lmlt -lpthread SRCS := $(OBJS:.o=.c) ifeq (, $(findstring MELT_NOSDL, $(CFLAGS))) ifeq (yes, $(shell pkg-config --exists sdl2 && echo yes)) CFLAGS += -DHAVE_SDL2 CFLAGS += $(shell pkg-config --cflags sdl2) LDFLAGS += $(shell pkg-config --libs sdl2) else ifeq (yes, $(shell pkg-config --exists sdl && echo yes)) CFLAGS += $(shell pkg-config --cflags sdl) LDFLAGS += $(shell pkg-config --libs sdl) else CFLAGS += $(shell sdl-config --cflags) LDFLAGS += $(shell sdl-config --libs) endif endif ifeq ($(targetos), MinGW) LDFLAGS += -mconsole ifeq ($(windeploy), true) bindir = $(prefix) endif endif all: $(meltname) $(meltname): $(OBJS) $(CC) -o $@ $(OBJS) $(LDFLAGS) depend: $(SRCS) $(CC) -MM $(CFLAGS) $^ 1>.depend distclean: clean rm -f .depend clean: rm -f $(OBJS) $(meltname) install: all install -d "$(DESTDIR)$(bindir)" install -c -m 755 $(meltname) "$(DESTDIR)$(bindir)" ifeq ($(extra_versioning), true) ifeq ($(melt_noversion), false) ln -s $(meltname) "$(DESTDIR)$(bindir)/melt" endif endif uninstall: rm -f "$(DESTDIR)$(bindir)/$(meltname)" ifeq ($(extra_versioning), true) ifeq ($(melt_noversion), false) rm -f "$(DESTDIR)$(bindir)/melt" endif endif ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/melt/io.c000066400000000000000000000111061362234133600147000ustar00rootroot00000000000000/* * io.c -- melt input/output * Copyright (C) 2002-2015 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif /* System header files */ #include #include #include #include #ifndef _WIN32 #include #else // MinGW defines struct timespec in pthread.h #include // for nanosleep() #include #include #endif #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 */ #ifndef _WIN32 static struct termios oldtty; #else static DWORD oldtty; #endif static int mode = 0; /** This is called automatically on application exit to restore the previous tty settings. */ void term_exit(void) { if ( mode == 1 ) { #ifndef _WIN32 tcsetattr( 0, TCSANOW, &oldtty ); #else HANDLE h = GetStdHandle( STD_INPUT_HANDLE ); if (h) { SetConsoleMode( h, oldtty ); } #endif mode = 0; } } /** Init terminal so that we can grab keys without blocking. */ void term_init( ) { #ifndef _WIN32 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 ); #else HANDLE h = GetStdHandle( STD_INPUT_HANDLE ); if (h) { DWORD tty; GetConsoleMode( h, &tty ); oldtty = tty; SetConsoleMode( h, mode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT ) ); } #endif 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( ) { #ifndef _WIN32 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; } #else HANDLE h = GetStdHandle( STD_INPUT_HANDLE ); if ( h && WaitForSingleObject( h, 0 ) == WAIT_OBJECT_0 ) { DWORD count; TCHAR c = 0; ReadConsole( h, &c, 1, &count, NULL ); return (int) c; } else { struct timespec tm = { 0, 40000000 }; nanosleep( &tm, NULL ); return 0; } #endif 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 ); } mlt-6.20.0/src/melt/io.h000066400000000000000000000023611362234133600147100ustar00rootroot00000000000000/* * io.h -- melt input/output * Copyright (C) 2002-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 mlt-6.20.0/src/melt/melt.c000066400000000000000000001033751362234133600152440ustar00rootroot00000000000000/* * melt.c -- MLT command line utility * Copyright (C) 2002-2020 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #if (defined(__APPLE__) || defined(_WIN32) || defined(HAVE_SDL2)) && !defined(MELT_NOSDL) #include #endif #include "io.h" static mlt_producer melt = NULL; static void stop_handler(int signum) { if ( melt ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( melt ); mlt_properties_set_int( properties, "done", 1 ); } } static void abnormal_exit_handler(int signum) { // The process is going down hard. Restore the terminal first. term_exit(); // Reset the default handler so the core gets dumped. signal( signum, SIG_DFL ); raise( signum ); } 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 jack = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES( consumer ), "jack_filter", NULL ); mlt_position position = producer? mlt_producer_position( producer ) : 0; mlt_properties_set_int( properties, "stats_off", 1 ); if ( strlen( value ) == 1 ) { switch( value[ 0 ] ) { case 'q': case 'Q': mlt_properties_set_int( properties, "done", 1 ); mlt_events_fire( jack, "jack-stop", NULL ); break; case '0': position = 0; mlt_producer_set_speed( producer, 1 ); mlt_producer_seek( producer, position ); mlt_consumer_purge( consumer ); mlt_events_fire( jack, "jack-seek", &position, NULL ); 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 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, mlt_consumer_position( consumer ) + 1 ); mlt_events_fire( jack, "jack-stop", NULL ); break; case '6': case ' ': if ( !jack || mlt_producer_get_speed( producer ) != 0 ) mlt_producer_set_speed( producer, 1 ); mlt_consumer_purge( consumer ); mlt_events_fire( jack, "jack-start", NULL ); 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 ++ ) { position = mlt_multitrack_clip( multitrack, mlt_whence_relative_start, i ); if ( position == last ) break; last = position; fprintf( stderr, "%d: %d\n", i, (int)position ); } } break; case 'g': if ( multitrack != NULL ) { position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 ); mlt_producer_seek( producer, position ); mlt_consumer_purge( consumer ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'H': if ( producer != NULL ) { position -= mlt_producer_get_fps( producer ) * 60; mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'h': if ( producer != NULL ) { position--; mlt_producer_set_speed( producer, 0 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-stop", NULL ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'j': if ( multitrack != NULL ) { position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'k': if ( multitrack != NULL ) { position = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; case 'l': if ( producer != NULL ) { position++; mlt_consumer_purge( consumer ); if ( mlt_producer_get_speed( producer ) != 0 ) { mlt_producer_set_speed( producer, 0 ); mlt_events_fire( jack, "jack-stop", NULL ); } else { mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } } break; case 'L': if ( producer != NULL ) { position += mlt_producer_get_fps( producer ) * 60; mlt_consumer_purge( consumer ); mlt_producer_seek( producer, position ); mlt_events_fire( jack, "jack-seek", &position, NULL ); } break; } mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 ); } mlt_properties_set_int( properties, "stats_off", 0 ); } static void on_jack_started( mlt_properties owner, mlt_consumer consumer, mlt_position *position ) { mlt_producer producer = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "transport_producer", NULL ); if ( producer ) { if ( mlt_producer_get_speed( producer ) != 0 ) { mlt_properties jack = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES( consumer ), "jack_filter", NULL ); mlt_events_fire( jack, "jack-stop", NULL ); } else { mlt_producer_set_speed( producer, 1 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, *position ); mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 ); } } } static void on_jack_stopped( mlt_properties owner, mlt_consumer consumer, mlt_position *position ) { mlt_producer producer = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "transport_producer", NULL ); if ( producer ) { mlt_producer_set_speed( producer, 0 ); mlt_consumer_purge( consumer ); mlt_producer_seek( producer, *position ); mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "refresh", 1 ); } } static void setup_jack_transport( mlt_consumer consumer, mlt_profile profile ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_filter jack = mlt_factory_filter( profile, "jackrack", NULL ); mlt_properties jack_properties = MLT_FILTER_PROPERTIES(jack); mlt_service_attach( MLT_CONSUMER_SERVICE(consumer), jack ); mlt_properties_set_int( properties, "audio_off", 1 ); mlt_properties_set_data( properties, "jack_filter", jack, 0, (mlt_destructor) mlt_filter_close, NULL ); // mlt_properties_set( jack_properties, "out_1", "system:playback_1" ); // mlt_properties_set( jack_properties, "out_2", "system:playback_2" ); mlt_events_listen( jack_properties, consumer, "jack-started", (mlt_listener) on_jack_started ); mlt_events_listen( jack_properties, consumer, "jack-stopped", (mlt_listener) on_jack_stopped ); } static mlt_consumer create_consumer( mlt_profile profile, char *id ) { char *myid = id ? strdup( id ) : NULL; char *arg = myid ? strchr( myid, ':' ) : NULL; if ( arg != NULL ) *arg ++ = '\0'; mlt_consumer consumer = mlt_factory_consumer( profile, myid, arg ); if ( consumer != NULL ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL ); } free( myid ); return consumer; } static int load_consumer( mlt_consumer *consumer, mlt_profile profile, int argc, char **argv ) { int i; int multi = 0; int qglsl = 0; for ( i = 1; i < argc; i ++ ) { // See if we need multi consumer. multi += !strcmp( argv[i], "-consumer" ); // Seee if we need the qglsl variant of multi consumer. if ( !strncmp( argv[i], "glsl.", 5 ) || !strncmp( argv[i], "movit.", 6 ) ) qglsl = 1; #if SDL_MAJOR_VERSION == 2 if ( !strcmp("sdl", argv[i]) || !strcmp("sdl_audio", argv[i]) || !strcmp("sdl_preview", argv[i]) || !strcmp("sdl_still", argv[i]) ) { fprintf(stderr, "Error: This program was linked against SDL2, which is incompatible with\nSDL1 consumers. Aborting.\n"); return EXIT_FAILURE; } #endif } // Disable qglsl if xgl is being used! for ( i = 1; qglsl && i < argc; i ++ ) if ( !strcmp( argv[i], "xgl" ) ) qglsl = 0; if ( multi > 1 || qglsl ) { // If there is more than one -consumer use the 'multi' consumer. int k = 0; char key[20]; if ( *consumer ) mlt_consumer_close( *consumer ); *consumer = create_consumer( profile, ( qglsl? "qglsl" : "multi" ) ); mlt_properties properties = MLT_CONSUMER_PROPERTIES( *consumer ); for ( i = 1; i < argc; i ++ ) { if ( !strcmp( argv[ i ], "-consumer" ) && argv[ i + 1 ]) { // Create a properties object for each sub-consumer mlt_properties new_props = mlt_properties_new(); snprintf( key, sizeof(key), "%d", k++ ); mlt_properties_set_data( properties, key, new_props, 0, (mlt_destructor) mlt_properties_close, NULL ); if ( strchr( argv[i + 1], ':' ) ) { char *temp = strdup( argv[++i] ); char *service = temp; char *target = strchr( temp, ':' ); *target++ = 0; mlt_properties_set( new_props, "mlt_service", service ); mlt_properties_set( new_props, "target", target ); } else { mlt_properties_set( new_props, "mlt_service", argv[ ++i ] ); } while ( argv[ i + 1 ] && strchr( argv[ i + 1 ], '=' ) ) mlt_properties_parse( new_props, argv[ ++ i ] ); } } } else for ( i = 1; i < argc; i ++ ) { if ( !strcmp( argv[ i ], "-consumer" ) ) { if ( *consumer ) mlt_consumer_close( *consumer ); *consumer = create_consumer( profile, argv[ ++ i ] ); if ( *consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( *consumer ); while ( argv[ i + 1 ] != NULL && strchr( argv[ i + 1 ], '=' ) ) mlt_properties_parse( properties, argv[ ++ i ] ); } } } return EXIT_SUCCESS; } #if defined(SDL_MAJOR_VERSION) 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( producer ), "done", 1 ); break; case SDL_KEYDOWN: #if SDL_MAJOR_VERSION == 2 if ( event.key.keysym.sym < 0x80 && event.key.keysym.sym > 0 ) { char keyboard[ 2 ] = { event.key.keysym.sym, 0 }; transport_action( producer, keyboard ); } break; case SDL_WINDOWEVENT: if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES(consumer), "mlt_service" ) && !strcmp( "sdl2", mlt_properties_get( MLT_CONSUMER_PROPERTIES(consumer), "mlt_service" ) ) ) if ( event.window.event == SDL_WINDOWEVENT_RESIZED || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ) { mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(consumer), "window_width", event.window.data1 ); mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(consumer), "window_height", event.window.data2 ); } break; #else 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 } } } #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" ); int is_getc = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "melt_getc" ); struct timespec tm = { 0, 40000000 }; int total_length = mlt_producer_get_playtime( producer ); int last_position = 0; if ( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) ) { if ( !silent && !progress ) { if ( !is_getc ) 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 || is_getc )? -1 : term_read( ); if ( is_getc ) { value = getc( stdin ); value = ( value == EOF )? 'q' : value; } if ( value != -1 ) { char string[ 2 ] = { value, 0 }; transport_action( producer, string ); } #if defined(SDL_MAJOR_VERSION) 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%c", current_position, 100 * current_position / total_length, progress == 2 ? '\n' : '\r' ); last_position = current_position; } } else { fprintf( stderr, "Current Position: %10d\r", (int)mlt_consumer_position( consumer ) ); } fflush( stderr ); } if ( silent || progress ) nanosleep( &tm, NULL ); } if ( !silent ) fprintf( stderr, "\n" ); } } static void show_usage( char *program_name ) { fprintf( stdout, "Usage: %s [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" " -getc Get keyboard input using getc\n" " -group [name=value]* Apply properties repeatedly\n" " -help Show this message\n" " -jack Enable JACK transport synchronization\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" " -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" " -query \"profiles\" | \"profile\"=id List profiles, show info about one\n" " -query \"presets\" | \"preset\"=id List presets, show info about one\n" " -query \"formats\" List audio/video formats\n" " -query \"audio_codecs\" List audio codecs\n" " -query \"video_codecs\" List video codecs\n" " -remove Remove the most recent cut\n" " -repeat times Repeat the last cut\n" " -repository path Set the directory of MLT modules\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" " -timings Set the logging level to timings\n" " -version Show the version and copyright\n" " -video-track | -hide-audio Add a video-only track\n" "For more help: \n", basename( program_name ) ); } 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( stdout, "%s", s ); free( s ); } else { fprintf( stdout, "# No metadata for %s \"%s\"\n", typestr, id ); } } static int is_service_hidden(mlt_repository repo, mlt_service_type type, const char *service_name ) { mlt_properties metadata = NULL; mlt_properties tags = NULL; metadata = mlt_repository_metadata(repo, type, service_name); if( metadata ) { tags = mlt_properties_get_data( metadata, "tags", NULL ); if( tags ) { int k; for ( k = 0; k < mlt_properties_count( tags ); k++ ) { const char* value = mlt_properties_get_value(tags, k); if( !strcmp("Hidden", value) ) { return 1; } } } } return 0; } 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( stdout, "---\n%s:\n", typestr ); if ( services ) { int j; for ( j = 0; j < mlt_properties_count( services ); j++ ) { const char* service_name = mlt_properties_get_name( services, j ); if( !is_service_hidden(repo, type, service_name ) ) fprintf( stdout, " - %s\n", service_name ); } } fprintf( stdout, "...\n" ); } static void query_profiles() { mlt_properties profiles = mlt_profile_list(); fprintf( stdout, "---\nprofiles:\n" ); if ( profiles ) { int j; for ( j = 0; j < mlt_properties_count( profiles ); j++ ) fprintf( stdout, " - %s\n", mlt_properties_get_name( profiles, j ) ); } fprintf( stdout, "...\n" ); mlt_properties_close( profiles ); } static void query_profile( const char *id ) { mlt_properties profiles = mlt_profile_list(); mlt_properties profile = mlt_properties_get_data( profiles, id, NULL ); if ( profile ) { char *s = mlt_properties_serialise_yaml( profile ); fprintf( stdout, "%s", s ); free( s ); } else { fprintf( stdout, "# No metadata for profile \"%s\"\n", id ); } mlt_properties_close( profiles ); } static void query_presets() { mlt_properties presets = mlt_repository_presets(); fprintf( stdout, "---\npresets:\n" ); if ( presets ) { int j; for ( j = 0; j < mlt_properties_count( presets ); j++ ) fprintf( stdout, " - %s\n", mlt_properties_get_name( presets, j ) ); } fprintf( stdout, "...\n" ); mlt_properties_close( presets ); } static void query_preset( const char *id ) { mlt_properties presets = mlt_repository_presets(); mlt_properties preset = mlt_properties_get_data( presets, id, NULL ); if ( preset ) { char *s = mlt_properties_serialise_yaml( preset ); fprintf( stdout, "%s", s ); free( s ); } else { fprintf( stdout, "# No metadata for preset \"%s\"\n", id ); } mlt_properties_close( presets ); } static void query_formats( ) { mlt_consumer consumer = mlt_factory_consumer( NULL, "avformat", NULL ); if ( consumer ) { mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "f", "list" ); mlt_consumer_start( consumer ); mlt_consumer_close( consumer ); } else { fprintf( stdout, "# No formats - failed to load avformat consumer\n" ); } } static void query_acodecs( ) { mlt_consumer consumer = mlt_factory_consumer( NULL, "avformat", NULL ); if ( consumer ) { mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "acodec", "list" ); mlt_consumer_start( consumer ); mlt_consumer_close( consumer ); } else { fprintf( stdout, "# No audio codecs - failed to load avformat consumer\n" ); } } static void query_vcodecs( ) { mlt_consumer consumer = mlt_factory_consumer( NULL, "avformat", NULL ); if ( consumer ) { mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "vcodec", "list" ); mlt_consumer_start( consumer ); mlt_consumer_close( consumer ); } else { fprintf( stdout, "# No video codecs - failed to load avformat consumer\n" ); } } static void on_fatal_error( mlt_properties owner, mlt_consumer consumer ) { mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(consumer), "done", 1 ); mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(consumer), "melt_error", 1 ); } static void set_preview_scale(mlt_profile *profile, mlt_profile *backup_profile, double scale) { *backup_profile = mlt_profile_clone(*profile); if (*backup_profile) { mlt_profile temp = *profile; *profile = *backup_profile; *backup_profile = temp; (*profile)->width *= scale; (*profile)->width -= (*profile)->width % 2; (*profile)->height *= scale; (*profile)->height -= (*profile)->height % 2; } } int main( int argc, char **argv ) { int i; mlt_consumer consumer = NULL; FILE *store = NULL; char *name = NULL; mlt_profile profile = NULL; int is_progress = 0; int is_silent = 0; int is_abort = 0; int is_getc = 0; int error = 0; mlt_profile backup_profile; mlt_repository repo = NULL; const char* repo_path = NULL; // Handle abnormal exit situations. signal( SIGSEGV, abnormal_exit_handler ); signal( SIGILL, abnormal_exit_handler ); signal( SIGABRT, abnormal_exit_handler ); for ( i = 1; i < argc; i ++ ) { // Check for serialisation switch if ( !strcmp( argv[ i ], "-serialise" ) ) { name = argv[ ++ i ]; if ( name != NULL && strstr( name, ".melt" ) ) 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" ) ) { // Construct the factory if ( !repo ) repo = mlt_factory_init( repo_path ); const char *pname = argv[ ++ i ]; if ( pname && pname[0] != '-' ) profile = mlt_profile_init( pname ); } else if ( !strcmp( argv[ i ], "-progress" ) ) { is_progress = 1; } else if ( !strcmp( argv[ i ], "-progress2" ) ) { is_progress = 2; } // Look for the query option else if ( !strcmp( argv[ i ], "-query" ) ) { // Construct the factory if ( !repo ) repo = mlt_factory_init( repo_path ); 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 ( !strcmp( pname, "profiles" ) || !strcmp( pname, "profile" ) ) query_profiles(); else if ( !strcmp( pname, "presets" ) || !strcmp( pname, "preset" ) ) query_presets(); else if ( !strncmp( pname, "format", 6 ) ) query_formats(); else if ( !strncmp( pname, "acodec", 6 ) || !strcmp( pname, "audio_codecs" ) ) query_acodecs(); else if ( !strncmp( pname, "vcodec", 6 ) || !strcmp( pname, "video_codecs" ) ) query_vcodecs(); 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 if ( !strncmp( pname, "profile=", 8 ) ) query_profile( strchr( pname, '=' ) + 1 ); else if ( !strncmp( pname, "preset=", 7 ) ) query_preset( 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( stdout, "# 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 ], "-timings" ) ) { mlt_log_set_level( MLT_LOG_TIMINGS ); } else if ( !strcmp( argv[ i ], "-version" ) || !strcmp( argv[ i ], "--version" ) ) { fprintf( stdout, "%s " VERSION "\n" "Copyright (C) 2002-2020 Meltytech, LLC\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", basename( argv[0] ) ); goto exit_factory; } else if ( !strcmp( argv[ i ], "-debug" ) ) { mlt_log_set_level( MLT_LOG_DEBUG ); } else if ( !strcmp( argv[ i ], "-abort" ) ) { is_abort = 1; } else if ( !strcmp( argv[ i ], "-getc" ) ) { is_getc = 1; } else if ( !repo && !strcmp( argv[ i ], "-repository" ) ) { if ( i+1 < argc && argv[i+1][0] != '-' ) repo_path = argv[++i]; } } if ( !is_silent && !isatty( STDIN_FILENO ) && !is_progress ) is_progress = 1; // Construct the factory if ( !repo ) repo = mlt_factory_init( repo_path ); // Create profile if not set explicitly if ( getenv( "MLT_PROFILE" ) ) profile = mlt_profile_init( NULL ); if ( profile == NULL ) profile = mlt_profile_init( NULL ); else profile->is_explicit = 1; // Look for the consumer option to load profile settings from consumer properties backup_profile = mlt_profile_clone( profile ); if ( load_consumer( &consumer, profile, argc, argv ) != EXIT_SUCCESS ) goto exit_factory; // If the consumer changed the profile, then it is explicit. if ( backup_profile && !profile->is_explicit && ( profile->width != backup_profile->width || profile->height != backup_profile->height || profile->sample_aspect_num != backup_profile->sample_aspect_num || profile->sample_aspect_den != backup_profile->sample_aspect_den || profile->frame_rate_den != backup_profile->frame_rate_den || profile->frame_rate_num != backup_profile->frame_rate_num || profile->colorspace != backup_profile->colorspace ) ) profile->is_explicit = 1; mlt_profile_close( backup_profile ); backup_profile = NULL; // Get melt producer if ( argc > 1 ) melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] ); if ( melt ) { // Generate an automatic profile if needed. if ( ! profile->is_explicit ) { mlt_profile_from_producer( profile, melt ); mlt_producer_close( melt ); melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] ); } double scale = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "scale"); if (scale > 0.0) { set_preview_scale(&profile, &backup_profile, scale); } // Reload the consumer with the fully qualified profile. // The producer or auto-profile could have changed the profile. load_consumer( &consumer, profile, argc, argv ); // See if producer has consumer already attached if ( !store && !consumer ) { consumer = MLT_CONSUMER( mlt_service_consumer( MLT_PRODUCER_SERVICE( melt ) ) ); if ( consumer ) { mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES(consumer) ); // because we explicitly close it mlt_properties_set_data( MLT_CONSUMER_PROPERTIES(consumer), "transport_callback", transport_action, 0, NULL, NULL ); } } // If we have no consumer, default to sdl if ( store == NULL && consumer == NULL ) consumer = create_consumer( profile, NULL ); } // Set transport properties on consumer and produder if ( consumer != NULL && melt != NULL ) { mlt_properties_set_data( MLT_CONSUMER_PROPERTIES( consumer ), "transport_producer", melt, 0, NULL, NULL ); mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( melt ), "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 ( is_getc ) mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "melt_getc", is_getc ); } if ( argc > 1 && melt != NULL && mlt_producer_get_length( melt ) > 0 ) { // Parse the arguments for ( i = 1; i < argc; i ++ ) { if ( !strcmp( argv[ i ], "-jack" ) && consumer ) { setup_jack_transport( consumer, profile ); } else 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 melt's properties mlt_properties melt_props = MLT_PRODUCER_PROPERTIES( melt ); // Get the last group mlt_properties group = mlt_properties_get_data( melt_props, "group", 0 ); // Apply group settings mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_properties_inherit( properties, group ); int in = mlt_properties_get_int( properties, "in" ); int out = mlt_properties_get_int( properties, "out" ); if ( in > 0 || out > 0 ) { if ( out == 0 ) { out = mlt_producer_get_length( melt ) - 1; } mlt_producer_set_in_and_out( melt, in, out ); mlt_producer_seek( melt, 0 ); } // Connect consumer to melt mlt_consumer_connect( consumer, MLT_PRODUCER_SERVICE( melt ) ); // Start the consumer mlt_events_listen( properties, consumer, "consumer-fatal-error", ( mlt_listener )on_fatal_error ); if ( mlt_consumer_start( consumer ) == 0 ) { // Try to exit gracefully upon these signals signal( SIGINT, stop_handler ); signal( SIGTERM, stop_handler ); #ifndef _WIN32 signal( SIGHUP, stop_handler ); signal( SIGPIPE, stop_handler ); #endif // Transport functionality transport( melt, 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 { show_usage( argv[0] ); } // Disconnect producer from consumer to prevent ref cycles from closing services if ( consumer ) { error = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "melt_error" ); mlt_consumer_connect( consumer, NULL ); if ( !is_abort ) mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-cleanup", NULL); } if ( is_abort ) return error; // Close the producer if ( melt != NULL ) mlt_producer_close( melt ); // Close the consumer if ( consumer != NULL ) mlt_consumer_close( consumer ); // Close the factory mlt_profile_close( profile ); mlt_profile_close( backup_profile ); exit_factory: // Workaround qmelt on OS X from crashing at exit. #if !defined(__MACH__) || !defined(QT_GUI_LIB) mlt_factory_close( ); #endif return error; } mlt-6.20.0/src/mlt++/000077500000000000000000000000001362234133600141075ustar00rootroot00000000000000mlt-6.20.0/src/mlt++/CMakeLists.txt000066400000000000000000000013421362234133600166470ustar00rootroot00000000000000file(GLOB mlt++_src *.cpp) add_library(mlt++ SHARED ${mlt++_src}) set_target_properties(mlt++ PROPERTIES SOVERSION 3 VERSION 3 CXX_STANDARD 14) if(WIN32) target_compile_definitions(mlt++ PUBLIC MLTPP_EXPORTS) endif() target_include_directories(mlt++ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../) target_link_libraries(mlt++ mlt) if(WIN32) install(TARGETS mlt++ DESTINATION ${CMAKE_INSTALL_BINDIR}) else() install(TARGETS mlt++ DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() file(GLOB mlt++_head *.h) install(FILES ${mlt++_head} DESTINATION include/mlt++) configure_file(mlt++.pc.in mlt++.pc @ONLY) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/mlt++.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT Development) mlt-6.20.0/src/mlt++/Makefile000066400000000000000000000057151362234133600155570ustar00rootroot00000000000000include ../../config.mak include config.mak INSTALL = install NAME = libmlt++$(LIBSUF) ifeq ($(targetos), Darwin) TARGET = libmlt++.$(version)$(LIBSUF) SONAME = libmlt++.$(soversion)$(LIBSUF) LIBFLAGS += -install_name $(libdir)/$(SONAME) -current_version $(version) -compatibility_version $(soversion) else ifeq ($(targetos), MinGW) TARGET = libmlt++-$(soversion)$(LIBSUF) CXXFLAGS += -DMLTPP_EXPORTS LIBFLAGS += -Wl,--output-def,libmlt++.def else TARGET = $(NAME).$(version) SONAME = $(NAME).$(soversion) LIBFLAGS += -Wl,-soname,$(SONAME) endif ifneq (, $(shell $(CXX) --version | grep -is -e g++ -e clang)) CXXFLAGS += -std=c++11 endif CXXFLAGS += -I.. $(RDYNAMIC) -DVERSION=\"$(version)\" -fvisibility=hidden LDFLAGS := -L../framework -lmlt $(LDFLAGS) ifeq ($(targetos), Linux) LDFLAGS += -Wl,--version-script=mlt++.vers endif OBJS = MltAnimation.o \ MltConsumer.o \ MltDeque.o \ MltEvent.o \ MltFactory.o \ MltField.o \ MltFilter.o \ MltFilteredConsumer.o \ MltFilteredProducer.o \ MltFrame.o \ MltGeometry.o \ MltMultitrack.o \ MltParser.o \ MltPlaylist.o \ MltProducer.o \ MltProfile.o \ MltProperties.o \ MltPushConsumer.o \ MltRepository.o \ MltService.o \ MltTokeniser.o \ MltTractor.o \ MltTransition.o SRCS = $(OBJS:.o=.cpp) HEADERS = MltConfig.h Mlt.h $(OBJS:.o=.h) all: $(TARGET) $(TARGET): $(OBJS) $(CXX) $(LIBFLAGS) -o $@ $(OBJS) $(LDFLAGS) ln -sf $(TARGET) $(NAME) if [ "$(targetos)" != "MinGW" ]; then \ ln -sf $(TARGET) $(SONAME) ; \ fi depend: $(SRCS) $(CXX) -MM $(CXXFLAGS) $^ 1>.depend clean: $(RM) $(OBJS) $(TARGET) $(NAME) $(SONAME) libmlt++.def distclean: clean install: $(INSTALL) -d "$(DESTDIR)$(libdir)" if [ "$(targetos)" = "MinGW" ]; then \ if [ "$(windeploy)" = true ]; then \ $(INSTALL) -m 755 $(TARGET) "$(DESTDIR)$(prefix)" ; \ $(INSTALL) -m 755 $(TARGET) "$(DESTDIR)$(libdir)/libmlt++.dll" ; \ else \ $(INSTALL) -m 755 $(TARGET) "$(DESTDIR)$(bindir)" ; \ $(INSTALL) -m 755 $(TARGET) "$(DESTDIR)$(bindir)/libmlt++.dll" ; \ fi; \ $(INSTALL) -m 644 libmlt++.def "$(DESTDIR)$(libdir)" ; \ else \ $(INSTALL) -m 755 $(TARGET) $(DESTDIR)$(libdir) ; \ ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(SONAME) ; \ ln -sf $(TARGET) $(DESTDIR)$(libdir)/$(NAME) ; \ fi $(INSTALL) -d "$(DESTDIR)$(prefix)/include/mlt++" $(INSTALL) -m 644 $(HEADERS) "$(DESTDIR)$(prefix)/include/mlt++" uninstall: rm -f "$(DESTDIR)$(libdir)/$(TARGET)" if [ "$(targetos)" != "MinGW" ]; then \ rm -f "$(DESTDIR)$(libdir)/$(NAME)" ; \ rm -f "$(DESTDIR)$(libdir)/$(SONAME)" ; \ else \ if [ "$(windeploy)" = true ]; then \ rm -f "$(DESTDIR)$(prefix)/$(TARGET)" ; \ rm -f "$(DESTDIR)$(libdir)/libmlt++.dll" ; \ else \ rm -f "$(DESTDIR)$(bindir)/$(TARGET)" ; \ rm -f "$(DESTDIR)$(bindir)/libmlt++.dll" ; \ fi ; \ rm -f "$(DESTDIR)$(libdir)/libmlt++.def" ; \ fi rm -rf "$(DESTDIR)$(prefix)/include/mlt++" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/mlt++/Mlt.h000066400000000000000000000027301362234133600150160ustar00rootroot00000000000000/** * Mlt.h - Convenience header file for all mlt++ objects * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_H #define MLTPP_H #include "MltAnimation.h" #include "MltConsumer.h" #include "MltDeque.h" #include "MltEvent.h" #include "MltFactory.h" #include "MltField.h" #include "MltFilter.h" #include "MltFilteredConsumer.h" #include "MltFrame.h" #include "MltGeometry.h" #include "MltMultitrack.h" #include "MltParser.h" #include "MltPlaylist.h" #include "MltProducer.h" #include "MltProfile.h" #include "MltProperties.h" #include "MltPushConsumer.h" #include "MltRepository.h" #include "MltService.h" #include "MltTokeniser.h" #include "MltTractor.h" #include "MltTransition.h" #endif mlt-6.20.0/src/mlt++/MltAnimation.cpp000066400000000000000000000106251362234133600172130ustar00rootroot00000000000000/** * MltAnimation.cpp - MLT Wrapper * Copyright (C) 2015-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "MltAnimation.h" using namespace Mlt; Animation::Animation() : instance( 0 ) { } Animation::Animation( mlt_animation animation ) : instance( animation ) { } Animation::Animation( const Animation &animation ) : instance( animation.instance ) { } Animation::~Animation() { // Do not call mlt_animation_close() because mlt_animation is not reference- // counted, and typically a mlt_properties owns it. instance = 0; } bool Animation::is_valid() const { return instance != 0; } mlt_animation Animation::get_animation() const { return instance; } Animation &Animation::operator=( const Animation &animation ) { if ( this != &animation ) { instance = animation.instance; } return *this; } int Animation::length() { return mlt_animation_get_length( instance ); } int Animation::get_item( int position, bool& is_key, mlt_keyframe_type& type ) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_get_item( instance, &item, position ); if ( !error ) { is_key = item.is_key; type = item.keyframe_type; } return error; } bool Animation::is_key( int position ) { struct mlt_animation_item_s item; item.is_key = 0; item.property = NULL; mlt_animation_get_item( instance, &item, position ); return item.is_key; } mlt_keyframe_type Animation::keyframe_type( int position ) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_get_item( instance, &item, position ); if ( !error ) return item.keyframe_type; else return (mlt_keyframe_type) -1; } int Animation::next_key( int position ) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_next_key( instance, &item, position ); if ( !error ) return item.frame; else return error; } int Animation::previous_key( int position ) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_prev_key( instance, &item, position ); if ( !error ) return item.frame; else return error; } int Animation::key_count() { return mlt_animation_key_count( instance ); } int Animation::key_get( int index, int& frame, mlt_keyframe_type& type ) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_key_get( instance, &item, index ); if ( !error ) { frame = item.frame; type = item.keyframe_type; } return error; } int Animation::key_get_frame( int index ) { struct mlt_animation_item_s item; item.is_key = 0; item.property = NULL; int error = mlt_animation_key_get( instance, &item, index ); if ( !error ) return item.frame; else return -1; } mlt_keyframe_type Animation::key_get_type( int index ) { struct mlt_animation_item_s item; item.property = NULL; int error = mlt_animation_key_get( instance, &item, index ); if ( !error ) return item.keyframe_type; else return (mlt_keyframe_type) -1; } int Animation::key_set_type(int index, mlt_keyframe_type type) { return mlt_animation_key_set_type(instance, index, type); } int Animation::key_set_frame(int index, int frame) { return mlt_animation_key_set_frame(instance, index, frame); } void Animation::set_length( int length ) { return mlt_animation_set_length( instance, length ); } int Animation::remove( int position ) { return mlt_animation_remove( instance, position ); } void Animation::interpolate() { mlt_animation_interpolate( instance ); } char *Animation::serialize_cut( int in, int out ) { return mlt_animation_serialize_cut( instance, in, out ); } char *Animation::serialize_cut( mlt_time_format format , int in, int out ) { return mlt_animation_serialize_cut_tf( instance, in, out, format ); } mlt-6.20.0/src/mlt++/MltAnimation.h000066400000000000000000000037071362234133600166630ustar00rootroot00000000000000/** * MltAnimation.h - MLT Wrapper * Copyright (C) 2015-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_ANIMATION_H #define MLTPP_ANIMATION_H #include "MltConfig.h" #include namespace Mlt { class MLTPP_DECLSPEC Animation { private: mlt_animation instance; public: Animation(); Animation( mlt_animation animation ); Animation( const Animation& ); ~Animation(); bool is_valid() const; mlt_animation get_animation() const; Animation& operator=( const Animation& ); int length(); int get_item( int position, bool& is_key, mlt_keyframe_type& ); bool is_key( int position ); mlt_keyframe_type keyframe_type( int position ); int next_key( int position ); int previous_key( int position ); int key_count(); int key_get( int index, int& frame, mlt_keyframe_type& ); int key_get_frame( int index ); mlt_keyframe_type key_get_type( int index ); int key_set_type( int index, mlt_keyframe_type type ); int key_set_frame( int index, int frame ); void set_length( int length ); int remove( int position ); void interpolate(); char* serialize_cut( int in = -1, int out = -1 ); char* serialize_cut( mlt_time_format format, int in = -1, int out = -1 ); }; } #endif mlt-6.20.0/src/mlt++/MltConfig.h000066400000000000000000000024231362234133600161430ustar00rootroot00000000000000/** * MltConfig.h - Convenience header file for all mlt++ objects * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_CONFIG_H #define MLTPP_CONFIG_H #if defined(_WIN32) #ifdef MLTPP_EXPORTS #define MLTPP_DECLSPEC __declspec( dllexport ) #else #define MLTPP_DECLSPEC __declspec( dllimport ) #endif #else #if __GNUC__ >= 4 #define MLTPP_DECLSPEC __attribute__ ((visibility ("default"))) #else #define MLTPP_DECLSPEC #endif #endif #endif mlt-6.20.0/src/mlt++/MltConsumer.cpp000066400000000000000000000057531362234133600170750ustar00rootroot00000000000000/** * MltConsumer.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "MltConsumer.h" #include "MltEvent.h" #include "MltProfile.h" using namespace Mlt; Consumer::Consumer( ) : instance( NULL ) { instance = mlt_factory_consumer( NULL, NULL, NULL ); } Consumer::Consumer( Profile& profile ) : instance( NULL ) { instance = mlt_factory_consumer( profile.get_profile(), NULL, NULL ); } Consumer::Consumer( Profile& profile, const char *id, const char *arg ) : Consumer ( profile.get_profile(), id, arg ) { } Consumer::Consumer( mlt_profile profile, const char *id, const char *arg ) : instance( NULL ) { if ( id == NULL || arg != NULL ) { instance = mlt_factory_consumer( profile, id, arg ); } else { if ( strchr( id, ':' ) ) { char *temp = strdup( id ); char *arg = strchr( temp, ':' ) + 1; *( arg - 1 ) = '\0'; instance = mlt_factory_consumer( profile, temp, arg ); free( temp ); } else { instance = mlt_factory_consumer( profile, id, NULL ); } } } Consumer::Consumer( Service &consumer ) : instance( NULL ) { if ( consumer.type( ) == consumer_type ) { instance = ( mlt_consumer )consumer.get_service( ); inc_ref( ); } } Consumer::Consumer( Consumer &consumer ) : Mlt::Service( consumer ), instance( consumer.get_consumer( ) ) { inc_ref( ); } Consumer::Consumer( mlt_consumer consumer ) : instance( consumer ) { inc_ref( ); } Consumer::~Consumer( ) { mlt_consumer_close( instance ); } mlt_consumer Consumer::get_consumer( ) { return instance; } mlt_service Consumer::get_service( ) { return mlt_consumer_service( get_consumer( ) ); } int Consumer::connect( Service &service ) { return connect_producer( service ); } int Consumer::start( ) { return mlt_consumer_start( get_consumer( ) ); } void Consumer::purge( ) { mlt_consumer_purge( get_consumer( ) ); } int Consumer::stop( ) { return mlt_consumer_stop( get_consumer( ) ); } bool Consumer::is_stopped( ) { return mlt_consumer_is_stopped( get_consumer( ) ) != 0; } int Consumer::run( ) { int ret = start( ); if ( !is_stopped( ) ) { Event *e = setup_wait_for( "consumer-stopped" ); wait_for( e ); delete e; } return ret; } int Consumer::position( ) { return mlt_consumer_position( get_consumer() ); } mlt-6.20.0/src/mlt++/MltConsumer.h000066400000000000000000000033031362234133600165270ustar00rootroot00000000000000/** * MltConsumer.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_CONSUMER_H #define MLTPP_CONSUMER_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Profile; class MLTPP_DECLSPEC Consumer : public Service { private: mlt_consumer instance; public: Consumer( ); Consumer( Profile& profile ); Consumer( Profile& profile, const char *id , const char *service = NULL ); Consumer( mlt_profile profile, const char *id , const char *service = NULL ); Consumer( Service &consumer ); Consumer( Consumer &consumer ); Consumer( mlt_consumer consumer ); virtual ~Consumer( ); virtual mlt_consumer get_consumer( ); mlt_service get_service( ); virtual int connect( Service &service ); int run( ); int start( ); void purge( ); int stop( ); bool is_stopped( ); int position( ); }; } #endif mlt-6.20.0/src/mlt++/MltDeque.cpp000066400000000000000000000030771362234133600163420ustar00rootroot00000000000000/** * MltDeque.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltDeque.h" using namespace Mlt; Deque::Deque( ) { deque = mlt_deque_init( ); } Deque::~Deque( ) { mlt_deque_close( deque ); } int Deque::count( ) { return mlt_deque_count( deque ); } int Deque::push_back( void *item ) { return mlt_deque_push_back( deque, item ); } void *Deque::pop_back( ) { return mlt_deque_pop_back( deque ); } int Deque::push_front( void *item ) { return mlt_deque_push_front( deque, item ); } void *Deque::pop_front( ) { return mlt_deque_pop_front( deque ); } void *Deque::peek_back( ) { return mlt_deque_peek_back( deque ); } void *Deque::peek_front( ) { return mlt_deque_peek_front( deque ); } void *Deque::peek( int index ) { return mlt_deque_peek( deque, index ); } mlt-6.20.0/src/mlt++/MltDeque.h000066400000000000000000000024261362234133600160040ustar00rootroot00000000000000/** * MltDeque.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_DEQUE_H #define MLTPP_DEQUE_H #include "MltConfig.h" #include namespace Mlt { class MLTPP_DECLSPEC Deque { private: mlt_deque deque; public: Deque( ); ~Deque( ); int count( ); int push_back( void *item ); void *pop_back( ); int push_front( void *item ); void *pop_front( ); void *peek_back( ); void *peek_front( ); void *peek( int index ); }; } #endif mlt-6.20.0/src/mlt++/MltEvent.cpp000066400000000000000000000025631362234133600163570ustar00rootroot00000000000000/** * MltEvent.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltEvent.h" using namespace Mlt; Event::Event( mlt_event event ) : instance( event ) { mlt_event_inc_ref( instance ); } Event::Event( Event &event ) : instance( event.get_event( ) ) { mlt_event_inc_ref( instance ); } Event::~Event( ) { mlt_event_close( instance ); } mlt_event Event::get_event( ) { return instance; } bool Event::is_valid( ) { return instance != NULL; } void Event::block( ) { mlt_event_block( get_event( ) ); } void Event::unblock( ) { mlt_event_unblock( get_event( ) ); } mlt-6.20.0/src/mlt++/MltEvent.h000066400000000000000000000023071362234133600160200ustar00rootroot00000000000000/** * MltEvent.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_EVENT_H #define MLTPP_EVENT_H #include "MltConfig.h" #include namespace Mlt { class MLTPP_DECLSPEC Event { private: mlt_event instance; public: Event( mlt_event ); Event( Event & ); ~Event( ); mlt_event get_event( ); bool is_valid( ); void block( ); void unblock( ); }; } #endif mlt-6.20.0/src/mlt++/MltFactory.cpp000066400000000000000000000033611362234133600167020ustar00rootroot00000000000000/** * MltFactory.cpp - MLT Wrapper * Copyright (C) 2004-2017 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFactory.h" #include "MltProducer.h" #include "MltFilter.h" #include "MltTransition.h" #include "MltConsumer.h" #include "MltRepository.h" using namespace Mlt; Repository *Factory::init( const char *directory ) { return new Repository( mlt_factory_init( directory ) ); } Properties *Factory::event_object( ) { return new Properties( mlt_factory_event_object( ) ); } Producer *Factory::producer( Profile& profile, char *id, char *arg ) { return new Producer( profile, id, arg ); } Filter *Factory::filter( Profile& profile, char *id, char *arg ) { return new Filter( profile, id, arg ); } Transition *Factory::transition( Profile& profile, char *id, char *arg ) { return new Transition( profile, id, arg ); } Consumer *Factory::consumer( Profile& profile, char *id, char *arg ) { return new Consumer( profile, id, arg ); } void Factory::close( ) { mlt_factory_close( ); } mlt-6.20.0/src/mlt++/MltFactory.h000066400000000000000000000031641362234133600163500ustar00rootroot00000000000000/** * MltFactory.h - MLT Wrapper * Copyright (C) 2004-2017 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FACTORY_H #define MLTPP_FACTORY_H #include "MltConfig.h" #ifdef SWIG #define MLTPP_DECLSPEC #endif #include namespace Mlt { class Properties; class Producer; class Filter; class Transition; class Consumer; class Profile; class Repository; class MLTPP_DECLSPEC Factory { public: static Repository *init( const char *directory = NULL ); static Properties *event_object( ); static Producer *producer( Profile& profile, char *id, char *arg = NULL ); static Filter *filter( Profile& profile, char *id, char *arg = NULL ); static Transition *transition( Profile& profile, char *id, char *arg = NULL ); static Consumer *consumer( Profile& profile, char *id, char *arg = NULL ); static void close( ); }; } #endif mlt-6.20.0/src/mlt++/MltField.cpp000066400000000000000000000033641362234133600163210ustar00rootroot00000000000000/** * MltField.cpp - Field wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltField.h" #include "MltFilter.h" #include "MltTransition.h" using namespace Mlt; Field::Field( mlt_field field ) : instance( field ) { inc_ref( ); } Field::Field( Field &field ) : Mlt::Service( field ), instance( field.get_field( ) ) { inc_ref( ); } Field::~Field( ) { mlt_field_close( instance ); } mlt_field Field::get_field( ) { return instance; } mlt_service Field::get_service( ) { return mlt_field_service( get_field( ) ); } int Field::plant_filter( Filter &filter, int track ) { return mlt_field_plant_filter( get_field( ), filter.get_filter( ), track ); } int Field::plant_transition( Transition &transition, int a_track, int b_track ) { return mlt_field_plant_transition( get_field( ), transition.get_transition( ), a_track, b_track ); } void Field::disconnect_service( Service &service ) { mlt_field_disconnect_service( get_field(), service.get_service() ); } mlt-6.20.0/src/mlt++/MltField.h000066400000000000000000000027261362234133600157670ustar00rootroot00000000000000/** * MltField.h - Field wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FIELD_H #define MLTPP_FIELD_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Filter; class Transition; class MLTPP_DECLSPEC Field : public Service { private: mlt_field instance; public: Field( mlt_field field ); Field( Field &field ); virtual ~Field( ); mlt_field get_field( ); mlt_service get_service( ); int plant_filter( Filter &filter, int track = 0 ); int plant_transition( Transition &transition, int a_track = 0, int b_track = 1 ); void disconnect_service( Service &service ); }; } #endif mlt-6.20.0/src/mlt++/MltFilter.cpp000066400000000000000000000064371362234133600165270ustar00rootroot00000000000000/** * MltFilter.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "MltFilter.h" #include "MltProfile.h" using namespace Mlt; Filter::Filter() : Service() , instance(nullptr) { } Filter::Filter( Profile& profile, const char *id, const char *arg ) : Filter( profile.get_profile(), id, arg ) { } Filter::Filter( mlt_profile profile, const char *id, const char *arg ) : instance( NULL ) { if ( arg != NULL ) { instance = mlt_factory_filter( profile, id, arg ); } else { if ( strchr( id, ':' ) ) { char *temp = strdup( id ); char *arg = strchr( temp, ':' ) + 1; *( arg - 1 ) = '\0'; instance = mlt_factory_filter( profile, temp, arg ); free( temp ); } else { instance = mlt_factory_filter( profile, id, NULL ); } } } Filter::Filter( Service &filter ) : instance( NULL ) { if ( filter.type( ) == filter_type ) { instance = ( mlt_filter )filter.get_service( ); inc_ref( ); } } Filter::Filter( Filter &filter ) : Mlt::Service( filter ), instance( filter.get_filter( ) ) { inc_ref( ); } Filter::Filter( const Filter &filter ) : Filter( const_cast(filter) ) { } Filter::Filter( mlt_filter filter ) : instance( filter ) { inc_ref( ); } Filter::~Filter( ) { mlt_filter_close( instance ); } Filter &Filter::operator=(const Filter &filter) { if (this != &filter) { mlt_filter_close( instance ); instance = filter.instance; inc_ref( ); } return *this; } mlt_filter Filter::get_filter( ) { return instance; } mlt_service Filter::get_service( ) { return mlt_filter_service( get_filter( ) ); } int Filter::connect( Service &service, int index ) { return mlt_filter_connect( get_filter( ), service.get_service( ), index ); } void Filter::set_in_and_out( int in, int out ) { mlt_filter_set_in_and_out( get_filter( ), in, out ); } int Filter::get_in( ) { return mlt_filter_get_in( get_filter( ) ); } int Filter::get_out( ) { return mlt_filter_get_out( get_filter( ) ); } int Filter::get_length( ) { return mlt_filter_get_length( get_filter( ) ); } int Filter::get_length2( Frame &frame ) { return mlt_filter_get_length2( get_filter( ), frame.get_frame() ); } int Filter::get_track( ) { return mlt_filter_get_track( get_filter( ) ); } int Filter::get_position( Frame &frame ) { return mlt_filter_get_position( get_filter( ), frame.get_frame( ) ); } double Filter::get_progress( Frame &frame ) { return mlt_filter_get_progress( get_filter( ), frame.get_frame( ) ); } void Filter::process( Frame &frame ) { mlt_filter_process( get_filter( ), frame.get_frame() ); } mlt-6.20.0/src/mlt++/MltFilter.h000066400000000000000000000035141362234133600161650ustar00rootroot00000000000000/** * MltFilter.h - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FILTER_H #define MLTPP_FILTER_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Profile; class Frame; class MLTPP_DECLSPEC Filter : public Service { private: mlt_filter instance; public: Filter(); Filter( Profile& profile, const char *id, const char *service = NULL ); Filter( mlt_profile profile, const char *id, const char *service = NULL ); Filter( Service &filter ); Filter( Filter &filter ); Filter( const Filter &filter ); Filter( mlt_filter filter ); virtual ~Filter( ); Filter& operator=( const Filter &filter ); virtual mlt_filter get_filter( ); mlt_service get_service( ); int connect( Service &service, int index = 0 ); void set_in_and_out( int in, int out ); int get_in( ); int get_out( ); int get_length( ); int get_length2( Frame &frame ); int get_track( ); int get_position( Frame &frame ); double get_progress( Frame &frame ); void process( Frame &frame ); }; } #endif mlt-6.20.0/src/mlt++/MltFilteredConsumer.cpp000066400000000000000000000055351362234133600205520ustar00rootroot00000000000000/** * MltFilteredConsumer.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFilteredConsumer.h" using namespace Mlt; FilteredConsumer::FilteredConsumer( Profile& profile, const char *id, const char *arg ) : Consumer( profile, id, arg ) { // Create a reference to the first service first = new Service( *this ); } FilteredConsumer::FilteredConsumer( Consumer &consumer ) : Consumer( consumer ) { // Create a reference to the first service first = new Service( *this ); } FilteredConsumer::~FilteredConsumer( ) { // Delete the reference to the first service delete first; } int FilteredConsumer::connect( Service &service ) { // All producers must connect to the first service, hence the use of the virtual here return first->connect_producer( service ); } int FilteredConsumer::attach( Filter &filter ) { int error = 0; if ( filter.is_valid( ) ) { Service *producer = first->producer( ); error = filter.connect( *producer ); if ( error == 0 ) { first->connect_producer( filter ); delete first; first = new Service( filter ); } delete producer; } else { error = 1; } return error; } int FilteredConsumer::last( Filter &filter ) { int error = 0; if ( filter.is_valid( ) ) { Service *producer = this->producer( ); error = filter.connect( *producer ); if ( error == 0 ) connect_producer( filter ); delete producer; } else { error = 1; } return error; } int FilteredConsumer::detach( Filter &filter ) { if ( filter.is_valid( ) ) { Service *it = new Service( *first ); while ( it->is_valid( ) && it->get_service( ) != filter.get_service( ) ) { Service *consumer = it->consumer( ); delete it; it = consumer; } if ( it->get_service( ) == filter.get_service( ) ) { Service *producer = it->producer( ); Service *consumer = it->consumer( ); consumer->connect_producer( *producer ); Service dummy( NULL ); it->connect_producer( dummy ); if ( first->get_service( ) == it->get_service( ) ) { delete first; first = new Service( *consumer ); } } delete it; } return 0; } mlt-6.20.0/src/mlt++/MltFilteredConsumer.h000066400000000000000000000030011362234133600202010ustar00rootroot00000000000000/** * MltFilteredConsumer.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FILTERED_CONSUMER_H #define MLTPP_FILTERED_CONSUMER_H #include "MltConfig.h" #include "MltConsumer.h" #include "MltFilter.h" #include "MltService.h" namespace Mlt { class Consumer; class Service; class Filter; class Profile; class MLTPP_DECLSPEC FilteredConsumer : public Consumer { private: Service *first; public: FilteredConsumer( Profile& profile, const char *id, const char *arg = NULL ); FilteredConsumer( Consumer &consumer ); virtual ~FilteredConsumer( ); int connect( Service &service ); int attach( Filter &filter ); int last( Filter &filter ); int detach( Filter &filter ); }; } #endif mlt-6.20.0/src/mlt++/MltFilteredProducer.cpp000066400000000000000000000044671362234133600205450ustar00rootroot00000000000000/** * MltFilteredProducer.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFilteredProducer.h" #include "MltProfile.h" using namespace Mlt; FilteredProducer::FilteredProducer( Profile& profile, const char *id, const char *arg ) : Producer( profile, id, arg ) { // Create a reference to the last service last = new Service( *this ); } FilteredProducer::~FilteredProducer( ) { // Delete the reference to the last service delete last; } int FilteredProducer::attach( Filter &filter ) { int error = 0; if ( filter.is_valid( ) ) { Service *consumer = last->consumer( ); filter.connect_producer( *last ); if ( consumer->is_valid( ) ) consumer->connect_producer( filter ); delete consumer; delete last; last = new Service( filter ); } else { error = 1; } return error; } int FilteredProducer::detach( Filter &filter ) { if ( filter.is_valid( ) ) { Service *it = new Service( *last ); while ( it->is_valid( ) && it->get_service( ) != filter.get_service( ) ) { Service *producer = it->producer( ); delete it; it = producer; } if ( it->get_service( ) == filter.get_service( ) ) { Service *producer = it->producer( ); Service *consumer = it->consumer( ); if ( consumer->is_valid( ) ) consumer->connect_producer( *producer ); Profile p( get_profile() ); Producer dummy( p, "colour" ); dummy.connect_producer( *it ); if ( last->get_service( ) == it->get_service( ) ) { delete last; last = new Service( *producer ); } } delete it; } return 0; } mlt-6.20.0/src/mlt++/MltFilteredProducer.h000066400000000000000000000026221362234133600202010ustar00rootroot00000000000000/** * MltFilteredProducer.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FILTERED_PRODUCER_H #define MLTPP_FILTERED_PRODUCER_H #include "MltConfig.h" #include "MltProducer.h" #include "MltFilter.h" #include "MltService.h" namespace Mlt { class Producer; class Service; class Filter; class Profile; class MLTPP_DECLSPEC FilteredProducer : public Producer { private: Service *last; public: FilteredProducer( Profile& profile, const char *id, const char *arg = NULL ); virtual ~FilteredProducer( ); int attach( Filter &filter ); int detach( Filter &filter ); }; } #endif mlt-6.20.0/src/mlt++/MltFrame.cpp000066400000000000000000000065101362234133600163240ustar00rootroot00000000000000/** * MltFrame.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltFrame.h" #include "MltProducer.h" using namespace Mlt; Frame::Frame() : Mlt::Properties( (mlt_properties)NULL ), instance( NULL ) { } Frame::Frame( mlt_frame frame ) : Mlt::Properties( (mlt_properties)NULL ), instance( frame ) { inc_ref( ); } Frame::Frame( Frame &frame ) : Mlt::Properties( (mlt_properties)NULL ), instance( frame.instance ) { inc_ref( ); } Frame::Frame( const Frame &frame ) : Mlt::Properties( (mlt_properties)NULL ), instance( frame.instance ) { inc_ref( ); } Frame::~Frame( ) { mlt_frame_close( instance ); } Frame& Frame::operator=( const Frame &frame ) { if (this != &frame) { mlt_frame_close( instance ); instance = frame.instance; inc_ref( ); } return *this; } mlt_frame Frame::get_frame( ) { return instance; } mlt_properties Frame::get_properties( ) { return mlt_frame_properties( get_frame( ) ); } uint8_t *Frame::get_image( mlt_image_format &format, int &w, int &h, int writable ) { uint8_t *image = NULL; if ( get_double( "consumer_aspect_ratio" ) == 0.0 ) set( "consumer_aspect_ratio", 1.0 ); mlt_frame_get_image( get_frame( ), &image, &format, &w, &h, writable ); set( "format", format ); set( "writable", writable ); return image; } unsigned char *Frame::fetch_image( mlt_image_format f, int w, int h, int writable ) { uint8_t *image = NULL; if ( get_double( "consumer_aspect_ratio" ) == 0.0 ) set( "consumer_aspect_ratio", 1.0 ); mlt_frame_get_image( get_frame( ), &image, &f, &w, &h, writable ); set( "format", f ); set( "writable", writable ); return image; } void *Frame::get_audio( mlt_audio_format &format, int &frequency, int &channels, int &samples ) { void *audio = NULL; mlt_frame_get_audio( get_frame( ), &audio, &format, &frequency, &channels, &samples ); return audio; } unsigned char *Frame::get_waveform( int w, int h ) { return mlt_frame_get_waveform( get_frame( ), w, h ); } Producer *Frame::get_original_producer( ) { return new Producer( mlt_frame_get_original_producer( get_frame( ) ) ); } mlt_properties Frame::get_unique_properties( Service &service ) { return mlt_frame_unique_properties( get_frame(), service.get_service() ); } int Frame::get_position( ) { return mlt_frame_get_position( get_frame() ); } int Frame::set_image( uint8_t *image, int size, mlt_destructor destroy ) { return mlt_frame_set_image( get_frame(), image, size, destroy ); } int Frame::set_alpha( uint8_t *alpha, int size, mlt_destructor destroy ) { return mlt_frame_set_alpha( get_frame(), alpha, size, destroy ); } mlt-6.20.0/src/mlt++/MltFrame.h000066400000000000000000000037111362234133600157710ustar00rootroot00000000000000/** * MltFilter.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_FRAME_H #define MLTPP_FRAME_H #include "MltConfig.h" #include #include "MltProperties.h" namespace Mlt { class Properties; class Producer; class Service; class MLTPP_DECLSPEC Frame : public Properties { private: mlt_frame instance; public: Frame(); Frame( mlt_frame frame ); Frame( Frame &frame ); Frame( const Frame &frame ); virtual ~Frame( ); Frame& operator=( const Frame &frame ); virtual mlt_frame get_frame( ); mlt_properties get_properties( ); uint8_t *get_image( mlt_image_format &format, int &w, int &h, int writable = 0 ); unsigned char *fetch_image( mlt_image_format format, int w, int h, int writable = 0 ); void *get_audio( mlt_audio_format &format, int &frequency, int &channels, int &samples ); unsigned char *get_waveform( int w, int h ); Producer *get_original_producer( ); int get_position( ); mlt_properties get_unique_properties( Service &service ); int set_image( uint8_t *image, int size, mlt_destructor destroy ); int set_alpha( uint8_t *alpha, int size, mlt_destructor destroy ); }; } #endif mlt-6.20.0/src/mlt++/MltGeometry.cpp000066400000000000000000000054701362234133600170710ustar00rootroot00000000000000/** * MltGeometry.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "MltGeometry.h" using namespace Mlt; Geometry::Geometry( char *data, int length, int nw, int nh ) { geometry = mlt_geometry_init( ); parse( data, length, nw, nh ); } Geometry::~Geometry( ) { mlt_geometry_close( geometry ); } int Geometry::parse( char *data, int length, int nw, int nh ) { return mlt_geometry_parse( geometry, data, length, nw, nh ); } // Fetch a geometry item for an absolute position int Geometry::fetch( GeometryItem &item, float position ) { return mlt_geometry_fetch( geometry, item.get_item( ), position ); } int Geometry::fetch( GeometryItem *item, float position ) { return mlt_geometry_fetch( geometry, item->get_item( ), position ); } // Specify a geometry item at an absolute position int Geometry::insert( GeometryItem &item ) { return mlt_geometry_insert( geometry, item.get_item( ) ); } int Geometry::insert( GeometryItem *item ) { return mlt_geometry_insert( geometry, item->get_item( ) ); } // Remove the key at the specified position int Geometry::remove( int position ) { return mlt_geometry_remove( geometry, position ); } void Geometry::interpolate( ) { mlt_geometry_interpolate( geometry ); } // Get the key at the position or the next following int Geometry::next_key( GeometryItem &item, int position ) { return mlt_geometry_next_key( geometry, item.get_item( ), position ); } int Geometry::next_key( GeometryItem *item, int position ) { return mlt_geometry_next_key( geometry, item->get_item( ), position ); } int Geometry::prev_key( GeometryItem &item, int position ) { return mlt_geometry_prev_key( geometry, item.get_item( ), position ); } int Geometry::prev_key( GeometryItem *item, int position ) { return mlt_geometry_prev_key( geometry, item->get_item( ), position ); } // Serialise the current geometry char *Geometry::serialise( int in, int out ) { return mlt_geometry_serialise_cut( geometry, in, out ); } char *Geometry::serialise( ) { return mlt_geometry_serialise( geometry ); } mlt-6.20.0/src/mlt++/MltGeometry.h000066400000000000000000000053611362234133600165350ustar00rootroot00000000000000/** * MltGeometry.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_GEOMETRY_H #define MLTPP_GEOMETRY_H #include "MltConfig.h" #include namespace Mlt { // Just for consistent naming purposes class MLTPP_DECLSPEC GeometryItem { private: struct mlt_geometry_item_s item; public: mlt_geometry_item get_item( ) { return &item; } bool key( ) { return item.key != 0; } int frame( ) { return item.frame; } void frame( int value ) { item.frame = value; } float x( ) { return item.x; } void x( float value ) { item.f[0] = 1; item.x = value; } float y( ) { return item.y; } void y( float value ) { item.f[1] = 1; item.y = value; } float w( ) { return item.w; } void w( float value ) { item.f[2] = 1; item.w = value; } float h( ) { return item.h; } void h( float value ) { item.f[3] = 1; item.h = value; } float mix( ) { return item.mix; } void mix( float value ) { item.f[4] = 1; item.mix = value; } }; class MLTPP_DECLSPEC Geometry { private: mlt_geometry geometry; public: Geometry( char *data = NULL, int length = 0, int nw = -1, int nh = -1 ); ~Geometry( ); int parse( char *data, int length, int nw = -1, int nh = -1 ); // Fetch a geometry item for an absolute position int fetch( GeometryItem &item, float position ); int fetch( GeometryItem *item, float position ); // Specify a geometry item at an absolute position int insert( GeometryItem &item ); int insert( GeometryItem *item ); // Remove the key at the specified position int remove( int position ); void interpolate( ); // Get the key at the position or the next following int next_key( GeometryItem &item, int position ); int next_key( GeometryItem *item, int position ); int prev_key( GeometryItem &item, int position ); int prev_key( GeometryItem *item, int position ); // Serialise the current geometry char *serialise( int in, int out ); char *serialise( ); }; } #endif mlt-6.20.0/src/mlt++/MltMultitrack.cpp000066400000000000000000000046051362234133600174140ustar00rootroot00000000000000/** * MltMultitrack.h - Multitrack wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltMultitrack.h" #include "MltProducer.h" using namespace Mlt; Multitrack::Multitrack( mlt_multitrack multitrack ) : instance( multitrack ) { inc_ref( ); } Multitrack::Multitrack( Service &multitrack ) : instance( NULL ) { if ( multitrack.type( ) == multitrack_type ) { instance = ( mlt_multitrack )multitrack.get_service( ); inc_ref( ); } } Multitrack::Multitrack( Multitrack &multitrack ) : Mlt::Producer( multitrack ), instance( multitrack.get_multitrack( ) ) { inc_ref( ); } Multitrack::~Multitrack( ) { mlt_multitrack_close( instance ); } mlt_multitrack Multitrack::get_multitrack( ) { return instance; } mlt_producer Multitrack::get_producer( ) { return mlt_multitrack_producer( get_multitrack( ) ); } int Multitrack::connect( Producer &producer, int index ) { return mlt_multitrack_connect( get_multitrack( ), producer.get_producer( ), index ); } int Multitrack::insert( Producer &producer, int index ) { return mlt_multitrack_insert( get_multitrack( ), producer.get_producer( ), index ); } int Multitrack::disconnect( int index ) { return mlt_multitrack_disconnect( get_multitrack(), index ); } int Multitrack::clip( mlt_whence whence, int index ) { return mlt_multitrack_clip( get_multitrack( ), whence, index ); } int Multitrack::count( ) { return mlt_multitrack_count( get_multitrack( ) ); } Producer *Multitrack::track( int index ) { return new Producer( mlt_multitrack_track( get_multitrack( ), index ) ); } void Multitrack::refresh( ) { return mlt_multitrack_refresh( get_multitrack( ) ); } mlt-6.20.0/src/mlt++/MltMultitrack.h000066400000000000000000000031631362234133600170570ustar00rootroot00000000000000/** * MltMultitrack.h - Multitrack wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_MULTITRACK_H #define MLTPP_MULTITRACK_H #include "MltConfig.h" #include #include "MltProducer.h" namespace Mlt { class Service; class Producer; class MLTPP_DECLSPEC Multitrack : public Producer { private: mlt_multitrack instance; public: Multitrack( mlt_multitrack multitrack ); Multitrack( Service &multitrack ); Multitrack( Multitrack &multitrack ); virtual ~Multitrack( ); mlt_multitrack get_multitrack( ); mlt_producer get_producer( ); int connect( Producer &producer, int index ); int insert( Producer &producer, int index ); int disconnect( int index ); int clip( mlt_whence whence, int index ); int count( ); Producer *track( int index ); void refresh( ); }; } #endif mlt-6.20.0/src/mlt++/MltParser.cpp000066400000000000000000000202341362234133600165250ustar00rootroot00000000000000/** * MltParser.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Mlt.h" using namespace Mlt; static int on_invalid_cb( mlt_parser self, mlt_service object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Service service( object ); return parser->on_invalid( &service ); } static int on_unknown_cb( mlt_parser self, mlt_service object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Service service( object ); return parser->on_unknown( &service ); } static int on_start_producer_cb( mlt_parser self, mlt_producer object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Producer producer( object ); return parser->on_start_producer( &producer ); } static int on_end_producer_cb( mlt_parser self, mlt_producer object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Producer producer( object ); return parser->on_end_producer( &producer ); } static int on_start_playlist_cb( mlt_parser self, mlt_playlist object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Playlist playlist( object ); return parser->on_start_playlist( &playlist ); } static int on_end_playlist_cb( mlt_parser self, mlt_playlist object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Playlist playlist( object ); return parser->on_end_playlist( &playlist ); } static int on_start_tractor_cb( mlt_parser self, mlt_tractor object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Tractor tractor( object ); return parser->on_start_tractor( &tractor ); } static int on_end_tractor_cb( mlt_parser self, mlt_tractor object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Tractor tractor( object ); return parser->on_end_tractor( &tractor ); } static int on_start_multitrack_cb( mlt_parser self, mlt_multitrack object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Multitrack multitrack( object ); return parser->on_start_multitrack( &multitrack ); } static int on_end_multitrack_cb( mlt_parser self, mlt_multitrack object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Multitrack multitrack( object ); return parser->on_end_multitrack( &multitrack ); } static int on_start_track_cb( mlt_parser self ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); return parser->on_start_track( ); } static int on_end_track_cb( mlt_parser self ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); return parser->on_end_track( ); } static int on_start_filter_cb( mlt_parser self, mlt_filter object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Filter filter( object ); return parser->on_start_filter( &filter ); } static int on_end_filter_cb( mlt_parser self, mlt_filter object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Filter filter( object ); return parser->on_end_filter( &filter ); } static int on_start_transition_cb( mlt_parser self, mlt_transition object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Transition transition( object ); return parser->on_start_transition( &transition ); } static int on_end_transition_cb( mlt_parser self, mlt_transition object ) { mlt_properties properties = mlt_parser_properties( self ); Parser *parser = ( Parser * )mlt_properties_get_data( properties, "_parser_object", NULL ); Transition transition( object ); return parser->on_end_transition( &transition ); } Parser::Parser( ) : Properties( false ) { parser = mlt_parser_new( ); set( "_parser_object", this, 0 ); parser->on_invalid = on_invalid_cb; parser->on_unknown = on_unknown_cb; parser->on_start_producer = on_start_producer_cb; parser->on_end_producer = on_end_producer_cb; parser->on_start_playlist = on_start_playlist_cb; parser->on_end_playlist = on_end_playlist_cb; parser->on_start_tractor = on_start_tractor_cb; parser->on_end_tractor = on_end_tractor_cb; parser->on_start_multitrack = on_start_multitrack_cb; parser->on_end_multitrack = on_end_multitrack_cb; parser->on_start_track = on_start_track_cb; parser->on_end_track = on_end_track_cb; parser->on_start_filter = on_start_filter_cb; parser->on_end_filter = on_end_filter_cb; parser->on_start_transition = on_start_transition_cb; parser->on_end_transition = on_end_transition_cb; } Parser::~Parser( ) { mlt_parser_close( parser ); } mlt_properties Parser::get_properties( ) { return mlt_parser_properties( parser ); } int Parser::start( Service &service ) { return mlt_parser_start( parser, service.get_service( ) ); } int Parser::on_invalid( Service *object ) { object->debug( "Invalid" ); return 0; } int Parser::on_unknown( Service *object ) { object->debug( "Unknown" ); return 0; } int Parser::on_start_producer( Producer *object ) { object->debug( "on_start_producer" ); return 0; } int Parser::on_end_producer( Producer *object ) { object->debug( "on_end_producer" ); return 0; } int Parser::on_start_playlist( Playlist *object ) { object->debug( "on_start_playlist" ); return 0; } int Parser::on_end_playlist( Playlist *object ) { object->debug( "on_end_playlist" ); return 0; } int Parser::on_start_tractor( Tractor *object ) { object->debug( "on_start_tractor" ); return 0; } int Parser::on_end_tractor( Tractor *object ) { object->debug( "on_end_tractor" ); return 0; } int Parser::on_start_multitrack( Multitrack *object ) { object->debug( "on_start_multitrack" ); return 0; } int Parser::on_end_multitrack( Multitrack *object ) { object->debug( "on_end_multitrack" ); return 0; } int Parser::on_start_track( ) { fprintf( stderr, "on_start_track\n" ); return 0; } int Parser::on_end_track( ) { fprintf( stderr, "on_end_track\n" ); return 0; } int Parser::on_start_filter( Filter *object ) { object->debug( "on_start_filter" ); return 0; } int Parser::on_end_filter( Filter *object ) { object->debug( "on_end_filter" ); return 0; } int Parser::on_start_transition( Transition *object ) { object->debug( "on_start_transition" ); return 0; } int Parser::on_end_transition( Transition *object ) { object->debug( "on_end_transition" ); return 0; } mlt-6.20.0/src/mlt++/MltParser.h000066400000000000000000000041741362234133600161770ustar00rootroot00000000000000/** * MltParser.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PARSER_H #define MLTPP_PARSER_H #include "MltConfig.h" #include #include "MltProperties.h" namespace Mlt { class Properties; class Service; class Producer; class Playlist; class Tractor; class Multitrack; class Filter; class Transition; class MLTPP_DECLSPEC Parser : public Properties { private: mlt_parser parser; public: Parser( ); ~Parser( ); int start( Service &service ); virtual mlt_properties get_properties( ); virtual int on_invalid( Service *object ); virtual int on_unknown( Service *object ); virtual int on_start_producer( Producer *object ); virtual int on_end_producer( Producer *object ); virtual int on_start_playlist( Playlist *object ); virtual int on_end_playlist( Playlist *object ); virtual int on_start_tractor( Tractor *object ); virtual int on_end_tractor( Tractor *object ); virtual int on_start_multitrack( Multitrack *object ); virtual int on_end_multitrack( Multitrack *object ); virtual int on_start_track( ); virtual int on_end_track( ); virtual int on_start_filter( Filter *object ); virtual int on_end_filter( Filter *object ); virtual int on_start_transition( Transition *object ); virtual int on_end_transition( Transition *object ); }; } #endif mlt-6.20.0/src/mlt++/MltPlaylist.cpp000066400000000000000000000177701362234133600171050ustar00rootroot00000000000000/** * MltPlaylist.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "MltPlaylist.h" #include "MltTransition.h" #include "MltProfile.h" using namespace Mlt; ClipInfo::ClipInfo( ) : clip( 0 ), producer( NULL ), cut( NULL ), start( 0 ), resource( NULL ), frame_in( 0 ), frame_out( 0 ), frame_count( 0 ), length( 0 ), fps( 0 ), repeat( 0 ) { } ClipInfo::ClipInfo( mlt_playlist_clip_info *info ) : clip( info->clip ), producer( new Producer( info->producer ) ), cut( new Producer( info->cut ) ), start( info->start ), resource( info->resource? strdup( info->resource ) : 0 ), frame_in( info->frame_in ), frame_out( info->frame_out ), frame_count( info->frame_count ), length( info->length ), fps( info->fps ), repeat( info->repeat ) { } ClipInfo::~ClipInfo( ) { delete producer; delete cut; free( resource ); } void ClipInfo::update( mlt_playlist_clip_info *info ) { delete producer; delete cut; free( resource ); clip = info->clip; producer = new Producer( info->producer ); cut = new Producer( info->cut ); start = info->start; resource = info->resource ? strdup( info->resource ) : 0; frame_in = info->frame_in; frame_out = info->frame_out; frame_count = info->frame_count; length = info->length; fps = info->fps; repeat = info->repeat; } Playlist::Playlist( ) : instance( NULL ) { instance = mlt_playlist_init( ); } Playlist::Playlist( Profile& profile ) : instance( NULL ) { instance = mlt_playlist_new( profile.get_profile() ); } Playlist::Playlist( Service &producer ) : instance( NULL ) { if ( producer.type( ) == playlist_type ) { instance = ( mlt_playlist )producer.get_service( ); inc_ref( ); } } Playlist::Playlist( Playlist &playlist ) : Mlt::Producer( playlist ), instance( playlist.get_playlist( ) ) { inc_ref( ); } Playlist::Playlist( mlt_playlist playlist ) : instance( playlist ) { inc_ref( ); } Playlist::~Playlist( ) { mlt_playlist_close( instance ); } mlt_playlist Playlist::get_playlist( ) { return instance; } mlt_producer Playlist::get_producer( ) { return mlt_playlist_producer( get_playlist( ) ); } int Playlist::count( ) { return mlt_playlist_count( get_playlist( ) ); } int Playlist::clear( ) { return mlt_playlist_clear( get_playlist( ) ); } int Playlist::append( Producer &producer, int in, int out ) { return mlt_playlist_append_io( get_playlist( ), producer.get_producer( ), in, out ); } int Playlist::blank( int out ) { return mlt_playlist_blank( get_playlist( ), out ); } int Playlist::blank( const char *length ) { return mlt_playlist_blank_time( get_playlist( ), length ); } int Playlist::clip( mlt_whence whence, int index ) { return mlt_playlist_clip( get_playlist( ), whence, index ); } int Playlist::current_clip( ) { return mlt_playlist_current_clip( get_playlist( ) ); } Producer *Playlist::current( ) { return new Producer( mlt_playlist_current( get_playlist( ) ) ); } ClipInfo *Playlist::clip_info( int index, ClipInfo *info ) { mlt_playlist_clip_info clip_info; if ( mlt_playlist_get_clip_info( get_playlist( ), &clip_info, index ) ) return NULL; if ( info == NULL ) return new ClipInfo( &clip_info ); info->update( &clip_info ); return info; } void Playlist::delete_clip_info( ClipInfo *info ) { delete info; } int Playlist::insert( Producer &producer, int where, int in, int out ) { return mlt_playlist_insert( get_playlist( ), producer.get_producer( ), where, in, out ); } int Playlist::remove( int where ) { return mlt_playlist_remove( get_playlist( ), where ); } int Playlist::move( int from, int to ) { return mlt_playlist_move( get_playlist( ), from, to ); } int Playlist::reorder( const int *indices ) { return mlt_playlist_reorder( get_playlist( ), indices ); } int Playlist::resize_clip( int clip, int in, int out ) { return mlt_playlist_resize_clip( get_playlist( ), clip, in, out ); } int Playlist::split( int clip, int position ) { return mlt_playlist_split( get_playlist( ), clip, position ); } int Playlist::split_at( int position, bool left ) { return mlt_playlist_split_at( get_playlist( ), position, left ); } int Playlist::join( int clip, int count, int merge ) { return mlt_playlist_join( get_playlist( ), clip, count, merge ); } int Playlist::mix( int clip, int length, Transition *transition ) { return mlt_playlist_mix( get_playlist( ), clip, length, transition == NULL ? NULL : transition->get_transition( ) ); } int Playlist::mix_in(int clip, int length) { return mlt_playlist_mix_in( get_playlist( ), clip, length ); } int Playlist::mix_out(int clip, int length) { return mlt_playlist_mix_out( get_playlist( ), clip, length ); } int Playlist::mix_add( int clip, Transition *transition ) { return mlt_playlist_mix_add( get_playlist( ), clip, transition == NULL ? NULL : transition->get_transition( ) ); } int Playlist::repeat( int clip, int count ) { return mlt_playlist_repeat_clip( get_playlist( ), clip, count ); } Producer *Playlist::get_clip( int clip ) { mlt_producer producer = mlt_playlist_get_clip( get_playlist( ), clip ); return producer != NULL ? new Producer( producer ) : NULL; } Producer *Playlist::get_clip_at( int position ) { mlt_producer producer = mlt_playlist_get_clip_at( get_playlist( ), position ); return producer != NULL ? new Producer( producer ) : NULL; } int Playlist::get_clip_index_at( int position ) { return mlt_playlist_get_clip_index_at( get_playlist( ), position ); } bool Playlist::is_mix( int clip ) { return mlt_playlist_clip_is_mix( get_playlist( ), clip ) != 0; } bool Playlist::is_blank( int clip ) { return mlt_playlist_is_blank( get_playlist( ), clip ) != 0; } bool Playlist::is_blank_at( int position ) { return mlt_playlist_is_blank_at( get_playlist( ), position ) != 0; } Producer *Playlist::replace_with_blank( int clip ) { mlt_producer producer = mlt_playlist_replace_with_blank( get_playlist( ), clip ); Producer *object = producer != NULL ? new Producer( producer ) : NULL; mlt_producer_close( producer ); return object; } void Playlist::consolidate_blanks( int keep_length ) { return mlt_playlist_consolidate_blanks( get_playlist( ), keep_length ); } void Playlist::insert_blank(int clip, int out ) { mlt_playlist_insert_blank( get_playlist( ), clip, out ); } void Playlist::pad_blanks( int position, int length, int find ) { mlt_playlist_pad_blanks( get_playlist( ), position, length, find ); } int Playlist::insert_at( int position, Producer *producer, int mode ) { return mlt_playlist_insert_at( get_playlist( ), position, producer->get_producer( ), mode ); } int Playlist::insert_at( int position, Producer &producer, int mode ) { return mlt_playlist_insert_at( get_playlist( ), position, producer.get_producer( ), mode ); } int Playlist::clip_start( int clip ) { return mlt_playlist_clip_start( get_playlist( ), clip ); } int Playlist::blanks_from( int clip, int bounded ) { return mlt_playlist_blanks_from( get_playlist( ), clip, bounded ); } int Playlist::clip_length( int clip ) { return mlt_playlist_clip_length( get_playlist( ), clip ); } int Playlist::remove_region( int position, int length ) { return mlt_playlist_remove_region( get_playlist( ), position, length ); } int Playlist::move_region( int position, int length, int new_position ) { return mlt_playlist_move_region( get_playlist( ), position, length, new_position ); } mlt-6.20.0/src/mlt++/MltPlaylist.h000066400000000000000000000070011362234133600165340ustar00rootroot00000000000000/** * MltPlaylist.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PLAYLIST_H #define MLTPP_PLAYLIST_H #include "MltConfig.h" #include #include "MltProducer.h" namespace Mlt { class Producer; class Service; class Playlist; class Transition; class Profile; class MLTPP_DECLSPEC ClipInfo { public: ClipInfo( ); ClipInfo( mlt_playlist_clip_info *info ); ~ClipInfo( ); void update( mlt_playlist_clip_info *info ); int clip; Producer *producer; Producer *cut; int start; char *resource; int frame_in; int frame_out; int frame_count; int length; float fps; int repeat; }; class MLTPP_DECLSPEC Playlist : public Producer { private: mlt_playlist instance; public: Playlist( ); Playlist( Profile& profile ); Playlist( Service &playlist ); Playlist( Playlist &playlist ); Playlist( mlt_playlist playlist ); virtual ~Playlist( ); virtual mlt_playlist get_playlist( ); mlt_producer get_producer( ); int count( ); int clear( ); int append( Producer &producer, int in = -1, int out = -1 ); int blank( int out ); int blank( const char *length ); int clip( mlt_whence whence, int index ); int current_clip( ); Producer *current( ); ClipInfo *clip_info( int index, ClipInfo *info = NULL ); static void delete_clip_info( ClipInfo *info ); int insert( Producer &producer, int where, int in = -1, int out = -1 ); int remove( int where ); int move( int from, int to ); int reorder( const int *indices ); int resize_clip( int clip, int in, int out ); int split( int clip, int position ); int split_at( int position, bool left = true ); int join( int clip, int count = 1, int merge = 1 ); int mix( int clip, int length, Transition *transition = NULL ); int mix_in( int clip, int length ); int mix_out( int clip, int length ); int mix_add( int clip, Transition *transition ); int repeat( int clip, int count ); Producer *get_clip( int clip ); Producer *get_clip_at( int position ); int get_clip_index_at( int position ); bool is_mix( int clip ); bool is_blank( int clip ); bool is_blank_at( int position ); void consolidate_blanks( int keep_length = 0 ); Producer *replace_with_blank( int clip ); void insert_blank( int clip, int out ); void pad_blanks( int position, int length, int find = 0 ); int insert_at( int position, Producer *producer, int mode = 0 ); int insert_at( int position, Producer &producer, int mode = 0 ); int clip_start( int clip ); int clip_length( int clip ); int blanks_from( int clip, int bounded = 0 ); int remove_region( int position, int length ); int move_region( int position, int length, int new_position ); }; } #endif mlt-6.20.0/src/mlt++/MltProducer.cpp000066400000000000000000000133751362234133600170640ustar00rootroot00000000000000/** * MltProducer.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltProducer.h" #include "MltFilter.h" #include "MltProfile.h" #include "MltEvent.h" #include "MltConsumer.h" using namespace Mlt; Producer::Producer( ) : instance( NULL ), parent_( NULL ) { } Producer::Producer( Profile& profile, const char *id, const char *service ) : Producer( profile.get_profile() , id, service ) { } Producer::Producer( mlt_profile profile, const char *id, const char *service ) : instance( NULL ), parent_( NULL ) { if ( id != NULL && service != NULL ) instance = mlt_factory_producer( profile, id, service ); else instance = mlt_factory_producer( profile, NULL, id != NULL ? id : service ); } Producer::Producer( Service &producer ) : instance( NULL ), parent_( NULL ) { mlt_service_type type = producer.type( ); if ( type == producer_type || type == playlist_type || type == tractor_type || type == multitrack_type ) { instance = ( mlt_producer )producer.get_service( ); inc_ref( ); } } Producer::Producer( mlt_producer producer ) : instance( producer ), parent_( NULL ) { inc_ref( ); } Producer::Producer( Producer &producer ) : Mlt::Service( producer ), instance( producer.get_producer( ) ), parent_( NULL ) { inc_ref( ); } Producer::Producer( const Producer &producer ) : Producer( const_cast(producer) ) { } Producer::Producer( Producer *producer ) : instance( producer != NULL ? producer->get_producer( ) : NULL ), parent_( NULL ) { if ( is_valid( ) ) inc_ref( ); } Producer::~Producer( ) { delete parent_; mlt_producer_close( instance ); instance = NULL; } Producer &Producer::operator=(const Producer &producer) { if (this != &producer) { delete parent_; parent_ = nullptr; mlt_producer_close( instance ); instance = producer.instance; inc_ref( ); } return *this; } mlt_producer Producer::get_producer( ) { return instance; } mlt_producer Producer::get_parent( ) { return get_producer( ) != NULL && mlt_producer_cut_parent( get_producer( ) ) != NULL ? mlt_producer_cut_parent( get_producer( ) ) : get_producer( ); } Producer &Producer::parent( ) { if ( is_cut( ) && parent_ == NULL ) parent_ = new Producer( get_parent( ) ); return parent_ == NULL ? *this : *parent_; } mlt_service Producer::get_service( ) { return mlt_producer_service( get_producer( ) ); } int Producer::seek( int position ) { return mlt_producer_seek( get_producer( ), position ); } int Producer::seek( const char *time ) { return mlt_producer_seek_time( get_producer( ), time ); } int Producer::position( ) { return mlt_producer_position( get_producer( ) ); } int Producer::frame( ) { return mlt_producer_frame( get_producer( ) ); } char* Producer::frame_time( mlt_time_format format ) { return mlt_producer_frame_time( get_producer(), format ); } int Producer::set_speed( double speed ) { return mlt_producer_set_speed( get_producer( ), speed ); } int Producer::pause() { int result = 0; if ( get_speed() != 0 ) { Consumer consumer( ( mlt_consumer ) mlt_service_consumer( get_service( ) ) ); Event *event = consumer.setup_wait_for( "consumer-sdl-paused" ); result = mlt_producer_set_speed( get_producer( ), 0 ); if ( result == 0 && consumer.is_valid() && !consumer.is_stopped() ) consumer.wait_for( event ); delete event; } return result; } double Producer::get_speed( ) { return mlt_producer_get_speed( get_producer( ) ); } double Producer::get_fps( ) { return mlt_producer_get_fps( get_producer( ) ); } int Producer::set_in_and_out( int in, int out ) { return mlt_producer_set_in_and_out( get_producer( ), in, out ); } int Producer::get_in( ) { return mlt_producer_get_in( get_producer( ) ); } int Producer::get_out( ) { return mlt_producer_get_out( get_producer( ) ); } int Producer::get_length( ) { return mlt_producer_get_length( get_producer( ) ); } char* Producer::get_length_time( mlt_time_format format ) { return mlt_producer_get_length_time( get_producer( ), format ); } int Producer::get_playtime( ) { return mlt_producer_get_playtime( get_producer( ) ); } Producer *Producer::cut( int in, int out ) { mlt_producer producer = mlt_producer_cut( get_producer( ), in, out ); Producer *result = new Producer( producer ); mlt_producer_close( producer ); return result; } bool Producer::is_cut( ) { return mlt_producer_is_cut( get_producer( ) ) != 0; } bool Producer::is_blank( ) { return mlt_producer_is_blank( get_producer( ) ) != 0; } bool Producer::same_clip( Producer &that ) { return mlt_producer_cut_parent( get_producer( ) ) == mlt_producer_cut_parent( that.get_producer( ) ); } bool Producer::runs_into( Producer &that ) { return same_clip( that ) && get_out( ) == ( that.get_in( ) - 1 ); } void Producer::optimise( ) { mlt_producer_optimise( get_producer( ) ); } int Producer::clear( ) { return mlt_producer_clear( get_producer( ) ); } int64_t Producer::get_creation_time( ) { return mlt_producer_get_creation_time( get_producer( ) ); } void Producer::set_creation_time( int64_t creation_time ) { mlt_producer_set_creation_time( get_producer( ), creation_time ); } mlt-6.20.0/src/mlt++/MltProducer.h000066400000000000000000000046271362234133600165310ustar00rootroot00000000000000/** * MltProducer.h - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PRODUCER_H #define MLTPP_PRODUCER_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Filter; class Profile; class Frame; class MLTPP_DECLSPEC Producer : public Service { private: mlt_producer instance; Producer *parent_; public: Producer( ); Producer( Profile& profile, const char *id, const char *service = NULL ); Producer( mlt_profile profile, const char *id, const char *service = NULL ); Producer( Service &producer ); Producer( mlt_producer producer ); Producer( Producer &producer ); Producer( const Producer &producer ); Producer( Producer *producer ); virtual ~Producer( ); Producer& operator=( const Producer &producer ); virtual mlt_producer get_producer( ); Producer &parent( ); mlt_producer get_parent( ); mlt_service get_service( ); int seek( int position ); int seek( const char* time ); int position( ); int frame( ); char* frame_time( mlt_time_format = mlt_time_smpte_df ); int set_speed( double speed ); int pause( ); double get_speed( ); double get_fps( ); int set_in_and_out( int in, int out ); int get_in( ); int get_out( ); int get_length( ); char* get_length_time( mlt_time_format = mlt_time_smpte_df ); int get_playtime( ); Producer *cut( int in = 0, int out = -1 ); bool is_cut( ); bool is_blank( ); bool same_clip( Producer &that ); bool runs_into( Producer &that ); void optimise( ); int clear( ); int64_t get_creation_time( ); void set_creation_time( int64_t creation_time ); }; } #endif mlt-6.20.0/src/mlt++/MltProfile.cpp000066400000000000000000000074411362234133600166760ustar00rootroot00000000000000/** * MltProfile.cpp - MLT Wrapper * Copyright (C) 2008-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltProfile.h" #include "MltProperties.h" #include "MltProducer.h" using namespace Mlt; Profile::Profile( ) : instance( NULL ) { instance = mlt_profile_init( NULL ); } Profile::Profile( const char* name ) : instance( NULL ) { instance = mlt_profile_init( name ); } Profile::Profile( Properties& properties ) : instance( NULL ) { instance = mlt_profile_load_properties( properties.get_properties() ); } Profile::Profile( mlt_profile profile ) : instance( profile ) { } Profile::~Profile( ) { if ( instance ) mlt_profile_close( instance ); instance = NULL; } bool Profile::is_valid() const { return instance != NULL; } mlt_profile Profile::get_profile( ) const { return instance; } char* Profile::description() const { return instance->description; } int Profile::frame_rate_num() const { return instance->frame_rate_num; } int Profile::frame_rate_den() const { return instance->frame_rate_den; } double Profile::fps() const { return mlt_profile_fps( instance ); } int Profile::width() const { return instance->width; } int Profile::height() const { return instance->height; } bool Profile::progressive() const { return instance->progressive; } int Profile::sample_aspect_num() const { return instance->sample_aspect_num; } int Profile::sample_aspect_den() const { return instance->sample_aspect_den; } double Profile::sar() const { return mlt_profile_sar( instance ); } int Profile::display_aspect_num() const { return instance->display_aspect_num; } int Profile::display_aspect_den() const { return instance->display_aspect_den; } double Profile::dar() const { return mlt_profile_dar( instance ); } int Profile::is_explicit() const { return instance->is_explicit; } int Profile::colorspace() const { return instance->colorspace; } Properties* Profile::list() { return new Properties( mlt_profile_list() ); } void Profile::from_producer( Producer &producer ) { mlt_profile_from_producer( instance, producer.get_producer() ); } void Profile::set_width( int width ) { instance->width = width; } void Profile::set_height( int height ) { instance->height = height; } void Profile::set_sample_aspect( int numerator, int denominator ) { instance->sample_aspect_num = numerator; instance->sample_aspect_den = denominator; } void Profile::set_display_aspect(int numerator, int denominator) { instance->display_aspect_num = numerator; instance->display_aspect_den = denominator; } void Profile::set_progressive( int progressive ) { instance->progressive = progressive; } void Profile::set_colorspace( int colorspace ) { instance->colorspace = colorspace; } void Profile::set_frame_rate( int numerator, int denominator ) { instance->frame_rate_num = numerator; instance->frame_rate_den = denominator; } void Profile::set_explicit( int boolean ) { instance->is_explicit = boolean; } double Profile::scale_width(int width) { return mlt_profile_scale_width(instance, width); } double Profile::scale_height(int height) { return mlt_profile_scale_height(instance, height); } mlt-6.20.0/src/mlt++/MltProfile.h000066400000000000000000000043261362234133600163420ustar00rootroot00000000000000/** * MltProfile.h - MLT Wrapper * Copyright (C) 2008-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PROFILE_H #define MLTPP_PROFILE_H #include "MltConfig.h" #ifdef SWIG #define MLTPP_DECLSPEC #endif #include namespace Mlt { class Properties; class Producer; class MLTPP_DECLSPEC Profile { private: mlt_profile instance; public: Profile( ); Profile( const char* name ); Profile( Properties& properties ); Profile( mlt_profile profile ); ~Profile(); bool is_valid( ) const; mlt_profile get_profile( ) const; char* description() const; int frame_rate_num() const; int frame_rate_den() const; double fps() const; int width() const; int height() const; bool progressive() const; int sample_aspect_num() const; int sample_aspect_den() const; double sar() const; int display_aspect_num() const; int display_aspect_den() const; double dar() const; int is_explicit() const; int colorspace() const; static Properties* list(); void from_producer( Producer &producer ); void set_width( int width ); void set_height( int height ); void set_sample_aspect( int numerator, int denominator ); void set_display_aspect( int numerator, int denominator ); void set_progressive( int progressive ); void set_colorspace( int colorspace ); void set_frame_rate( int numerator, int denominator ); void set_explicit( int boolean ); double scale_width( int width ); double scale_height( int height ); }; } #endif mlt-6.20.0/src/mlt++/MltProperties.cpp000066400000000000000000000254571362234133600174410ustar00rootroot00000000000000/** * MltProperties.cpp - MLT Wrapper * Copyright (C) 2004-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltProperties.h" #include "MltEvent.h" #include "MltAnimation.h" using namespace Mlt; Properties::Properties( ) : instance( NULL ) { instance = mlt_properties_new( ); } Properties::Properties( bool /*dummy*/ ) : instance( NULL ) { } Properties::Properties( Properties &properties ) : instance( properties.get_properties( ) ) { inc_ref( ); } Properties::Properties( const Properties &properties ) : Properties(const_cast(properties)) { } Properties::Properties( mlt_properties properties ) : instance( properties ) { inc_ref( ); } Properties::Properties( void *properties ) : instance( mlt_properties( properties ) ) { inc_ref( ); } Properties::Properties( const char *file ) : instance( NULL ) { instance = mlt_properties_load( file ); } Properties::~Properties( ) { mlt_properties_close( instance ); } Properties &Properties::operator=(const Properties &properties) { if (this != &properties) { mlt_properties_close( instance ); instance = properties.instance; inc_ref( ); } return *this; } mlt_properties Properties::get_properties( ) { return instance; } int Properties::inc_ref( ) { return mlt_properties_inc_ref( get_properties( ) ); } int Properties::dec_ref( ) { return mlt_properties_dec_ref( get_properties( ) ); } int Properties::ref_count( ) { return mlt_properties_ref_count( get_properties( ) ); } void Properties::lock( ) { mlt_properties_lock( get_properties( ) ); } void Properties::unlock( ) { mlt_properties_unlock( get_properties( ) ); } void Properties::block( void *object ) { mlt_events_block( get_properties( ), object != NULL ? object : get_properties( ) ); } void Properties::unblock( void *object ) { mlt_events_unblock( get_properties( ), object != NULL ? object : get_properties( ) ); } int Properties::fire_event( const char *event ) { return mlt_events_fire( get_properties( ), event, NULL ); } bool Properties::is_valid( ) { return get_properties( ) != NULL; } int Properties::count( ) { return mlt_properties_count( get_properties( ) ); } char *Properties::get( const char *name ) { return mlt_properties_get( get_properties( ), name ); } int Properties::get_int( const char *name ) { return mlt_properties_get_int( get_properties( ), name ); } int64_t Properties::get_int64( const char *name ) { return mlt_properties_get_int64( get_properties( ), name ); } double Properties::get_double( const char *name ) { return mlt_properties_get_double( get_properties( ), name ); } void *Properties::get_data( const char *name, int &size ) { return mlt_properties_get_data( get_properties( ), name, &size ); } void *Properties::get_data( const char *name ) { return mlt_properties_get_data( get_properties( ), name, NULL ); } int Properties::set( const char *name, const char *value ) { return mlt_properties_set( get_properties( ), name, value ); } int Properties::set_string(const char *name, const char *value) { return mlt_properties_set_string( get_properties( ), name, value ); } int Properties::set( const char *name, int value ) { return mlt_properties_set_int( get_properties( ), name, value ); } int Properties::set( const char *name, int64_t value ) { return mlt_properties_set_int64( get_properties( ), name, value ); } int Properties::set( const char *name, double value ) { return mlt_properties_set_double( get_properties( ), name, value ); } int Properties::set( const char *name, void *value, int size, mlt_destructor destructor, mlt_serialiser serialiser ) { return mlt_properties_set_data( get_properties( ), name, value, size, destructor, serialiser ); } void Properties::pass_property( Properties &that, const char *name ) { return mlt_properties_pass_property( get_properties( ), that.get_properties( ), name ); } int Properties::pass_values( Properties &that, const char *prefix ) { return mlt_properties_pass( get_properties( ), that.get_properties( ), prefix ); } int Properties::pass_list( Properties &that, const char *list ) { return mlt_properties_pass_list( get_properties( ), that.get_properties( ), list ); } int Properties::parse( const char *namevalue ) { return mlt_properties_parse( get_properties( ), namevalue ); } char *Properties::get_name( int index ) { return mlt_properties_get_name( get_properties( ), index ); } char *Properties::get( int index ) { return mlt_properties_get_value( get_properties( ), index ); } char *Properties::get( int index , mlt_time_format format ) { return mlt_properties_get_value_tf( get_properties( ), index, format ); } void *Properties::get_data( int index, int &size ) { return mlt_properties_get_data_at( get_properties( ), index, &size ); } void Properties::mirror( Properties &that ) { mlt_properties_mirror( get_properties( ), that.get_properties( ) ); } int Properties::inherit( Properties &that ) { return mlt_properties_inherit( get_properties( ), that.get_properties( ) ); } int Properties::rename( const char *source, const char *dest ) { return mlt_properties_rename( get_properties( ), source, dest ); } void Properties::dump( FILE *output ) { mlt_properties_dump( get_properties( ), output ); } void Properties::debug( const char *title, FILE *output ) { mlt_properties_debug( get_properties( ), title, output ); } void Properties::load( const char *file ) { mlt_properties properties = mlt_properties_load( file ); if ( properties != NULL ) mlt_properties_pass( get_properties( ), properties, "" ); mlt_properties_close( properties ); } int Properties::save( const char *file ) { return mlt_properties_save( get_properties( ), file ); } #if defined( __APPLE__ ) && GCC_VERSION < 40000 Event *Properties::listen( const char *id, void *object, void (*listener)( ... ) ) { mlt_event event = mlt_events_listen( get_properties( ), object, id, ( mlt_listener )listener ); return new Event( event ); } #else Event *Properties::listen( const char *id, void *object, mlt_listener listener ) { mlt_event event = mlt_events_listen( get_properties( ), object, id, listener ); return new Event( event ); } #endif Event *Properties::setup_wait_for( const char *id ) { return new Event( mlt_events_setup_wait_for( get_properties( ), id ) ); } void Properties::delete_event( Event *event ) { delete event; } void Properties::wait_for( Event *event, bool destroy ) { mlt_events_wait_for( get_properties( ), event->get_event( ) ); if ( destroy ) mlt_events_close_wait_for( get_properties( ), event->get_event( ) ); } void Properties::wait_for( const char *id ) { Event *event = setup_wait_for( id ); wait_for( event ); delete event; } bool Properties::is_sequence( ) { return mlt_properties_is_sequence( get_properties( ) ); } Properties *Properties::parse_yaml( const char *file ) { return new Properties( mlt_properties_parse_yaml( file ) ); } char *Properties::serialise_yaml( ) { return mlt_properties_serialise_yaml( get_properties( ) ); } int Properties::preset( const char *name ) { return mlt_properties_preset( get_properties(), name ); } int Properties::set_lcnumeric( const char *locale ) { return mlt_properties_set_lcnumeric( get_properties(), locale ); } const char *Properties::get_lcnumeric( ) { return mlt_properties_get_lcnumeric( get_properties() ); } void Properties::clear( const char *name ) { return mlt_properties_clear( get_properties(), name ); } char *Properties::get_time( const char *name, mlt_time_format format ) { return mlt_properties_get_time( get_properties(), name, format ); } char *Properties::frames_to_time( int frames, mlt_time_format format ) { return mlt_properties_frames_to_time( get_properties(), frames, format ); } int Properties::time_to_frames( const char *time ) { return mlt_properties_time_to_frames( get_properties(), time ); } mlt_color Properties::get_color( const char *name ) { return mlt_properties_get_color( get_properties(), name ); } int Properties::set( const char *name, mlt_color value ) { return mlt_properties_set_color( get_properties(), name, value ); } char *Properties::anim_get( const char *name, int position, int length ) { return mlt_properties_anim_get( get_properties(), name, position, length ); } int Properties::anim_set( const char *name, const char *value, int position, int length ) { return mlt_properties_anim_set( get_properties(), name, value, position, length ); } int Properties::anim_get_int( const char *name, int position, int length ) { return mlt_properties_anim_get_int( get_properties(), name, position, length ); } int Properties::anim_set( const char *name, int value, int position, int length, mlt_keyframe_type keyframe_type ) { return mlt_properties_anim_set_int( get_properties(), name, value, position, length, keyframe_type ); } double Properties::anim_get_double(const char *name, int position, int length) { return mlt_properties_anim_get_double( get_properties(), name, position, length ); } int Properties::anim_set( const char *name, double value, int position, int length, mlt_keyframe_type keyframe_type ) { return mlt_properties_anim_set_double( get_properties(), name, value, position, length, keyframe_type ); } int Properties::set( const char *name, mlt_rect value ) { return mlt_properties_set_rect( get_properties(), name, value ); } int Properties::set( const char *name, double x, double y, double w, double h, double opacity ) { mlt_rect value = { x, y, w, h, opacity }; return mlt_properties_set_rect( get_properties(), name, value ); } mlt_rect Properties::get_rect( const char *name ) { return mlt_properties_get_rect( get_properties(), name ); } int Properties::anim_set( const char *name, mlt_rect value, int position, int length, mlt_keyframe_type keyframe_type ) { return mlt_properties_anim_set_rect( get_properties(), name, value, position, length, keyframe_type ); } mlt_rect Properties::anim_get_rect(const char *name, int position, int length) { return mlt_properties_anim_get_rect( get_properties(), name, position, length ); } mlt_animation Properties::get_animation( const char *name ) { return mlt_properties_get_animation( get_properties(), name ); } Animation *Properties::get_anim(const char *name) { return new Animation( mlt_properties_get_animation( get_properties(), name ) ); } mlt-6.20.0/src/mlt++/MltProperties.h000066400000000000000000000116461362234133600171010ustar00rootroot00000000000000/** * MltProperties.h - MLT Wrapper * Copyright (C) 2004-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PROPERTIES_H #define MLTPP_PROPERTIES_H #include "MltConfig.h" #include #include namespace Mlt { class Event; class Animation; /** Abstract Properties class. */ class MLTPP_DECLSPEC Properties { private: mlt_properties instance; public: Properties( ); Properties( bool dummy ); Properties( Properties &properties ); Properties( const Properties &properties ); Properties( mlt_properties properties ); Properties( void *properties ); Properties( const char *file ); virtual ~Properties( ); Properties& operator=( const Properties &properties ); virtual mlt_properties get_properties( ); int inc_ref( ); int dec_ref( ); int ref_count( ); void lock( ); void unlock( ); void block( void *object = NULL ); void unblock( void *object = NULL ); int fire_event( const char *event ); bool is_valid( ); int count( ); char *get( const char *name ); int get_int( const char *name ); int64_t get_int64( const char *name ); double get_double( const char *name ); void *get_data( const char *name, int &size ); void *get_data( const char *name ); int set( const char *name, const char *value ); int set_string( const char *name, const char *value ); int set( const char *name, int value ); int set( const char *name, int64_t value ); int set( const char *name, double value ); int set( const char *name, void *value, int size, mlt_destructor destroy = NULL, mlt_serialiser serial = NULL ); void pass_property( Properties &that, const char *name ); int pass_values( Properties &that, const char *prefix ); int pass_list( Properties &that, const char *list ); int parse( const char *namevalue ); char *get_name( int index ); char *get( int index ); char *get( int index, mlt_time_format ); void *get_data( int index, int &size ); void mirror( Properties &that ); int inherit( Properties &that ); int rename( const char *source, const char *dest ); void dump( FILE *output = stderr ); void debug( const char *title = "Object", FILE *output = stderr ); void load( const char *file ); int save( const char *file ); #if defined( __APPLE__ ) && GCC_VERSION < 40000 Event *listen( const char *id, void *object, void (*)( ... ) ); #else Event *listen( const char *id, void *object, mlt_listener ); #endif static void delete_event( Event * ); Event *setup_wait_for( const char *id ); void wait_for( Event *, bool destroy = true ); void wait_for( const char *id ); bool is_sequence( ); static Properties *parse_yaml( const char *file ); char *serialise_yaml( ); int preset( const char *name ); int set_lcnumeric( const char *locale ); const char *get_lcnumeric( ); void clear( const char *name ); char *get_time( const char *name, mlt_time_format = mlt_time_smpte_df ); char *frames_to_time( int, mlt_time_format = mlt_time_smpte_df ); int time_to_frames( const char* time ); mlt_color get_color( const char *name ); int set( const char *name , mlt_color value ); char* anim_get( const char *name, int position, int length = 0 ); int anim_set( const char *name, const char *value, int position, int length = 0 ); int anim_get_int( const char *name, int position, int length = 0 ); int anim_set( const char *name, int value, int position, int length = 0, mlt_keyframe_type keyframe_type = mlt_keyframe_linear ); double anim_get_double( const char *name, int position, int length = 0 ); int anim_set( const char *name, double value, int position, int length = 0, mlt_keyframe_type keyframe_type = mlt_keyframe_linear ); int set( const char *name, mlt_rect value ); int set( const char *name, double x, double y, double w, double h, double opacity = 1.0 ); mlt_rect get_rect( const char* name ); int anim_set( const char *name, mlt_rect value, int position, int length = 0, mlt_keyframe_type keyframe_type = mlt_keyframe_linear ); mlt_rect anim_get_rect( const char *name, int position, int length = 0 ); mlt_animation get_animation( const char *name ); Animation* get_anim( const char *name ); }; } #endif mlt-6.20.0/src/mlt++/MltPushConsumer.cpp000066400000000000000000000102731362234133600177260ustar00rootroot00000000000000/** * MltPushConsumer.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltPushConsumer.h" #include "MltFilter.h" using namespace Mlt; namespace Mlt { class PushPrivate { public: PushPrivate( ) { } }; } static void filter_destructor( void *arg ) { Filter *filter = ( Filter * )arg; delete filter; } PushConsumer::PushConsumer( Profile& profile, const char *id , const char *service ) : Consumer( profile, id, service ), m_private( new PushPrivate( ) ) { if ( is_valid( ) ) { // Set up push mode (known as put mode in mlt) set( "real_time", 0 ); set( "put_mode", 1 ); set( "terminate_on_pause", 0 ); set( "buffer", 0 ); // We might need resize and rescale filters so we'll create them now // NB: Try to use the best rescaler available here Filter *resize = new Filter( profile, "resize" ); Filter *rescale = new Filter( profile, "mcrescale" ); if ( !rescale->is_valid( ) ) { delete rescale; rescale = new Filter( profile, "gtkrescale" ); } if ( !rescale->is_valid( ) ) { delete rescale; rescale = new Filter( profile, "rescale" ); } Filter *convert = new Filter( profile, "avcolour_space" ); set( "filter_convert", convert, 0, filter_destructor ); set( "filter_resize", resize, 0, filter_destructor ); set( "filter_rescale", rescale, 0, filter_destructor ); } } PushConsumer::~PushConsumer( ) { } void PushConsumer::set_render( int width, int height, double aspect_ratio ) { set( "render_width", width ); set( "render_height", height ); set( "render_aspect_ratio", aspect_ratio ); } int PushConsumer::connect( Service &/*service*/ ) { return -1; } int PushConsumer::push( Frame *frame ) { frame->inc_ref( ); // Here we have the option to process the frame at a render resolution (this will // typically be PAL or NTSC) prior to scaling according to the consumers profile // This is done to optimise quality, esp. with regard to compositing positions if ( get_int( "render_width" ) ) { // Process the projects render resolution first mlt_image_format format = mlt_image_yuv422; int w = get_int( "render_width" ); int h = get_int( "render_height" ); frame->set( "consumer_aspect_ratio", get_double( "render_aspect_ratio" ) ); frame->set( "consumer_deinterlace", get_int( "deinterlace" ) ); frame->set( "deinterlace_method", get_int( "deinterlace_method" ) ); frame->set( "rescale.interp", get( "rescale" ) ); // Render the frame frame->get_image( format, w, h ); // Now set up the post image scaling Filter *convert = ( Filter * )get_data( "filter_convert" ); mlt_filter_process( convert->get_filter( ), frame->get_frame( ) ); Filter *rescale = ( Filter * )get_data( "filter_rescale" ); mlt_filter_process( rescale->get_filter( ), frame->get_frame( ) ); Filter *resize = ( Filter * )get_data( "filter_resize" ); mlt_filter_process( resize->get_filter( ), frame->get_frame( ) ); } return mlt_consumer_put_frame( ( mlt_consumer )get_service( ), frame->get_frame( ) ); } int PushConsumer::push( Frame &frame ) { return push( &frame ); } int PushConsumer::drain( ) { return 0; } // Convenience function - generates a frame with an image of a given size Frame *PushConsumer::construct( int size ) { mlt_frame f = mlt_frame_init( get_service() ); Frame *frame = new Frame( f ); uint8_t *buffer = ( uint8_t * )mlt_pool_alloc( size ); frame->set( "image", buffer, size, mlt_pool_release ); mlt_frame_close( f ); return frame; } mlt-6.20.0/src/mlt++/MltPushConsumer.h000066400000000000000000000027551362234133600174010ustar00rootroot00000000000000/** * MltPushConsumer.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_PUSH_CONSUMER_H #define MLTPP_PUSH_CONSUMER_H #include "MltConfig.h" #include "MltConsumer.h" namespace Mlt { class Frame; class Service; class PushPrivate; class Profile; class MLTPP_DECLSPEC PushConsumer : public Consumer { private: PushPrivate *m_private; public: PushConsumer( Profile& profile, const char *id , const char *service = NULL ); virtual ~PushConsumer( ); void set_render( int width, int height, double aspect_ratio ); virtual int connect( Service &service ); int push( Frame *frame ); int push( Frame &frame ); int drain( ); Frame *construct( int ); }; } #endif mlt-6.20.0/src/mlt++/MltRepository.cpp000066400000000000000000000050631362234133600174530ustar00rootroot00000000000000/** * MltRepository.cpp - MLT Wrapper * Copyright (C) 2008-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltRepository.h" #include "MltProfile.h" #include "MltProperties.h" using namespace Mlt; Repository::Repository( const char* directory ) : instance( NULL ) { instance = mlt_repository_init( directory ); } Repository::Repository( mlt_repository repository ) : instance( repository ) { } Repository::~Repository( ) { instance = NULL; } void Repository::register_service( mlt_service_type service_type, const char *service, mlt_register_callback symbol ) { mlt_repository_register( instance, service_type, service, symbol ); } void *Repository::create( Profile& profile, mlt_service_type type, const char *service, void *arg ) { return mlt_repository_create( instance, profile.get_profile(), type, service, arg ); } Properties *Repository::consumers( ) const { return new Properties( mlt_repository_consumers( instance ) ); } Properties *Repository::filters( ) const { return new Properties( mlt_repository_filters( instance ) ); } Properties *Repository::producers( ) const { return new Properties( mlt_repository_producers( instance ) ); } Properties *Repository::transitions( ) const { return new Properties( mlt_repository_transitions( instance ) ); } void Repository::register_metadata( mlt_service_type type, const char *service, mlt_metadata_callback callback, void *callback_data ) { mlt_repository_register_metadata( instance, type, service, callback, callback_data ); } Properties *Repository::metadata( mlt_service_type type, const char *service ) const { return new Properties( mlt_repository_metadata( instance, type, service ) ); } Properties *Repository::languages( ) const { return new Properties( mlt_repository_languages( instance ) ); } Properties *Repository::presets( ) { return new Properties( mlt_repository_presets( ) ); } mlt-6.20.0/src/mlt++/MltRepository.h000066400000000000000000000035011362234133600171130ustar00rootroot00000000000000/** * MltRepository.h - MLT Wrapper * Copyright (C) 2008-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_REPOSITORY_H #define MLTPP_REPOSITORY_H #include "MltConfig.h" #ifdef SWIG #define MLTPP_DECLSPEC #endif #include namespace Mlt { class Profile; class Properties; class MLTPP_DECLSPEC Repository { private: mlt_repository instance; Repository( ) { } public: Repository( const char* directory ); Repository( mlt_repository repository ); ~Repository(); void register_service( mlt_service_type service_type, const char *service, mlt_register_callback symbol ); void *create( Profile& profile, mlt_service_type type, const char *service, void *arg ); Properties *consumers( ) const; Properties *filters( ) const; Properties *producers( ) const; Properties *transitions( ) const; void register_metadata( mlt_service_type type, const char *service, mlt_metadata_callback, void *callback_data ); Properties *metadata( mlt_service_type type, const char *service ) const; Properties *languages( ) const; static Properties *presets(); }; } #endif mlt-6.20.0/src/mlt++/MltService.cpp000066400000000000000000000073601362234133600166760ustar00rootroot00000000000000/** * MltService.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "MltService.h" #include "MltFilter.h" #include "MltProfile.h" using namespace Mlt; Service::Service( ) : Properties( false ), instance( NULL ) { } Service::Service( Service &service ) : Properties( false ), instance( service.get_service( ) ) { inc_ref( ); } Service::Service(const Service &service ) : Service(const_cast(service)) { } Service::Service( mlt_service service ) : Properties( false ), instance( service ) { inc_ref( ); } Service::~Service( ) { mlt_service_close( instance ); } Service &Service::operator=(const Service &service) { if (this != &service) { mlt_service_close( instance ); instance = service.instance; inc_ref( ); } return *this; } mlt_service Service::get_service( ) { return instance; } mlt_properties Service::get_properties( ) { return mlt_service_properties( get_service( ) ); } void Service::lock( ) { mlt_service_lock( get_service( ) ); } void Service::unlock( ) { mlt_service_unlock( get_service( ) ); } int Service::connect_producer( Service &producer, int index ) { return mlt_service_connect_producer( get_service( ), producer.get_service( ), index ); } int Mlt::Service::insert_producer(Mlt::Service &producer, int index) { return mlt_service_insert_producer( get_service(), producer.get_service(), index ); } int Service::disconnect_producer( int index ) { return mlt_service_disconnect_producer( get_service(), index ); } int Service::disconnect_all_producers( ) { return mlt_service_disconnect_all_producers( get_service() ); } Service *Service::producer( ) { return new Service( mlt_service_producer( get_service( ) ) ); } Service *Service::consumer( ) { return new Service( mlt_service_consumer( get_service( ) ) ); } Profile *Service::profile( ) { return new Profile( mlt_service_profile( get_service() ) ); } mlt_profile Service::get_profile() { return mlt_service_profile( get_service() ); } Frame *Service::get_frame( int index ) { mlt_frame frame = NULL; mlt_service_get_frame( get_service( ), &frame, index ); Frame *result = new Frame( frame ); mlt_frame_close( frame ); return result; } mlt_service_type Service::type( ) { return mlt_service_identify( get_service( ) ); } int Service::attach( Filter &filter ) { return mlt_service_attach( get_service( ), filter.get_filter( ) ); } int Service::detach( Filter &filter ) { return mlt_service_detach( get_service( ), filter.get_filter( ) ); } int Service::filter_count() { return mlt_service_filter_count( get_service() ); } int Service::move_filter(int from, int to) { return mlt_service_move_filter( get_service(), from, to ); } Filter *Service::filter( int index ) { mlt_filter result = mlt_service_filter( get_service( ), index ); return result == NULL ? NULL : new Filter( result ); } void Service::set_profile( mlt_profile profile ) { mlt_service_set_profile( get_service( ), profile ); } void Service::set_profile( Profile &profile ) { set_profile( profile.get_profile( ) ); } mlt-6.20.0/src/mlt++/MltService.h000066400000000000000000000040701362234133600163360ustar00rootroot00000000000000/** * MltService.h - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_SERVICE_H #define MLTPP_SERVICE_H #include "MltConfig.h" #include #include "MltProperties.h" #include "MltFrame.h" namespace Mlt { class Properties; class Filter; class Frame; class Profile; class MLTPP_DECLSPEC Service : public Properties { private: mlt_service instance; public: Service( ); Service( Service &service ); Service( const Service &service ); Service( mlt_service service ); virtual ~Service( ); Service& operator=( const Service &service ); virtual mlt_service get_service( ); void lock( ); void unlock( ); virtual mlt_properties get_properties( ); int connect_producer( Service &producer, int index = 0 ); int insert_producer( Service &producer, int index = 0 ); int disconnect_producer( int index = 0 ); int disconnect_all_producers( ); Service *consumer( ); Service *producer( ); Profile *profile( ); mlt_profile get_profile( ); Frame *get_frame( int index = 0 ); mlt_service_type type( ); int attach( Filter &filter ); int detach( Filter &filter ); int filter_count( ); int move_filter( int from, int to ); Filter *filter( int index ); void set_profile( mlt_profile profile ); void set_profile( Profile &profile ); }; } #endif mlt-6.20.0/src/mlt++/MltTokeniser.cpp000066400000000000000000000030261362234133600172340ustar00rootroot00000000000000/** * MltTokeniser.cpp - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "MltTokeniser.h" using namespace Mlt; Tokeniser::Tokeniser( char *text, char *delimiter ) { tokens = mlt_tokeniser_init( ); if ( text != NULL ) mlt_tokeniser_parse_new( tokens, text, delimiter? delimiter : " " ); } Tokeniser::~Tokeniser( ) { mlt_tokeniser_close( tokens ); } int Tokeniser::parse( char *text, char *delimiter ) { return mlt_tokeniser_parse_new( tokens, text, delimiter? delimiter : " " ); } int Tokeniser::count( ) { return mlt_tokeniser_count( tokens ); } char *Tokeniser::get( int index ) { return mlt_tokeniser_get_string( tokens, index ); } char *Tokeniser::input( ) { return mlt_tokeniser_get_input( tokens ); } mlt-6.20.0/src/mlt++/MltTokeniser.h000066400000000000000000000024071362234133600167030ustar00rootroot00000000000000/** * MltTokeniser.h - MLT Wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_TOKENISER_H #define MLTPP_TOKENISER_H #include "MltConfig.h" #include namespace Mlt { class MLTPP_DECLSPEC Tokeniser { private: mlt_tokeniser tokens; public: Tokeniser( char *text = NULL, char *delimiter = NULL ); ~Tokeniser( ); int parse( char *text, char *delimiter = NULL ); int count( ); char *get( int index ); char *input( ); }; } #endif mlt-6.20.0/src/mlt++/MltTractor.cpp000066400000000000000000000107621362234133600167140ustar00rootroot00000000000000/** * MltTractor.cpp - Tractor wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "MltTractor.h" #include "MltMultitrack.h" #include "MltField.h" #include "MltTransition.h" #include "MltFilter.h" #include "MltPlaylist.h" #include "MltProfile.h" using namespace Mlt; Tractor::Tractor( ) : instance( mlt_tractor_new( ) ) { } Tractor::Tractor( Profile& profile ) : instance( mlt_tractor_new( ) ) { set_profile( profile ); } Tractor::Tractor( Service &tractor ) : instance( NULL ) { if ( tractor.type( ) == tractor_type ) { instance = ( mlt_tractor )tractor.get_service( ); inc_ref( ); } } Tractor::Tractor( mlt_tractor tractor ) : instance( tractor ) { inc_ref( ); } Tractor::Tractor( Tractor &tractor ) : Mlt::Producer( tractor ), instance( tractor.get_tractor( ) ) { inc_ref( ); } Tractor::Tractor( Profile& profile, char *id, char *resource ) : Tractor( profile.get_profile(), id, resource ) { } Tractor::Tractor( mlt_profile profile, char *id, char *resource ) : instance( NULL ) { Producer producer( profile, id, resource ); if ( producer.is_valid( ) && producer.type( ) == tractor_type ) { instance = ( mlt_tractor )producer.get_producer( ); inc_ref( ); } else if ( producer.is_valid( ) ) { instance = mlt_tractor_new( ); set_profile( profile ); set_track( producer, 0 ); } } Tractor::~Tractor( ) { mlt_tractor_close( instance ); } mlt_tractor Tractor::get_tractor( ) { return instance; } mlt_producer Tractor::get_producer( ) { return mlt_tractor_producer( get_tractor( ) ); } Multitrack *Tractor::multitrack( ) { return new Multitrack( mlt_tractor_multitrack( get_tractor( ) ) ); } Field *Tractor::field( ) { return new Field( mlt_tractor_field( get_tractor( ) ) ); } void Tractor::refresh( ) { return mlt_tractor_refresh( get_tractor( ) ); } int Tractor::set_track( Producer &producer, int index ) { return mlt_tractor_set_track( get_tractor( ), producer.get_producer( ), index ); } int Tractor::insert_track( Producer &producer, int index ) { return mlt_tractor_insert_track( get_tractor( ), producer.get_producer( ), index ); } int Tractor::remove_track( int index ) { return mlt_tractor_remove_track( get_tractor(), index ); } Producer *Tractor::track( int index ) { mlt_producer producer = mlt_tractor_get_track( get_tractor( ), index ); return producer != NULL ? new Producer( producer ) : NULL; } int Tractor::count( ) { return mlt_multitrack_count( mlt_tractor_multitrack( get_tractor( ) ) ); } void Tractor::plant_transition( Transition &transition, int a_track, int b_track ) { mlt_field_plant_transition( mlt_tractor_field( get_tractor( ) ), transition.get_transition( ), a_track, b_track ); } void Tractor::plant_transition( Transition *transition, int a_track, int b_track ) { if ( transition != NULL ) mlt_field_plant_transition( mlt_tractor_field( get_tractor( ) ), transition->get_transition( ), a_track, b_track ); } void Tractor::plant_filter( Filter &filter, int track ) { mlt_field_plant_filter( mlt_tractor_field( get_tractor( ) ), filter.get_filter( ), track ); } void Tractor::plant_filter( Filter *filter, int track ) { mlt_field_plant_filter( mlt_tractor_field( get_tractor( ) ), filter->get_filter( ), track ); } bool Tractor::locate_cut( Producer *producer, int &track, int &cut ) { bool found = false; for ( track = 0; producer != NULL && !found && track < count( ); track ++ ) { Playlist playlist( ( mlt_playlist )mlt_tractor_get_track( get_tractor( ), track ) ); for ( cut = 0; !found && cut < playlist.count( ); cut ++ ) { Producer *clip = playlist.get_clip( cut ); found = producer->get_producer( ) == clip->get_producer( ); delete clip; } } track --; cut --; return found; } int Tractor::connect( Producer &producer ) { return mlt_tractor_connect( get_tractor( ), producer.get_service( ) ); } mlt-6.20.0/src/mlt++/MltTractor.h000066400000000000000000000043041362234133600163540ustar00rootroot00000000000000/** * MltTractor.h - Tractor wrapper * Copyright (C) 2004-2015 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_TRACTOR_H #define MLTPP_TRACTOR_H #include "MltConfig.h" #include #include "MltProducer.h" namespace Mlt { class Producer; class Field; class Multitrack; class Transition; class Filter; class Profile; class MLTPP_DECLSPEC Tractor : public Producer { private: mlt_tractor instance; public: Tractor( ); Tractor( Profile& profile ); Tractor( Service &tractor ); Tractor( mlt_tractor tractor ); Tractor( Tractor &tractor ); Tractor( Profile& profile, char *id, char *arg = NULL ); Tractor( mlt_profile profile, char *id, char *arg = NULL ); virtual ~Tractor( ); virtual mlt_tractor get_tractor( ); mlt_producer get_producer( ); Multitrack *multitrack( ); Field *field( ); void refresh( ); int set_track( Producer &producer, int index ); int insert_track( Producer &producer, int index ); int remove_track( int index ); Producer *track( int index ); int count( ); void plant_transition( Transition &transition, int a_track = 0, int b_track = 1 ); void plant_transition( Transition *transition, int a_track = 0, int b_track = 1 ); void plant_filter( Filter &filter, int track = 0 ); void plant_filter( Filter *filter, int track = 0 ); bool locate_cut( Producer *producer, int &track, int &cut ); int connect( Producer &producer ); }; } #endif mlt-6.20.0/src/mlt++/MltTransition.cpp000066400000000000000000000077071362234133600174350ustar00rootroot00000000000000/** * MltTransition.cpp - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "MltTransition.h" #include "MltProfile.h" #include "MltProducer.h" using namespace Mlt; Transition::Transition() : Service() , instance(nullptr) { } Transition::Transition( Profile& profile, const char *id, const char *arg ) : Transition( profile.get_profile(), id, arg ) { } Transition::Transition( mlt_profile profile, const char *id, const char *arg ) : instance( NULL ) { if ( arg != NULL ) { instance = mlt_factory_transition( profile, id, arg ); } else { if ( strchr( id, ':' ) ) { char *temp = strdup( id ); char *arg = strchr( temp, ':' ) + 1; *( arg - 1 ) = '\0'; instance = mlt_factory_transition( profile, temp, arg ); free( temp ); } else { instance = mlt_factory_transition( profile, id, NULL ); } } } Transition::Transition( Service &transition ) : instance( NULL ) { if ( transition.type( ) == transition_type ) { instance = ( mlt_transition )transition.get_service( ); inc_ref( ); } } Transition::Transition( Transition &transition ) : Mlt::Service( transition ), instance( transition.get_transition( ) ) { inc_ref( ); } Transition::Transition( const Transition &transition ) : Transition(const_cast(transition)) { } Transition::Transition( mlt_transition transition ) : instance( transition ) { inc_ref( ); } Transition::~Transition( ) { mlt_transition_close( instance ); } Transition &Transition::operator=(const Transition &transition) { if (this != &transition) { mlt_transition_close( instance ); instance = transition.instance; inc_ref( ); } return *this; } mlt_transition Transition::get_transition( ) { return instance; } mlt_service Transition::get_service( ) { return mlt_transition_service( get_transition( ) ); } void Transition::set_in_and_out( int in, int out ) { mlt_transition_set_in_and_out( get_transition( ), in, out ); } void Transition::set_tracks( int a_track, int b_track ) { mlt_transition_set_tracks( get_transition(), a_track, b_track ); } int Transition::connect( Producer &producer, int a_track, int b_track ) { return mlt_transition_connect( get_transition(), producer.get_service(), a_track, b_track ); } int Transition::connect(Service &service, int a_track, int b_track) { return mlt_transition_connect( get_transition(), service.get_service(), a_track, b_track ); } int Transition::get_a_track( ) { return mlt_transition_get_a_track( get_transition() ); } int Transition::get_b_track( ) { return mlt_transition_get_b_track( get_transition() ); } int Transition::get_in( ) { return mlt_transition_get_in( get_transition() ); } int Transition::get_out( ) { return mlt_transition_get_out( get_transition() ); } int Transition::get_length( ) { return mlt_transition_get_length( get_transition( ) ); } int Transition::get_position( Frame &frame ) { return mlt_transition_get_position( get_transition( ), frame.get_frame( ) ); } double Transition::get_progress( Frame &frame ) { return mlt_transition_get_progress( get_transition( ), frame.get_frame( ) ); } double Transition::get_progress_delta( Frame &frame ) { return mlt_transition_get_progress_delta( get_transition( ), frame.get_frame( ) ); } mlt-6.20.0/src/mlt++/MltTransition.h000066400000000000000000000040441362234133600170710ustar00rootroot00000000000000/** * MltTransition.h - MLT Wrapper * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLTPP_TRANSITION_H #define MLTPP_TRANSITION_H #include "MltConfig.h" #include #include "MltService.h" namespace Mlt { class Service; class Profile; class Frame; class MLTPP_DECLSPEC Transition : public Service { private: mlt_transition instance; public: Transition(); Transition( Profile& profile, const char *id, const char *arg = NULL ); Transition( mlt_profile profile, const char *id, const char *arg = NULL ); Transition( Service &transition ); Transition( Transition &transition ); Transition( const Transition &transition ); Transition( mlt_transition transition ); virtual ~Transition( ); Transition& operator=( const Transition &transition ); virtual mlt_transition get_transition( ); mlt_service get_service( ); void set_in_and_out( int in, int out ); void set_tracks( int a_track, int b_track ); int connect( Producer &producer, int a_track, int b_track ); int connect( Service &service, int a_track, int b_track ); int get_a_track( ); int get_b_track( ); int get_in( ); int get_out( ); int get_length( ); int get_position( Frame &frame ); double get_progress( Frame &frame ); double get_progress_delta( Frame &frame ); }; } #endif mlt-6.20.0/src/mlt++/configure000077500000000000000000000012271362234133600160200ustar00rootroot00000000000000#!/bin/sh echo "soversion=3" > config.mak echo "mlt++ -I$prefix/include -I$prefix/include/mlt++ -D_REENTRANT -L$libdir -lmlt++" >> ../../packages.dat WARNINGS="-W -Wwrite-strings -Wcast-qual -Wpointer-arith -Wcast-align -Wredundant-decls" case $targetos in Darwin) echo LIBSUF=.dylib echo "CXXFLAGS+=-Wall -fPIC" echo "LIBFLAGS=-dynamiclib -single_module" ;; Linux|FreeBSD|NetBSD|GNU/kFreeBSD|GNU) echo LIBSUF=.so echo "CXXFLAGS+=-Wall $WARNINGS -fPIC -DPIC" echo "LIBFLAGS=-shared" ;; MinGW) echo LIBSUF=.dll echo "CXXFLAGS+=-Wall $WARNINGS -DPIC" echo "LIBFLAGS=-Wl,-enable-auto-import -shared" ;; esac >> config.mak mlt-6.20.0/src/mlt++/mlt++.pc.in000066400000000000000000000005521362234133600157640ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ datadir=@CMAKE_INSTALL_FULL_DATADIR@ Name: mlt++ Description: C++ API for MLT multimedia framework Version: @MLT_VERSION@ Requires: mlt-framework Libs: -L${libdir} -lmlt++ Cflags: -I${includedir} -I${includedir}/mlt++ mlt-6.20.0/src/mlt++/mlt++.vers000066400000000000000000000605101362234133600157340ustar00rootroot00000000000000MLTPP_0.8.8 { global: extern "C++" { "Mlt::ClipInfo::~ClipInfo()"; "Mlt::ClipInfo::ClipInfo()"; "Mlt::ClipInfo::ClipInfo(mlt_playlist_clip_info*)"; "Mlt::ClipInfo::update(mlt_playlist_clip_info*)"; "typeinfo for Mlt::Consumer"; "typeinfo name for Mlt::Consumer"; "vtable for Mlt::Consumer"; "Mlt::Consumer::connect(Mlt::Service&)"; "Mlt::Consumer::~Consumer()"; "Mlt::Consumer::Consumer()"; "Mlt::Consumer::Consumer(Mlt::Consumer&)"; "Mlt::Consumer::Consumer(mlt_consumer_s*)"; "Mlt::Consumer::Consumer(Mlt::Profile&)"; "Mlt::Consumer::Consumer(Mlt::Profile&, char const*, char const*)"; "Mlt::Consumer::Consumer(Mlt::Service&)"; "Mlt::Consumer::get_consumer()"; "Mlt::Consumer::get_service()"; "Mlt::Consumer::is_stopped()"; "Mlt::Consumer::position()"; "Mlt::Consumer::purge()"; "Mlt::Consumer::run()"; "Mlt::Consumer::start()"; "Mlt::Consumer::stop()"; "Mlt::Deque::count()"; "Mlt::Deque::~Deque()"; "Mlt::Deque::Deque()"; "Mlt::Deque::peek_back()"; "Mlt::Deque::peek_front()"; "Mlt::Deque::pop_back()"; "Mlt::Deque::pop_front()"; "Mlt::Deque::push_back(void*)"; "Mlt::Deque::push_front(void*)"; "Mlt::Event::block()"; "Mlt::Event::~Event()"; "Mlt::Event::Event(Mlt::Event&)"; "Mlt::Event::Event(mlt_event_struct*)"; "Mlt::Event::get_event()"; "Mlt::Event::is_valid()"; "Mlt::Event::unblock()"; "Mlt::Factory::close()"; "Mlt::Factory::consumer(Mlt::Profile&, char*, char*)"; "Mlt::Factory::event_object()"; "Mlt::Factory::filter(Mlt::Profile&, char*, char*)"; "Mlt::Factory::init(char const*)"; "Mlt::Factory::producer(Mlt::Profile&, char*, char*)"; "Mlt::Factory::transition(Mlt::Profile&, char*, char*)"; "typeinfo for Mlt::Field"; "typeinfo name for Mlt::Field"; "vtable for Mlt::Field"; "Mlt::Field::disconnect_service(Mlt::Service&)"; "Mlt::Field::~Field()"; "Mlt::Field::Field(Mlt::Field&)"; "Mlt::Field::Field(mlt_field_s*)"; "Mlt::Field::get_field()"; "Mlt::Field::get_service()"; "Mlt::Field::plant_filter(Mlt::Filter&, int)"; "Mlt::Field::plant_transition(Mlt::Transition&, int, int)"; "typeinfo for Mlt::FilteredConsumer"; "typeinfo name for Mlt::FilteredConsumer"; "vtable for Mlt::FilteredConsumer"; "Mlt::FilteredConsumer::attach(Mlt::Filter&)"; "Mlt::FilteredConsumer::connect(Mlt::Service&)"; "Mlt::FilteredConsumer::detach(Mlt::Filter&)"; "Mlt::FilteredConsumer::~FilteredConsumer()"; "Mlt::FilteredConsumer::FilteredConsumer(Mlt::Consumer&)"; "Mlt::FilteredConsumer::FilteredConsumer(Mlt::Profile&, char const*, char const*)"; "Mlt::FilteredConsumer::last(Mlt::Filter&)"; "typeinfo for Mlt::FilteredProducer"; "typeinfo name for Mlt::FilteredProducer"; "vtable for Mlt::FilteredProducer"; "Mlt::FilteredProducer::attach(Mlt::Filter&)"; "Mlt::FilteredProducer::detach(Mlt::Filter&)"; "Mlt::FilteredProducer::~FilteredProducer()"; "Mlt::FilteredProducer::FilteredProducer(Mlt::Profile&, char const*, char const*)"; "typeinfo for Mlt::Filter"; "typeinfo name for Mlt::Filter"; "vtable for Mlt::Filter"; "Mlt::Filter::connect(Mlt::Service&, int)"; "Mlt::Filter::~Filter()"; "Mlt::Filter::Filter(Mlt::Filter&)"; "Mlt::Filter::Filter(mlt_filter_s*)"; "Mlt::Filter::Filter(Mlt::Profile&, char const*, char const*)"; "Mlt::Filter::Filter(Mlt::Service&)"; "Mlt::Filter::get_filter()"; "Mlt::Filter::get_in()"; "Mlt::Filter::get_length()"; "Mlt::Filter::get_length2(Mlt::Frame&)"; "Mlt::Filter::get_out()"; "Mlt::Filter::get_position(Mlt::Frame&)"; "Mlt::Filter::get_progress(Mlt::Frame&)"; "Mlt::Filter::get_service()"; "Mlt::Filter::get_track()"; "Mlt::Filter::set_in_and_out(int, int)"; "typeinfo for Mlt::Frame"; "typeinfo name for Mlt::Frame"; "vtable for Mlt::Frame"; "Mlt::Frame::fetch_image(mlt_image_format, int, int, int)"; "Mlt::Frame::~Frame()"; "Mlt::Frame::Frame(Mlt::Frame&)"; "Mlt::Frame::Frame(mlt_frame_s*)"; "Mlt::Frame::get_audio(mlt_audio_format&, int&, int&, int&)"; "Mlt::Frame::get_frame()"; "Mlt::Frame::get_image(mlt_image_format&, int&, int&, int)"; "Mlt::Frame::get_original_producer()"; "Mlt::Frame::get_position()"; "Mlt::Frame::get_properties()"; "Mlt::Frame::get_unique_properties(Mlt::Service&)"; "Mlt::Frame::get_waveform(int, int)"; "Mlt::Frame::set_alpha(unsigned char*, int, void (*)(void*))"; "Mlt::Frame::set_image(unsigned char*, int, void (*)(void*))"; "Mlt::Geometry::fetch(Mlt::GeometryItem*, float)"; "Mlt::Geometry::fetch(Mlt::GeometryItem&, float)"; "Mlt::Geometry::~Geometry()"; "Mlt::Geometry::Geometry(char*, int, int, int)"; "Mlt::Geometry::insert(Mlt::GeometryItem*)"; "Mlt::Geometry::insert(Mlt::GeometryItem&)"; "Mlt::Geometry::interpolate()"; "Mlt::Geometry::next_key(Mlt::GeometryItem*, int)"; "Mlt::Geometry::next_key(Mlt::GeometryItem&, int)"; "Mlt::Geometry::parse(char*, int, int, int)"; "Mlt::Geometry::prev_key(Mlt::GeometryItem*, int)"; "Mlt::Geometry::prev_key(Mlt::GeometryItem&, int)"; "Mlt::Geometry::remove(int)"; "Mlt::Geometry::serialise()"; "Mlt::Geometry::serialise(int, int)"; "typeinfo for Mlt::Multitrack"; "typeinfo name for Mlt::Multitrack"; "vtable for Mlt::Multitrack"; "Mlt::Multitrack::clip(mlt_whence, int)"; "Mlt::Multitrack::connect(Mlt::Producer&, int)"; "Mlt::Multitrack::count()"; "Mlt::Multitrack::get_multitrack()"; "Mlt::Multitrack::get_producer()"; "Mlt::Multitrack::~Multitrack()"; "Mlt::Multitrack::Multitrack(Mlt::Multitrack&)"; "Mlt::Multitrack::Multitrack(mlt_multitrack_s*)"; "Mlt::Multitrack::Multitrack(Mlt::Service&)"; "Mlt::Multitrack::refresh()"; "Mlt::Multitrack::track(int)"; "typeinfo for Mlt::Parser"; "typeinfo name for Mlt::Parser"; "vtable for Mlt::Parser"; "Mlt::Parser::get_properties()"; "Mlt::Parser::on_end_filter(Mlt::Filter*)"; "Mlt::Parser::on_end_multitrack(Mlt::Multitrack*)"; "Mlt::Parser::on_end_playlist(Mlt::Playlist*)"; "Mlt::Parser::on_end_producer(Mlt::Producer*)"; "Mlt::Parser::on_end_track()"; "Mlt::Parser::on_end_tractor(Mlt::Tractor*)"; "Mlt::Parser::on_end_transition(Mlt::Transition*)"; "Mlt::Parser::on_invalid(Mlt::Service*)"; "Mlt::Parser::on_start_filter(Mlt::Filter*)"; "Mlt::Parser::on_start_multitrack(Mlt::Multitrack*)"; "Mlt::Parser::on_start_playlist(Mlt::Playlist*)"; "Mlt::Parser::on_start_producer(Mlt::Producer*)"; "Mlt::Parser::on_start_track()"; "Mlt::Parser::on_start_tractor(Mlt::Tractor*)"; "Mlt::Parser::on_start_transition(Mlt::Transition*)"; "Mlt::Parser::on_unknown(Mlt::Service*)"; "Mlt::Parser::~Parser()"; "Mlt::Parser::Parser()"; "Mlt::Parser::start(Mlt::Service&)"; "typeinfo for Mlt::Playlist"; "typeinfo name for Mlt::Playlist"; "vtable for Mlt::Playlist"; "Mlt::Playlist::append(Mlt::Producer&, int, int)"; "Mlt::Playlist::blank(char const*)"; "Mlt::Playlist::blank(int)"; "Mlt::Playlist::blanks_from(int, int)"; "Mlt::Playlist::clear()"; "Mlt::Playlist::clip_info(int, Mlt::ClipInfo*)"; "Mlt::Playlist::clip_length(int)"; "Mlt::Playlist::clip(mlt_whence, int)"; "Mlt::Playlist::clip_start(int)"; "Mlt::Playlist::consolidate_blanks(int)"; "Mlt::Playlist::count()"; "Mlt::Playlist::current()"; "Mlt::Playlist::current_clip()"; "Mlt::Playlist::delete_clip_info(Mlt::ClipInfo*)"; "Mlt::Playlist::get_clip_at(int)"; "Mlt::Playlist::get_clip_index_at(int)"; "Mlt::Playlist::get_clip(int)"; "Mlt::Playlist::get_playlist()"; "Mlt::Playlist::get_producer()"; "Mlt::Playlist::insert_at(int, Mlt::Producer*, int)"; "Mlt::Playlist::insert_at(int, Mlt::Producer&, int)"; "Mlt::Playlist::insert_blank(int, int)"; "Mlt::Playlist::insert(Mlt::Producer&, int, int, int)"; "Mlt::Playlist::is_blank_at(int)"; "Mlt::Playlist::is_blank(int)"; "Mlt::Playlist::is_mix(int)"; "Mlt::Playlist::join(int, int, int)"; "Mlt::Playlist::mix_add(int, Mlt::Transition*)"; "Mlt::Playlist::mix(int, int, Mlt::Transition*)"; "Mlt::Playlist::move(int, int)"; "Mlt::Playlist::move_region(int, int, int)"; "Mlt::Playlist::pad_blanks(int, int, int)"; "Mlt::Playlist::~Playlist()"; "Mlt::Playlist::Playlist()"; "Mlt::Playlist::Playlist(Mlt::Playlist&)"; "Mlt::Playlist::Playlist(mlt_playlist_s*)"; "Mlt::Playlist::Playlist(Mlt::Profile&)"; "Mlt::Playlist::Playlist(Mlt::Service&)"; "Mlt::Playlist::remove(int)"; "Mlt::Playlist::remove_region(int, int)"; "Mlt::Playlist::repeat(int, int)"; "Mlt::Playlist::replace_with_blank(int)"; "Mlt::Playlist::resize_clip(int, int, int)"; "Mlt::Playlist::split_at(int, bool)"; "Mlt::Playlist::split(int, int)"; "typeinfo for Mlt::Producer"; "typeinfo name for Mlt::Producer"; "vtable for Mlt::Producer"; "Mlt::Producer::clear()"; "Mlt::Producer::cut(int, int)"; "Mlt::Producer::frame()"; "Mlt::Producer::frame_time(mlt_time_format)"; "Mlt::Producer::get_fps()"; "Mlt::Producer::get_in()"; "Mlt::Producer::get_length()"; "Mlt::Producer::get_length_time(mlt_time_format)"; "Mlt::Producer::get_out()"; "Mlt::Producer::get_parent()"; "Mlt::Producer::get_playtime()"; "Mlt::Producer::get_producer()"; "Mlt::Producer::get_service()"; "Mlt::Producer::get_speed()"; "Mlt::Producer::is_blank()"; "Mlt::Producer::is_cut()"; "Mlt::Producer::optimise()"; "Mlt::Producer::parent()"; "Mlt::Producer::pause()"; "Mlt::Producer::position()"; "Mlt::Producer::~Producer()"; "Mlt::Producer::Producer()"; "Mlt::Producer::Producer(Mlt::Producer*)"; "Mlt::Producer::Producer(Mlt::Producer&)"; "Mlt::Producer::Producer(mlt_producer_s*)"; "Mlt::Producer::Producer(Mlt::Profile&, char const*, char const*)"; "Mlt::Producer::Producer(Mlt::Service&)"; "Mlt::Producer::runs_into(Mlt::Producer&)"; "Mlt::Producer::same_clip(Mlt::Producer&)"; "Mlt::Producer::seek(char const*)"; "Mlt::Producer::seek(int)"; "Mlt::Producer::set_in_and_out(int, int)"; "Mlt::Producer::set_speed(double)"; "Mlt::Profile::colorspace() const"; "Mlt::Profile::dar() const"; "Mlt::Profile::description() const"; "Mlt::Profile::display_aspect_den() const"; "Mlt::Profile::display_aspect_num() const"; "Mlt::Profile::fps() const"; "Mlt::Profile::frame_rate_den() const"; "Mlt::Profile::frame_rate_num() const"; "Mlt::Profile::from_producer(Mlt::Producer&)"; "Mlt::Profile::get_profile() const"; "Mlt::Profile::height() const"; "Mlt::Profile::is_explicit() const"; "Mlt::Profile::list()"; "Mlt::Profile::~Profile()"; "Mlt::Profile::Profile()"; "Mlt::Profile::Profile(char const*)"; "Mlt::Profile::Profile(mlt_profile_s*)"; "Mlt::Profile::Profile(Mlt::Properties&)"; "Mlt::Profile::progressive() const"; "Mlt::Profile::sample_aspect_den() const"; "Mlt::Profile::sample_aspect_num() const"; "Mlt::Profile::sar() const"; "Mlt::Profile::set_colorspace(int)"; "Mlt::Profile::set_explicit(int)"; "Mlt::Profile::set_frame_rate(int, int)"; "Mlt::Profile::set_height(int)"; "Mlt::Profile::set_progressive(int)"; "Mlt::Profile::set_sample_aspect(int, int)"; "Mlt::Profile::set_width(int)"; "Mlt::Profile::width() const"; "typeinfo for Mlt::Properties"; "typeinfo name for Mlt::Properties"; "vtable for Mlt::Properties"; "Mlt::Properties::block(void*)"; "Mlt::Properties::count()"; "Mlt::Properties::debug(char const*, _IO_FILE*)"; "Mlt::Properties::dec_ref()"; "Mlt::Properties::delete_event(Mlt::Event*)"; "Mlt::Properties::dump(_IO_FILE*)"; "Mlt::Properties::fire_event(char const*)"; "Mlt::Properties::get(char const*)"; "Mlt::Properties::get_data(char const*)"; "Mlt::Properties::get_data(char const*, int&)"; "Mlt::Properties::get_data(int, int&)"; "Mlt::Properties::get_double(char const*)"; "Mlt::Properties::get(int)"; "Mlt::Properties::get_int64(char const*)"; "Mlt::Properties::get_int(char const*)"; "Mlt::Properties::get_lcnumeric()"; "Mlt::Properties::get_name(int)"; "Mlt::Properties::get_properties()"; "Mlt::Properties::get_time(char const*, mlt_time_format)"; "Mlt::Properties::inc_ref()"; "Mlt::Properties::inherit(Mlt::Properties&)"; "Mlt::Properties::is_sequence()"; "Mlt::Properties::is_valid()"; "Mlt::Properties::listen(char const*, void*, void (*)(void*, ...))"; "Mlt::Properties::load(char const*)"; "Mlt::Properties::lock()"; "Mlt::Properties::mirror(Mlt::Properties&)"; "Mlt::Properties::parse(char const*)"; "Mlt::Properties::parse_yaml(char const*)"; "Mlt::Properties::pass_list(Mlt::Properties&, char const*)"; "Mlt::Properties::pass_property(Mlt::Properties&, char const*)"; "Mlt::Properties::pass_values(Mlt::Properties&, char const*)"; "Mlt::Properties::preset(char const*)"; "Mlt::Properties::~Properties()"; "Mlt::Properties::Properties()"; "Mlt::Properties::Properties(bool)"; "Mlt::Properties::Properties(char const*)"; "Mlt::Properties::Properties(Mlt::Properties&)"; "Mlt::Properties::Properties(mlt_properties_s*)"; "Mlt::Properties::Properties(void*)"; "Mlt::Properties::ref_count()"; "Mlt::Properties::rename(char const*, char const*)"; "Mlt::Properties::save(char const*)"; "Mlt::Properties::serialise_yaml()"; "Mlt::Properties::set(char const*, char const*)"; "Mlt::Properties::set(char const*, double)"; "Mlt::Properties::set(char const*, int)"; "Mlt::Properties::set(char const*, long)"; "Mlt::Properties::set(char const*, long long)"; "Mlt::Properties::set(char const*, void*, int, void (*)(void*), char* (*)(void*, int))"; "Mlt::Properties::set_lcnumeric(char const*)"; "Mlt::Properties::setup_wait_for(char const*)"; "Mlt::Properties::unblock(void*)"; "Mlt::Properties::unlock()"; "Mlt::Properties::wait_for(char const*)"; "Mlt::Properties::wait_for(Mlt::Event*, bool)"; "typeinfo for Mlt::PushConsumer"; "typeinfo name for Mlt::PushConsumer"; "vtable for Mlt::PushConsumer"; "Mlt::PushConsumer::connect(Mlt::Service&)"; "Mlt::PushConsumer::construct(int)"; "Mlt::PushConsumer::drain()"; "Mlt::PushConsumer::~PushConsumer()"; "Mlt::PushConsumer::PushConsumer(Mlt::Profile&, char const*, char const*)"; "Mlt::PushConsumer::push(Mlt::Frame*)"; "Mlt::PushConsumer::push(Mlt::Frame&)"; "Mlt::PushConsumer::set_render(int, int, double)"; "Mlt::Repository::consumers() const"; "Mlt::Repository::create(Mlt::Profile&, mlt_service_type, char const*, void*)"; "Mlt::Repository::filters() const"; "Mlt::Repository::languages() const"; "Mlt::Repository::metadata(mlt_service_type, char const*) const"; "Mlt::Repository::presets()"; "Mlt::Repository::producers() const"; "Mlt::Repository::register_metadata(mlt_service_type, char const*, mlt_properties_s* (*)(mlt_service_type, char const*, void*), void*)"; "Mlt::Repository::register_service(mlt_service_type, char const*, void* (*)(mlt_profile_s*, mlt_service_type, char const*, void const*))"; "Mlt::Repository::~Repository()"; "Mlt::Repository::Repository(char const*)"; "Mlt::Repository::Repository(mlt_repository_s*)"; "Mlt::Repository::transitions() const"; "typeinfo for Mlt::Service"; "typeinfo name for Mlt::Service"; "vtable for Mlt::Service"; "Mlt::Service::attach(Mlt::Filter&)"; "Mlt::Service::connect_producer(Mlt::Service&, int)"; "Mlt::Service::consumer()"; "Mlt::Service::detach(Mlt::Filter&)"; "Mlt::Service::filter(int)"; "Mlt::Service::get_frame(int)"; "Mlt::Service::get_profile()"; "Mlt::Service::get_properties()"; "Mlt::Service::get_service()"; "Mlt::Service::lock()"; "Mlt::Service::producer()"; "Mlt::Service::profile()"; "Mlt::Service::~Service()"; "Mlt::Service::Service()"; "Mlt::Service::Service(Mlt::Service&)"; "Mlt::Service::Service(mlt_service_s*)"; "Mlt::Service::set_profile(Mlt::Profile&)"; "Mlt::Service::type()"; "Mlt::Service::unlock()"; "Mlt::Tokeniser::count()"; "Mlt::Tokeniser::get(int)"; "Mlt::Tokeniser::input()"; "Mlt::Tokeniser::parse(char*, char*)"; "Mlt::Tokeniser::~Tokeniser()"; "Mlt::Tokeniser::Tokeniser(char*, char*)"; "typeinfo for Mlt::Tractor"; "typeinfo name for Mlt::Tractor"; "vtable for Mlt::Tractor"; "Mlt::Tractor::connect(Mlt::Producer&)"; "Mlt::Tractor::count()"; "Mlt::Tractor::field()"; "Mlt::Tractor::get_producer()"; "Mlt::Tractor::get_tractor()"; "Mlt::Tractor::locate_cut(Mlt::Producer*, int&, int&)"; "Mlt::Tractor::multitrack()"; "Mlt::Tractor::plant_filter(Mlt::Filter*, int)"; "Mlt::Tractor::plant_filter(Mlt::Filter&, int)"; "Mlt::Tractor::plant_transition(Mlt::Transition*, int, int)"; "Mlt::Tractor::plant_transition(Mlt::Transition&, int, int)"; "Mlt::Tractor::refresh()"; "Mlt::Tractor::set_track(Mlt::Producer&, int)"; "Mlt::Tractor::track(int)"; "Mlt::Tractor::~Tractor()"; "Mlt::Tractor::Tractor()"; "Mlt::Tractor::Tractor(Mlt::Profile&, char*, char*)"; "Mlt::Tractor::Tractor(Mlt::Service&)"; "Mlt::Tractor::Tractor(Mlt::Tractor&)"; "Mlt::Tractor::Tractor(mlt_tractor_s*)"; "typeinfo for Mlt::Transition"; "typeinfo name for Mlt::Transition"; "vtable for Mlt::Transition"; "Mlt::Transition::connect(Mlt::Producer&, int, int)"; "Mlt::Transition::get_a_track()"; "Mlt::Transition::get_b_track()"; "Mlt::Transition::get_in()"; "Mlt::Transition::get_length()"; "Mlt::Transition::get_out()"; "Mlt::Transition::get_position(Mlt::Frame&)"; "Mlt::Transition::get_progress_delta(Mlt::Frame&)"; "Mlt::Transition::get_progress(Mlt::Frame&)"; "Mlt::Transition::get_service()"; "Mlt::Transition::get_transition()"; "Mlt::Transition::set_in_and_out(int, int)"; "Mlt::Transition::~Transition()"; "Mlt::Transition::Transition(Mlt::Profile&, char const*, char const*)"; "Mlt::Transition::Transition(Mlt::Service&)"; "Mlt::Transition::Transition(Mlt::Transition&)"; "Mlt::Transition::Transition(mlt_transition_s*)"; }; local: *; }; MLTPP_0.9.0 { global: extern "C++" { "Mlt::Deque::peek(int)"; "Mlt::Properties::anim_get(char const*, int, int)"; "Mlt::Properties::anim_get_double(char const*, int, int)"; "Mlt::Properties::anim_get_int(char const*, int, int)"; "Mlt::Properties::anim_get_rect(char const*, int, int)"; "Mlt::Properties::anim_set(char const*, char const*, int, int)"; "Mlt::Properties::anim_set(char const*, double, int, int, mlt_keyframe_type)"; "Mlt::Properties::anim_set(char const*, int, int, int, mlt_keyframe_type)"; "Mlt::Properties::anim_set(char const*, mlt_rect, int, int, mlt_keyframe_type)"; "Mlt::Properties::get_color(char const*)"; "Mlt::Properties::get_rect(char const*)"; "Mlt::Properties::set(char const*, double, double, double, double, double)"; "Mlt::Properties::set(char const*, mlt_color)"; "Mlt::Properties::set(char const*, mlt_rect)"; "Mlt::Service::filter_count()"; "Mlt::Service::move_filter(int, int)"; }; } MLTPP_0.8.8; MLTPP_0.9.2 { global: extern "C++" { "Mlt::Playlist::mix_in(int, int)"; "Mlt::Playlist::mix_out(int, int)"; "Mlt::Properties::frames_to_time(int, mlt_time_format)"; "Mlt::Properties::time_to_frames(char const*)"; }; } MLTPP_0.9.0; MLTPP_0.9.4 { global: extern "C++" { "Mlt::Profile::set_display_aspect(int, int)"; "Mlt::Tractor::Tractor(Mlt::Profile&)"; "Mlt::Frame::Frame()"; "Mlt::Frame::Frame(Mlt::Frame const&)"; "Mlt::Frame::operator=(Mlt::Frame const&)"; "Mlt::Filter::process(Mlt::Frame&)"; }; } MLTPP_0.9.2; MLTPP_0.9.8 { global: extern "C++" { "Mlt::Multitrack::disconnect(int)"; "Mlt::Service::disconnect_producer(int)"; "Mlt::Tractor::remove_track(int)"; "Mlt::Service::insert_producer(Mlt::Service&, int)"; "Mlt::Multitrack::insert(Mlt::Producer&, int)"; "Mlt::Tractor::insert_track(Mlt::Producer&, int)"; "Mlt::Transition::set_tracks(int, int)"; "Mlt::Properties::get_animation(char const*)"; "Mlt::Properties::get_anim(char const*)"; "Mlt::Animation::Animation()"; "Mlt::Animation::Animation(mlt_animation_s*)"; "Mlt::Animation::Animation(Mlt::Animation const&)"; "Mlt::Animation::~Animation()"; "Mlt::Animation::is_valid() const"; "Mlt::Animation::get_animation() const"; "Mlt::Animation::operator=(Mlt::Animation const&)"; "Mlt::Animation::length()"; "Mlt::Animation::is_key(int)"; "Mlt::Animation::keyframe_type(int)"; "Mlt::Animation::get_item(int, bool&, mlt_keyframe_type&)"; "Mlt::Animation::next_key(int)"; "Mlt::Animation::previous_key(int)"; "Mlt::Animation::key_count()"; "Mlt::Animation::key_get(int, int&, mlt_keyframe_type&)"; "Mlt::Animation::key_get_frame(int)"; "Mlt::Animation::key_get_type(int)"; "Mlt::Animation::set_length(int)"; "Mlt::Animation::remove(int)"; "Mlt::Animation::interpolate()"; "Mlt::Animation::serialize_cut(int, int)"; }; } MLTPP_0.9.4; MLTPP_6.4.0 { global: extern "C++" { "Mlt::Profile::is_valid() const"; }; } MLTPP_0.9.8; MLTPP_6.6.0 { global: extern "C++" { "Mlt::Service::disconnect_all_producers()"; }; } MLTPP_6.4.0; MLTPP_6.8.0 { global: extern "C++" { "Mlt::Animation::key_set_type(int, mlt_keyframe_type)"; "Mlt::Animation::key_set_frame(int, int)"; }; } MLTPP_6.6.0; MLTPP_6.10.0 { global: extern "C++" { "Mlt::Properties::get(int, mlt_time_format)"; "Mlt::Properties::clear(char const*)"; "Mlt::Animation::serialize_cut(mlt_time_format, int, int)"; }; } MLTPP_6.8.0; MLTPP_6.14.0 { global: extern "C++" { "Mlt::Producer::Producer(mlt_profile_s*, char const*, char const*)"; "Mlt::Consumer::Consumer(mlt_profile_s*, char const*, char const*)"; "Mlt::Transition::Transition(mlt_profile_s*, char const*, char const*)"; "Mlt::Filter::Filter(mlt_profile_s*, char const*, char const*)"; "Mlt::Tractor::Tractor(mlt_profile_s*, char*, char*)"; "Mlt::Service::set_profile(mlt_profile_s*)"; "Mlt::Playlist::reorder(int const*)"; "Mlt::Transition::connect(Mlt::Service&, int, int)"; "Mlt::Producer::set_creation_time(long)"; "Mlt::Producer::set_creation_time(long long)"; "Mlt::Producer::get_creation_time()"; }; } MLTPP_6.10.0; MLTPP_6.18.0 { global: extern "C++" { "Mlt::Filter::Filter()"; "Mlt::Filter::Filter(Mlt::Filter const&)"; "Mlt::Filter::operator=(Mlt::Filter const&)"; "Mlt::Producer::Producer(Mlt::Producer const&)"; "Mlt::Producer::operator=(Mlt::Producer const&)"; "Mlt::Properties::Properties(Mlt::Properties const&)"; "Mlt::Properties::operator=(Mlt::Properties const&)"; "Mlt::Service::Service(Mlt::Service const&)"; "Mlt::Service::operator=(Mlt::Service const&)"; "Mlt::Transition::Transition()"; "Mlt::Transition::Transition(Mlt::Transition const&)"; "Mlt::Transition::operator=(Mlt::Transition const&)"; }; } MLTPP_6.14.0; MLTPP_6.20.0 { global: extern "C++" { "Mlt::Profile::scale_width(int)"; "Mlt::Profile::scale_height(int)"; "Mlt::Properties::set_string(char const*, char const*)"; }; } MLTPP_6.18.0; mlt-6.20.0/src/modules/000077500000000000000000000000001362234133600146355ustar00rootroot00000000000000mlt-6.20.0/src/modules/Makefile000066400000000000000000000014471362234133600163030ustar00rootroot00000000000000include ../../config.mak include make.inc all clean depend: list='$(SUBDIRS)'; \ for subdir in $$list; do \ if [ -f $$subdir/Makefile -a ! -f disable-$$subdir -a ! -f $$subdir/deprecated ] ; \ then $(MAKE) -C $$subdir $@ || exit 1; \ fi \ done distclean: rm -f consumers.dat filters.dat producers.dat transitions.dat echo > make.inc list='$(SUBDIRS)'; \ for subdir in $$list; do \ if [ -f $$subdir/Makefile -a ! -f disable-$$subdir -a ! -f $$subdir/deprecated ] ; \ then $(MAKE) -C $$subdir $@ || exit 1; \ fi \ done install: list='$(SUBDIRS)'; \ for subdir in $$list; do \ if [ -f $$subdir/Makefile -a ! -f disable-$$subdir -a ! -f $$subdir/deprecated ] ; \ then $(MAKE) DESTDIR=$(DESTDIR) -C $$subdir $@ || exit 1; \ fi \ done uninstall: rm -rf "$(DESTDIR)$(moduledir)" mlt-6.20.0/src/modules/avformat/000077500000000000000000000000001362234133600164545ustar00rootroot00000000000000mlt-6.20.0/src/modules/avformat/CMakeLists.txt000066400000000000000000000044431362234133600212210ustar00rootroot00000000000000#list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) #find_package(FFmpeg) pkg_check_modules(libavformat IMPORTED_TARGET libavformat) pkg_check_modules(libswscale IMPORTED_TARGET libswscale) pkg_check_modules(libavutil IMPORTED_TARGET libavutil) if( TARGET PkgConfig::libavformat AND TARGET PkgConfig::libswscale AND TARGET PkgConfig::libavutil) set(mltavformat_libs mlt m Threads::Threads PkgConfig::libavformat PkgConfig::libswscale PkgConfig::libavutil) set(mltavformat_defs FILTERS) if(WIN32) list(APPEND mltavformat_defs AVDATADIR="share/ffmpeg/") endif() set(mltavformat_srcs common.c factory.c filter_avcolour_space.c filter_avdeinterlace.c filter_swscale.c) pkg_check_modules(libavcodec IMPORTED_TARGET libavcodec) if(TARGET PkgConfig::libavcodec) list(APPEND mltavformat_libs PkgConfig::libavcodec) list(APPEND mltavformat_defs CODECS) list(APPEND mltavformat_srcs producer_avformat.c consumer_avformat.c) endif() pkg_check_modules(libavfilter IMPORTED_TARGET libavfilter) if(TARGET PkgConfig::libavfilter) list(APPEND mltavformat_libs PkgConfig::libavfilter) list(APPEND mltavformat_defs FILTERS AVFILTER ) list(APPEND mltavformat_srcs filter_avfilter.c) endif() pkg_check_modules(libavdevice IMPORTED_TARGET libavdevice) if(TARGET PkgConfig::libavdevice) list(APPEND mltavformat_libs PkgConfig::libavdevice) list(APPEND mltavformat_defs AVDEVICE) endif() pkg_check_modules(libswresample IMPORTED_TARGET libswresample) if(TARGET PkgConfig::libswresample) list(APPEND mltavformat_libs PkgConfig::libswresample) list(APPEND mltavformat_defs SWRESAMPLE) list(APPEND mltavformat_srcs filter_swresample.c) endif() add_library(mltavformat MODULE ${mltavformat_srcs}) target_link_libraries(mltavformat ${mltavformat_libs}) target_compile_definitions(mltavformat PRIVATE ${mltavformat_defs}) install(TARGETS mltavformat LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) install(FILES blacklist.txt producer_avformat.yml consumer_avformat.yml DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/avformat) endif() mlt-6.20.0/src/modules/avformat/Makefile000066400000000000000000000033021362234133600201120ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread -lm ifneq (, $(shell $(CC) --version | grep -is -e 'Free Software Foundation')) LDFLAGS += -latomic endif include ../../../config.mak include config.mak LDFLAGS += $(EXTRA_LIBS) ifndef CODECS TARGET = ../libmltffmpeg$(LIBSUF) else TARGET = ../libmltavformat$(LIBSUF) endif OBJS = common.o \ factory.o ifdef FILTERS OBJS += filter_avcolour_space.o \ filter_avdeinterlace.o \ filter_swscale.o CFLAGS += -DFILTERS endif ifdef VDPAU CFLAGS += -DVDPAU $(shell pkg-config pkg-config --cflags x11) LDFLAGS += $(LIBDL) $(shell pkg-config pkg-config --libs x11) endif ifdef CODECS OBJS += producer_avformat.o \ consumer_avformat.o CFLAGS += -DCODECS endif ifdef DEVICES CFLAGS += -DAVDEVICE endif ifdef AVFILTER CFLAGS += -DAVFILTER OBJS += filter_avfilter.o endif ifdef SWRESAMPLE OBJS += filter_swresample.o CFLAGS += -DSWRESAMPLE 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) ../libmltffmpeg$(LIBSUF) ../libmltavformat$(LIBSUF) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/avformat" install -m 644 blacklist.txt "$(DESTDIR)$(mltdatadir)/avformat" install -m 644 producer_avformat.yml "$(DESTDIR)$(mltdatadir)/avformat" install -m 644 consumer_avformat.yml "$(DESTDIR)$(mltdatadir)/avformat" uninstall: rm -f "$(DESTDIR)$(moduledir)/libmltavformat$(LIBSUF)" rm -f "$(DESTDIR)$(moduledir)/libmltffmpeg$(LIBSUF)" rm -rf "$(DESTDIR)$(mltdatadir)/avformat" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/avformat/blacklist.txt000066400000000000000000000015221362234133600211650ustar00rootroot00000000000000acopy acrossfade afifo aformat amerge amix amultiply anull apad aperms aresample areverse asendcmd asetnsamples asetpts asetrate asettb astreamselect atempo atrim channelsplit codecview copy cover_rect dejudder detelecine displace extractplanes fifo format fps framerate framestep frei0r hysteresis join interlace ladspa maskedclamp maskedmerge mergeplanes mestimate midequalizer minterpolate mix mpdecimate noformat null overlay palettegen paletteuse pan perms pixdesctest premultiply psnr pullup qp readeia608 readvitc repeatfields remap repeatfields replaygain resample reverse scale scale2ref sendcmd separatefields setdar setfield setparams setpts setsar settb showpalette sidechaincompress sidechaingate silenceremove ssim streamselect surround telecine thumbnail tile tinterlace trim unpremulitply vidstabdetect vidstabtransform vfrdet xstack mlt-6.20.0/src/modules/avformat/common.c000066400000000000000000000213401362234133600201100ustar00rootroot00000000000000/* * common.h * Copyright (C) 2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include int mlt_get_sws_flags(int srcwidth, int srcheight, int srcformat, int dstwidth, int dstheight, int dstformat) { // Use default flags unless there is a reason to use something different. int flags = SWS_BICUBIC | SWS_FULL_CHR_H_INP | SWS_FULL_CHR_H_INT | SWS_ACCURATE_RND; if( srcwidth != dstwidth || srcheight != dstheight ) { // Any resolution change should use default flags return flags; } const AVPixFmtDescriptor* srcDesc = av_pix_fmt_desc_get(srcformat); const AVPixFmtDescriptor* dstDesc = av_pix_fmt_desc_get(dstformat); if( !srcDesc || !dstDesc ) { return flags; } if( (srcDesc->flags & AV_PIX_FMT_FLAG_RGB) != 0 && (dstDesc->flags & AV_PIX_FMT_FLAG_RGB) == 0) { // RGB -> YUV flags = SWS_BICUBIC; flags |= SWS_ACCURATE_RND; // Can improve precision by one bit flags |= SWS_FULL_CHR_H_INT; // Avoids luma reduction. Causes chroma bleeding when used with SWS_BICUBIC } else if( (srcDesc->flags & AV_PIX_FMT_FLAG_RGB) == 0 && (dstDesc->flags & AV_PIX_FMT_FLAG_RGB) != 0) { // YUV -> RGB // Going from lower sampling to full sampling - so pick the closest sample without interpolation flags = SWS_POINT; flags |= SWS_ACCURATE_RND; // Can improve precision by one bit flags |= SWS_FULL_CHR_H_INT; // Avoids luma reduction. Does not cause chroma bleeding when used with SWS_POINT } else if( (srcDesc->flags & AV_PIX_FMT_FLAG_RGB) == 0 && (dstDesc->flags & AV_PIX_FMT_FLAG_RGB) == 0) { // YUV -> YUV if( srcDesc->log2_chroma_w == dstDesc->log2_chroma_w && srcDesc->log2_chroma_h == dstDesc->log2_chroma_h ) { // No chroma subsampling conversion. No interpolation required flags = SWS_POINT; flags |= SWS_ACCURATE_RND; // Can improve precision by one bit } else { // Chroma will be interpolated. Bilinear is suitable. flags = SWS_BILINEAR | SWS_ACCURATE_RND; } } return flags; } int mlt_to_av_sample_format( mlt_audio_format format ) { switch( format ) { case mlt_audio_none: return AV_SAMPLE_FMT_NONE; case mlt_audio_s16: return AV_SAMPLE_FMT_S16; case mlt_audio_s32: return AV_SAMPLE_FMT_S32P; case mlt_audio_float: return AV_SAMPLE_FMT_FLTP; case mlt_audio_s32le: return AV_SAMPLE_FMT_S32; case mlt_audio_f32le: return AV_SAMPLE_FMT_FLT; case mlt_audio_u8: return AV_SAMPLE_FMT_U8; } mlt_log_error( NULL, "[avformat] Unknown audio format: %d\n", format ); return AV_SAMPLE_FMT_NONE; } int64_t mlt_to_av_channel_layout( mlt_channel_layout layout ) { switch( layout ) { case mlt_channel_auto: case mlt_channel_independent: mlt_log_error( NULL, "[avformat] No matching channel layout: %s\n", mlt_channel_layout_name( layout ) ); return 0; case mlt_channel_mono: return AV_CH_LAYOUT_MONO; case mlt_channel_stereo: return AV_CH_LAYOUT_STEREO; case mlt_channel_2p1: return AV_CH_LAYOUT_2POINT1; case mlt_channel_3p0: return AV_CH_LAYOUT_SURROUND; case mlt_channel_3p0_back: return AV_CH_LAYOUT_2_1; case mlt_channel_3p1: return AV_CH_LAYOUT_3POINT1; case mlt_channel_4p0: return AV_CH_LAYOUT_4POINT0; case mlt_channel_quad_back: return AV_CH_LAYOUT_QUAD; case mlt_channel_quad_side: return AV_CH_LAYOUT_2_2; case mlt_channel_5p0: return AV_CH_LAYOUT_5POINT0; case mlt_channel_5p0_back: return AV_CH_LAYOUT_5POINT0_BACK; case mlt_channel_4p1: return AV_CH_LAYOUT_4POINT1; case mlt_channel_5p1: return AV_CH_LAYOUT_5POINT1; case mlt_channel_5p1_back: return AV_CH_LAYOUT_5POINT1_BACK; case mlt_channel_6p0: return AV_CH_LAYOUT_6POINT0; case mlt_channel_6p0_front: return AV_CH_LAYOUT_6POINT0_FRONT; case mlt_channel_hexagonal: return AV_CH_LAYOUT_HEXAGONAL; case mlt_channel_6p1: return AV_CH_LAYOUT_6POINT1; case mlt_channel_6p1_back: return AV_CH_LAYOUT_6POINT1_BACK; case mlt_channel_6p1_front: return AV_CH_LAYOUT_6POINT1_FRONT; case mlt_channel_7p0: return AV_CH_LAYOUT_7POINT0; case mlt_channel_7p0_front: return AV_CH_LAYOUT_7POINT0_FRONT; case mlt_channel_7p1: return AV_CH_LAYOUT_7POINT1; case mlt_channel_7p1_wide_side: return AV_CH_LAYOUT_7POINT1_WIDE; case mlt_channel_7p1_wide_back: return AV_CH_LAYOUT_7POINT1_WIDE_BACK; } mlt_log_error( NULL, "[avformat] Unknown channel configuration: %d\n", layout ); return 0; } mlt_channel_layout av_channel_layout_to_mlt( int64_t layout ) { switch( layout ) { case 0: return mlt_channel_independent; case AV_CH_LAYOUT_MONO: return mlt_channel_mono; case AV_CH_LAYOUT_STEREO: return mlt_channel_stereo; case AV_CH_LAYOUT_STEREO_DOWNMIX: return mlt_channel_stereo; case AV_CH_LAYOUT_2POINT1: return mlt_channel_2p1; case AV_CH_LAYOUT_SURROUND: return mlt_channel_3p0; case AV_CH_LAYOUT_2_1: return mlt_channel_3p0_back; case AV_CH_LAYOUT_3POINT1: return mlt_channel_3p1; case AV_CH_LAYOUT_4POINT0: return mlt_channel_4p0; case AV_CH_LAYOUT_QUAD: return mlt_channel_quad_back; case AV_CH_LAYOUT_2_2: return mlt_channel_quad_side; case AV_CH_LAYOUT_5POINT0: return mlt_channel_5p0; case AV_CH_LAYOUT_5POINT0_BACK: return mlt_channel_5p0_back; case AV_CH_LAYOUT_4POINT1: return mlt_channel_4p1; case AV_CH_LAYOUT_5POINT1: return mlt_channel_5p1; case AV_CH_LAYOUT_5POINT1_BACK: return mlt_channel_5p1_back; case AV_CH_LAYOUT_6POINT0: return mlt_channel_6p0; case AV_CH_LAYOUT_6POINT0_FRONT: return mlt_channel_6p0_front; case AV_CH_LAYOUT_HEXAGONAL: return mlt_channel_hexagonal; case AV_CH_LAYOUT_6POINT1: return mlt_channel_6p1; case AV_CH_LAYOUT_6POINT1_BACK: return mlt_channel_6p1_back; case AV_CH_LAYOUT_6POINT1_FRONT: return mlt_channel_6p1_front; case AV_CH_LAYOUT_7POINT0: return mlt_channel_7p0; case AV_CH_LAYOUT_7POINT0_FRONT: return mlt_channel_7p0_front; case AV_CH_LAYOUT_7POINT1: return mlt_channel_7p1; case AV_CH_LAYOUT_7POINT1_WIDE: return mlt_channel_7p1_wide_side; case AV_CH_LAYOUT_7POINT1_WIDE_BACK: return mlt_channel_7p1_wide_back; } mlt_log_error( NULL, "[avformat] Unknown channel layout: %lu\n", (unsigned long)layout ); return mlt_channel_independent; } mlt_channel_layout mlt_get_channel_layout_or_default( const char* name, int channels ) { mlt_channel_layout layout = mlt_channel_layout_id( name ); if( layout == mlt_channel_auto || ( layout != mlt_channel_independent && mlt_channel_layout_channels( layout ) != channels ) ) { layout = mlt_channel_layout_default( channels ); } return layout; } int mlt_set_luma_transfer( struct SwsContext *context, int src_colorspace, int dst_colorspace, int src_full_range, int dst_full_range ) { const int *src_coefficients = sws_getCoefficients( SWS_CS_DEFAULT ); const int *dst_coefficients = sws_getCoefficients( SWS_CS_DEFAULT ); int brightness = 0; int contrast = 1 << 16; int saturation = 1 << 16; int src_range = src_full_range ? 1 : 0; int dst_range = dst_full_range ? 1 : 0; switch ( src_colorspace ) { case 170: case 470: case 601: case 624: src_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); break; case 240: src_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); break; case 709: src_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); break; default: break; } switch ( dst_colorspace ) { case 170: case 470: case 601: case 624: dst_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); break; case 240: dst_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); break; case 709: dst_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); break; default: break; } return sws_setColorspaceDetails( context, src_coefficients, src_range, dst_coefficients, dst_range, brightness, contrast, saturation ); } mlt-6.20.0/src/modules/avformat/common.h000066400000000000000000000026331362234133600201210ustar00rootroot00000000000000/* * common.h * Copyright (C) 2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_H #define COMMON_H #include #include int mlt_to_av_sample_format( mlt_audio_format format ); int64_t mlt_to_av_channel_layout( mlt_channel_layout layout ); mlt_channel_layout av_channel_layout_to_mlt( int64_t layout ); mlt_channel_layout mlt_get_channel_layout_or_default( const char* name, int channels ); int mlt_set_luma_transfer( struct SwsContext *context, int src_colorspace, int dst_colorspace, int src_full_range, int dst_full_range ); int mlt_get_sws_flags(int srcwidth, int srcheight, int srcformat, int dstwidth, int dstheight, int dstformat); #endif // COMMON_H mlt-6.20.0/src/modules/avformat/configure000077500000000000000000000144431362234133600203710ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF FFmpeg/avformat options: This module requires at least FFmpeg v2.4 or Libav 12. Features using libavfilter requires FFmpeg (not Libav). --avformat-shared=path - Link against a shared installation of libavformat (default) --avformat-static=path - Link against a static build of libavformat --avformat-ldextra=libs - Provide additional libs to link with --avformat-suffix=suff - Specify a custom suffix --avformat-no-codecs - Disable the producer and consumer to avoid the codecs --avformat-no-filters - Disable the filters to make a codecs+muxers-only plugin --avformat-no-devices - Disable support for libavdevice --avformat-no-avfilter - Disable support for libavfilter EOF else targetos=$(uname -s) case $targetos in Darwin) export LIBSUF=.dylib ;; Linux|FreeBSD|NetBSD) 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= export extra_libs= export avformat_suffix= export codecs=true export filters=true export devices=true export vdpau=false export avfilter=true export swresample=false pkg-config x11 > /dev/null 2>&1 export x11=$? 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-suffix=* ) avformat_suffix="${i#--avformat-suffix=}" ;; --avformat-no-codecs ) codecs=false ;; --avformat-no-filters ) filters=false ;; --avformat-no-devices ) devices=false ;; --avformat-no-avfilter ) avfilter=false ;; --avformat-no-vdpau ) vdpau=false ;; --avformat-vdpau ) vdpau=true ;; esac done : ${shared_ffmpeg:=$(pkg-config --variable=prefix libavformat${avformat_suffix})} if [ "$static_ffmpeg" != "" ] then if [ -d "$static_ffmpeg" ] then echo "CFLAGS+=-DAVDATADIR=\\\"${static_ffmpeg}/ffpresets/\\\"" >> config.mak echo "CFLAGS+=-I$static_ffmpeg" >> config.mak echo "LDFLAGS+=-L$static_ffmpeg/libavformat -L$static_ffmpeg/libavcodec -L$static_ffmpeg/libavutil" >> config.mak echo "LDFLAGS+=-L$static_ffmpeg/libswscale" >> config.mak [ $targetos = "Darwin" ] && echo "LDFLAGS+=-single_module" >> config.mak if [ "$devices" = "true" ] then echo "LDFLAGS+=-L$static_ffmpeg/libavdevice" >> config.mak fi if [ "$avfilter" = "true" ] then echo "LDFLAGS+=-L$static_ffmpeg/libavfilter" >> config.mak fi echo "LDFLAGS+=-Wl,-Bsymbolic" >> config.mak extra_libs="$extra_libs -lm -lz -lbz2" if [ "$vdpau" = "true" ] then printf "#include \n int main(){ VdpBitstreamBuffer test; test.struct_version; return 0;}" | $CC -I"$static_ffmpeg" $CFLAGS -c -x c - >/dev/null 2>&1 [ "$x11" = "0" -a "$?" = "0" ] && echo "VDPAU=1" >> config.mak fi else echo "avformat: Invalid path specified: $static_ffmpeg" touch ../disable-avformat echo 0 fi elif [ "$shared_ffmpeg" != "" ] then if ! $(pkg-config libswscale${avformat_suffix}); then echo "- libswscale not found: disabling" touch ../disable-avformat exit 0 fi if $(pkg-config "libavformat${avformat_suffix} < 56.4.101"); then echo "- libavformat version $(pkg-config --modversion libavformat) is too old: disabling" touch ../disable-avformat exit 0 fi echo "PREFIX=$shared_ffmpeg" >> config.mak case $targetos in MINGW32_NT-*) echo "CFLAGS+=-DAVDATADIR=\\\"share/ffmpeg/\\\"" >> config.mak ;; *) echo "CFLAGS+=-DAVDATADIR=\\\"${shared_ffmpeg}/share/ffmpeg${avformat_suffix}/\\\"" >> config.mak ;; esac echo "CFLAGS+=$(pkg-config --cflags libavcodec${avformat_suffix})" >> config.mak echo "LDFLAGS+=$(pkg-config --libs libavcodec${avformat_suffix})" >> config.mak echo "CFLAGS+=$(pkg-config --cflags libavutil${avformat_suffix})" >> config.mak echo "LDFLAGS+=$(pkg-config --libs libavutil${avformat_suffix})" >> config.mak echo "CFLAGS+=$(pkg-config --cflags libavformat${avformat_suffix})" >> config.mak echo "LDFLAGS+=$(pkg-config --libs libavformat${avformat_suffix})" >> config.mak echo "CFLAGS+=$(pkg-config --cflags libswscale${avformat_suffix})" >> config.mak echo "LDFLAGS+=$(pkg-config --libs libswscale${avformat_suffix})" >> config.mak if [ "$devices" = "true" ] then if ! $(pkg-config libavdevice${avformat_suffix}); then echo "- libavdevice not found: disabling" touch ../disable-avformat exit 0 fi echo "CFLAGS+=$(pkg-config --cflags libavdevice${avformat_suffix})" >> config.mak echo "LDFLAGS+=$(pkg-config --libs libavdevice${avformat_suffix})" >> config.mak fi if [ "$avfilter" = "true" ] then if $(pkg-config "libavfilter${avformat_suffix} >= 4.11.100") && $(pkg-config --list-all | grep libavfilter | grep -q FFmpeg); then echo "CFLAGS+=$(pkg-config --cflags libavfilter${avformat_suffix})" >> config.mak echo "LDFLAGS+=$(pkg-config --libs libavfilter${avformat_suffix})" >> config.mak else echo " - FFmpeg libavfilter >= 4.11.100 not found, disabling some features" avfilter=false fi fi if $(pkg-config libswresample${avformat_suffix}); then swresample=true echo "CFLAGS+=$(pkg-config --cflags libswresample${avformat_suffix})" >> config.mak echo "LDFLAGS+=$(pkg-config --libs libswresample${avformat_suffix})" >> config.mak else echo " - libswresample not found, disabling some features" fi if [ "$vdpau" = "true" ] then printf "#include \n int main(){ VdpBitstreamBuffer test; test.struct_version; return 0;}" | $CC $(pkg-config --cflags libavformat${avformat_suffix}) -I"$shared_ffmpeg/include" $CFLAGS -c -x c - >/dev/null 2>&1 [ "$x11" = "0" -a "$?" = "0" ] && echo "VDPAU=1" >> config.mak fi else echo "- libavformat not found: disabling" touch ../disable-avformat exit 0 fi echo "EXTRA_LIBS=$extra_libs" >> config.mak [ "$codecs" = "true" ] && echo "CODECS=1" >> config.mak [ "$filters" = "true" ] && echo "FILTERS=1" >> config.mak [ "$devices" = "true" ] && echo "DEVICES=1" >> config.mak [ "$avfilter" = "true" ] && echo "AVFILTER=1" >> config.mak [ "$swresample" = "true" ] && echo "SWRESAMPLE=1" >> config.mak exit 0 fi mlt-6.20.0/src/modules/avformat/consumer_avformat.c000066400000000000000000002211301362234133600223510ustar00rootroot00000000000000/* * consumer_avformat.c -- an encoder based on avformat * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" // mlt Header files #include #include #include #include #include // System header files #include #include #include #include #include #include #include // avformat header files #include #include #include #include #include #include #include #include #include #ifdef AVFILTER #include #include #include #endif #ifndef AV_CODEC_FLAG_GLOBAL_HEADER #define AV_CODEC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER #define AV_CODEC_FLAG_QSCALE CODEC_FLAG_QSCALE #define AV_CODEC_FLAG_INTERLACED_DCT CODEC_FLAG_INTERLACED_DCT #define AV_CODEC_FLAG_INTERLACED_ME CODEC_FLAG_INTERLACED_ME #define AV_CODEC_FLAG_PASS1 CODEC_FLAG_PASS1 #define AV_CODEC_FLAG_PASS2 CODEC_FLAG_PASS2 #endif #define MAX_AUDIO_STREAMS (8) #define AUDIO_ENCODE_BUFFER_SIZE (48000 * 2 * MAX_AUDIO_STREAMS) #define AUDIO_BUFFER_SIZE (1024 * 42) #define VIDEO_BUFFER_SIZE (8192 * 8192) #define IMAGE_ALIGN (4) // // This structure should be extended and made globally available in mlt // typedef struct { uint8_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 fifo = calloc( 1, sizeof( sample_fifo_s ) ); fifo->frequency = frequency; fifo->channels = channels; return fifo; } // count is the number of samples multiplied by the number of bytes per sample void sample_fifo_append( sample_fifo fifo, uint8_t *samples, int count ) { if ( ( fifo->size - fifo->used ) < count ) { fifo->size += count * 5; fifo->buffer = realloc( fifo->buffer, fifo->size ); } memcpy( &fifo->buffer[ fifo->used ], samples, count ); fifo->used += count; } int sample_fifo_used( sample_fifo fifo ) { return fifo->used; } int sample_fifo_fetch( sample_fifo fifo, uint8_t *samples, int count ) { if ( count > fifo->used ) count = fifo->used; memcpy( samples, fifo->buffer, count ); fifo->used -= count; memmove( fifo->buffer, &fifo->buffer[ count ], fifo->used ); fifo->time += ( double )count / fifo->channels / fifo->frequency; return count; } void sample_fifo_close( sample_fifo fifo ) { free( fifo->buffer ); free( fifo ); } #if defined(AVFILTER) && LIBAVUTIL_VERSION_MAJOR >= 56 static AVFilterGraph *vfilter_graph; static int setup_hwupload_filter(mlt_properties properties, AVStream* stream, AVCodecContext *codec_context) { AVFilterContext *vfilter_in; AVFilterContext *vfilter_out; AVFilterContext *vfilter_hwupload; vfilter_graph = avfilter_graph_alloc(); mlt_properties_set_data(properties, "vfilter_graph", &vfilter_graph, 0, (mlt_destructor) avfilter_graph_free, NULL); // From ffplay.c:configure_video_filters(). char buffersrc_args[256]; snprintf(buffersrc_args, sizeof(buffersrc_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:frame_rate=%d/%d", codec_context->width, codec_context->height, AV_PIX_FMT_NV12, stream->time_base.num, stream->time_base.den, codec_context->sample_aspect_ratio.num, codec_context->sample_aspect_ratio.den, codec_context->time_base.den, codec_context->time_base.num); int result = avfilter_graph_create_filter(&vfilter_in, avfilter_get_by_name("buffer"), "mlt_buffer", buffersrc_args, NULL, vfilter_graph); if (result >= 0) { result = avfilter_graph_create_filter(&vfilter_out, avfilter_get_by_name("buffersink"), "mlt_buffersink", NULL, NULL, vfilter_graph); if (result >= 0) { enum AVPixelFormat pix_fmts[] = { codec_context->pix_fmt, AV_PIX_FMT_NONE }; result = av_opt_set_int_list(vfilter_out, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); result = avfilter_graph_create_filter(&vfilter_hwupload, avfilter_get_by_name("hwupload"), "mlt_hwupload", "", NULL, vfilter_graph); if (result >= 0) { vfilter_hwupload->hw_device_ctx = av_buffer_ref(codec_context->hw_device_ctx); result = avfilter_link(vfilter_in, 0, vfilter_hwupload, 0); if (result >= 0) { result = avfilter_link(vfilter_hwupload, 0, vfilter_out, 0); if (result >= 0) { result = avfilter_graph_config(vfilter_graph, NULL); if (result >= 0) codec_context->hw_frames_ctx = av_buffer_ref(av_buffersink_get_hw_frames_ctx(vfilter_out)); } } } mlt_properties_set_data(properties, "vfilter_in", vfilter_in, 0, NULL, NULL); mlt_properties_set_data(properties, "vfilter_out", vfilter_out, 0, NULL, NULL); } } return result; } static AVBufferRef *hw_device_ctx; static int init_vaapi(mlt_properties properties, AVCodecContext *codec_context) { int err = 0; const char* vaapi_device = mlt_properties_get(properties, "vaapi_device"); AVDictionary *opts = NULL; av_dict_set(&opts, "connection_type", mlt_properties_get(properties, "connection_type"), 0); av_dict_set(&opts, "driver", mlt_properties_get(properties, "driver"), 0); av_dict_set(&opts, "kernel_driver", mlt_properties_get(properties, "kernel_driver"), 0); if ((err = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, vaapi_device, opts, 0)) < 0) { mlt_log_warning(NULL, "Failed to create VAAPI device.\n"); } else { codec_context->hw_device_ctx = av_buffer_ref(hw_device_ctx); mlt_properties_set_data(properties, "hw_device_ctx", &hw_device_ctx, 0, (mlt_destructor) av_buffer_unref, NULL); } av_dict_free(&opts); return err; } #endif // Forward references. static void property_changed( mlt_properties owner, mlt_consumer self, char *name ); static int consumer_start( mlt_consumer consumer ); static int consumer_stop( mlt_consumer consumer ); static int consumer_is_stopped( mlt_consumer consumer ); static void *consumer_thread( void *arg ); static void consumer_close( mlt_consumer consumer ); /** Initialise the consumer. */ mlt_consumer consumer_avformat_init( mlt_profile profile, char *arg ) { // Allocate the consumer mlt_consumer consumer = mlt_consumer_new( profile ); // If memory allocated and initialises without error if ( consumer != NULL ) { // Get properties from the consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Assign close callback consumer->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 consumer->start = consumer_start; consumer->stop = consumer_stop; consumer->is_stopped = consumer_is_stopped; mlt_events_register( properties, "consumer-fatal-error", NULL ); mlt_event event = mlt_events_listen( properties, consumer, "property-changed", ( mlt_listener )property_changed ); mlt_properties_set_data( properties, "property-changed event", event, 0, NULL, NULL ); } // Return consumer return consumer; } static void recompute_aspect_ratio( mlt_properties properties ) { double ar = mlt_properties_get_double( properties, "aspect" ); if (ar > 0.0) { AVRational rational = av_d2q( ar, 255 ); int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); // 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 ); // Now compute the sample aspect ratio rational = av_d2q( ar * height / FFMAX(width, 1), 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, "sample_aspect_num", rational.num ); mlt_properties_set_int( properties, "sample_aspect_den", rational.den ); } } static void color_trc_from_colorspace( mlt_properties properties ) { // Default color transfer characteristic from colorspace. switch ( mlt_properties_get_int( properties, "colorspace" ) ) { case 709: mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_BT709 ); break; case 470: mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_GAMMA28 ); break; case 240: mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_SMPTE240M ); break; case 0: // sRGB mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_IEC61966_2_1 ); break; case 601: case 170: mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_SMPTE170M ); break; default: break; } } static void property_changed( mlt_properties owner, mlt_consumer self, char *name ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( self ); if ( !strcmp( name, "s" ) ) { // Obtain the size property char *size = mlt_properties_get( properties, "s" ); int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); int tw, th; if ( sscanf( size, "%dx%d", &tw, &th ) == 2 && tw > 0 && th > 0 ) { width = tw; height = th; } else { mlt_log_warning( MLT_CONSUMER_SERVICE(self), "Invalid size property %s - ignoring.\n", 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 ); recompute_aspect_ratio( properties ); } // "-aspect" on ffmpeg command line is display aspect ratio else if ( !strcmp( name, "aspect" ) || !strcmp( name, "width" ) || !strcmp( name, "height" ) ) { recompute_aspect_ratio( properties ); } // Handle the ffmpeg command line "-r" property for frame rate else if ( !strcmp( name, "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 ); } // Apply AVOptions that are synonyms for standard mlt_consumer options else if ( !strcmp( name, "ac" ) ) { mlt_properties_set_int( properties, "channels", mlt_properties_get_int( properties, "ac" ) ); } else if ( !strcmp( name, "ar" )) { mlt_properties_set_int( properties, "frequency", mlt_properties_get_int( properties, "ar" ) ); } } /** Start the consumer. */ static int consumer_start( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); 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 ) { mlt_properties doc = mlt_properties_new(); mlt_properties formats = mlt_properties_new(); char key[20]; AVOutputFormat *format = NULL; mlt_properties_set_data( properties, "f", formats, 0, (mlt_destructor) mlt_properties_close, NULL ); mlt_properties_set_data( doc, "formats", formats, 0, NULL, NULL ); while ( ( format = av_oformat_next( format ) ) ) { snprintf( key, sizeof(key), "%d", mlt_properties_count( formats ) ); mlt_properties_set( formats, key, format->name ); } s = mlt_properties_serialise_yaml( doc ); fprintf( stdout, "%s", s ); free( s ); mlt_properties_close( doc ); error = 1; } s = mlt_properties_get( properties, "acodec" ); if ( s && strcmp( s, "list" ) == 0 ) { mlt_properties doc = mlt_properties_new(); mlt_properties codecs = mlt_properties_new(); char key[20]; AVCodec *codec = NULL; mlt_properties_set_data( properties, "acodec", codecs, 0, (mlt_destructor) mlt_properties_close, NULL ); mlt_properties_set_data( doc, "audio_codecs", codecs, 0, NULL, NULL ); while ( ( codec = av_codec_next( codec ) ) ) #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(37<<8)+0) if ( (codec->encode2 || codec->send_frame) && codec->type == AVMEDIA_TYPE_AUDIO ) #else if ( codec->encode2 && codec->type == AVMEDIA_TYPE_AUDIO ) #endif { snprintf( key, sizeof(key), "%d", mlt_properties_count( codecs ) ); mlt_properties_set( codecs, key, codec->name ); } s = mlt_properties_serialise_yaml( doc ); fprintf( stdout, "%s", s ); free( s ); mlt_properties_close( doc ); error = 1; } s = mlt_properties_get( properties, "vcodec" ); if ( s && strcmp( s, "list" ) == 0 ) { mlt_properties doc = mlt_properties_new(); mlt_properties codecs = mlt_properties_new(); char key[20]; AVCodec *codec = NULL; mlt_properties_set_data( properties, "vcodec", codecs, 0, (mlt_destructor) mlt_properties_close, NULL ); mlt_properties_set_data( doc, "video_codecs", codecs, 0, NULL, NULL ); while ( ( codec = av_codec_next( codec ) ) ) #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(37<<8)+0) if ( (codec->encode2 || codec->send_frame) && codec->type == AVMEDIA_TYPE_VIDEO ) #else if ( codec->encode2 && codec->type == AVMEDIA_TYPE_VIDEO ) #endif { snprintf( key, sizeof(key), "%d", mlt_properties_count( codecs ) ); mlt_properties_set( codecs, key, codec->name ); } s = mlt_properties_serialise_yaml( doc ); fprintf( stdout, "%s", s ); free( s ); mlt_properties_close( doc ); 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 ) ); mlt_event_block( mlt_properties_get_data( properties, "property-changed event", NULL ) ); // Because Movit only reads this on the first frame, // we must do this after properties have been set but before first frame request. if ( !mlt_properties_get( properties, "color_trc") ) color_trc_from_colorspace( properties ); // Assign the thread to properties mlt_properties_set_data( properties, "thread", thread, sizeof( pthread_t ), free, NULL ); // Create the thread pthread_create( thread, NULL, consumer_thread, consumer ); // Set the running state mlt_properties_set_int( properties, "running", 1 ); } return error; } /** Stop the consumer. */ static int consumer_stop( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL ); // Check that we're running if ( thread ) { // Stop the thread mlt_properties_set_int( properties, "running", 0 ); // Wait for termination pthread_join( *thread, NULL ); mlt_properties_set_data( properties, "thread", NULL, 0, NULL, NULL ); mlt_event_unblock( mlt_properties_get_data( properties, "property-changed event", NULL ) ); } return 0; } /** Determine if the consumer is stopped. */ static int consumer_is_stopped( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); 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 ); int search_flags = AV_OPT_SEARCH_CHILDREN; const AVOption *opt = av_opt_find( obj, opt_name, NULL, flags, search_flags ); // If option not found, see if it was prefixed with a or v (-vb) if ( !opt && ( ( opt_name[0] == 'v' && ( flags & AV_OPT_FLAG_VIDEO_PARAM ) ) || ( opt_name[0] == 'a' && ( flags & AV_OPT_FLAG_AUDIO_PARAM ) ) ) ) opt = av_opt_find( obj, ++opt_name, NULL, flags, search_flags ); // Apply option if found if ( opt && strcmp( opt_name, "channel_layout" ) ) av_opt_set( obj, opt_name, mlt_properties_get_value( properties, i), search_flags ); } } static enum AVPixelFormat pick_pix_fmt( mlt_image_format img_fmt ) { switch ( img_fmt ) { case mlt_image_rgb24: return AV_PIX_FMT_RGB24; case mlt_image_rgb24a: return AV_PIX_FMT_RGBA; case mlt_image_yuv420p: return AV_PIX_FMT_YUV420P; case mlt_image_yuv422p16: return AV_PIX_FMT_YUV422P16LE; default: return AV_PIX_FMT_YUYV422; } } static int get_mlt_audio_format( int av_sample_fmt ) { switch ( av_sample_fmt ) { case AV_SAMPLE_FMT_U8: return mlt_audio_u8; case AV_SAMPLE_FMT_S32: return mlt_audio_s32le; case AV_SAMPLE_FMT_FLT: return mlt_audio_f32le; case AV_SAMPLE_FMT_U8P: return mlt_audio_u8; case AV_SAMPLE_FMT_S32P: return mlt_audio_s32le; case AV_SAMPLE_FMT_FLTP: return mlt_audio_f32le; default: return mlt_audio_s16; } } static int pick_sample_fmt( mlt_properties properties, AVCodec *codec ) { int sample_fmt = AV_SAMPLE_FMT_S16; const char *format = mlt_properties_get( properties, "mlt_audio_format" ); const int *p = codec->sample_fmts; // get default av_sample_fmt from mlt_audio_format if ( format ) { if ( !strcmp( format, "s32le" ) ) sample_fmt = AV_SAMPLE_FMT_S32; else if ( !strcmp( format, "f32le" ) ) sample_fmt = AV_SAMPLE_FMT_FLT; else if ( !strcmp( format, "u8" ) ) sample_fmt = AV_SAMPLE_FMT_U8; else if ( !strcmp( format, "s32" ) ) sample_fmt = AV_SAMPLE_FMT_S32P; else if ( !strcmp( format, "float" ) ) sample_fmt = AV_SAMPLE_FMT_FLTP; } // check if codec supports our mlt_audio_format for ( ; *p != -1; p++ ) { if ( *p == sample_fmt ) return sample_fmt; } // no match - pick first one we support for ( p = codec->sample_fmts; *p != -1; p++ ) { switch (*p) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_FLTP: return *p; default: break; } } mlt_log_error( properties, "audio codec sample_fmt not compatible" ); return AV_SAMPLE_FMT_NONE; } static uint8_t* interleaved_to_planar( int samples, int channels, uint8_t* audio, int bytes_per_sample ) { uint8_t *buffer = mlt_pool_alloc( AUDIO_ENCODE_BUFFER_SIZE ); uint8_t *p = buffer; int c; memset( buffer, 0, AUDIO_ENCODE_BUFFER_SIZE ); for ( c = 0; c < channels; c++ ) { uint8_t *q = audio + c * bytes_per_sample; int i = samples + 1; while ( --i ) { memcpy( p, q, bytes_per_sample ); p += bytes_per_sample; q += channels * bytes_per_sample; } } return buffer; } /** Add an audio output stream */ static AVStream *add_audio_stream( mlt_consumer consumer, AVFormatContext *oc, AVCodec *codec, int channels, int64_t channel_layout ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Create a new stream AVStream *st = avformat_new_stream( oc, codec ); // If created, then initialise from properties if ( st != NULL ) { AVCodecContext *c = st->codec; // Establish defaults from AVOptions avcodec_get_context_defaults3( c, codec ); c->codec_id = codec->id; c->codec_type = AVMEDIA_TYPE_AUDIO; c->sample_fmt = pick_sample_fmt( properties, codec ); c->channel_layout = channel_layout; #if 0 // disabled until some audio codecs are multi-threaded // 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 >= 0 ) c->thread_count = thread_count; #endif if (oc->oformat->flags & AVFMT_GLOBALHEADER) c->flags |= AV_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 char *apre = mlt_properties_get( properties, "apre" ); if ( apre ) { mlt_properties p = mlt_properties_load( apre ); apply_properties( c, p, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM ); mlt_properties_close( p ); } 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 |= AV_CODEC_FLAG_QSCALE; c->global_quality = FF_QP2LAMBDA * audio_qscale; } // Set parameters controlled by MLT c->sample_rate = mlt_properties_get_int( properties, "frequency" ); st->time_base = ( AVRational ){ 1, c->sample_rate }; c->channels = channels; if ( mlt_properties_get( properties, "alang" ) != NULL ) av_dict_set( &oc->metadata, "language", mlt_properties_get( properties, "alang" ), 0 ); } else { mlt_log_error( MLT_CONSUMER_SERVICE( consumer ), "Could not allocate a stream for audio\n" ); } return st; } static int open_audio( mlt_properties properties, AVFormatContext *oc, AVStream *st, int audio_outbuf_size, const char *codec_name ) { // 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; if ( codec_name ) codec = avcodec_find_encoder_by_name( codec_name ); else codec = avcodec_find_encoder( c->codec_id ); // Process properties as AVOptions on the AVCodec if ( codec && codec->priv_class ) { char *apre = mlt_properties_get( properties, "apre" ); if ( !c->priv_data && codec->priv_data_size ) { c->priv_data = av_mallocz( codec->priv_data_size ); *(const AVClass **) c->priv_data = codec->priv_class; // av_opt_set_defaults( c ); } if ( apre ) { mlt_properties p = mlt_properties_load( apre ); apply_properties( c->priv_data, p, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM ); mlt_properties_close( p ); } apply_properties( c->priv_data, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM ); } // Continue if codec found and we can open it if ( codec && avcodec_open2( c, codec, NULL ) >= 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 = 1; else audio_input_frame_size = c->frame_size; // Some formats want stream headers to be separate (hmm) if ( !strcmp( oc->oformat->name, "mp4" ) || !strcmp( oc->oformat->name, "mov" ) || !strcmp( oc->oformat->name, "3gp" ) ) c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } else { mlt_log_warning( NULL, "%s: Unable to encode audio - disabling audio output.\n", __FILE__ ); audio_input_frame_size = 0; } return audio_input_frame_size; } static void close_audio( AVFormatContext *oc, AVStream *st ) { if ( st && st->codec ) avcodec_close( st->codec ); } /** Add a video output stream */ static AVStream *add_video_stream( mlt_consumer consumer, AVFormatContext *oc, AVCodec *codec ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Create a new stream AVStream *st = avformat_new_stream( oc, codec ); if ( st != NULL ) { char *pix_fmt = mlt_properties_get( properties, "pix_fmt" ); AVCodecContext *c = st->codec; // Establish defaults from AVOptions avcodec_get_context_defaults3( c, codec ); c->codec_id = codec->id; c->codec_type = AVMEDIA_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 >= 0 ) c->thread_count = thread_count; // Process properties as AVOptions char *vpre = mlt_properties_get( properties, "vpre" ); if ( vpre ) { mlt_properties p = mlt_properties_load( vpre ); #ifdef AVDATADIR if ( mlt_properties_count( p ) < 1 ) { AVCodec *codec = avcodec_find_encoder( c->codec_id ); if ( codec ) { char *path = malloc( strlen(AVDATADIR) + strlen(codec->name) + strlen(vpre) + strlen(".ffpreset") + 2 ); strcpy( path, AVDATADIR ); strcat( path, codec->name ); strcat( path, "-" ); strcat( path, vpre ); strcat( path, ".ffpreset" ); mlt_properties_close( p ); p = mlt_properties_load( path ); if ( mlt_properties_count( p ) > 0 ) mlt_properties_debug( p, path, stderr ); free( path ); } } else { mlt_properties_debug( p, vpre, stderr ); } #endif apply_properties( c, p, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM ); mlt_properties_close( p ); } int colorspace = mlt_properties_get_int( properties, "colorspace" ); mlt_properties_set( properties, "colorspace", NULL ); apply_properties( c, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM ); mlt_properties_set_int( properties, "colorspace", colorspace ); // 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" ); st->time_base = c->time_base; st->avg_frame_rate = av_inv_q( c->time_base ); #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 5, 0) c->framerate = av_inv_q( c->time_base ); #endif // Default to the codec's first pix_fmt if possible. c->pix_fmt = pix_fmt ? av_get_pix_fmt( pix_fmt ) : codec ? ( codec->pix_fmts ? codec->pix_fmts[0] : AV_PIX_FMT_YUV422P ): AV_PIX_FMT_YUV420P; #if defined(AVFILTER) && LIBAVUTIL_VERSION_MAJOR >= 56 if (AV_PIX_FMT_VAAPI == c->pix_fmt) { int result = init_vaapi(properties, c); if (result >= 0) { int result = setup_hwupload_filter(properties, st, c); if (result < 0) mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Failed to setup hwfilter: %d\n", result); } else { mlt_log_error(MLT_CONSUMER_SERVICE(consumer), "Failed to initialize VA-API: %d\n", result); } } #endif switch ( colorspace ) { case 170: c->colorspace = AVCOL_SPC_SMPTE170M; break; case 240: c->colorspace = AVCOL_SPC_SMPTE240M; break; case 470: c->colorspace = AVCOL_SPC_BT470BG; break; case 601: c->colorspace = ( 576 % c->height ) ? AVCOL_SPC_SMPTE170M : AVCOL_SPC_BT470BG; break; case 709: c->colorspace = AVCOL_SPC_BT709; break; } if ( mlt_properties_get( properties, "aspect" ) ) { // "-aspect" on ffmpeg command line is display aspect ratio double ar = mlt_properties_get_double( properties, "aspect" ); c->sample_aspect_ratio = av_d2q( ar * c->height / c->width, 255 ); } 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" ); } st->sample_aspect_ratio = c->sample_aspect_ratio; if ( mlt_properties_get_double( properties, "qscale" ) > 0 ) { c->flags |= AV_CODEC_FLAG_QSCALE; c->global_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 separate if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) c->flags |= AV_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 |= AV_CODEC_FLAG_INTERLACED_DCT; if ( ! mlt_properties_get( properties, "ilme" ) || mlt_properties_get_int( properties, "ilme" ) ) c->flags |= AV_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 ) mlt_log_warning( MLT_CONSUMER_SERVICE( consumer ), "Error parsing rc_override\n" ); 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 |= AV_CODEC_FLAG_PASS1; else if ( i == 2 ) c->flags |= AV_CODEC_FLAG_PASS2; #ifdef AV_CODEC_ID_H265 if ( codec->id != AV_CODEC_ID_H265 ) #endif if ( codec->id != AV_CODEC_ID_H264 && ( c->flags & ( AV_CODEC_FLAG_PASS1 | AV_CODEC_FLAG_PASS2 ) ) ) { FILE *f; int size; char *logbuffer; char *filename; if ( mlt_properties_get( properties, "passlogfile" ) ) { filename = mlt_properties_get( properties, "passlogfile" ); } else { char logfilename[1024]; snprintf( logfilename, sizeof(logfilename), "%s_2pass.log", mlt_properties_get( properties, "target" ) ); mlt_properties_set( properties, "_logfilename", logfilename ); filename = mlt_properties_get( properties, "_logfilename" ); } if ( c->flags & AV_CODEC_FLAG_PASS1 ) { f = mlt_fopen( filename, "w" ); if ( !f ) perror( filename ); else mlt_properties_set_data( properties, "_logfile", f, 0, ( mlt_destructor )fclose, NULL ); } else { /* read the log file */ f = mlt_fopen( filename, "r" ); if ( !f ) { perror( filename ); } else { fseek( f, 0, SEEK_END ); size = ftell( f ); fseek( f, 0, SEEK_SET ); logbuffer = av_malloc( size + 1 ); if ( !logbuffer ) mlt_log_fatal( MLT_CONSUMER_SERVICE( consumer ), "Could not allocate log buffer\n" ); else { if ( size >= 0 ) { size = fread( logbuffer, 1, size, f ); logbuffer[size] = '\0'; c->stats_in = logbuffer; } } fclose( f ); } } } } else { mlt_log_error( MLT_CONSUMER_SERVICE( consumer ), "Could not allocate a stream for video\n" ); } return st; } static AVFrame *alloc_picture( int pix_fmt, int width, int height ) { // Allocate a frame AVFrame *picture = av_frame_alloc(); if ( picture ) { int size = av_image_alloc(picture->data, picture->linesize, width, height, pix_fmt, IMAGE_ALIGN); if (size > 0) { picture->format = pix_fmt; picture->width = width; picture->height = height; } else { av_free( picture ); picture = NULL; } } else { // Something failed - clean up what we can av_free( picture ); picture = NULL; } return picture; } static int open_video( mlt_properties properties, AVFormatContext *oc, AVStream *st, const char *codec_name ) { // Get the codec AVCodecContext *video_enc = st->codec; // find the video encoder AVCodec *codec; if ( codec_name ) codec = avcodec_find_encoder_by_name( codec_name ); else codec = avcodec_find_encoder( video_enc->codec_id ); // Process properties as AVOptions on the AVCodec if ( codec && codec->priv_class ) { char *vpre = mlt_properties_get( properties, "vpre" ); if ( !video_enc->priv_data && codec->priv_data_size ) { video_enc->priv_data = av_mallocz( codec->priv_data_size ); *(const AVClass **) video_enc->priv_data = codec->priv_class; // av_opt_set_defaults( video_enc ); } if ( vpre ) { mlt_properties p = mlt_properties_load( vpre ); apply_properties( video_enc->priv_data, p, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM ); mlt_properties_close( p ); } apply_properties( video_enc->priv_data, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM ); } if( codec && codec->pix_fmts ) { const enum AVPixelFormat *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 ]; } int result = codec && avcodec_open2( video_enc, codec, NULL ) >= 0; return result; } void close_video(AVFormatContext *oc, AVStream *st) { if ( st && st->codec ) { av_freep( &st->codec->stats_in ); 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; } static int mlt_write(void *h, uint8_t *buf, int size) { mlt_properties properties = (mlt_properties) h; mlt_events_fire( properties, "avformat-write", buf, &size, NULL ); return 0; } static void write_transmitter( mlt_listener listener, mlt_properties owner, mlt_service service, void **args ) { int *p_size = (int*) args[1]; listener( owner, service, (uint8_t*) args[0], *p_size ); } typedef struct encode_ctx_desc { mlt_consumer consumer; int audio_outbuf_size; int audio_input_frame_size; uint8_t audio_outbuf[AUDIO_BUFFER_SIZE], audio_buf_1[AUDIO_ENCODE_BUFFER_SIZE], audio_buf_2[AUDIO_ENCODE_BUFFER_SIZE]; int channels; int total_channels; int frequency; int sample_bytes; sample_fifo fifo; AVFormatContext *oc; AVStream *video_st; AVStream *audio_st[ MAX_AUDIO_STREAMS ]; int64_t sample_count[ MAX_AUDIO_STREAMS ]; // Used to store and override codec ids int video_codec_id; int audio_codec_id; int error_count; int frame_count; double audio_pts; double video_pts; int terminate_on_pause; int terminated; mlt_properties properties; mlt_properties frame_meta_properties; AVFrame *audio_avframe; } encode_ctx_t; static int encode_audio(encode_ctx_t* ctx) { char key[27]; int i, j = 0, samples = ctx->audio_input_frame_size; int frame_length = ctx->audio_input_frame_size * ctx->channels * ctx->sample_bytes; // Get samples count to fetch from fifo if ( sample_fifo_used( ctx->fifo ) < frame_length ) { samples = sample_fifo_used( ctx->fifo ) / ( ctx->channels * ctx->sample_bytes ); } else if ( ctx->audio_input_frame_size == 1 ) { // PCM consumes as much as possible. samples = FFMIN( sample_fifo_used( ctx->fifo ), AUDIO_ENCODE_BUFFER_SIZE ) / frame_length; } // Get the audio samples if ( samples > 0 ) { sample_fifo_fetch( ctx->fifo, ctx->audio_buf_1, samples * ctx->sample_bytes * ctx->channels ); } else if ( ctx->audio_codec_id == AV_CODEC_ID_VORBIS && ctx->terminated ) { // This prevents an infinite loop when some versions of vorbis do not // increment pts when encoding silence. ctx->audio_pts = ctx->video_pts; return 1; } else { memset( ctx->audio_buf_1, 0, AUDIO_ENCODE_BUFFER_SIZE ); } // For each output stream for ( i = 0; i < MAX_AUDIO_STREAMS && ctx->audio_st[i] && j < ctx->total_channels; i++ ) { AVStream *stream = ctx->audio_st[i]; AVCodecContext *codec = stream->codec; AVPacket pkt; av_init_packet( &pkt ); pkt.data = ctx->audio_outbuf; pkt.size = ctx->audio_outbuf_size; // Optimized for single track and no channel remap if ( !ctx->audio_st[1] && !mlt_properties_count( ctx->frame_meta_properties ) ) { void* p = ctx->audio_buf_1; if ( codec->sample_fmt == AV_SAMPLE_FMT_FLTP ) p = interleaved_to_planar( samples, ctx->channels, p, sizeof( float ) ); else if ( codec->sample_fmt == AV_SAMPLE_FMT_S16P ) p = interleaved_to_planar( samples, ctx->channels, p, sizeof( int16_t ) ); else if ( codec->sample_fmt == AV_SAMPLE_FMT_S32P ) p = interleaved_to_planar( samples, ctx->channels, p, sizeof( int32_t ) ); else if ( codec->sample_fmt == AV_SAMPLE_FMT_U8P ) p = interleaved_to_planar( samples, ctx->channels, p, sizeof( uint8_t ) ); ctx->audio_avframe->nb_samples = FFMAX( samples, ctx->audio_input_frame_size ); ctx->audio_avframe->pts = ctx->sample_count[i]; ctx->sample_count[i] += ctx->audio_avframe->nb_samples; avcodec_fill_audio_frame( ctx->audio_avframe, codec->channels, codec->sample_fmt, (const uint8_t*) p, AUDIO_ENCODE_BUFFER_SIZE, 0 ); #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(37<<8)+0) int ret = avcodec_send_frame( codec, samples ? ctx->audio_avframe : NULL ); if ( ret < 0 ) { pkt.size = ret; } else { ret = avcodec_receive_packet( codec, &pkt ); if ( ret == AVERROR(EAGAIN) || ret == AVERROR_EOF ) pkt.size = 0; else if ( ret < 0 ) pkt.size = ret; } #else int got_packet = 0; int ret = avcodec_encode_audio2( codec, &pkt, samples ? ctx->audio_avframe : NULL, &got_packet ); if ( ret < 0 ) pkt.size = ret; else if ( !got_packet ) pkt.size = 0; #endif if ( p != ctx->audio_buf_1 ) mlt_pool_release( p ); } else { // Extract the audio channels according to channel mapping int dest_offset = 0; // channel offset into interleaved dest buffer // Get the number of channels for this stream sprintf( key, "channels.%d", i ); int current_channels = mlt_properties_get_int( ctx->properties, key ); // Clear the destination audio buffer. memset( ctx->audio_buf_2, 0, AUDIO_ENCODE_BUFFER_SIZE ); // For each output channel while ( dest_offset < current_channels && j < ctx->total_channels ) { int map_start = -1, map_channels = 0; int source_offset = 0; int k; // Look for a mapping that starts at j for ( k = 0; k < (MAX_AUDIO_STREAMS * 2) && map_start != j; k++ ) { sprintf( key, "%d.channels", k ); map_channels = mlt_properties_get_int( ctx->frame_meta_properties, key ); sprintf( key, "%d.start", k ); if ( mlt_properties_get( ctx->frame_meta_properties, key ) ) map_start = mlt_properties_get_int( ctx->frame_meta_properties, key ); if ( map_start != j ) source_offset += map_channels; } // If no mapping if ( map_start != j ) { map_channels = current_channels; source_offset = j; } // Copy samples if source offset valid if ( source_offset < ctx->channels ) { // Interleave the audio buffer with the # channels for this stream/mapping. for ( k = 0; k < map_channels; k++, j++, source_offset++, dest_offset++ ) { void *src = ctx->audio_buf_1 + source_offset * ctx->sample_bytes; void *dest = ctx->audio_buf_2 + dest_offset * ctx->sample_bytes; int s = samples + 1; while ( --s ) { memcpy( dest, src, ctx->sample_bytes ); dest += current_channels * ctx->sample_bytes; src += ctx->channels * ctx->sample_bytes; } } } // Otherwise silence else { j += current_channels; dest_offset += current_channels; } } ctx->audio_avframe->nb_samples = FFMAX( samples, ctx->audio_input_frame_size ); ctx->audio_avframe->pts = ctx->sample_count[i]; ctx->sample_count[i] += ctx->audio_avframe->nb_samples; avcodec_fill_audio_frame( ctx->audio_avframe, codec->channels, codec->sample_fmt, (const uint8_t*) ctx->audio_buf_2, AUDIO_ENCODE_BUFFER_SIZE, 0 ); #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(37<<8)+0) int ret = avcodec_send_frame( codec, samples ? ctx->audio_avframe : NULL ); if ( ret < 0 ) { pkt.size = ret; } else { receive_audio_packet: ret = avcodec_receive_packet( codec, &pkt ); if ( ret == AVERROR(EAGAIN) || ret == AVERROR_EOF ) pkt.size = 0; else if ( ret < 0 ) pkt.size = ret; } #else int got_packet = 0; int ret = avcodec_encode_audio2( codec, &pkt, samples ? ctx->audio_avframe : NULL, &got_packet ); if ( ret < 0 ) pkt.size = ret; else if ( !got_packet ) pkt.size = 0; #endif } if ( pkt.size > 0 ) { // Write the compressed frame in the media file if ( pkt.pts != AV_NOPTS_VALUE ) pkt.pts = av_rescale_q( pkt.pts, codec->time_base, stream->time_base ); if ( pkt.dts != AV_NOPTS_VALUE ) pkt.dts = av_rescale_q( pkt.dts, codec->time_base, stream->time_base ); if ( pkt.duration > 0 ) pkt.duration = av_rescale_q( pkt.duration, codec->time_base, stream->time_base ); pkt.stream_index = stream->index; if ( av_interleaved_write_frame( ctx->oc, &pkt ) ) { mlt_log_fatal( MLT_CONSUMER_SERVICE( ctx->consumer ), "error writing audio frame\n" ); mlt_events_fire( ctx->properties, "consumer-fatal-error", NULL ); return -1; } ctx->error_count = 0; mlt_log_debug( MLT_CONSUMER_SERVICE( ctx->consumer ), "audio stream %d pkt pts %"PRId64" frame_size %d\n", stream->index, pkt.pts, codec->frame_size ); #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(37<<8)+0) goto receive_audio_packet; #endif } else if ( pkt.size < 0 ) { mlt_log_warning( MLT_CONSUMER_SERVICE( ctx->consumer ), "error with audio encode: %d (frame %d)\n", pkt.size, ctx->frame_count ); if ( ++ctx->error_count > 2 ) return -1; } if ( i == 0 ) { ctx->audio_pts = (double) ctx->sample_count[0] * av_q2d( stream->codec->time_base ); } } return 0; } /** The main thread - the argument is simply the consumer. */ static void *consumer_thread( void *arg ) { int i; // Encoding content encode_ctx_t* enc_ctx = mlt_pool_alloc(sizeof(encode_ctx_t)); memset(enc_ctx, 0, sizeof(encode_ctx_t)); // Map the argument to the object mlt_consumer consumer = enc_ctx->consumer = arg; // Get the properties mlt_properties properties = enc_ctx->properties = MLT_CONSUMER_PROPERTIES( consumer ); // Get the terminate on pause property enc_ctx->terminate_on_pause = mlt_properties_get_int( enc_ctx->properties, "terminate_on_pause" ); // 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 enc_ctx->total_channels = enc_ctx->channels = mlt_properties_get_int( properties, "channels" ); enc_ctx->frequency = mlt_properties_get_int( properties, "frequency" ); void *pcm = NULL; int samples = 0; // AVFormat audio buffer and frame size enc_ctx->audio_outbuf_size = AUDIO_BUFFER_SIZE; // AVFormat video buffer and frame count int video_outbuf_size = VIDEO_BUFFER_SIZE; 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 ); enc_ctx->fifo = mlt_properties_get_data( properties, "sample_fifo", NULL ); // For receiving images from an mlt_frame uint8_t *image; mlt_image_format img_fmt = mlt_image_yuv422; // Need two av pictures for converting AVFrame *converted_avframe = NULL; AVFrame *avframe = NULL; // For receiving audio samples back from the fifo int count = 0; // Frames dispatched 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" ); AVCodec *audio_codec = NULL; AVCodec *video_codec = NULL; // Misc char key[27]; enc_ctx->frame_meta_properties = mlt_properties_new(); int header_written = 0; int dst_colorspace = mlt_properties_get_int( properties, "colorspace" ); const char* color_range = mlt_properties_get( properties, "color_range" ); int dst_full_range = color_range && (!strcmp("pc", color_range) || !strcmp("jpeg", color_range)); // Check for user selected format first if ( format != NULL ) fmt = av_guess_format( format, NULL, NULL ); // Otherwise check on the filename if ( fmt == NULL && filename != NULL ) fmt = av_guess_format( NULL, filename, NULL ); // Otherwise default to mpeg if ( fmt == NULL ) fmt = av_guess_format( "mpeg", NULL, NULL ); // We need a filename - default to stdout? if ( filename == NULL || !strcmp( filename, "" ) ) filename = "pipe:"; #if defined(FFUDIV) avformat_alloc_output_context2( &enc_ctx->oc, fmt, format, filename ); #else enc_ctx->oc = avformat_alloc_context( ); enc_ctx->oc->oformat = fmt; snprintf( enc_ctx->oc->filename, sizeof(enc_ctx->oc->filename), "%s", filename ); if ( enc_ctx->oc->oformat && enc_ctx->oc->oformat->priv_class && !enc_ctx->oc->priv_data && enc_ctx->oc->oformat->priv_data_size ) { enc_ctx->oc->priv_data = av_mallocz( enc_ctx->oc->oformat->priv_data_size ); if ( enc_ctx->oc->priv_data ) { *(const AVClass**)enc_ctx->oc->priv_data = enc_ctx->oc->oformat->priv_class; av_opt_set_defaults( enc_ctx->oc->priv_data ); } } #endif // Get the codec ids selected enc_ctx->audio_codec_id = fmt->audio_codec; enc_ctx->video_codec_id = fmt->video_codec; // Check for audio codec overrides if ( ( acodec && strcmp( acodec, "none" ) == 0 ) || mlt_properties_get_int( properties, "an" ) ) enc_ctx->audio_codec_id = AV_CODEC_ID_NONE; else if ( acodec ) { audio_codec = avcodec_find_encoder_by_name( acodec ); if ( audio_codec ) { enc_ctx->audio_codec_id = audio_codec->id; if ( enc_ctx->audio_codec_id == AV_CODEC_ID_AC3 && avcodec_find_encoder_by_name( "ac3_fixed" ) ) { mlt_properties_set( enc_ctx->properties, "_acodec", "ac3_fixed" ); acodec = mlt_properties_get( enc_ctx->properties, "_acodec" ); audio_codec = avcodec_find_encoder_by_name( acodec ); } else if ( !strcmp( acodec, "aac" ) || !strcmp( acodec, "vorbis" ) ) { mlt_properties_set( enc_ctx->properties, "astrict", "experimental" ); } } else { enc_ctx->audio_codec_id = AV_CODEC_ID_NONE; mlt_log_warning( MLT_CONSUMER_SERVICE( consumer ), "audio codec %s unrecognised - ignoring\n", acodec ); } } else { audio_codec = avcodec_find_encoder( enc_ctx->audio_codec_id ); } // Check for video codec overrides if ( ( vcodec && strcmp( vcodec, "none" ) == 0 ) || mlt_properties_get_int( properties, "vn" ) ) enc_ctx->video_codec_id = AV_CODEC_ID_NONE; else if ( vcodec ) { video_codec = avcodec_find_encoder_by_name( vcodec ); if ( video_codec ) { enc_ctx->video_codec_id = video_codec->id; } else { enc_ctx->video_codec_id = AV_CODEC_ID_NONE; mlt_log_warning( MLT_CONSUMER_SERVICE( consumer ), "video codec %s unrecognised - ignoring\n", vcodec ); } } else { video_codec = avcodec_find_encoder( enc_ctx->video_codec_id ); } // Write metadata for ( i = 0; i < mlt_properties_count( properties ); i++ ) { char *name = mlt_properties_get_name( properties, i ); if ( name && !strncmp( name, "meta.attr.", 10 ) ) { char *key = strdup( name + 10 ); char *markup = strrchr( key, '.' ); if ( markup && !strcmp( markup, ".markup") ) { markup[0] = '\0'; if ( !strstr( key, ".stream." ) ) av_dict_set( &enc_ctx->oc->metadata, key, mlt_properties_get_value( properties, i ), 0 ); } free( key ); } } // Add audio and video streams if ( enc_ctx->video_codec_id != AV_CODEC_ID_NONE ) { if ( ( enc_ctx->video_st = add_video_stream( consumer, enc_ctx->oc, video_codec ) ) ) { const char* img_fmt_name = mlt_properties_get( properties, "mlt_image_format" ); if ( img_fmt_name ) { // Set the mlt_image_format from explicit property. mlt_image_format f = mlt_image_format_id( img_fmt_name ); if ( mlt_image_invalid != f ) img_fmt = f; } else { // Set the mlt_image_format from the selected pix_fmt. const char *pix_fmt_name = av_get_pix_fmt_name( enc_ctx->video_st->codec->pix_fmt ); if ( !strcmp( pix_fmt_name, "rgba" ) || !strcmp( pix_fmt_name, "argb" ) || !strcmp( pix_fmt_name, "bgra" ) ) { mlt_properties_set( properties, "mlt_image_format", "rgb24a" ); img_fmt = mlt_image_rgb24a; } else if ( strstr( pix_fmt_name, "rgb" ) || strstr( pix_fmt_name, "bgr" ) ) { mlt_properties_set( properties, "mlt_image_format", "rgb24" ); img_fmt = mlt_image_rgb24; } } } } if ( enc_ctx->audio_codec_id != AV_CODEC_ID_NONE ) { int is_multi = 0; enc_ctx->total_channels = 0; // multitrack audio for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) { sprintf( key, "channels.%d", i ); int j = mlt_properties_get_int( properties, key ); if ( j ) { is_multi = 1; enc_ctx->total_channels += j; enc_ctx->audio_st[i] = add_audio_stream( consumer, enc_ctx->oc, audio_codec, j, av_get_default_channel_layout( j ) ); } } // single track if ( !is_multi ) { mlt_channel_layout layout = mlt_channel_layout_id( mlt_properties_get( properties, "channel_layout" ) ); if( layout == mlt_channel_auto || layout == mlt_channel_independent || mlt_channel_layout_channels( layout ) != enc_ctx->channels ) { layout = mlt_channel_layout_default( enc_ctx->channels ); } enc_ctx->audio_st[0] = add_audio_stream( consumer, enc_ctx->oc, audio_codec, enc_ctx->channels, mlt_to_av_channel_layout( layout ) ); enc_ctx->total_channels = enc_ctx->channels; } } mlt_properties_set_int( properties, "channels", enc_ctx->total_channels ); // Audio format is determined when adding the audio stream mlt_audio_format aud_fmt = mlt_audio_none; if ( enc_ctx->audio_st[0] ) aud_fmt = get_mlt_audio_format( enc_ctx->audio_st[0]->codec->sample_fmt ); enc_ctx->sample_bytes = mlt_audio_format_size( aud_fmt, 1, 1 ); enc_ctx->sample_bytes = enc_ctx->sample_bytes ? enc_ctx->sample_bytes : 1; // prevent divide by zero // Set the parameters (even though we have none...) { if ( mlt_properties_get( properties, "muxpreload" ) && ! mlt_properties_get( properties, "preload" ) ) mlt_properties_set_double( properties, "preload", mlt_properties_get_double( properties, "muxpreload" ) ); enc_ctx->oc->max_delay= ( int )( mlt_properties_get_double( properties, "muxdelay" ) * AV_TIME_BASE ); // Process properties as AVOptions char *fpre = mlt_properties_get( properties, "fpre" ); if ( fpre ) { mlt_properties p = mlt_properties_load( fpre ); apply_properties( enc_ctx->oc, p, AV_OPT_FLAG_ENCODING_PARAM ); if ( enc_ctx->oc->oformat && enc_ctx->oc->oformat->priv_class && enc_ctx->oc->priv_data ) apply_properties( enc_ctx->oc->priv_data, p, AV_OPT_FLAG_ENCODING_PARAM ); mlt_properties_close( p ); } apply_properties( enc_ctx->oc, properties, AV_OPT_FLAG_ENCODING_PARAM ); if ( enc_ctx->oc->oformat && enc_ctx->oc->oformat->priv_class && enc_ctx->oc->priv_data ) apply_properties( enc_ctx->oc->priv_data, properties, AV_OPT_FLAG_ENCODING_PARAM ); if ( enc_ctx->video_st && !open_video( properties, enc_ctx->oc, enc_ctx->video_st, vcodec? vcodec : NULL ) ) enc_ctx->video_st = NULL; for ( i = 0; i < MAX_AUDIO_STREAMS && enc_ctx->audio_st[i]; i++ ) { enc_ctx->audio_input_frame_size = open_audio( properties, enc_ctx->oc, enc_ctx->audio_st[i], enc_ctx->audio_outbuf_size, acodec? acodec : NULL ); if ( !enc_ctx->audio_input_frame_size ) { // Remove the audio stream from the output context int j; for ( j = 0; j < enc_ctx->oc->nb_streams; j++ ) { if ( enc_ctx->oc->streams[j] == enc_ctx->audio_st[i] ) av_freep( &enc_ctx->oc->streams[j] ); } --enc_ctx->oc->nb_streams; enc_ctx->audio_st[i] = NULL; } } // Setup custom I/O if redirecting if ( mlt_properties_get_int( properties, "redirect" ) ) { int buffer_size = 32768; unsigned char *buffer = av_malloc( buffer_size ); AVIOContext* io = avio_alloc_context( buffer, buffer_size, 1, properties, NULL, mlt_write, NULL ); if ( buffer && io ) { enc_ctx->oc->pb = io; enc_ctx->oc->flags |= AVFMT_FLAG_CUSTOM_IO; mlt_properties_set_data( properties, "avio_buffer", buffer, buffer_size, av_free, NULL ); mlt_properties_set_data( properties, "avio_context", io, 0, av_free, NULL ); mlt_events_register( properties, "avformat-write", (mlt_transmitter) write_transmitter ); } else { av_free( buffer ); mlt_log_error( MLT_CONSUMER_SERVICE(consumer), "failed to setup output redirection\n" ); } } // Open the output file, if needed else if ( !( fmt->flags & AVFMT_NOFILE ) ) { if ( avio_open( &enc_ctx->oc->pb, filename, AVIO_FLAG_WRITE ) < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE( consumer ), "Could not open '%s'\n", filename ); mlt_events_fire( properties, "consumer-fatal-error", NULL ); goto on_fatal_error; } } } // Last check - need at least one stream if ( !enc_ctx->audio_st[0] && !enc_ctx->video_st ) { mlt_events_fire( properties, "consumer-fatal-error", NULL ); goto on_fatal_error; } // Allocate picture enum AVPixelFormat pix_fmt; if ( enc_ctx->video_st ) { #if defined(AVFILTER) && LIBAVUTIL_VERSION_MAJOR >= 56 pix_fmt = enc_ctx->video_st->codec->pix_fmt == AV_PIX_FMT_VAAPI ? AV_PIX_FMT_NV12 : enc_ctx->video_st->codec->pix_fmt; #else pix_fmt = enc_ctx->video_st->codec->pix_fmt; #endif converted_avframe = alloc_picture( pix_fmt, width, height ); if ( !converted_avframe ) { mlt_log_error( MLT_CONSUMER_SERVICE( consumer ), "failed to allocate video AVFrame\n" ); mlt_events_fire( properties, "consumer-fatal-error", NULL ); goto on_fatal_error; } } // Allocate audio AVFrame if ( enc_ctx->audio_st[0] ) { enc_ctx->audio_avframe = av_frame_alloc(); if ( enc_ctx->audio_avframe ) { AVCodecContext *c = enc_ctx->audio_st[0]->codec; enc_ctx->audio_avframe->format = c->sample_fmt; enc_ctx->audio_avframe->nb_samples = enc_ctx->audio_input_frame_size; enc_ctx->audio_avframe->channel_layout = c->channel_layout; #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(96<<8)) && LIBAVCODEC_VERSION_MICRO >= 100 enc_ctx->audio_avframe->channels = c->channels; #endif } else { mlt_log_error( MLT_CONSUMER_SERVICE(consumer), "failed to allocate audio AVFrame\n" ); mlt_events_fire( properties, "consumer-fatal-error", NULL ); goto on_fatal_error; } } // Get the starting time (can ignore the times above) gettimeofday( &ante, NULL ); // Loop while running while( mlt_properties_get_int( properties, "running" ) && ( !enc_ctx->terminated || ( enc_ctx->video_st && mlt_deque_count( queue ) ) ) ) { if ( !frame ) frame = mlt_consumer_rt_frame( consumer ); // Check that we have a frame to work with if ( frame != NULL ) { // Default audio args frame_properties = MLT_FRAME_PROPERTIES( frame ); // Write the stream header. if ( !header_written ) { // set timecode from first frame if not been set from metadata if( !mlt_properties_get( properties, "timecode" ) ) { char *vitc = mlt_properties_get( frame_properties, "meta.attr.vitc.markup" ); if ( vitc && vitc[0] ) { mlt_log_debug( MLT_CONSUMER_SERVICE( consumer ), "timecode=[%s]\n", vitc ); av_dict_set( &enc_ctx->oc->metadata, "timecode", vitc, 0 ); if ( enc_ctx->video_st ) av_dict_set( &enc_ctx->video_st->metadata, "timecode", vitc, 0 ); }; }; if ( avformat_write_header( enc_ctx->oc, NULL ) < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE( consumer ), "Could not write header '%s'\n", filename ); mlt_events_fire( properties, "consumer-fatal-error", NULL ); goto on_fatal_error; } header_written = 1; } // Increment frames dispatched frames ++; // Check for the terminated condition enc_ctx->terminated = enc_ctx->terminate_on_pause && mlt_properties_get_double( frame_properties, "_speed" ) == 0.0; // Get audio and append to the fifo if ( !enc_ctx->terminated && enc_ctx->audio_st[0] ) { samples = mlt_sample_calculator( fps, enc_ctx->frequency, count ++ ); enc_ctx->channels = enc_ctx->total_channels; mlt_frame_get_audio( frame, &pcm, &aud_fmt, &enc_ctx->frequency, &enc_ctx->channels, &samples ); // Save the audio channel remap properties for later mlt_properties_pass( enc_ctx->frame_meta_properties, frame_properties, "meta.map.audio." ); // Create the fifo if we don't have one if ( enc_ctx->fifo == NULL ) { enc_ctx->fifo = sample_fifo_init( enc_ctx->frequency, enc_ctx->channels ); mlt_properties_set_data( properties, "sample_fifo", enc_ctx->fifo, 0, ( mlt_destructor )sample_fifo_close, NULL ); } if ( pcm ) { // Silence if not normal forward speed if ( mlt_properties_get_double( frame_properties, "_speed" ) != 1.0 ) memset( pcm, 0, samples * enc_ctx->channels * enc_ctx->sample_bytes ); // Append the samples sample_fifo_append( enc_ctx->fifo, pcm, samples * enc_ctx->channels * enc_ctx->sample_bytes ); total_time += ( samples * 1000000 ) / enc_ctx->frequency; } if ( !enc_ctx->video_st ) mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } // Encode the image if ( !enc_ctx->terminated && enc_ctx->video_st ) mlt_deque_push_back( queue, frame ); else mlt_frame_close( frame ); frame = NULL; } // While we have stuff to process, process... while ( 1 ) { // Write interleaved audio and video frames if ( !enc_ctx->video_st || ( enc_ctx->video_st && enc_ctx->audio_st[0] && enc_ctx->audio_pts < enc_ctx->video_pts ) ) { // Write audio int fifo_frames = sample_fifo_used( enc_ctx->fifo ) / ( enc_ctx->audio_input_frame_size * enc_ctx->channels * enc_ctx->sample_bytes ); if ( ( enc_ctx->video_st && enc_ctx->terminated ) || fifo_frames ) { int r = encode_audio(enc_ctx); if ( r > 0 ) break; else if ( r < 0 ) goto on_fatal_error; } else { break; } } else if ( enc_ctx->video_st ) { // Write video if ( mlt_deque_count( queue ) ) { int ret = 0; AVCodecContext *c = enc_ctx->video_st->codec; frame = mlt_deque_pop_front( queue ); frame_properties = MLT_FRAME_PROPERTIES( frame ); if ( mlt_properties_get_int( frame_properties, "rendered" ) ) { AVFrame video_avframe; mlt_frame_get_image( frame, &image, &img_fmt, &img_width, &img_height, 0 ); mlt_image_format_planes( img_fmt, width, height, image, video_avframe.data, video_avframe.linesize ); // Do the colour space conversion int srcfmt = pick_pix_fmt( img_fmt ); int flags = mlt_get_sws_flags( width, height, srcfmt, width, height, pix_fmt); struct SwsContext *context = sws_getContext( width, height, srcfmt, width, height, pix_fmt, flags, NULL, NULL, NULL); int src_colorspace = mlt_properties_get_int( frame_properties, "colorspace" ); int src_full_range = mlt_properties_get_int( frame_properties, "full_luma" ); if ( (src_colorspace && dst_colorspace != src_colorspace) || dst_full_range != src_full_range ) mlt_set_luma_transfer( context, src_colorspace, dst_colorspace, src_full_range, dst_full_range ); sws_scale( context, (const uint8_t* const*) video_avframe.data, video_avframe.linesize, 0, height, converted_avframe->data, converted_avframe->linesize); sws_freeContext( context ); mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); // Apply the alpha if applicable if ( !mlt_properties_get( properties, "mlt_image_format" ) || strcmp( mlt_properties_get( properties, "mlt_image_format" ), "rgb24a" ) ) if ( c->pix_fmt == AV_PIX_FMT_RGBA || c->pix_fmt == AV_PIX_FMT_ARGB || c->pix_fmt == AV_PIX_FMT_BGRA ) { uint8_t *p; uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); register int n; for ( i = 0; i < height; i ++ ) { n = ( width + 7 ) / 8; p = converted_avframe->data[ 0 ] + i * converted_avframe->linesize[ 0 ] + 3; 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 defined(AVFILTER) && LIBAVUTIL_VERSION_MAJOR >= 56 if (AV_PIX_FMT_VAAPI == c->pix_fmt) { AVFilterContext *vfilter_in = mlt_properties_get_data(properties, "vfilter_in", NULL); AVFilterContext *vfilter_out = mlt_properties_get_data(properties, "vfilter_out", NULL); if (vfilter_in && vfilter_out) { if (!avframe) avframe = av_frame_alloc(); ret = av_buffersrc_add_frame(vfilter_in, converted_avframe); ret = av_buffersink_get_frame(vfilter_out, avframe); if (ret < 0) { mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "error with hwupload: %d (frame %d)\n", ret, enc_ctx->frame_count); if (++enc_ctx->error_count > 2) goto on_fatal_error; ret = 0; } } } else { avframe = converted_avframe; } #else avframe = converted_avframe; #endif } #ifdef AVFMT_RAWPICTURE if (enc_ctx->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); // Set frame interlace hints if ( mlt_properties_get_int( frame_properties, "progressive" ) ) c->field_order = AV_FIELD_PROGRESSIVE; else c->field_order = (mlt_properties_get_int( frame_properties, "top_field_first" )) ? AV_FIELD_TB : AV_FIELD_BT; pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = enc_ctx->video_st->index; pkt.data = (uint8_t*) avframe; pkt.size = sizeof(AVPicture); ret = av_write_frame(enc_ctx->oc, &pkt); } else #endif { AVPacket pkt; av_init_packet( &pkt ); if ( c->codec->id == AV_CODEC_ID_RAWVIDEO ) { pkt.data = NULL; pkt.size = 0; } else { pkt.data = video_outbuf; pkt.size = video_outbuf_size; } // Set the quality avframe->quality = c->global_quality; avframe->pts = enc_ctx->frame_count; // Set frame interlace hints avframe->interlaced_frame = !mlt_properties_get_int( frame_properties, "progressive" ); avframe->top_field_first = mlt_properties_get_int( frame_properties, "top_field_first" ); if ( mlt_properties_get_int( frame_properties, "progressive" ) ) c->field_order = AV_FIELD_PROGRESSIVE; else if ( c->codec_id == AV_CODEC_ID_MJPEG ) c->field_order = (mlt_properties_get_int( frame_properties, "top_field_first" )) ? AV_FIELD_TT : AV_FIELD_BB; else c->field_order = (mlt_properties_get_int( frame_properties, "top_field_first" )) ? AV_FIELD_TB : AV_FIELD_BT; // Encode the image #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(37<<8)+0) ret = avcodec_send_frame( c, avframe ); if ( ret < 0 ) { pkt.size = ret; } else { receive_video_packet: ret = avcodec_receive_packet( c, &pkt ); if ( ret == AVERROR(EAGAIN) || ret == AVERROR_EOF ) pkt.size = ret = 0; else if ( ret < 0 ) pkt.size = ret; } #else int got_packet; ret = avcodec_encode_video2( c, &pkt, avframe, &got_packet ); if ( ret < 0 ) pkt.size = ret; else if ( !got_packet ) pkt.size = 0; #endif // If zero size, it means the image was buffered if ( pkt.size > 0 ) { if ( pkt.pts != AV_NOPTS_VALUE ) pkt.pts = av_rescale_q( pkt.pts, c->time_base, enc_ctx->video_st->time_base ); if ( pkt.dts != AV_NOPTS_VALUE ) pkt.dts = av_rescale_q( pkt.dts, c->time_base, enc_ctx->video_st->time_base ); pkt.stream_index = enc_ctx->video_st->index; // write the compressed frame in the media file ret = av_interleaved_write_frame(enc_ctx->oc, &pkt); mlt_log_debug( MLT_CONSUMER_SERVICE( consumer ), " frame_size %d\n", 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 ); enc_ctx->error_count = 0; #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(37<<8)+0) if ( !ret ) goto receive_video_packet; #endif } else if ( pkt.size < 0 ) { mlt_log_warning( MLT_CONSUMER_SERVICE(consumer), "error with video encode: %d (frame %d)\n", pkt.size, enc_ctx->frame_count ); if ( ++enc_ctx->error_count > 2 ) goto on_fatal_error; ret = 0; } } enc_ctx->frame_count++; enc_ctx->video_pts = (double) enc_ctx->frame_count * av_q2d( enc_ctx->video_st->codec->time_base ); if ( ret ) { mlt_log_fatal( MLT_CONSUMER_SERVICE(consumer), "error writing video frame: %d\n", ret ); mlt_events_fire( properties, "consumer-fatal-error", NULL ); goto on_fatal_error; } mlt_frame_close( frame ); frame = NULL; #if defined(AVFILTER) && LIBAVUTIL_VERSION_MAJOR >= 56 if (AV_PIX_FMT_VAAPI == c->pix_fmt) av_frame_unref( avframe ); #endif } else { break; } } if ( enc_ctx->audio_st[0] ) mlt_log_debug( MLT_CONSUMER_SERVICE( consumer ), "audio pts %f ", enc_ctx->audio_pts ); if ( enc_ctx->video_st ) mlt_log_debug( MLT_CONSUMER_SERVICE( consumer ), "video pts %f ", enc_ctx->video_pts ); mlt_log_debug( MLT_CONSUMER_SERVICE( consumer ), "\n" ); } if ( real_time_output == 1 && frames % 2 == 0 ) { long passed = time_difference( &ante ); if ( enc_ctx->fifo != NULL ) { long pending = ( ( ( long )sample_fifo_used( enc_ctx->fifo ) / enc_ctx->sample_bytes * 1000 ) / enc_ctx->frequency ) * 1000; passed -= pending; } if ( passed < total_time ) { long total = ( total_time - passed ); struct timespec t = { total / 1000000, ( total % 1000000 ) * 1000 }; nanosleep( &t, NULL ); } } } // Flush the encoder buffers if ( real_time_output <= 0 ) { // Flush audio fifo // TODO: flush all audio streams if ( enc_ctx->fifo && enc_ctx->audio_st[0] ) for (;;) { int sz = sample_fifo_used( enc_ctx->fifo ); int ret = encode_audio( enc_ctx ); mlt_log_debug( MLT_CONSUMER_SERVICE( consumer ), "flushing audio: sz=%d, ret=%d\n", sz, ret ); if ( !sz || ret < 0 ) break; } // Flush video #ifdef AVFMT_RAWPICTURE if ( enc_ctx->video_st && !( enc_ctx->oc->oformat->flags & AVFMT_RAWPICTURE ) ) for (;;) #else if ( enc_ctx->video_st ) for (;;) #endif { AVCodecContext *c = enc_ctx->video_st->codec; AVPacket pkt; av_init_packet( &pkt ); if ( c->codec->id == AV_CODEC_ID_RAWVIDEO ) { pkt.data = NULL; pkt.size = 0; } else { pkt.data = video_outbuf; pkt.size = video_outbuf_size; } // Encode the image #if LIBAVCODEC_VERSION_INT >= ((57<<16)+(37<<8)+0) int ret; while ( (ret = avcodec_receive_packet( c, &pkt )) == AVERROR(EAGAIN) ) { ret = avcodec_send_frame( c, NULL ); if ( ret < 0 ) { mlt_log_warning( MLT_CONSUMER_SERVICE(consumer), "error with video encode: %d\n", ret ); break; } } #else int got_packet = 0; int ret = avcodec_encode_video2( c, &pkt, NULL, &got_packet ); if ( ret < 0 ) pkt.size = ret; else if ( !got_packet ) pkt.size = 0; #endif mlt_log_debug( MLT_CONSUMER_SERVICE( consumer ), "flushing video size %d\n", pkt.size ); if ( pkt.size < 0 ) break; // 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 ); if ( !pkt.size ) break; if ( pkt.pts != AV_NOPTS_VALUE ) pkt.pts = av_rescale_q( pkt.pts, c->time_base, enc_ctx->video_st->time_base ); if ( pkt.dts != AV_NOPTS_VALUE ) pkt.dts = av_rescale_q( pkt.dts, c->time_base, enc_ctx->video_st->time_base ); pkt.stream_index = enc_ctx->video_st->index; // write the compressed frame in the media file if ( av_interleaved_write_frame( enc_ctx->oc, &pkt ) != 0 ) { mlt_log_fatal( MLT_CONSUMER_SERVICE(consumer), "error writing flushed video frame\n" ); mlt_events_fire( properties, "consumer-fatal-error", NULL ); goto on_fatal_error; } } } on_fatal_error: if ( frame ) mlt_frame_close( frame ); // Write the trailer, if any if ( frames ) av_write_trailer( enc_ctx->oc ); // Clean up input and output frames if ( converted_avframe ) av_free( converted_avframe->data[0] ); av_free( converted_avframe ); #if defined(AVFILTER) && LIBAVUTIL_VERSION_MAJOR >= 56 if (enc_ctx->video_st && enc_ctx->video_st->codec && AV_PIX_FMT_VAAPI == enc_ctx->video_st->codec->pix_fmt) av_frame_free(&avframe); #endif av_free( video_outbuf ); av_free( enc_ctx->audio_avframe ); // close each codec if ( enc_ctx->video_st ) close_video(enc_ctx->oc, enc_ctx->video_st); for ( i = 0; i < MAX_AUDIO_STREAMS && enc_ctx->audio_st[i]; i++ ) close_audio( enc_ctx->oc, enc_ctx->audio_st[i] ); // Free the streams for ( i = 0; i < enc_ctx->oc->nb_streams; i++ ) av_freep( &enc_ctx->oc->streams[i] ); // Close the output file if ( !( fmt->flags & AVFMT_NOFILE ) && !mlt_properties_get_int( properties, "redirect" ) ) { if ( enc_ctx->oc->pb ) avio_close( enc_ctx->oc->pb ); } // Free the stream av_free( enc_ctx->oc ); // Just in case we terminated on pause mlt_consumer_stopped( consumer ); mlt_properties_close( enc_ctx->frame_meta_properties ); if ( mlt_properties_get_int( properties, "pass" ) > 1 ) { // 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 ); file = "x264_2pass.log.mbtree"; full = malloc( strlen( cwd ) + strlen( file ) + 2 ); sprintf( full, "%s/%s", cwd, file ); remove( full ); free( full ); free( cwd ); remove( "x264_2pass.log.temp" ); // Recent versions of libavcodec/x264 support passlogfile and need cleanup if specified. if ( !mlt_properties_get( properties, "_logfilename" ) && mlt_properties_get( properties, "passlogfile" ) ) { mlt_properties_get( properties, "passlogfile" ); file = mlt_properties_get( properties, "passlogfile" ); remove( file ); full = malloc( strlen( file ) + strlen( ".mbtree" ) + 1 ); sprintf( full, "%s.mbtree", file ); remove( full ); free( full ); } } while ( ( frame = mlt_deque_pop_back( queue ) ) ) mlt_frame_close( frame ); mlt_pool_release( enc_ctx ); return NULL; } /** Close the consumer. */ static void consumer_close( mlt_consumer consumer ) { // Stop the consumer mlt_consumer_stop( consumer ); // Close the parent mlt_consumer_close( consumer ); // Free the memory free( consumer ); } mlt-6.20.0/src/modules/avformat/consumer_avformat.yml000066400000000000000000000256731362234133600227460ustar00rootroot00000000000000schema_version: 0.3 type: consumer identifier: avformat title: FFmpeg Output version: 3 copyright: Copyright (C) 2003-2019 Meltytech, LLC license: LGPL language: en url: http://www.ffmpeg.org/ creator: Charles Yates contributor: - Dan Dennedy tags: - Audio - Video description: Write or stream audio and/or video using FFmpeg. notes: > The avformat consumer uses the FFmpeg/libav libraries to encode to a file or network stream. You can get a lot of information about how to encode with FFmpeg all over the web including FFmpeg/libav's web site. With melt, you simply need to add "-consumer avformat:output.file" to the command line followed by the encoding parameters by translating ffmpeg's '-option value' syntax to melt's 'option=value' syntax. Not all ffmpeg options are supported. Some are very specific to avconv/ffmpeg, the command line utility, and not an "AVOption" used in the libraries. In some cases, there are ffmpeg options that are not AVOptions but which closely resemble an existing MLT property. In that case, MLT supports the ffmpeg option name. For example, ffmpeg's "-ac" is equivalent to the MLT "channels" property. Therefore, the avformat consumer also supports the "ac" property. Complete details are below. Please note that the exact options depend on the version of libavformat and libavcodec on your system. The following is based on FFmpeg v4.0. parameters: - identifier: target argument: yes title: File/URL type: string description: > This is not the same thing as the ffmpeg -target option! If this is not supplied then it will output to stdout. widget: filesave - identifier: mlt_profile title: MLT Profile type: string description: > Choose a MLT basic video settings preset. This overrides a profile that may have been set elsewhere. - identifier: redirect title: Redirect I/O description: > This option allows other services to encapsulate the avformat consumer and do something different (not already available in a protocol) with its output by listening to the avformat-write event. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox # These override the MLT profile - identifier: width title: Width type: integer minimum: 0 unit: pixels - identifier: height title: Height type: integer minimum: 0 unit: pixels - identifier: display_aspect_num title: Display aspect ratio numerator type: integer minimum: 0 - identifier: display_aspect_den title: Display aspect ratio denominator type: integer minimum: 0 - identifier: display_ratio title: Display aspect ratio readonly: yes - identifier: sample_aspect_num title: Sample aspect ratio numerator type: integer minimum: 0 - identifier: sample_aspect_den title: Sample aspect ratio denominator type: integer minimum: 1 - identifier: progressive title: Progressive type: integer minimum: 0 maximum: 1 widget: checkbox - identifier: colorspace title: Colorspace type: integer description: Set the video colorspace (Y'CbCr only). values: - 240 # SMPTE 240M - 601 # ITU-R BT.601 - 709 # ITU-R BT.709 - identifier: frame_rate_num title: Frame rate numerator type: integer minimum: 0 unit: frames/second - identifier: frame_rate_den title: Frame rate denominator type: integer minimum: 1 unit: frames/second - identifier: fps title: Frame rate readonly: yes unit: frames/second # These are common to all consumers. - identifier: deinterlace_method title: Deinterlacer type: string default: yadif values: - greedy - linearblend - onefield - yadif - yadif-nospatial - identifier: rescale title: Image scaler type: string description: Set the pixel interpolation mode. values: - nearest - bilinear - bicubic - bicublin - gauss - sinc - lanczos - spline - identifier: frequency title: Audio sample rate type: integer minimum: 0 maximum: 256000 default: 48000 unit: Hz - identifier: channels title: Audio channels type: integer minimum: 1 maximum: 16 default: 2 - identifier: channels.0 title: Channels on track 1 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.1 title: Channels on track 2 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.2 title: Channels on track 3 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.3 title: Channels on track 4 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.4 title: Channels on track 5 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.5 title: Channels on track 6 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.6 title: Channels on track 7 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 - identifier: channels.7 title: Channels on track 8 type: integer description: Used to map a bundle of channels to multi-track audio. minimum: 0 maximum: 16 default: 0 # These are common to all consumers and affect runtime behavior - identifier: terminate_on_pause title: File output type: integer description: Disable this for streaming. minimum: 0 maximum: 1 default: 1 widget: checkbox - identifier: real_time title: Drop frames type: integer description: > Set the number of processing threads and enable frame-dropping (positive) or disable frame-dropping (negative). default: -1 widget: spinner unit: threads - identifier: prefill title: Pre-roll type: integer description: Set the number of frames to buffer before starting actual output. minimum: 1 default: 1 unit: frames - identifier: buffer title: Buffer type: integer description: > Set the maximum number of frames to buffer - process ahead of the output position. minimum: 1 default: 25 unit: frames # These are ffmpeg-compatible aliases to MLT properties - identifier: s title: Size type: string description: > This is a ffmpeg-compatible equivalent to the MLT profile and width and height parameters. format: WxH unit: pixels - identifier: aspect title: Aspect ratio type: string description: > This is a ffmpeg-compatible equivalent to the MLT profile and other aspect ratio parameters. format: numerator:denominator - identifier: deinterlace title: Deinterlace type: integer description: > This is a ffmpeg-compatible equivalent to the MLT profile and progressive parameter. minimum: 0 maximum: 1 - identifier: r title: Frame rate type: float description: > This is a ffmpeg-compatible equivalent to the MLT profile and frame rate parameters. minimum: 5.0 - identifier: ac title: Audio channels type: integer description: > This is a ffmpeg-compatible equivalent to the channels parameter. minimum: 1 maximum: 16 default: 2 - identifier: ar title: Audio sample rate type: integer description: > This is a ffmpeg-compatible equivalent to the frequency parameter. minimum: 0 maximum: 256000 default: 48000 unit: Hz # These are other non-AVOption parameters specific to FFmpeg. - identifier: threads title: Encoding threads type: integer minimum: 0 maximum: 16 default: 1 widget: spinner unit: threads - identifier: aq title: Audio quality type: integer description: The meaning depends upon the codec. - identifier: dc title: Intra DC precision type: integer default: 8 - identifier: muxdelay title: Muxer delay type: float description: Set the maximum demux-decode delay. default: 0.7 unit: seconds - identifier: muxpreload title: Muxer preload type: float description: Set the initial demux-decode delay. default: 0.5 unit: seconds - identifier: f title: Format type: string description: Use "list" to see the list of formats. default: mpeg - identifier: acodec title: Audio codec description: Use "list" to see the list of audio codecs. default: mp2 - identifier: vcodec title: Video codec description: Use "list" to see the list of video codecs. default: mpeg2video - identifier: atag title: Audio FourCC type: string - identifier: apre title: Audio codec preset type: string - identifier: vpre title: Video codec preset type: string - identifier: fpre title: Format preset type: string - identifier: alang title: Audio language type: string description: Set the 3-character ISO 639 language code of the current audio stream. - identifier: pix_fmt title: Pixel format type: string description: > See 'ffmpeg -pix_fmt list' to see a list of values. Normally, this is not required, but some codecs support multiple pixel formats, especially chroma bit-depth. - identifier: qscale title: Video quantizer type: float description: Set a fixed video quantizer scale for constant quality VBR output. - identifier: vtag title: Video FourCC type: string - identifier: rc_override title: Rate control type: string format: start_frame,end_frame,qscale/... description: This is an override for specific intervals. - identifier: pass title: Pass type: integer description: Select the pass number for two-pass encoding. minimum: 1 maximum: 2 - identifier: passlogfile title: Two-pass log file type: string - identifier: vb title: Video bitrate type: string unit: bits/second description: > Normally this is an integer, but you can append a K suffix for convenience. minimum: 0 - identifier: ab title: Audio bitrate type: string unit: bits/second description: > Normally this is an integer, but you can append a K suffix for convenience. - identifier: an title: Disable audio type: integer minimum: 0 maximum: 1 widget: checkbox - identifier: vn title: Disable video type: integer minimum: 0 maximum: 1 widget: checkbox mlt-6.20.0/src/modules/avformat/factory.c000066400000000000000000000370271362234133600203000ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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_swresample_init( mlt_profile profile, char *arg ); extern mlt_filter filter_swscale_init( mlt_profile profile, char *arg ); extern mlt_producer producer_avformat_init( mlt_profile profile, const char *service, char *file ); extern mlt_filter filter_avfilter_init( mlt_profile, mlt_service_type, const char*, char* ); // ffmpeg Header files #include #ifdef AVDEVICE #include #endif #ifdef AVFILTER #include #endif #include // A static flag used to determine if avformat has been initialised static int avformat_initialised = 0; static int avformat_lockmgr(void **mutex, enum AVLockOp op) { pthread_mutex_t** pmutex = (pthread_mutex_t**) mutex; switch (op) { case AV_LOCK_CREATE: *pmutex = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t)); if (!*pmutex) return -1; pthread_mutex_init(*pmutex, NULL); break; case AV_LOCK_OBTAIN: if (!*pmutex) return -1; pthread_mutex_lock(*pmutex); break; case AV_LOCK_RELEASE: if (!*pmutex) return -1; pthread_mutex_unlock(*pmutex); break; case AV_LOCK_DESTROY: if (!*pmutex) return -1; pthread_mutex_destroy(*pmutex); free(*pmutex); *pmutex = NULL; break; } return 0; } static void unregister_lockmgr( void *p ) { av_lockmgr_register( NULL ); } static void avformat_init( ) { // Initialise avformat if necessary if ( avformat_initialised == 0 ) { avformat_initialised = 1; av_lockmgr_register( &avformat_lockmgr ); mlt_factory_register_for_clean_up( &avformat_lockmgr, unregister_lockmgr ); av_register_all( ); #ifdef AVDEVICE avdevice_register_all(); #endif #ifdef AVFILTER avfilter_register_all(); #endif avformat_network_init(); av_log_set_level( mlt_log_get_level() ); if ( getenv("MLT_AVFORMAT_PRODUCER_CACHE") ) { int n = atoi( getenv("MLT_AVFORMAT_PRODUCER_CACHE" ) ); mlt_service_cache_set_size( NULL, "producer_avformat", n ); } } } static void *create_service( mlt_profile profile, mlt_service_type type, const char *id, void *arg ) { avformat_init( ); #ifdef CODECS if ( !strncmp( id, "avformat", 8 ) ) { if ( type == producer_type ) return producer_avformat_init( profile, id, arg ); else if ( type == consumer_type ) return consumer_avformat_init( profile, arg ); } #endif #ifdef FILTERS if ( !strcmp( id, "avcolor_space" ) ) return filter_avcolour_space_init( arg ); if ( !strcmp( id, "avcolour_space" ) ) return filter_avcolour_space_init( arg ); if ( !strcmp( id, "avdeinterlace" ) ) return filter_avdeinterlace_init( arg ); if ( !strcmp( id, "swscale" ) ) return filter_swscale_init( profile, arg ); #endif #ifdef SWRESAMPLE if ( !strcmp( id, "swresample" ) ) return filter_swresample_init( profile, arg ); #endif return NULL; } static void add_parameters( mlt_properties params, void *object, int req_flags, const char *unit, const char *subclass, const char *id_prefix ) { const AVOption *opt = NULL; // For each AVOption on the AVClass object while ( ( opt = av_opt_next( object, opt ) ) ) { // If matches flags and not a binary option (not supported by Mlt) if ( !( opt->flags & req_flags ) || ( opt->type == AV_OPT_TYPE_BINARY ) ) continue; // Ignore constants (keyword values) if ( !unit && opt->type == AV_OPT_TYPE_CONST ) continue; // When processing a groups of options (unit)... // ...ignore non-constants else if ( unit && opt->type != AV_OPT_TYPE_CONST ) continue; // ...ignore constants not in this group else if ( unit && opt->type == AV_OPT_TYPE_CONST && strcmp( unit, opt->unit ) ) continue; // ..add constants to the 'values' sequence else if ( unit && opt->type == AV_OPT_TYPE_CONST ) { char key[20]; snprintf( key, 20, "%d", mlt_properties_count( params ) ); mlt_properties_set( params, key, opt->name ); continue; } // Create a map for this option. mlt_properties p = mlt_properties_new(); char key[20]; snprintf( key, 20, "%d", mlt_properties_count( params ) ); // Add the map to the 'parameters' sequence. mlt_properties_set_data( params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL ); // Add the parameter metadata for this AVOption. if ( id_prefix ) { char id[200]; snprintf( id, sizeof(id), "%s%s", id_prefix, opt->name ); mlt_properties_set( p, "identifier", id ); } else { mlt_properties_set( p, "identifier", opt->name ); } if ( opt->help ) { if ( subclass ) { char *s = malloc( strlen( opt->help ) + strlen( subclass ) + 4 ); strcpy( s, opt->help ); strcat( s, " (" ); strcat( s, subclass ); strcat( s, ")" ); mlt_properties_set( p, "description", s ); free( s ); } else mlt_properties_set( p, "description", opt->help ); } switch ( opt->type ) { case AV_OPT_TYPE_FLAGS: mlt_properties_set( p, "type", "string" ); mlt_properties_set( p, "format", "flags" ); break; case AV_OPT_TYPE_INT: if ( !opt->unit ) { mlt_properties_set( p, "type", "integer" ); if ( opt->min != INT_MIN ) mlt_properties_set_int( p, "minimum", (int) opt->min ); if ( opt->max != INT_MAX ) mlt_properties_set_int( p, "maximum", (int) opt->max ); mlt_properties_set_int( p, "default", (int) opt->default_val.i64 ); } else { mlt_properties_set( p, "type", "string" ); mlt_properties_set( p, "format", "integer or keyword" ); } break; case AV_OPT_TYPE_INT64: mlt_properties_set( p, "type", "integer" ); mlt_properties_set( p, "format", "64-bit" ); if ( opt->min != INT64_MIN ) mlt_properties_set_int64( p, "minimum", (int64_t) opt->min ); if ( opt->max != INT64_MAX ) mlt_properties_set_int64( p, "maximum", (int64_t) opt->max ); mlt_properties_set_int64( p, "default", (int64_t) opt->default_val.i64 ); break; case AV_OPT_TYPE_FLOAT: mlt_properties_set( p, "type", "float" ); if ( opt->min != FLT_MIN && opt->min != -340282346638528859811704183484516925440.0 ) mlt_properties_set_double( p, "minimum", opt->min ); if ( opt->max != FLT_MAX ) mlt_properties_set_double( p, "maximum", opt->max ); mlt_properties_set_double( p, "default", opt->default_val.dbl ); break; case AV_OPT_TYPE_DOUBLE: mlt_properties_set( p, "type", "float" ); mlt_properties_set( p, "format", "double" ); if ( opt->min != DBL_MIN ) mlt_properties_set_double( p, "minimum", opt->min ); if ( opt->max != DBL_MAX ) mlt_properties_set_double( p, "maximum", opt->max ); mlt_properties_set_double( p, "default", opt->default_val.dbl ); break; case AV_OPT_TYPE_STRING: mlt_properties_set( p, "type", "string" ); if ( opt->default_val.str ) { size_t len = strlen( opt->default_val.str ) + 3; char* quoted = malloc( len ); snprintf( quoted, len, "'%s'", opt->default_val.str ); mlt_properties_set( p, "default", quoted ); free( quoted ); } break; case AV_OPT_TYPE_RATIONAL: mlt_properties_set( p, "type", "string" ); mlt_properties_set( p, "format", "numerator/denominator" ); break; case AV_OPT_TYPE_CONST: mlt_properties_set( p, "type", "integer" ); mlt_properties_set( p, "format", "constant" ); break; default: mlt_properties_set( p, "type", "string" ); break; } // If the option belongs to a group (unit) and is not a constant (keyword value) if ( opt->unit && opt->type != AV_OPT_TYPE_CONST ) { // Create a 'values' sequence. mlt_properties values = mlt_properties_new(); // Recurse to add constants in this group to the 'values' sequence. add_parameters( values, object, req_flags, opt->unit, NULL, NULL ); if ( mlt_properties_count( values ) ) mlt_properties_set_data( p, "values", values, 0, (mlt_destructor) mlt_properties_close, NULL ); else mlt_properties_close( values ); } } } static mlt_properties avformat_metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; const char *service_type = NULL; mlt_properties result = NULL; // Convert the service type to a string. 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; } // Load the yaml file snprintf( file, PATH_MAX, "%s/avformat/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id ); result = mlt_properties_parse_yaml( file ); if ( result && ( type == consumer_type || type == producer_type ) ) { // Annotate the yaml properties with AVOptions. mlt_properties params = (mlt_properties) mlt_properties_get_data( result, "parameters", NULL ); AVFormatContext *avformat = avformat_alloc_context(); AVCodecContext *avcodec = avcodec_alloc_context3( NULL ); int flags = ( type == consumer_type )? AV_OPT_FLAG_ENCODING_PARAM : AV_OPT_FLAG_DECODING_PARAM; add_parameters( params, avformat, flags, NULL, NULL, NULL ); avformat_init(); if ( type == producer_type ) { AVInputFormat *f = NULL; while ( ( f = av_iformat_next( f ) ) ) if ( f->priv_class ) add_parameters( params, &f->priv_class, flags, NULL, f->name, NULL ); } else { AVOutputFormat *f = NULL; while ( ( f = av_oformat_next( f ) ) ) if ( f->priv_class ) add_parameters( params, &f->priv_class, flags, NULL, f->name, NULL ); } add_parameters( params, avcodec, flags, NULL, NULL, NULL ); AVCodec *c = NULL; while ( ( c = av_codec_next( c ) ) ) if ( c->priv_class ) add_parameters( params, &c->priv_class, flags, NULL, c->name, NULL ); av_free( avformat ); av_free( avcodec ); } return result; } #ifdef AVFILTER static mlt_properties avfilter_metadata( mlt_service_type type, const char *id, void *name ) { AVFilter *f = (AVFilter*)avfilter_get_by_name ( name ); if( !f ) return NULL; mlt_properties metadata = mlt_properties_new(); mlt_properties_set_double ( metadata, "schema_version" , 0.3 ); mlt_properties_set ( metadata, "title" , f->name ); mlt_properties_set ( metadata, "version", LIBAVFILTER_IDENT ); mlt_properties_set ( metadata, "identifier" , id ); mlt_properties_set ( metadata, "description" , f->description ); mlt_properties_set ( metadata, "creator" , "libavfilter maintainers" ); mlt_properties_set ( metadata, "type" , "filter" ); mlt_properties tags = mlt_properties_new ( ); mlt_properties_set_data ( metadata , "tags" , tags , 0 , ( mlt_destructor )mlt_properties_close, NULL ); if( avfilter_pad_get_type( f->inputs, 0 ) == AVMEDIA_TYPE_VIDEO ) { mlt_properties_set ( tags , "0" , "Video" ); } if( avfilter_pad_get_type( f->inputs, 0 ) == AVMEDIA_TYPE_AUDIO ) { mlt_properties_set ( tags , "0" , "Audio" ); } if ( f->priv_class ) { mlt_properties params = mlt_properties_new ( ); mlt_properties_set_data( metadata , "parameters" , params , 0 , ( mlt_destructor )mlt_properties_close, NULL ); add_parameters( params, &f->priv_class, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM, NULL, NULL, "av." ); // Add the parameters common to all avfilters. if ( f->flags & AVFILTER_FLAG_SLICE_THREADS ) { mlt_properties p = mlt_properties_new(); char key[20]; snprintf( key, 20, "%d", mlt_properties_count( params ) ); mlt_properties_set_data( params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL ); mlt_properties_set( p, "identifier", "av.threads" ); mlt_properties_set( p, "description", "Maximum number of threads" ); mlt_properties_set( p, "type", "integer" ); mlt_properties_set_int( p, "minimum", 0 ); mlt_properties_set_int( p, "default", 0 ); } { mlt_properties p = mlt_properties_new(); char key[20]; int i = 0; snprintf( key, 20, "%d", mlt_properties_count( params ) ); mlt_properties_set_data( params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL ); mlt_properties_set( p, "identifier", "position" ); mlt_properties_set( p, "description", "The MLT position value to set on avfilter frames" ); mlt_properties_set( p, "type", "string" ); mlt_properties_set( p, "default", "frame" ); mlt_properties values = mlt_properties_new(); mlt_properties_set_data( p, "values", values, 0, (mlt_destructor) mlt_properties_close, NULL ); snprintf( key, 20, "%d", i++ ); mlt_properties_set( values, key, "frame" ); snprintf( key, 20, "%d", i++ ); mlt_properties_set( values, key, "filter" ); snprintf( key, 20, "%d", i++ ); mlt_properties_set( values, key, "source" ); snprintf( key, 20, "%d", i++ ); mlt_properties_set( values, key, "producer" ); } } return metadata; } #endif MLT_REPOSITORY { #ifdef CODECS MLT_REGISTER( consumer_type, "avformat", create_service ); MLT_REGISTER( producer_type, "avformat", create_service ); MLT_REGISTER( producer_type, "avformat-novalidate", create_service ); MLT_REGISTER_METADATA( consumer_type, "avformat", avformat_metadata, NULL ); 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, "swscale", create_service ); #ifdef AVFILTER char dirname[PATH_MAX]; snprintf( dirname, PATH_MAX, "%s/avformat/blacklist.txt", mlt_environment( "MLT_DATA" ) ); mlt_properties blacklist = mlt_properties_load( dirname ); // Load a list of parameters impacted by consumer scale into global properties. snprintf(dirname, PATH_MAX, "%s/avformat/resolution_scale.yml", mlt_environment("MLT_DATA")); mlt_properties_set_data(mlt_global_properties(), "avfilter.resolution_scale", mlt_properties_parse_yaml(dirname), 0, (mlt_destructor) mlt_properties_close, NULL); avfilter_register_all(); AVFilter *f = NULL; while ( ( f = (AVFilter*)avfilter_next( f ) ) ) { // Support filters that have one input and one output of the same type. if ( avfilter_pad_count( f->inputs ) == 1 && avfilter_pad_count( f->outputs ) == 1 && avfilter_pad_get_type( f->inputs, 0 ) == avfilter_pad_get_type( f->outputs, 0 ) && !mlt_properties_get( blacklist, f->name ) ) { char service_name[1024]="avfilter."; strncat( service_name, f->name, sizeof( service_name ) - strlen( service_name ) -1 ); MLT_REGISTER( filter_type, service_name, filter_avfilter_init ); MLT_REGISTER_METADATA( filter_type, service_name, avfilter_metadata, (void*)f->name ); } } mlt_properties_close( blacklist ); #endif // AVFILTER #endif #ifdef SWRESAMPLE MLT_REGISTER( filter_type, "swresample", create_service ); #endif } mlt-6.20.0/src/modules/avformat/filter_avcolour_space.c000066400000000000000000000201631362234133600231740ustar00rootroot00000000000000/* * filter_avcolour_space.c -- Colour space filter * Copyright (C) 2004-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include // ffmpeg Header files #include #include #include #include #include #if 0 // This test might come in handy elsewhere someday. static 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; } #endif #define IMAGE_ALIGN (1) static int convert_mlt_to_av_cs( mlt_image_format format ) { int value = 0; switch( format ) { case mlt_image_rgb24: value = AV_PIX_FMT_RGB24; break; case mlt_image_rgb24a: case mlt_image_opengl: value = AV_PIX_FMT_RGBA; break; case mlt_image_yuv422: value = AV_PIX_FMT_YUYV422; break; case mlt_image_yuv420p: value = AV_PIX_FMT_YUV420P; break; case mlt_image_yuv422p16: value = AV_PIX_FMT_YUV422P16LE; break; default: mlt_log_error( NULL, "[filter avcolor_space] Invalid format %s\n", mlt_image_format_name( format ) ); break; } return value; } // returns set_lumage_transfer result static int av_convert_image( uint8_t *out, uint8_t *in, int out_fmt, int in_fmt, int width, int height, int src_colorspace, int dst_colorspace, int use_full_range ) { uint8_t *in_data[4]; int in_stride[4]; uint8_t *out_data[4]; int out_stride[4]; int flags = mlt_get_sws_flags( width, height, in_fmt, width, height, out_fmt); int error = -1; if ( in_fmt == AV_PIX_FMT_YUV422P16LE ) mlt_image_format_planes(mlt_image_yuv422p16, width, height, in, in_data, in_stride); else av_image_fill_arrays(in_data, in_stride, in, in_fmt, width, height, IMAGE_ALIGN); if ( out_fmt == AV_PIX_FMT_YUV422P16LE ) mlt_image_format_planes(mlt_image_yuv422p16, width, height, out, out_data, out_stride); else av_image_fill_arrays(out_data, out_stride, out, out_fmt, width, height, IMAGE_ALIGN); struct SwsContext *context = sws_getContext( width, height, in_fmt, width, height, out_fmt, flags, NULL, NULL, NULL); if ( context ) { // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. if ( out_fmt == AV_PIX_FMT_RGB24 || out_fmt == AV_PIX_FMT_RGBA ) dst_colorspace = 601; error = mlt_set_luma_transfer( context, src_colorspace, dst_colorspace, use_full_range, use_full_range ); sws_scale(context, (const uint8_t* const*) in_data, in_stride, 0, height, out_data, out_stride); sws_freeContext( context ); } return error; } /** Do it :-). */ static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); int error = 0; if ( *format != output_format ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( mlt_frame_get_original_producer( frame ) ) ); int profile_colorspace = profile ? profile->colorspace : 601; int colorspace = mlt_properties_get_int( properties, "colorspace" ); int force_full_luma = 0; mlt_log_debug( NULL, "[filter avcolor_space] %s -> %s @ %dx%d space %d->%d\n", mlt_image_format_name( *format ), mlt_image_format_name( output_format ), width, height, colorspace, profile_colorspace ); int in_fmt = convert_mlt_to_av_cs( *format ); int out_fmt = convert_mlt_to_av_cs( output_format ); int size = FFMAX( av_image_get_buffer_size(out_fmt, width, height, IMAGE_ALIGN), mlt_image_format_size( output_format, width, height, NULL ) ); uint8_t *output = mlt_pool_alloc( size ); if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { register int len = width * height; uint8_t *alpha = mlt_pool_alloc( len ); if ( alpha ) { // Extract the alpha mask from the RGBA image using Duff's Device register uint8_t *s = *image + 3; // start on the alpha component register uint8_t *d = alpha; register int n = ( len + 7 ) / 8; switch ( len % 8 ) { case 0: do { *d++ = *s; s += 4; case 7: *d++ = *s; s += 4; case 6: *d++ = *s; s += 4; case 5: *d++ = *s; s += 4; case 4: *d++ = *s; s += 4; case 3: *d++ = *s; s += 4; case 2: *d++ = *s; s += 4; case 1: *d++ = *s; s += 4; } while ( --n > 0 ); } mlt_frame_set_alpha( frame, alpha, len, mlt_pool_release ); } } // Update the output if ( !av_convert_image( output, *image, out_fmt, in_fmt, width, height, colorspace, profile_colorspace, force_full_luma ) ) { // The new colorspace is only valid if destination is YUV. if ( output_format == mlt_image_yuv422 || output_format == mlt_image_yuv420p || output_format == mlt_image_yuv422p16 ) mlt_properties_set_int( properties, "colorspace", profile_colorspace ); } *image = output; *format = output_format; mlt_frame_set_image( frame, output, size, mlt_pool_release ); mlt_properties_set_int( properties, "format", output_format ); if ( output_format == mlt_image_rgb24a || output_format == mlt_image_opengl ) { register int len = width * height; int alpha_size = 0; uint8_t *alpha = mlt_frame_get_alpha( frame ); mlt_properties_get_data( properties, "alpha", &alpha_size ); if ( alpha && alpha_size >= len ) { // Merge the alpha mask from into the RGBA image using Duff's Device register uint8_t *s = alpha; register uint8_t *d = *image + 3; // start on the alpha component register int n = ( len + 7 ) / 8; switch ( len % 8 ) { case 0: do { *d = *s++; d += 4; case 7: *d = *s++; d += 4; case 6: *d = *s++; d += 4; case 5: *d = *s++; d += 4; case 4: *d = *s++; d += 4; case 3: *d = *s++; d += 4; case 2: *d = *s++; d += 4; case 1: *d = *s++; d += 4; } while ( --n > 0 ); } } } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Set a default colorspace on the frame if not yet set by the producer. // The producer may still change it during get_image. // This way we do not have to modify each producer to set a valid colorspace. mlt_properties properties = MLT_FRAME_PROPERTIES(frame); if ( mlt_properties_get_int( properties, "colorspace" ) <= 0 ) mlt_properties_set_int( properties, "colorspace", mlt_service_profile( MLT_FILTER_SERVICE(filter) )->colorspace ); if ( !frame->convert_image ) frame->convert_image = convert_image; // Not working yet - see comment for get_image() above. // mlt_frame_push_service( frame, mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ); // mlt_frame_push_get_image( frame, get_image ); return frame; } /** Constructor for the filter. */ mlt_filter filter_avcolour_space_init( void *arg ) { // Test to see if swscale accepts the arg as resolution if ( arg ) { int *width = (int*) arg; if ( *width > 0 ) { struct SwsContext *context = sws_getContext( *width, *width, AV_PIX_FMT_RGB32, 64, 64, AV_PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); if ( context ) sws_freeContext( context ); else return NULL; } } mlt_filter filter = mlt_filter_new( ); if ( filter != NULL ) filter->process = filter_process; return filter; } mlt-6.20.0/src/modules/avformat/filter_avdeinterlace.c000066400000000000000000000246541362234133600230060ustar00rootroot00000000000000/* * filter_avdeinterlace.c -- deinterlace filter * Copyright (C) 2003-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include // ffmpeg Header files #include #include #ifdef USE_MMX #include "mmx.h" #else #define MAX_NEG_CROP 1024 static uint8_t ff_cropTbl[256 + 2 * MAX_NEG_CROP] = {0,}; #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 /* 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(uint8_t *dst_data[4], int dst_stride[4], uint8_t *src_data[4], int src_stride[4], int pix_fmt, int width, int height) { int i; if (pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUV422P && pix_fmt != AV_PIX_FMT_YUYV422 && pix_fmt != AV_PIX_FMT_YUV444P && pix_fmt != AV_PIX_FMT_YUV411P) return -1; if ((width & 3) != 0 || (height & 3) != 0) return -1; if ( pix_fmt != AV_PIX_FMT_YUYV422 ) { for(i=0;i<3;i++) { if (i == 1) { switch(pix_fmt) { case AV_PIX_FMT_YUV420P: width >>= 1; height >>= 1; break; case AV_PIX_FMT_YUV422P: width >>= 1; break; case AV_PIX_FMT_YUV411P: width >>= 2; break; default: break; } } if (src_data[0] == dst_data[0]) { deinterlace_bottom_field_inplace(dst_data[i], dst_stride[i], width, height); } else { deinterlace_bottom_field(dst_data[i], dst_stride[i], src_data[i], src_stride[i], width, height); } } } else { if (src_data[0] == dst_data[0]) { deinterlace_bottom_field_inplace(dst_data[0], dst_stride[0], width<<1, height); } else { deinterlace_bottom_field(dst_data[0], dst_stride[0], src_data[0], src_stride[0], width<<1, height); } } #ifdef USE_MMX emms(); #endif return 0; } /** Do it :-). */ static int filter_get_image( mlt_frame frame, 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( frame ), "consumer_deinterlace" ); // Determine if we need a writable version or not if ( deinterlace && !writable ) writable = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" ); // Get the input image *format = mlt_image_yuv422; error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // 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( frame ), "progressive" ) ) { // Create a picture uint8_t *image_data[4]; int strides[4]; // Fill the picture av_image_fill_arrays(image_data, strides, *image, AV_PIX_FMT_YUYV422, *width, *height, 1); mlt_log_timings_begin(); mlt_avpicture_deinterlace( image_data, strides, image_data, strides, AV_PIX_FMT_YUYV422, *width, *height ); mlt_log_timings_end( NULL, "mlt_avpicture_deinterlace" ); // Make sure that others know the frame is deinterlaced mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "progressive", 1 ); } return error; } /** Deinterlace filter processing - this should be lazy evaluation here... */ static mlt_frame deinterlace_process( mlt_filter filter, 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 ) { #ifndef USE_MMX if ( ff_cropTbl[MAX_NEG_CROP + 1] == 0 ) { int i; for(i=0;i<256;i++) ff_cropTbl[i + MAX_NEG_CROP] = i; for(i=0;iprocess = deinterlace_process; return filter; } mlt-6.20.0/src/modules/avformat/filter_avfilter.c000066400000000000000000000711111362234133600220020ustar00rootroot00000000000000/* * filter_avfilter.c -- provide various filters based on libavfilter * Copyright (C) 2016-2020 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #define PARAM_PREFIX "av." #define PARAM_PREFIX_LEN (sizeof(PARAM_PREFIX) - 1) #define MLT_SWS_FLAGS "bicubic+accurate_rnd+full_chroma_int+full_chroma_inp" typedef struct { AVFilter* avfilter; AVFilterContext* avbuffsink_ctx; AVFilterContext* avbuffsrc_ctx; AVFilterContext* avfilter_ctx; AVFilterContext* scale_ctx; AVFilterContext* pad_ctx; AVFilterGraph* avfilter_graph; AVFrame* avinframe; AVFrame* avoutframe; int format; int width; int height; int reset; } private_data; static void property_changed( mlt_service owner, mlt_filter filter, char *name ) { if( strncmp( PARAM_PREFIX, name, PARAM_PREFIX_LEN ) == 0 ) { private_data* pdata = (private_data*)filter->child; if( pdata->avfilter ) { const AVOption *opt = NULL; while( ( opt = av_opt_next( &pdata->avfilter->priv_class, opt ) ) ) { if( !strcmp( opt->name, name + PARAM_PREFIX_LEN ) ) { pdata->reset = 1; break; } } } } } static int mlt_to_av_image_format( mlt_image_format format ) { switch( format ) { case mlt_image_none: return AV_PIX_FMT_NONE; case mlt_image_rgb24: return AV_PIX_FMT_RGB24; case mlt_image_rgb24a: return AV_PIX_FMT_RGBA; case mlt_image_yuv422: return AV_PIX_FMT_YUYV422; case mlt_image_yuv420p: return AV_PIX_FMT_YUV420P; default: mlt_log_error(NULL, "[filter_avfilter] Unknown image format: %d\n", format ); return AV_PIX_FMT_NONE; } } static mlt_image_format get_supported_image_format( mlt_image_format format ) { switch( format ) { case mlt_image_rgb24a: return mlt_image_rgb24a; case mlt_image_rgb24: return mlt_image_rgb24; case mlt_image_yuv420p: return mlt_image_yuv420p; default: mlt_log_error(NULL, "[filter_avfilter] Unknown image format requested: %d\n", format ); case mlt_image_none: case mlt_image_yuv422: case mlt_image_opengl: case mlt_image_glsl: case mlt_image_glsl_texture: return mlt_image_yuv422; } } static void set_avfilter_options( mlt_filter filter, double scale) { private_data* pdata = (private_data*)filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); int i; int count = mlt_properties_count( filter_properties ); mlt_properties scale_map = mlt_properties_get_data(filter_properties, "_resolution_scale", NULL); for( i = 0; i < count; i++ ) { const char *param_name = mlt_properties_get_name( filter_properties, i ); if( param_name && strncmp( PARAM_PREFIX, param_name, PARAM_PREFIX_LEN ) == 0 ) { const AVOption *opt = av_opt_find( pdata->avfilter_ctx->priv, param_name + PARAM_PREFIX_LEN, 0, 0, 0 ); const char* value = mlt_properties_get_value( filter_properties, i ); if( opt && value ) { if (scale != 1.0) { double scale2 = mlt_properties_get_double(scale_map, opt->name); if (scale2 != 0.0) { double x = mlt_properties_get_double(filter_properties, param_name); x *= scale * scale2; mlt_properties_set_double(filter_properties, "_avfilter_temp", x); value = mlt_properties_get(filter_properties, "_avfilter_temp"); } } av_opt_set( pdata->avfilter_ctx->priv, opt->name, value, 0 ); } } } } static void init_audio_filtergraph( mlt_filter filter, mlt_audio_format format, int frequency, int channels ) { private_data* pdata = (private_data*)filter->child; AVFilter *abuffersrc = avfilter_get_by_name("abuffer"); AVFilter *abuffersink = avfilter_get_by_name("abuffersink"); int sample_fmts[] = { -1, -1 }; int sample_rates[] = { -1, -1 }; int channel_counts[] = { -1, -1 }; int64_t channel_layouts[] = { -1, -1 }; char channel_layout_str[64]; int ret; pdata->format = format; // Set up formats sample_fmts[0] = mlt_to_av_sample_format( format ); sample_rates[0] = frequency; channel_counts[0] = channels; channel_layouts[0] = av_get_default_channel_layout( channels ); av_get_channel_layout_string( channel_layout_str, sizeof(channel_layout_str), 0, channel_layouts[0]); // Destroy the current filter graph avfilter_graph_free( &pdata->avfilter_graph ); // Create the new filter graph pdata->avfilter_graph = avfilter_graph_alloc(); if( !pdata->avfilter_graph ) { mlt_log_error( filter, "Cannot create filter graph\n" ); goto fail; } // Set thread count if supported. if ( pdata->avfilter->flags & AVFILTER_FLAG_SLICE_THREADS ) { av_opt_set_int( pdata->avfilter_graph, "threads", FFMAX( 0, mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "av.threads" ) ), 0 ); } // Initialize the buffer source filter context pdata->avbuffsrc_ctx = avfilter_graph_alloc_filter( pdata->avfilter_graph, abuffersrc, "in"); if( !pdata->avbuffsrc_ctx ) { mlt_log_error( filter, "Cannot create audio buffer source\n" ); goto fail; } ret = av_opt_set_int( pdata->avbuffsrc_ctx, "sample_rate", sample_rates[0], AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src sample rate %d\n", sample_rates[0] ); goto fail; } ret = av_opt_set_int( pdata->avbuffsrc_ctx, "sample_fmt", sample_fmts[0], AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src sample format %d\n", sample_fmts[0] ); goto fail; } ret = av_opt_set_int( pdata->avbuffsrc_ctx, "channels", channel_counts[0], AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src channels %d\n", channel_counts[0] ); goto fail; } ret = av_opt_set( pdata->avbuffsrc_ctx, "channel_layout", channel_layout_str, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src channel layout %s\n", channel_layout_str ); goto fail; } ret = avfilter_init_str( pdata->avbuffsrc_ctx, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot init buffer source\n" ); goto fail; } // Initialize the buffer sink filter context pdata->avbuffsink_ctx = avfilter_graph_alloc_filter( pdata->avfilter_graph, abuffersink, "out"); if( !pdata->avbuffsink_ctx ) { mlt_log_error( filter, "Cannot create audio buffer sink\n" ); goto fail; } ret = av_opt_set_int_list( pdata->avbuffsink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set sink sample formats\n" ); goto fail; } ret = av_opt_set_int_list( pdata->avbuffsink_ctx, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set sink sample rates\n" ); goto fail; } ret = av_opt_set_int_list( pdata->avbuffsink_ctx, "channel_counts", channel_counts, -1, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set sink channel counts\n" ); goto fail; } ret = av_opt_set_int_list( pdata->avbuffsink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set sink channel_layouts\n" ); goto fail; } ret = avfilter_init_str( pdata->avbuffsink_ctx, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot init buffer sink\n" ); goto fail; } // Initialize the filter context pdata->avfilter_ctx = avfilter_graph_alloc_filter( pdata->avfilter_graph, pdata->avfilter, pdata->avfilter->name ); if( !pdata->avfilter_ctx ) { mlt_log_error( filter, "Cannot create audio filter\n" ); goto fail; } set_avfilter_options( filter, 1.0 ); ret = avfilter_init_str( pdata->avfilter_ctx, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot init filter\n" ); goto fail; } // Connect the filters ret = avfilter_link( pdata->avbuffsrc_ctx, 0, pdata->avfilter_ctx, 0 ); if( ret < 0 ) { mlt_log_error( filter, "Cannot link src to filter\n" ); goto fail; } ret = avfilter_link( pdata->avfilter_ctx, 0, pdata->avbuffsink_ctx, 0 ); if( ret < 0 ) { mlt_log_error( filter, "Cannot link filter to sink\n" ); goto fail; } // Configure the graph. ret = avfilter_graph_config( pdata->avfilter_graph, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot configure the filter graph\n" ); goto fail; } return; fail: avfilter_graph_free( &pdata->avfilter_graph ); } static void init_image_filtergraph( mlt_filter filter, mlt_image_format format, int width, int height, double resolution_scale ) { private_data* pdata = (private_data*)filter->child; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); AVFilter *buffersrc = avfilter_get_by_name("buffer"); AVFilter *buffersink = avfilter_get_by_name("buffersink"); AVFilter *scale = avfilter_get_by_name("scale"); AVFilter *pad = avfilter_get_by_name("pad"); mlt_properties p = mlt_properties_new(); enum AVPixelFormat pixel_fmts[] = { -1, -1 }; AVRational sar = (AVRational){ profile->sample_aspect_num, profile->frame_rate_den }; AVRational timebase = (AVRational){ profile->frame_rate_den, profile->frame_rate_num }; AVRational framerate = (AVRational){ profile->frame_rate_num, profile->frame_rate_den }; int ret; pdata->format = format; pdata->width = width; pdata->height = height; // Set up formats pixel_fmts[0] = mlt_to_av_image_format( format ); // Destroy the current filter graph avfilter_graph_free( &pdata->avfilter_graph ); // Create the new filter graph pdata->avfilter_graph = avfilter_graph_alloc(); if( !pdata->avfilter_graph ) { mlt_log_error( filter, "Cannot create filter graph\n" ); goto fail; } pdata->avfilter_graph->scale_sws_opts = av_strdup("flags=" MLT_SWS_FLAGS); // Set thread count if supported. if ( pdata->avfilter->flags & AVFILTER_FLAG_SLICE_THREADS ) { av_opt_set_int( pdata->avfilter_graph, "threads", FFMAX( 0, mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "av.threads" ) ), 0 ); } // Initialize the buffer source filter context pdata->avbuffsrc_ctx = avfilter_graph_alloc_filter( pdata->avfilter_graph, buffersrc, "in"); if( !pdata->avbuffsrc_ctx ) { mlt_log_error( filter, "Cannot create image buffer source\n" ); goto fail; } ret = av_opt_set_int( pdata->avbuffsrc_ctx, "width", width, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src width %d\n", width ); goto fail; } ret = av_opt_set_int( pdata->avbuffsrc_ctx, "height", height, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src height %d\n", height ); goto fail; } ret = av_opt_set_pixel_fmt( pdata->avbuffsrc_ctx, "pix_fmt", pixel_fmts[0], AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src pixel format %d\n", pixel_fmts[0] ); goto fail; } ret = av_opt_set_q( pdata->avbuffsrc_ctx, "sar", sar, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src sar %d/%d\n", sar.num, sar.den ); goto fail; } ret = av_opt_set_q( pdata->avbuffsrc_ctx, "time_base", timebase, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src time_base %d/%d\n", timebase.num, timebase.den ); goto fail; } ret = av_opt_set_q( pdata->avbuffsrc_ctx, "frame_rate", framerate, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set src frame_rate %d/%d\n", framerate.num, framerate.den ); goto fail; } ret = avfilter_init_str( pdata->avbuffsrc_ctx, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot init buffer source\n" ); goto fail; } // Initialize the buffer sink filter context pdata->avbuffsink_ctx = avfilter_graph_alloc_filter( pdata->avfilter_graph, buffersink, "out"); if( !pdata->avbuffsink_ctx ) { mlt_log_error( filter, "Cannot create image buffer sink\n" ); goto fail; } ret = av_opt_set_int_list( pdata->avbuffsink_ctx, "pix_fmts", pixel_fmts, -1, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set sink pixel formats\n" ); goto fail; } ret = avfilter_init_str( pdata->avbuffsink_ctx, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot init buffer sink\n" ); goto fail; } // Initialize the filter context pdata->avfilter_ctx = avfilter_graph_alloc_filter( pdata->avfilter_graph, pdata->avfilter, pdata->avfilter->name ); if( !pdata->avfilter_ctx ) { mlt_log_error( filter, "Cannot create video filter\n" ); goto fail; } set_avfilter_options( filter, resolution_scale ); if ( !strcmp( "lut3d", pdata->avfilter->name ) ) { #if defined(__GLIBC__) || defined(__APPLE__) || (__FreeBSD__) // LUT data files use period for the decimal point regardless of LC_NUMERIC. locale_t posix_locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL ); // Get the current locale and switch to POSIX local. locale_t orig_locale = uselocale( posix_locale ); // Initialize the filter. ret = avfilter_init_str( pdata->avfilter_ctx, NULL ); // Restore the original locale. uselocale( orig_locale ); freelocale( posix_locale ); #else // Get the current locale and switch to POSIX local. char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) ); setlocale( LC_NUMERIC, "C" ); // Initialize the filter. ret = avfilter_init_str( pdata->avfilter_ctx, NULL ); // Restore the original locale. setlocale( LC_NUMERIC, orig_localename ); free( orig_localename ); #endif } else { ret = avfilter_init_str( pdata->avfilter_ctx, NULL ); } if( ret < 0 ) { mlt_log_error( filter, "Cannot init scale filter: %s\n", av_err2str(ret) ); goto fail; } // scale=w=1280:h=720:force_original_aspect_ratio=decrease, pad=w=1280:h=720:x=(ow-iw)/2:y=(oh-ih)/2 // Initialize the scale filter context pdata->scale_ctx = avfilter_graph_alloc_filter( pdata->avfilter_graph, scale, "scale"); if( !pdata->scale_ctx ) { mlt_log_error( filter, "Cannot create scale filer\n" ); goto fail; } mlt_properties_set_int( p, "w", width ); mlt_properties_set_int( p, "h", height ); const AVOption *opt = av_opt_find( pdata->scale_ctx->priv, "w", 0, 0, 0 ); if ( opt ) { ret = av_opt_set( pdata->scale_ctx->priv, opt->name, mlt_properties_get(p, "w"), 0 ); if ( ret < 0 ) { mlt_log_error( filter, "Cannot set scale width\n" ); goto fail; } } opt = av_opt_find( pdata->scale_ctx->priv, "h", 0, 0, 0 ); if ( opt ) { ret = av_opt_set( pdata->scale_ctx->priv, opt->name, mlt_properties_get(p, "h"), 0 ); if ( ret < 0 ) { mlt_log_error( filter, "Cannot set scale height\n" ); goto fail; } } ret = av_opt_set_int( pdata->scale_ctx, "force_original_aspect_ratio", 1, AV_OPT_SEARCH_CHILDREN ); if( ret < 0 ) { mlt_log_error( filter, "Cannot set scale force_original_aspect_ratio\n" ); goto fail; } opt = av_opt_find( pdata->scale_ctx->priv, "flags", 0, 0, 0 ); if ( opt ) { ret = av_opt_set( pdata->scale_ctx->priv, opt->name, MLT_SWS_FLAGS, 0 ); if ( ret < 0 ) { mlt_log_error( filter, "Cannot set scale flags\n" ); goto fail; } } ret = avfilter_init_str( pdata->scale_ctx, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot init scale filter\n" ); goto fail; } // Initialize the padding filter context pdata->pad_ctx = avfilter_graph_alloc_filter( pdata->avfilter_graph, pad, "pad"); if( !pdata->pad_ctx ) { mlt_log_error( filter, "Cannot create pad filter\n" ); goto fail; } opt = av_opt_find( pdata->pad_ctx->priv, "w", 0, 0, 0 ); if ( opt ) { ret = av_opt_set( pdata->pad_ctx->priv, opt->name, mlt_properties_get(p, "w"), 0 ); if ( ret < 0 ) { mlt_log_error( filter, "Cannot set pad width\n" ); goto fail; } } opt = av_opt_find( pdata->pad_ctx->priv, "h", 0, 0, 0 ); if ( opt ) { ret = av_opt_set( pdata->pad_ctx->priv, opt->name, mlt_properties_get(p, "h"), 0 ); if ( ret < 0 ) { mlt_log_error( filter, "Cannot pad scale height\n" ); goto fail; } } opt = av_opt_find( pdata->pad_ctx->priv, "x", 0, 0, 0 ); if ( opt ) { ret = av_opt_set( pdata->pad_ctx->priv, opt->name, "(ow-iw)/2", 0 ); if ( ret < 0 ) { mlt_log_error( filter, "Cannot set pad x\n" ); goto fail; } } opt = av_opt_find( pdata->pad_ctx->priv, "y", 0, 0, 0 ); if ( opt ) { ret = av_opt_set( pdata->pad_ctx->priv, opt->name, "(oh-ih)/2", 0 ); if ( ret < 0 ) { mlt_log_error( filter, "Cannot set pad y\n" ); goto fail; } } ret = avfilter_init_str( pdata->pad_ctx, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot init pad filter\n" ); goto fail; } // Connect the filters ret = avfilter_link( pdata->avbuffsrc_ctx, 0, pdata->avfilter_ctx, 0 ); if( ret < 0 ) { mlt_log_error( filter, "Cannot link src to filter\n" ); goto fail; } ret = avfilter_link( pdata->avfilter_ctx, 0, pdata->scale_ctx, 0 ); if( ret < 0 ) { mlt_log_error( filter, "Cannot link filter to scale\n" ); goto fail; } ret = avfilter_link( pdata->scale_ctx, 0, pdata->pad_ctx, 0 ); if( ret < 0 ) { mlt_log_error( filter, "Cannot link scale to pad\n" ); goto fail; } ret = avfilter_link( pdata->pad_ctx, 0, pdata->avbuffsink_ctx, 0 ); if( ret < 0 ) { mlt_log_error( filter, "Cannot link pad to sink\n" ); goto fail; } // Configure the graph. ret = avfilter_graph_config( pdata->avfilter_graph, NULL ); if( ret < 0 ) { mlt_log_error( filter, "Cannot configure the filter graph\n" ); goto fail; } return; fail: mlt_properties_close( p ); avfilter_graph_free( &pdata->avfilter_graph ); } mlt_position get_position(mlt_filter filter, mlt_frame frame) { mlt_position position = mlt_frame_get_position(frame); const char* pos_type = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "position"); if (pos_type) { if (!strcmp("filter", pos_type)) { position = mlt_filter_get_position(filter, frame); } else if (!strcmp("source", pos_type)) { position = mlt_frame_original_position(frame); } else if (!strcmp("producer", pos_type)) { mlt_producer producer = mlt_properties_get_data(MLT_FILTER_PROPERTIES(filter), "service", NULL); if (producer) position = mlt_producer_position(producer); } } else { private_data* pdata = (private_data*)filter->child; if (!strcmp("subtitles", pdata->avfilter->name)) position = mlt_frame_original_position(frame); } return position; } static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_filter filter = mlt_frame_pop_audio( frame ); private_data* pdata = (private_data*)filter->child; double fps = mlt_profile_fps( mlt_service_profile(MLT_FILTER_SERVICE(filter)) ); int64_t samplepos = mlt_sample_calculator_to_now( fps, *frequency, get_position(filter, frame) ); int bufsize = 0; int ret; // Get the producer's audio mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); bufsize = mlt_audio_format_size( *format, *samples, *channels ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); if( pdata->reset || pdata->format != *format ) { init_audio_filtergraph( filter, *format, *frequency, *channels ); pdata->reset = 0; } if( pdata->avfilter_graph ) { // Set up the input frame mlt_channel_layout layout = mlt_get_channel_layout_or_default( mlt_properties_get( MLT_FRAME_PROPERTIES(frame), "channel_layout" ) , *channels ); pdata->avinframe->sample_rate = *frequency; pdata->avinframe->format = mlt_to_av_sample_format( *format ); pdata->avinframe->channel_layout = mlt_to_av_channel_layout( layout ); pdata->avinframe->channels = *channels; pdata->avinframe->nb_samples = *samples; pdata->avinframe->pts = samplepos; ret = av_frame_get_buffer( pdata->avinframe, 1 ); if( ret < 0 ) { mlt_log_error( filter, "Cannot get in frame buffer\n" ); } if( av_sample_fmt_is_planar( pdata->avinframe->format ) ) { int i = 0; int stride = bufsize / *channels; for( i = 0; i < * channels; i++ ) { memcpy( pdata->avinframe->extended_data[i], (uint8_t*)*buffer + stride * i, stride ); } } else { memcpy( pdata->avinframe->extended_data[0], (uint8_t*)*buffer, bufsize ); } // Run the frame through the filter graph ret = av_buffersrc_add_frame( pdata->avbuffsrc_ctx, pdata->avinframe ); if( ret < 0 ) { mlt_log_error( filter, "Cannot add frame to buffer source\n" ); } ret = av_buffersink_get_frame( pdata->avbuffsink_ctx, pdata->avoutframe ); if( ret < 0 ) { mlt_log_error( filter, "Cannot get frame from buffer sink\n" ); } // Sanity check the output frame if( *channels != pdata->avoutframe->channels || *samples != pdata->avoutframe->nb_samples || *frequency != pdata->avoutframe->sample_rate ) { mlt_log_error( filter, "Unexpected return format\n" ); goto exit; } // Copy the filter output into the original buffer if( av_sample_fmt_is_planar( pdata->avoutframe->format ) ) { int stride = bufsize / *channels; int i = 0; for( i = 0; i < * channels; i++ ) { memcpy( (uint8_t*)*buffer + stride * i, pdata->avoutframe->extended_data[i], stride ); } } else { memcpy( (uint8_t*)*buffer, pdata->avoutframe->extended_data[0], bufsize ); } } exit: av_frame_unref( pdata->avinframe ); av_frame_unref( pdata->avoutframe ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return 0; } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = mlt_frame_pop_service( frame ); private_data* pdata = (private_data*)filter->child; int64_t pos = get_position( filter, frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(frame); int ret; mlt_log_debug(MLT_FILTER_SERVICE(filter), "position %"PRId64"\n", pos); *format = get_supported_image_format( *format ); mlt_frame_get_image( frame, image, format, width, height, 0 ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); if( pdata->reset || pdata->format != *format || pdata->width != *width || pdata->height != *height ) { double scale = mlt_profile_scale_width(profile, *width); init_image_filtergraph( filter, *format, *width, *height, scale ); pdata->reset = 0; } if( pdata->avfilter_graph ) { pdata->avinframe->width = *width; pdata->avinframe->height = *height; pdata->avinframe->format = mlt_to_av_image_format( *format ); pdata->avinframe->sample_aspect_ratio = (AVRational) { profile->sample_aspect_num, profile->frame_rate_den }; pdata->avinframe->pts = pos; pdata->avinframe->interlaced_frame = !mlt_properties_get_int( frame_properties, "progressive" ); pdata->avinframe->top_field_first = mlt_properties_get_int( frame_properties, "top_field_first" ); pdata->avinframe->color_primaries = mlt_properties_get_int( frame_properties, "color_primaries" ); pdata->avinframe->color_trc = mlt_properties_get_int( frame_properties, "color_trc" ); av_frame_set_color_range( pdata->avinframe, mlt_properties_get_int( frame_properties, "full_luma" )? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG ); switch (mlt_properties_get_int( frame_properties, "colorspace" )) { case 240: av_frame_set_colorspace( pdata->avinframe, AVCOL_SPC_SMPTE240M ); break; case 601: av_frame_set_colorspace( pdata->avinframe, AVCOL_SPC_BT470BG ); break; case 709: av_frame_set_colorspace( pdata->avinframe, AVCOL_SPC_BT709 ); break; case 2020: av_frame_set_colorspace( pdata->avinframe, AVCOL_SPC_BT2020_NCL ); break; case 2021: av_frame_set_colorspace( pdata->avinframe, AVCOL_SPC_BT2020_CL ); break; } ret = av_frame_get_buffer( pdata->avinframe, 1 ); if( ret < 0 ) { mlt_log_error( filter, "Cannot get in frame buffer\n" ); } // Set up the input frame if( *format == mlt_image_yuv420p ) { int i = 0; int p = 0; int widths[3] = { *width, *width / 2, *width / 2 }; int heights[3] = { *height, *height / 2, *height / 2 }; uint8_t* src = *image; for( p = 0; p < 3; p ++ ) { uint8_t* dst = pdata->avinframe->data[p]; for( i = 0; i < heights[p]; i ++ ) { memcpy( dst, src, widths[p] ); src += widths[p]; dst += pdata->avinframe->linesize[p]; } } } else { int i; uint8_t* src = *image; uint8_t* dst = pdata->avinframe->data[0]; int stride = mlt_image_format_size( *format, *width, 0, NULL ); for( i = 0; i < *height; i ++ ) { memcpy( dst, src, stride ); src += stride; dst += pdata->avinframe->linesize[0]; } } // Run the frame through the filter graph ret = av_buffersrc_add_frame( pdata->avbuffsrc_ctx, pdata->avinframe ); if( ret < 0 ) { mlt_log_error( filter, "Cannot add frame to buffer source\n" ); } ret = av_buffersink_get_frame( pdata->avbuffsink_ctx, pdata->avoutframe ); if( ret < 0 ) { mlt_log_error( filter, "Cannot get frame from buffer sink\n" ); } // Sanity check the output frame if( *width != pdata->avoutframe->width || *height != pdata->avoutframe->height ) { mlt_log_error( filter, "Unexpected return format\n" ); goto exit; } // Copy the filter output into the original buffer if( *format == mlt_image_yuv420p ) { int i = 0; int p = 0; int widths[3] = { *width, *width / 2, *width / 2 }; int heights[3] = { *height, *height / 2, *height / 2 }; uint8_t* dst = *image; for ( p = 0; p < 3; p ++ ) { uint8_t* src = pdata->avoutframe->data[p]; for ( i = 0; i < heights[p]; i ++ ) { memcpy( dst, src, widths[p] ); dst += widths[p]; src += pdata->avoutframe->linesize[p]; } } } else { int i; uint8_t* dst = *image; uint8_t* src = pdata->avoutframe->data[0]; int stride = mlt_image_format_size( *format, *width, 0, NULL ); for( i = 0; i < *height; i ++ ) { memcpy( dst, src, stride ); dst += stride; src += pdata->avoutframe->linesize[0]; } } } exit: av_frame_unref( pdata->avinframe ); av_frame_unref( pdata->avoutframe ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { private_data* pdata = (private_data*)filter->child; if( avfilter_pad_get_type( pdata->avfilter->inputs, 0 ) == AVMEDIA_TYPE_VIDEO ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); } else if( avfilter_pad_get_type( pdata->avfilter->inputs, 0 ) == AVMEDIA_TYPE_AUDIO ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); } return frame; } /** Destructor for the filter. */ static void filter_close( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if( pdata ) { avfilter_graph_free( &pdata->avfilter_graph ); av_frame_free( &pdata->avinframe ); av_frame_free( &pdata->avoutframe ); free( pdata ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ mlt_filter filter_avfilter_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); avfilter_register_all(); if( pdata && id ) { id += 9; // Move past "avfilter." pdata->avfilter = (AVFilter*)avfilter_get_by_name( id ); } if( filter && pdata && pdata->avfilter ) { pdata->avbuffsink_ctx = NULL; pdata->avbuffsrc_ctx = NULL; pdata->avfilter_ctx = NULL; pdata->avfilter_graph = NULL; pdata->avinframe = av_frame_alloc(); pdata->avoutframe = av_frame_alloc(); pdata->format = -1; pdata->width = -1; pdata->height = -1; pdata->reset = 1; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; mlt_events_listen( MLT_FILTER_PROPERTIES(filter), filter, "property-changed", (mlt_listener)property_changed ); mlt_properties param_name_map = mlt_properties_get_data(mlt_global_properties(), "avfilter.resolution_scale", NULL); if (param_name_map) { // Lookup my plugin in the map param_name_map = mlt_properties_get_data(param_name_map, id, NULL); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "_resolution_scale", param_name_map, 0, NULL, NULL); } } else { mlt_filter_close( filter ); free( pdata ); } return filter; } mlt-6.20.0/src/modules/avformat/filter_swresample.c000066400000000000000000000265121362234133600223550ustar00rootroot00000000000000/* * filter_swresample.c -- convert from one format/ configuration to another * Copyright (C) 2018 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include typedef struct { SwrContext* ctx; uint8_t** in_buffers; uint8_t** out_buffers; mlt_audio_format in_format; mlt_audio_format out_format; int in_frequency; int out_frequency; int in_channels; int out_channels; mlt_channel_layout in_layout; mlt_channel_layout out_layout; } private_data; static int audio_plane_count( mlt_audio_format format, int channels ) { switch ( format ) { case mlt_audio_none: return 0; case mlt_audio_s16: return 1; case mlt_audio_s32le: return 1; case mlt_audio_s32: return channels; case mlt_audio_f32le: return 1; case mlt_audio_float: return channels; case mlt_audio_u8: return 1; } return 0; } static int audio_plane_size( mlt_audio_format format, int samples, int channels ) { switch ( format ) { case mlt_audio_none: return 0; case mlt_audio_s16: return samples * channels * sizeof( int16_t ); case mlt_audio_s32le: return samples * channels * sizeof( int32_t ); case mlt_audio_s32: return samples * sizeof( int32_t ); case mlt_audio_f32le: return samples * channels * sizeof( float ); case mlt_audio_float: return samples * sizeof( float ); case mlt_audio_u8: return samples * channels; } return 0; } static void audio_format_planes( mlt_audio_format format, int samples, int channels, uint8_t* buffer, uint8_t** planes ) { int plane_count = audio_plane_count( format, channels ); size_t plane_size = audio_plane_size( format, samples, channels ); int p = 0; for( p = 0; p < plane_count; p++ ) { planes[p] = buffer + ( p * plane_size ); } } static void collapse_channels( mlt_audio_format format, int channels, int allocated_samples, int used_samples, uint8_t* buffer ) { int plane_count = audio_plane_count( format, channels ); if( plane_count > 1 && allocated_samples != used_samples ) { size_t src_plane_size = audio_plane_size( format, allocated_samples, channels ); size_t dst_plane_size = audio_plane_size( format, used_samples, channels ); int p = 0; for( p = 0; p < plane_count; p++ ) { uint8_t* src = buffer + ( p * src_plane_size ); uint8_t* dst = buffer + ( p * dst_plane_size ); memmove( dst, src, dst_plane_size ); } } } static int configure_swr_context( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; int error = 0; mlt_log_info( MLT_FILTER_SERVICE(filter), "%d(%s) %s %dHz -> %d(%s) %s %dHz\n", pdata->in_channels, mlt_channel_layout_name( pdata->in_layout ), mlt_audio_format_name( pdata->in_format ), pdata->in_frequency, pdata->out_channels, mlt_channel_layout_name( pdata->out_layout ), mlt_audio_format_name( pdata->out_format ), pdata->out_frequency ); swr_free( &pdata->ctx ); pdata->ctx = swr_alloc(); if( !pdata->ctx ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "Cannot allocate context\n" ); return 1; } // Configure format, frequency and channels. av_opt_set_int( pdata->ctx, "osf", mlt_to_av_sample_format( pdata->out_format ), 0 ); av_opt_set_int( pdata->ctx, "osr", pdata->out_frequency, 0 ); av_opt_set_int( pdata->ctx, "och", pdata->out_channels, 0); av_opt_set_int( pdata->ctx, "isf", mlt_to_av_sample_format( pdata->in_format ), 0 ); av_opt_set_int( pdata->ctx, "isr", pdata->in_frequency, 0 ); av_opt_set_int( pdata->ctx, "ich", pdata->in_channels, 0 ); if( pdata->in_layout != mlt_channel_independent && pdata->out_layout != mlt_channel_independent ) { // Use standard channel layout and matrix for known channel configurations. av_opt_set_int( pdata->ctx, "ocl", mlt_to_av_channel_layout( pdata->out_layout ), 0 ); av_opt_set_int( pdata->ctx, "icl", mlt_to_av_channel_layout( pdata->in_layout ), 0 ); } else { // Use a custom channel layout and matrix for independent channels. // This matrix will simply map input channels to output channels in order. // If input channels > output channels, channels will be dropped. // If input channels < output channels, silent channels will be added. int64_t custom_in_layout = 0; int64_t custom_out_layout = 0; double* matrix = av_mallocz_array( pdata->in_channels * pdata->out_channels, sizeof(double) ); int stride = pdata->in_channels; int i = 0; for( i = 0; i < pdata->in_channels; i++ ) { custom_in_layout = (custom_in_layout << 1) | 0x01; } for( i = 0; i < pdata->out_channels; i++ ) { custom_out_layout = (custom_out_layout << 1) | 0x01; if( i <= pdata->in_channels ) { double* matrix_row = matrix + (stride * i); matrix_row[i] = 1.0; } } av_opt_set_int( pdata->ctx, "ocl", custom_out_layout, 0 ); av_opt_set_int( pdata->ctx, "icl", custom_in_layout, 0 ); error = swr_set_matrix( pdata->ctx, matrix, stride ); av_free( matrix ); if( error != 0 ) { swr_free( &pdata->ctx ); mlt_log_error( MLT_FILTER_SERVICE(filter), "Unable to create custom matrix\n" ); return error; } } error = swr_init( pdata->ctx ); if( error != 0 ) { swr_free( &pdata->ctx ); mlt_log_error( MLT_FILTER_SERVICE(filter), "Cannot initialize context\n" ); return error; } // Allocate the channel buffer pointers av_freep( &pdata->in_buffers ); pdata->in_buffers = av_mallocz_array( pdata->in_channels, sizeof(uint8_t*) ); av_freep( &pdata->out_buffers ); pdata->out_buffers = av_mallocz_array( pdata->out_channels, sizeof(uint8_t*) ); return error; } static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_filter filter = mlt_frame_pop_audio( frame ); private_data* pdata = (private_data*)filter->child; mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_audio_format in_format = *format; mlt_audio_format out_format = *format; int in_frequency = *frequency; int out_frequency = *frequency; int in_channels = *channels; int out_channels = *channels; mlt_channel_layout in_layout; mlt_channel_layout out_layout; // Get the producer's audio int error = mlt_frame_get_audio( frame, buffer, &in_format, &in_frequency, &in_channels, samples ); if ( error || in_format == mlt_audio_none || out_format == mlt_audio_none || in_frequency <= 0 || out_frequency <= 0 || in_channels <= 0 || out_channels <= 0 ) { // Error situation. Do not attempt to convert. *format = in_format; *frequency = in_frequency; *channels = in_channels; mlt_log_error( MLT_FILTER_SERVICE(filter), "Invalid Parameters: %dS - %dHz %dC %s -> %dHz %dC %s\n", *samples, in_frequency, in_channels, mlt_audio_format_name( in_format ), out_frequency, out_channels, mlt_audio_format_name( out_format ) ); return error; } if (*samples == 0) { // Noting to convert. return error; } // Determine the input/output channel layout. in_layout = mlt_get_channel_layout_or_default( mlt_properties_get( frame_properties, "channel_layout" ), in_channels ); out_layout = mlt_get_channel_layout_or_default( mlt_properties_get( frame_properties, "consumer_channel_layout" ), out_channels ); if( in_format == out_format && in_frequency == out_frequency && in_channels == out_channels && in_layout == out_layout ) { // No change necessary return error; } mlt_service_lock( MLT_FILTER_SERVICE(filter) ); // Detect configuration change if( !pdata->ctx || pdata->in_format != in_format || pdata->out_format != out_format || pdata->in_frequency != in_frequency || pdata->out_frequency != out_frequency || pdata->in_channels != in_channels || pdata->out_channels != out_channels || pdata->in_layout != in_layout || pdata->out_layout != out_layout ) { // Save the configuration pdata->in_format = in_format; pdata->out_format = out_format; pdata->in_frequency = in_frequency; pdata->out_frequency = out_frequency; pdata->in_channels = in_channels; pdata->out_channels = out_channels; pdata->in_layout = in_layout; pdata->out_layout = out_layout; // Reconfigure the context error = configure_swr_context( filter ); } if( !error ) { int in_samples = *samples; int out_samples = 0; int alloc_samples = in_samples; if( in_frequency != out_frequency ) { // Number of output samples will change if sampling frequency changes. uint64_t tmp = (uint64_t)in_samples * (uint64_t)out_frequency / (uint64_t)in_frequency; alloc_samples = (int)tmp; // Round up to make sure all available samples are received from swresample. alloc_samples += 1; } int size = mlt_audio_format_size( out_format, alloc_samples, out_channels ); uint8_t* out_buffer = mlt_pool_alloc( size ); audio_format_planes( in_format, in_samples, in_channels, *buffer, pdata->in_buffers ); audio_format_planes( out_format, alloc_samples, out_channels, out_buffer, pdata->out_buffers ); out_samples = swr_convert( pdata->ctx, pdata->out_buffers, alloc_samples, (const uint8_t**)pdata->in_buffers, in_samples ); if( out_samples > 0 ) { collapse_channels( out_format, out_channels, alloc_samples, out_samples, out_buffer ); mlt_frame_set_audio( frame, out_buffer, out_format, size, mlt_pool_release ); *buffer = out_buffer; *samples = out_samples; *format = out_format; *channels = out_channels; mlt_properties_set( frame_properties, "channel_layout", mlt_channel_layout_name( pdata->out_layout ) ); } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "swr_convert() failed. Alloc: %d\tIn: %d\tOut: %d\n", alloc_samples, in_samples, out_samples ); mlt_pool_release( out_buffer ); error = 1; } } mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; } static void filter_close( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if( pdata ) { swr_free( &pdata->ctx ); av_freep( &pdata->in_buffers ); av_freep( &pdata->out_buffers ); free( pdata ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } mlt_filter filter_swresample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); if( filter && pdata ) { memset( pdata, 0, sizeof( *pdata ) ); filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_filter_close( filter ); free( pdata ); } return filter; } mlt-6.20.0/src/modules/avformat/filter_swscale.c000066400000000000000000000136711362234133600216360ustar00rootroot00000000000000/* * filter_swscale.c -- image scaling filter * Copyright (C) 2008-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include // ffmpeg Header files #include #include #include #include #include #include #define IMAGE_ALIGN (1) static inline int convert_mlt_to_av_cs( mlt_image_format format ) { int value = 0; switch( format ) { case mlt_image_rgb24: value = AV_PIX_FMT_RGB24; break; case mlt_image_rgb24a: case mlt_image_opengl: value = AV_PIX_FMT_RGBA; break; case mlt_image_yuv422: value = AV_PIX_FMT_YUYV422; break; case mlt_image_yuv420p: value = AV_PIX_FMT_YUV420P; break; default: fprintf( stderr, "Invalid format...\n" ); break; } return value; } static int filter_scale( mlt_frame frame, uint8_t **image, mlt_image_format *format, int iwidth, int iheight, int owidth, int oheight ) { // Get the properties mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // 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; // Set swscale flags to get good quality interp |= SWS_FULL_CHR_H_INP | SWS_FULL_CHR_H_INT | SWS_ACCURATE_RND; // Determine the output image size. int out_size = mlt_image_format_size( *format, owidth, oheight, NULL ); switch ( *format ) { case mlt_image_yuv422: case mlt_image_rgb24: case mlt_image_rgb24a: case mlt_image_opengl: break; default: // XXX: we only know how to rescale packed formats return 1; } // Convert the pixel formats int avformat = convert_mlt_to_av_cs( *format ); // Fill out the AVPictures uint8_t *in_data[4]; int in_stride[4]; uint8_t *out_data[4]; int out_stride[4]; uint8_t *outbuf = mlt_pool_alloc( out_size ); av_image_fill_arrays(in_data, in_stride, *image, avformat, iwidth, iheight, IMAGE_ALIGN); av_image_fill_arrays(out_data, out_stride, outbuf, avformat, owidth, oheight, IMAGE_ALIGN); // Create the context and output image struct SwsContext *context = sws_getContext( iwidth, iheight, avformat, owidth, oheight, avformat, interp, NULL, NULL, NULL); if ( context ) { // Perform the scaling sws_scale( context, (const uint8_t **) in_data, in_stride, 0, iheight, out_data, out_stride); sws_freeContext( context ); // Now update the frame mlt_frame_set_image( frame, outbuf, out_size, mlt_pool_release ); // Return the output *image = outbuf; // 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( frame ); if ( alpha ) { avformat = AV_PIX_FMT_GRAY8; struct SwsContext *context = sws_getContext( iwidth, iheight, avformat, owidth, oheight, avformat, interp, NULL, NULL, NULL); outbuf = mlt_pool_alloc( owidth * oheight ); av_image_fill_arrays(in_data, in_stride, alpha, avformat, iwidth, iheight, IMAGE_ALIGN); av_image_fill_arrays(out_data, out_stride, outbuf, avformat, owidth, oheight, IMAGE_ALIGN); // Perform the scaling sws_scale( context, (const uint8_t **) in_data, in_stride, 0, iheight, out_data, out_stride); sws_freeContext( context ); // Set it back on the frame mlt_frame_set_alpha( frame, outbuf, owidth * oheight, mlt_pool_release ); } } return 0; } else { return 1; } } /** Constructor for the filter. */ mlt_filter filter_swscale_init( mlt_profile profile, void *arg ) { // Test to see if swscale accepts the arg as resolution if ( arg ) { int *width = (int*) arg; if ( *width > 0 ) { struct SwsContext *context = sws_getContext( *width, *width, AV_PIX_FMT_RGB32, 64, 64, AV_PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL); if ( context ) sws_freeContext( context ); else return NULL; } } // Create a new scaler mlt_filter filter = mlt_factory_filter( profile, "rescale", NULL ); // If successful, then initialise it if ( filter != NULL ) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Set the inerpolation mlt_properties_set( properties, "interpolation", "bilinear" ); // Set the method mlt_properties_set_data( properties, "method", filter_scale, 0, NULL, NULL ); } return filter; } mlt-6.20.0/src/modules/avformat/mmx.h000066400000000000000000000225341362234133600174340ustar00rootroot00000000000000/* * 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 */ mlt-6.20.0/src/modules/avformat/producer_avformat.c000066400000000000000000003210051362234133600223430ustar00rootroot00000000000000/* * producer_avformat.c -- avformat producer * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" // MLT Header files #include #include #include #include #include #include #include #include // ffmpeg Header files #include #include #include #include #include #include #include #include #include #ifdef VDPAU # include #endif #ifdef AVFILTER #include #include #include #endif // System header files #include #include #include #include #include #include #include #define POSITION_INITIAL (-2) #define POSITION_INVALID (-1) #define MAX_AUDIO_STREAMS (32) #define MAX_VDPAU_SURFACES (10) #define MAX_AUDIO_FRAME_SIZE (192000) // 1 second of 48khz 32bit audio #define IMAGE_ALIGN (1) #define VFR_THRESHOLD (3) // The minimum number of video frames with differing durations to be considered VFR. struct producer_avformat_s { mlt_producer parent; AVFormatContext *dummy_context; AVFormatContext *audio_format; AVFormatContext *video_format; AVCodecContext *audio_codec[ MAX_AUDIO_STREAMS ]; AVCodecContext *video_codec; AVFrame *video_frame; AVFrame *audio_frame; AVPacket pkt; mlt_position audio_expected; mlt_position video_expected; int audio_index; int video_index; int64_t first_pts; atomic_int_fast64_t last_position; int video_seekable; int seekable; /// This one is used for both audio and file level seekability. atomic_int_fast64_t current_position; mlt_position nonseek_position; atomic_int top_field_first; uint8_t *audio_buffer[ MAX_AUDIO_STREAMS ]; size_t audio_buffer_size[ MAX_AUDIO_STREAMS ]; uint8_t *decode_buffer[ MAX_AUDIO_STREAMS ]; int audio_used[ MAX_AUDIO_STREAMS ]; int audio_streams; int audio_max_stream; int total_channels; int max_channel; int max_frequency; unsigned int invalid_pts_counter; unsigned int invalid_dts_counter; mlt_cache image_cache; int yuv_colorspace, color_primaries, color_trc; int full_luma; pthread_mutex_t video_mutex; pthread_mutex_t audio_mutex; mlt_deque apackets; mlt_deque vpackets; pthread_mutex_t packets_mutex; pthread_mutex_t open_mutex; int is_mutex_init; AVRational video_time_base; mlt_frame last_good_frame; // for video error concealment int last_good_position; // for video error concealment #ifdef VDPAU struct { // from FFmpeg struct vdpau_render_state render_states[MAX_VDPAU_SURFACES]; // internal mlt_deque deque; int b_age; int ip_age[2]; int is_decoded; uint8_t *buffer; VdpDevice device; VdpDecoder decoder; } *vdpau; #endif #ifdef AVFILTER AVFilterGraph *vfilter_graph; AVFilterContext *vfilter_in; AVFilterContext* vfilter_out; #endif int autorotate; int is_audio_synchronizing; }; typedef struct producer_avformat_s *producer_avformat; // Forward references. static int list_components( char* file ); static int producer_open( producer_avformat self, mlt_profile profile, const char *URL, int take_lock, int test_open ); static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); static void producer_avformat_close( producer_avformat ); static void producer_close( mlt_producer parent ); static void producer_set_up_video( producer_avformat self, mlt_frame frame ); static void producer_set_up_audio( producer_avformat self, mlt_frame frame ); static void apply_properties( void *obj, mlt_properties properties, int flags ); static int video_codec_init( producer_avformat self, int index, mlt_properties properties ); static void get_audio_streams_info( producer_avformat self ); static mlt_audio_format pick_audio_format( int sample_fmt ); static int pick_av_pixel_format( int *pix_fmt ); #ifdef VDPAU #include "vdpau.c" #endif /** Constructor for libavformat. */ mlt_producer producer_avformat_init( mlt_profile profile, const char *service, char *file ) { if ( list_components( file ) ) return NULL; mlt_producer producer = NULL; // Check that we have a non-NULL argument if ( file ) { // Construct the producer producer_avformat self = calloc( 1, sizeof( struct producer_avformat_s ) ); producer = calloc( 1, sizeof( struct mlt_producer_s ) ); // Initialise it if ( mlt_producer_init( producer, self ) == 0 ) { self->parent = producer; // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Set the resource property (required for all producers) mlt_properties_set( properties, "resource", file ); // Register transport implementation with the producer producer->close = (mlt_destructor) producer_close; // Register our get_frame implementation producer->get_frame = producer_get_frame; // Force the duration to be computed unless explicitly provided. mlt_properties_set_position( properties, "length", 0 ); mlt_properties_set_position( properties, "out", 0 ); if ( strcmp( service, "avformat-novalidate" ) ) { // Open the file if ( producer_open( self, profile, mlt_properties_get( properties, "resource" ), 1, 1 ) != 0 ) { // Clean up mlt_producer_close( producer ); producer = NULL; producer_avformat_close( self ); } else if ( self->seekable ) { // Close the file to release resources for large playlists - reopen later as needed if ( self->audio_format ) avformat_close_input( &self->audio_format ); if ( self->video_format ) avformat_close_input( &self->video_format ); self->audio_format = NULL; self->video_format = NULL; } } if ( producer ) { // Default the user-selectable indices from the auto-detected indices mlt_properties_set_int( properties, "audio_index", self->audio_index ); mlt_properties_set_int( properties, "video_index", self->video_index ); #ifdef VDPAU mlt_service_cache_set_size( MLT_PRODUCER_SERVICE(producer), "producer_avformat", 5 ); #endif mlt_service_cache_put( MLT_PRODUCER_SERVICE(producer), "producer_avformat", self, 0, (mlt_destructor) producer_avformat_close ); mlt_properties_set_int( properties, "mute_on_pause", 1 ); } } } return producer; } int list_components( char* file ) { int skip = 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" ); skip = 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 == AVMEDIA_TYPE_AUDIO ) fprintf( stderr, " - %s\n", codec->name ); fprintf( stderr, "...\n" ); skip = 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 == AVMEDIA_TYPE_VIDEO ) fprintf( stderr, " - %s\n", codec->name ); fprintf( stderr, "...\n" ); skip = 1; } return skip; } static int first_video_index( producer_avformat self ) { AVFormatContext *context = self->video_format? self->video_format : self->audio_format; int i = -1; // not found if ( context ) { for ( i = 0; i < context->nb_streams; i++ ) { if ( context->streams[i]->codec && context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) break; } if ( i == context->nb_streams ) i = -1; } return i; } #include static double get_rotation(AVStream *st) { AVDictionaryEntry *rotate_tag = av_dict_get( st->metadata, "rotate", NULL, 0 ); uint8_t* displaymatrix = av_stream_get_side_data( st, AV_PKT_DATA_DISPLAYMATRIX, NULL); double theta = 0; if ( rotate_tag && *rotate_tag->value && strcmp( rotate_tag->value, "0" ) ) { char *tail; theta = strtod( rotate_tag->value, &tail ); if ( *tail ) theta = 0; } if ( displaymatrix && !theta ) theta = -av_display_rotation_get( (int32_t*) displaymatrix ); theta -= 360 * floor( theta/360 + 0.9/360 ); return theta; } static char* filter_restricted( const char *in ) { if ( !in ) return NULL; size_t n = strlen( in ); char *out = calloc( 1, n + 1 ); char *p = out; mbstate_t mbs; memset( &mbs, 0, sizeof(mbs) ); while ( *in ) { wchar_t w; size_t c = mbrtowc( &w, in, n, &mbs ); if ( c <= 0 || c > n ) break; n -= c; in += c; if ( w == 0x9 || w == 0xA || w == 0xD || ( w >= 0x20 && w <= 0xD7FF ) || ( w >= 0xE000 && w <= 0xFFFD ) || ( w >= 0x10000 && w <= 0x10FFFF ) ) { mbstate_t ps; memset( &ps, 0, sizeof(ps) ); c = wcrtomb( p, w, &ps ); if ( c > 0 ) p += c; } } return out; } /** Find the default streams. */ static mlt_properties find_default_streams( producer_avformat self ) { int i; char key[200]; AVDictionaryEntry *tag = NULL; AVFormatContext *context = self->video_format; mlt_properties meta_media = MLT_PRODUCER_PROPERTIES( self->parent ); // Default to the first audio and video streams found self->audio_index = -1; int first_video_index = self->video_index = -1; 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 AVMEDIA_TYPE_VIDEO: // Save the first video stream if ( first_video_index < 0 ) first_video_index = i; // Only set the video stream if not album art if (self->video_index < 0 && (codec_context->codec_id != AV_CODEC_ID_MJPEG || codec_context->time_base.num != 1 || codec_context->time_base.den != 90000)) { self->video_index = i; } mlt_properties_set( meta_media, key, "video" ); snprintf( key, sizeof(key), "meta.media.%d.stream.frame_rate", i ); double ffmpeg_fps = av_q2d( context->streams[ i ]->avg_frame_rate ); mlt_properties_set_double( meta_media, key, ffmpeg_fps ); 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 ) ); snprintf( key, sizeof(key), "meta.media.%d.codec.width", i ); mlt_properties_set_int( meta_media, key, codec_context->width ); snprintf( key, sizeof(key), "meta.media.%d.codec.height", i ); mlt_properties_set_int( meta_media, key, codec_context->height ); snprintf( key, sizeof(key), "meta.media.%d.codec.rotate", i ); mlt_properties_set_int( meta_media, key, get_rotation(context->streams[i]) ); snprintf( key, sizeof(key), "meta.media.%d.codec.frame_rate", i ); AVRational frame_rate = { codec_context->time_base.den, codec_context->time_base.num * codec_context->ticks_per_frame }; mlt_properties_set_double( meta_media, key, av_q2d( frame_rate ) ); snprintf( key, sizeof(key), "meta.media.%d.codec.pix_fmt", i ); mlt_properties_set( meta_media, key, av_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 ) ); snprintf( key, sizeof(key), "meta.media.%d.codec.colorspace", i ); switch ( codec_context->colorspace ) { case AVCOL_SPC_SMPTE240M: mlt_properties_set_int( meta_media, key, 240 ); break; case AVCOL_SPC_BT470BG: case AVCOL_SPC_SMPTE170M: mlt_properties_set_int( meta_media, key, 601 ); break; case AVCOL_SPC_BT709: mlt_properties_set_int( meta_media, key, 709 ); break; default: // This is a heuristic Charles Poynton suggests in "Digital Video and HDTV" mlt_properties_set_int( meta_media, key, codec_context->width * codec_context->height > 750000 ? 709 : 601 ); break; } if ( codec_context->color_trc && codec_context->color_trc != 2 ) { snprintf( key, sizeof(key), "meta.media.%d.codec.color_trc", i ); mlt_properties_set_double( meta_media, key, codec_context->color_trc ); } break; case AVMEDIA_TYPE_AUDIO: if ( !codec_context->channels ) break; // Use first audio stream if ( self->audio_index < 0 && pick_audio_format( codec_context->sample_fmt ) != mlt_audio_none ) self->audio_index = i; mlt_properties_set( meta_media, key, "audio" ); snprintf( key, sizeof(key), "meta.media.%d.codec.sample_fmt", i ); mlt_properties_set( meta_media, key, av_get_sample_fmt_name( codec_context->sample_fmt ) ); 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 ); snprintf( key, sizeof(key), "meta.media.%d.codec.long_name", i ); mlt_properties_set( meta_media, key, codec->long_name ); snprintf( key, sizeof(key), "meta.media.%d.codec.bit_rate", i ); mlt_properties_set_int64( 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 ); // Read Metadata while ( ( tag = av_dict_get( stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX ) ) ) { if ( tag->value && strcmp( tag->value, "" ) && strcmp( tag->value, "und" ) ) { snprintf( key, sizeof(key), "meta.attr.%d.stream.%s.markup", i, tag->key ); char* value = filter_restricted( tag->value ); mlt_properties_set( meta_media, key, value ); free( value ); } } } // Use the album art if that is all we have if (self->video_index < 0 && first_video_index >= 0) self->video_index = first_video_index; while ( ( tag = av_dict_get( context->metadata, "", tag, AV_DICT_IGNORE_SUFFIX ) ) ) { if ( tag->value && strcmp( tag->value, "" ) && strcmp( tag->value, "und" ) ) { snprintf( key, sizeof(key), "meta.attr.%s.markup", tag->key ); char* value = filter_restricted( tag->value ); mlt_properties_set( meta_media, key, value ); free( value ); } } return meta_media; } static void get_aspect_ratio( mlt_properties properties, AVStream *stream, AVCodecContext *codec_context ) { AVRational sar = stream->sample_aspect_ratio; if ( sar.num <= 0 || sar.den <= 0 ) sar = codec_context->sample_aspect_ratio; if ( sar.num <= 0 || sar.den <= 0 ) sar.num = sar.den = 1; mlt_properties_set_int( properties, "meta.media.sample_aspect_num", sar.num ); mlt_properties_set_int( properties, "meta.media.sample_aspect_den", sar.den ); mlt_properties_set_double( properties, "aspect_ratio", av_q2d( sar ) ); } static char* parse_url( mlt_profile profile, const char* URL, AVInputFormat **format, AVDictionary **params ) { if ( !URL ) return NULL; char *protocol = strdup( URL ); char *url = strchr( protocol, ':' ); // Only if there is not a protocol specification that avformat can handle if ( url && avio_check( URL, 0 ) < 0 ) { // Truncate protocol string url[0] = 0; ++url; mlt_log_debug( NULL, "%s: protocol=%s resource=%s\n", __FUNCTION__, protocol, url ); // Lookup the format *format = av_find_input_format( protocol ); if ( *format ) { // Eat the format designator char *result = url; // support for legacy width and height parameters char *width = NULL; char *height = NULL; // Parse out params url = strchr( url, '?' ); while ( url ) { url[0] = 0; char *name = strdup( ++url ); char *value = strchr( name, '=' ); if ( !value ) // Also accept : as delimiter for backwards compatibility. value = strchr( name, ':' ); if ( value ) { value[0] = 0; value++; char *t = strchr( value, '&' ); if ( t ) t[0] = 0; // translate old parameters to new av_dict names if ( !strcmp( name, "frame_rate" ) ) av_dict_set( params, "framerate", value, 0 ); else if ( !strcmp( name, "pix_fmt" ) ) av_dict_set( params, "pixel_format", value, 0 ); else if ( !strcmp( name, "width" ) ) width = strdup( value ); else if ( !strcmp( name, "height" ) ) height = strdup( value ); else // generic demux/device option support av_dict_set( params, name, value, 0 ); } free( name ); url = strchr( url, '&' ); } // continued support for legacy width and height parameters if ( width && height ) { char *s = malloc( strlen( width ) + strlen( height ) + 2 ); strcpy( s, width ); strcat( s, "x"); strcat( s, height ); av_dict_set( params, "video_size", s, 0 ); free( s ); } free( width ); free( height ); result = strdup(result); free( protocol ); return result; } } free( protocol ); return strdup( URL ); } static enum AVPixelFormat pick_pix_fmt( enum AVPixelFormat pix_fmt ) { switch ( pix_fmt ) { case AV_PIX_FMT_ARGB: case AV_PIX_FMT_RGBA: case AV_PIX_FMT_ABGR: case AV_PIX_FMT_BGRA: return AV_PIX_FMT_RGBA; #if defined(FFUDIV) case AV_PIX_FMT_BAYER_RGGB16LE: return AV_PIX_FMT_RGB24; #endif default: return AV_PIX_FMT_YUV422P; } } static int get_basic_info( producer_avformat self, mlt_profile profile, const char *filename ) { int error = 0; // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent ); AVFormatContext *format = self->video_format; // Get the duration if ( mlt_properties_get_position( properties, "length" ) <= 0 || mlt_properties_get_position( properties, "out" ) <= 0 ) { if ( format->duration != AV_NOPTS_VALUE ) { // This isn't going to be accurate for all formats // We will treat everything with the producer fps. mlt_position frames = ( mlt_position ) lrint( format->duration * mlt_profile_fps( profile ) / AV_TIME_BASE ); if ( mlt_properties_get_position( properties, "out" ) <= 0 ) mlt_properties_set_position( properties, "out", frames - 1 ); if ( mlt_properties_get_position( properties, "length" ) <= 0 ) mlt_properties_set_position( properties, "length", frames ); } else if ( format->nb_streams > 0 && format->streams[0]->codec && format->streams[0]->codec->codec_id == AV_CODEC_ID_WEBP ) { char *e = getenv( "MLT_DEFAULT_PRODUCER_LENGTH" ); int p = e ? atoi( e ) : 15000; mlt_properties_set_int( properties, "out", MAX(0, p - 1) ); mlt_properties_set_int( properties, "length", p ); } else { // Set live sources to run forever if ( mlt_properties_get_position( properties, "length" ) <= 0 ) mlt_properties_set_position( properties, "length", INT_MAX ); if ( mlt_properties_get_position( properties, "out" ) <= 0 ) mlt_properties_set_position( properties, "out", INT_MAX - 1 ); mlt_properties_set( properties, "eof", "loop" ); } } // Check if we're seekable // avdevices are typically AVFMT_NOFILE and not seekable self->seekable = !format->iformat || !( format->iformat->flags & AVFMT_NOFILE ); if ( format->pb ) { // protocols can indicate if they support seeking self->seekable = format->pb->seekable; } if ( self->seekable ) { // Do a more rigorous test of seekable on a disposable context if ( format->nb_streams > 0 && format->streams[0]->codec && format->streams[0]->codec->codec_id != AV_CODEC_ID_WEBP ) self->seekable = av_seek_frame( format, -1, format->start_time, AVSEEK_FLAG_BACKWARD ) >= 0; mlt_properties_set_int( properties, "seekable", self->seekable ); self->dummy_context = format; self->video_format = NULL; avformat_open_input( &self->video_format, filename, NULL, NULL ); avformat_find_stream_info( self->video_format, NULL ); format = self->video_format; } self->video_seekable = self->seekable; // Fetch the width, height and aspect ratio if ( self->video_index != -1 ) { AVCodecContext *codec_context = format->streams[ self->video_index ]->codec; mlt_properties_set_int( properties, "width", codec_context->width ); mlt_properties_set_int( properties, "height", codec_context->height ); get_aspect_ratio( properties, format->streams[ self->video_index ], codec_context ); int pix_fmt = codec_context->pix_fmt; pick_av_pixel_format( &pix_fmt ); if ( pix_fmt != AV_PIX_FMT_NONE ) { // Verify that we can convert this to one of our image formats. struct SwsContext *context = sws_getContext( codec_context->width, codec_context->height, pix_fmt, codec_context->width, codec_context->height, pick_pix_fmt( codec_context->pix_fmt ), SWS_BILINEAR, NULL, NULL, NULL); if ( context ) sws_freeContext( context ); else error = 1; } else { self->video_index = -1; } } return error; } #ifdef AVFILTER static int setup_video_filters( producer_avformat self ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self->parent); AVFormatContext *format = self->video_format; AVStream* stream = format->streams[ self->video_index ]; AVCodecContext *codec_context = stream->codec; self->vfilter_graph = avfilter_graph_alloc(); // From ffplay.c:configure_video_filters(). char buffersrc_args[256]; snprintf(buffersrc_args, sizeof(buffersrc_args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:frame_rate=%d/%d", codec_context->width, codec_context->height, codec_context->pix_fmt, stream->time_base.num, stream->time_base.den, mlt_properties_get_int(properties, "meta.media.sample_aspect_num"), FFMAX(mlt_properties_get_int(properties, "meta.media.sample_aspect_den"), 1), stream->avg_frame_rate.num, FFMAX(stream->avg_frame_rate.den, 1)); int result = avfilter_graph_create_filter(&self->vfilter_in, avfilter_get_by_name("buffer"), "mlt_buffer", buffersrc_args, NULL, self->vfilter_graph); if (result >= 0) { result = avfilter_graph_create_filter(&self->vfilter_out, avfilter_get_by_name("buffersink"), "mlt_buffersink", NULL, NULL, self->vfilter_graph); if (result >= 0) { enum AVPixelFormat pix_fmts[] = { codec_context->pix_fmt, AV_PIX_FMT_NONE }; result = av_opt_set_int_list(self->vfilter_out, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); } } return result; } static int insert_filter(AVFilterGraph *graph, AVFilterContext **last_filter, const char *name, const char *args) { AVFilterContext *filt_ctx; int result = avfilter_graph_create_filter(&filt_ctx, avfilter_get_by_name(name), name, args, NULL, graph); if (result >= 0) { result = avfilter_link(filt_ctx, 0, *last_filter, 0); if (result >= 0) *last_filter = filt_ctx; } return result; } #endif /** Open the file. */ static int producer_open(producer_avformat self, mlt_profile profile, const char *URL, int take_lock, int test_open ) { // Return an error code (0 == no error) int error = 0; mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent ); if ( !self->is_mutex_init ) { pthread_mutex_init( &self->audio_mutex, NULL ); pthread_mutex_init( &self->video_mutex, NULL ); pthread_mutex_init( &self->packets_mutex, NULL ); pthread_mutex_init( &self->open_mutex, NULL ); self->is_mutex_init = 1; } // Lock the service if ( take_lock ) { pthread_mutex_lock( &self->audio_mutex ); pthread_mutex_lock( &self->video_mutex ); } mlt_events_block( properties, self->parent ); // Parse URL AVInputFormat *format = NULL; AVDictionary *params = NULL; char *filename = parse_url( profile, URL, &format, ¶ms ); // Now attempt to open the file or device with filename error = avformat_open_input( &self->video_format, filename, format, ¶ms ) < 0; if ( error ) // If the URL is a network stream URL, then we probably need to open with full URL error = avformat_open_input( &self->video_format, URL, format, ¶ms ) < 0; // Set MLT properties onto video AVFormatContext if ( !error && self->video_format ) { apply_properties( self->video_format, properties, AV_OPT_FLAG_DECODING_PARAM ); if ( self->video_format->iformat && self->video_format->iformat->priv_class && self->video_format->priv_data ) apply_properties( self->video_format->priv_data, properties, AV_OPT_FLAG_DECODING_PARAM ); } av_dict_free( ¶ms ); // If successful, then try to get additional info if ( !error && self->video_format ) { // Get the stream info error = avformat_find_stream_info( self->video_format, NULL ) < 0; // Continue if no error if ( !error && self->video_format ) { // Find default audio and video streams find_default_streams( self ); error = get_basic_info( self, profile, filename ); // Initialize position info self->first_pts = AV_NOPTS_VALUE; self->last_position = POSITION_INITIAL; if ( !self->audio_format ) { // We're going to cheat here - for seekable A/V files, we will have separate contexts // to support independent seeking of audio from video. // TODO: Is this really necessary? if ( self->audio_index != -1 && self->video_index != -1 ) { if ( self->seekable ) { // And open again for our audio context avformat_open_input( &self->audio_format, filename, NULL, NULL ); apply_properties( self->audio_format, properties, AV_OPT_FLAG_DECODING_PARAM ); if ( self->audio_format->iformat && self->audio_format->iformat->priv_class && self->audio_format->priv_data ) apply_properties( self->audio_format->priv_data, properties, AV_OPT_FLAG_DECODING_PARAM ); avformat_find_stream_info( self->audio_format, NULL ); } else { self->audio_format = self->video_format; } } else if ( self->audio_index != -1 ) { // We only have an audio context self->audio_format = self->video_format; self->video_format = NULL; } else if ( self->video_index == -1 ) { // Something has gone wrong error = -1; } if ( self->audio_format && !self->audio_streams ) get_audio_streams_info( self ); #ifdef AVFILTER // Setup autorotate filters. if (self->video_index != -1) { self->autorotate = !mlt_properties_get(properties, "autorotate") || mlt_properties_get_int(properties, "autorotate"); if (!test_open && self->autorotate && !self->vfilter_graph) { double theta = get_rotation(self->video_format->streams[self->video_index]); if (fabs(theta - 90) < 1.0) { error = ( setup_video_filters(self) < 0 ); AVFilterContext *last_filter = self->vfilter_out; if (!error) error = ( insert_filter(self->vfilter_graph, &last_filter, "transpose", "clock") < 0 ); if (!error) error = ( avfilter_link(self->vfilter_in, 0, last_filter, 0) < 0 ); if (!error) error = ( avfilter_graph_config(self->vfilter_graph, NULL) < 0 ); } else if (fabs(theta - 180) < 1.0) { error = ( setup_video_filters(self) < 0 ); AVFilterContext *last_filter = self->vfilter_out; if (!error) error = ( insert_filter(self->vfilter_graph, &last_filter, "hflip", NULL) < 0 ); if (!error) error = ( insert_filter(self->vfilter_graph, &last_filter, "vflip", NULL) < 0 ); if (!error) error = ( avfilter_link(self->vfilter_in, 0, last_filter, 0) < 0 ); if (!error) error = ( avfilter_graph_config(self->vfilter_graph, NULL) < 0 ); } else if (fabs(theta - 270) < 1.0) { error = ( setup_video_filters(self) < 0 ); AVFilterContext *last_filter = self->vfilter_out; if (!error) error = ( insert_filter(self->vfilter_graph, &last_filter, "transpose", "cclock") < 0 ); if (!error) error = ( avfilter_link(self->vfilter_in, 0, last_filter, 0) < 0 ); if (!error) error = ( avfilter_graph_config(self->vfilter_graph, NULL) < 0 ); } } } #endif } } } free( filename ); if ( !error ) { self->apackets = mlt_deque_init(); self->vpackets = mlt_deque_init(); } if ( self->dummy_context ) { pthread_mutex_lock( &self->open_mutex ); avformat_close_input( &self->dummy_context ); self->dummy_context = NULL; pthread_mutex_unlock( &self->open_mutex ); } // Unlock the service if ( take_lock ) { pthread_mutex_unlock( &self->audio_mutex ); pthread_mutex_unlock( &self->video_mutex ); } mlt_events_unblock( properties, self->parent ); return error; } static void prepare_reopen( producer_avformat self ) { mlt_service_lock( MLT_PRODUCER_SERVICE( self->parent ) ); pthread_mutex_lock( &self->audio_mutex ); pthread_mutex_lock( &self->open_mutex ); int i; for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) { mlt_pool_release( self->audio_buffer[i] ); self->audio_buffer[i] = NULL; av_free( self->decode_buffer[i] ); self->decode_buffer[i] = NULL; if ( self->audio_codec[i] ) avcodec_close( self->audio_codec[i] ); self->audio_codec[i] = NULL; } if ( self->video_codec ) avcodec_close( self->video_codec ); self->video_codec = NULL; if ( self->seekable && self->audio_format ) avformat_close_input( &self->audio_format ); if ( self->video_format ) avformat_close_input( &self->video_format ); self->audio_format = NULL; self->video_format = NULL; #ifdef AVFILTER avfilter_graph_free( &self->vfilter_graph ); #endif pthread_mutex_unlock( &self->open_mutex ); // Cleanup the packet queues AVPacket *pkt; if ( self->apackets ) { while ( ( pkt = mlt_deque_pop_back( self->apackets ) ) ) { av_free_packet( pkt ); free( pkt ); } mlt_deque_close( self->apackets ); self->apackets = NULL; } if ( self->vpackets ) { while ( ( pkt = mlt_deque_pop_back( self->vpackets ) ) ) { av_free_packet( pkt ); free( pkt ); } mlt_deque_close( self->vpackets ); self->vpackets = NULL; } pthread_mutex_unlock( &self->audio_mutex ); mlt_service_unlock( MLT_PRODUCER_SERVICE( self->parent ) ); } static int64_t best_pts( producer_avformat self, int64_t pts, int64_t dts ) { self->invalid_pts_counter += pts == AV_NOPTS_VALUE; self->invalid_dts_counter += dts == AV_NOPTS_VALUE; if ( ( self->invalid_pts_counter <= self->invalid_dts_counter || dts == AV_NOPTS_VALUE ) && pts != AV_NOPTS_VALUE ) return pts; else return dts; } static void find_first_pts( producer_avformat self, int video_index ) { // find initial PTS AVFormatContext *context = self->video_format? self->video_format : self->audio_format; int ret = 0; int pkt_countdown = 500; // check max 500 packets for first video keyframe PTS int vfr_countdown = 20; // check max 20 video frames for VFR int vfr_counter = 0; // counts the number of frame duration changes AVPacket pkt; int64_t prev_pkt_duration = AV_NOPTS_VALUE; av_init_packet( &pkt ); while ( ret >= 0 && pkt_countdown-- > 0 && ( self->first_pts == AV_NOPTS_VALUE || ( vfr_counter < VFR_THRESHOLD && vfr_countdown > 0 ) ) ) { ret = av_read_frame( context, &pkt ); if ( ret >= 0 && pkt.stream_index == video_index ) { // Variable frame rate check if ( pkt.duration != AV_NOPTS_VALUE && pkt.duration != prev_pkt_duration ) { mlt_log_verbose( MLT_PRODUCER_SERVICE(self->parent), "checking VFR: pkt.duration %"PRId64"\n", pkt.duration ); if ( prev_pkt_duration != AV_NOPTS_VALUE ) ++vfr_counter; } prev_pkt_duration = pkt.duration; vfr_countdown--; // Finding PTS of first video key frame if ( ( pkt.flags & AV_PKT_FLAG_KEY ) && self->first_pts == AV_NOPTS_VALUE ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "first_pts %"PRId64" dts %"PRId64" pts_dts_delta %d\n", pkt.pts, pkt.dts, (int)(pkt.pts - pkt.dts) ); if ( pkt.dts != AV_NOPTS_VALUE && pkt.dts < 0 ) // Decoding Time Stamps with negative values are reported by ffmpeg code for // (at least) MP4 files containing h.264 video using b-frames. // For reasons not understood yet, the first PTS computed then is that of the // third frame, causing MLT to display the third frame as if it was the first. // This if-clause is meant to catch and work around this issue - if there is // a valid, but negative DTS value, we just guess that the first valid // Presentation Time Stamp is == 0. self->first_pts = 0; else self->first_pts = best_pts( self, pkt.pts, pkt.dts ); } } av_free_packet( &pkt ); } if ( vfr_counter >= VFR_THRESHOLD ) mlt_properties_set_int( MLT_PRODUCER_PROPERTIES(self->parent), "meta.media.variable_frame_rate", 1 ); av_seek_frame( context, -1, 0, AVSEEK_FLAG_BACKWARD ); } static int seek_video( producer_avformat self, mlt_position position, int64_t req_position, int preseek ) { mlt_producer producer = self->parent; mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); int paused = 0; int seek_threshold = mlt_properties_get_int( properties, "seek_threshold" ); if ( seek_threshold <= 0 ) seek_threshold = 12; pthread_mutex_lock( &self->packets_mutex ); if ( self->video_seekable && ( position != self->video_expected || self->last_position < 0 ) ) { // Fetch the video format context AVFormatContext *context = self->video_format; // Get the video stream AVStream *stream = context->streams[ self->video_index ]; // Get codec context AVCodecContext *codec_context = stream->codec; // We may want to use the source fps if available double source_fps = mlt_properties_get_double( properties, "meta.media.frame_rate_num" ) / mlt_properties_get_double( properties, "meta.media.frame_rate_den" ); if ( self->first_pts == AV_NOPTS_VALUE && self->last_position == POSITION_INITIAL ) find_first_pts( self, self->video_index ); if ( self->video_frame && position + 1 == self->video_expected ) { // We're paused - use last image paused = 1; } else if ( position < self->video_expected || position - self->video_expected >= seek_threshold || self->last_position < 0 ) { // Calculate the timestamp for the requested frame int64_t timestamp = req_position / ( av_q2d( self->video_time_base ) * source_fps ); if ( req_position <= 0 ) timestamp = 0; else if ( self->first_pts != AV_NOPTS_VALUE ) timestamp += self->first_pts; else if ( context->start_time != AV_NOPTS_VALUE ) timestamp += context->start_time; if ( preseek && av_q2d( self->video_time_base ) != 0 ) timestamp -= 2 / av_q2d( self->video_time_base ); if ( timestamp < 0 ) timestamp = 0; mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "seeking timestamp %"PRId64" position " MLT_POSITION_FMT " expected "MLT_POSITION_FMT" last_pos %"PRId64"\n", timestamp, position, self->video_expected, self->last_position ); // Seek to the timestamp codec_context->skip_loop_filter = AVDISCARD_NONREF; av_seek_frame( context, self->video_index, timestamp, AVSEEK_FLAG_BACKWARD ); // flush any pictures still in decode buffer avcodec_flush_buffers( codec_context ); // Remove the cached info relating to the previous position self->current_position = POSITION_INVALID; self->last_position = POSITION_INVALID; av_freep( &self->video_frame ); } } pthread_mutex_unlock( &self->packets_mutex ); return paused; } /** Convert a frame position to a time code. */ static double producer_time_of_frame( mlt_producer producer, mlt_position position ) { return ( double )position / mlt_producer_get_fps( producer ); } // Collect information about all audio streams static void get_audio_streams_info( producer_avformat self ) { // Fetch the audio format context AVFormatContext *context = self->audio_format; int i; for ( i = 0; i < context->nb_streams; i++ ) { if ( context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) { AVCodecContext *codec_context = context->streams[i]->codec; AVCodec *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... pthread_mutex_lock( &self->open_mutex ); if ( codec && avcodec_open2( codec_context, codec, NULL ) >= 0 ) { self->audio_streams++; self->audio_max_stream = i; self->total_channels += codec_context->channels; if ( codec_context->channels > self->max_channel ) self->max_channel = codec_context->channels; if ( codec_context->sample_rate > self->max_frequency ) self->max_frequency = codec_context->sample_rate; avcodec_close( codec_context ); } pthread_mutex_unlock( &self->open_mutex ); } } mlt_log_verbose( NULL, "[producer avformat] audio: total_streams %d max_stream %d total_channels %d max_channels %d\n", self->audio_streams, self->audio_max_stream, self->total_channels, self->max_channel ); } static mlt_image_format pick_image_format( enum AVPixelFormat pix_fmt ) { switch ( pix_fmt ) { case AV_PIX_FMT_ARGB: case AV_PIX_FMT_RGBA: case AV_PIX_FMT_ABGR: case AV_PIX_FMT_BGRA: return mlt_image_rgb24a; case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUVJ420P: case AV_PIX_FMT_YUVA420P: return mlt_image_yuv420p; case AV_PIX_FMT_RGB24: case AV_PIX_FMT_BGR24: case AV_PIX_FMT_GRAY8: case AV_PIX_FMT_MONOWHITE: case AV_PIX_FMT_MONOBLACK: case AV_PIX_FMT_RGB8: case AV_PIX_FMT_BGR8: #if defined(FFUDIV) case AV_PIX_FMT_BAYER_RGGB16LE: return mlt_image_rgb24; #endif default: return mlt_image_yuv422; } } static mlt_audio_format pick_audio_format( int sample_fmt ) { switch ( sample_fmt ) { // interleaved case AV_SAMPLE_FMT_U8: return mlt_audio_u8; case AV_SAMPLE_FMT_S16: return mlt_audio_s16; case AV_SAMPLE_FMT_S32: return mlt_audio_s32le; case AV_SAMPLE_FMT_FLT: return mlt_audio_f32le; // planar - this producer converts planar to interleaved case AV_SAMPLE_FMT_U8P: return mlt_audio_u8; case AV_SAMPLE_FMT_S16P: return mlt_audio_s16; case AV_SAMPLE_FMT_S32P: return mlt_audio_s32le; case AV_SAMPLE_FMT_FLTP: return mlt_audio_f32le; default: return mlt_audio_none; } } /** * Handle deprecated pixel format (JPEG range in YUV420P for example). * * Replace pix_fmt with the official pixel format to use. * @return 0 if no pix_fmt replacement, 1 otherwise */ static int pick_av_pixel_format( int *pix_fmt ) { #if defined(FFUDIV) switch (*pix_fmt) { case AV_PIX_FMT_YUVJ420P: *pix_fmt = AV_PIX_FMT_YUV420P; return 1; case AV_PIX_FMT_YUVJ411P: *pix_fmt = AV_PIX_FMT_YUV411P; return 1; case AV_PIX_FMT_YUVJ422P: *pix_fmt = AV_PIX_FMT_YUV422P; return 1; case AV_PIX_FMT_YUVJ444P: *pix_fmt = AV_PIX_FMT_YUV444P; return 1; case AV_PIX_FMT_YUVJ440P: *pix_fmt = AV_PIX_FMT_YUV440P; return 1; } #endif return 0; } #if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((3<<16)+(1<<8)+101)) struct sliced_pix_fmt_conv_t { int width, height, slice_w; AVFrame *frame; uint8_t *out_data[4]; int out_stride[4]; enum AVPixelFormat src_format, dst_format; const AVPixFmtDescriptor *src_desc, *dst_desc; int flags, src_colorspace, dst_colorspace, src_full_range, dst_full_range; }; static int sliced_h_pix_fmt_conv_proc( int id, int idx, int jobs, void* cookie ) { uint8_t *out[4]; const uint8_t *in[4]; int in_stride[4], out_stride[4]; int src_v_chr_pos = -513, dst_v_chr_pos = -513, ret, i, slice_x, slice_w, h, mul, field, slices, interlaced = 0; struct SwsContext *sws; struct sliced_pix_fmt_conv_t* ctx = ( struct sliced_pix_fmt_conv_t* )cookie; interlaced = ctx->frame->interlaced_frame; field = ( interlaced ) ? ( idx & 1 ) : 0; idx = ( interlaced ) ? ( idx / 2 ) : idx; slices = ( interlaced ) ? ( jobs / 2 ) : jobs; mul = ( interlaced ) ? 2 : 1; h = ctx->height >> !!interlaced; slice_w = ctx->slice_w; slice_x = slice_w * idx; slice_w = FFMIN( slice_w, ctx->width - slice_x ); if ( AV_PIX_FMT_YUV420P == ctx->src_format ) src_v_chr_pos = ( !interlaced ) ? 128 : ( !field ) ? 64 : 192; if ( AV_PIX_FMT_YUV420P == ctx->dst_format ) dst_v_chr_pos = ( !interlaced ) ? 128 : ( !field ) ? 64 : 192; mlt_log_debug( NULL, "%s:%d: [id=%d, idx=%d, jobs=%d], interlaced=%d, field=%d, slices=%d, mul=%d, h=%d, slice_w=%d, slice_x=%d ctx->src_desc=[log2_chroma_h=%d, log2_chroma_w=%d], src_v_chr_pos=%d, dst_v_chr_pos=%d\n", __FUNCTION__, __LINE__, id, idx, jobs, interlaced, field, slices, mul, h, slice_w, slice_x, ctx->src_desc->log2_chroma_h, ctx->src_desc->log2_chroma_w, src_v_chr_pos, dst_v_chr_pos ); if ( slice_w <= 0 ) return 0; sws = sws_alloc_context(); av_opt_set_int( sws, "srcw", slice_w, 0 ); av_opt_set_int( sws, "srch", h, 0 ); av_opt_set_int( sws, "src_format", ctx->src_format, 0 ); av_opt_set_int( sws, "dstw", slice_w, 0 ); av_opt_set_int( sws, "dsth", h, 0 ); av_opt_set_int( sws, "dst_format", ctx->dst_format, 0 ); av_opt_set_int( sws, "sws_flags", ctx->flags, 0 ); av_opt_set_int( sws, "src_h_chr_pos", -513, 0 ); av_opt_set_int( sws, "src_v_chr_pos", src_v_chr_pos, 0 ); av_opt_set_int( sws, "dst_h_chr_pos", -513, 0 ); av_opt_set_int( sws, "dst_v_chr_pos", dst_v_chr_pos, 0 ); if ( ( ret = sws_init_context( sws, NULL, NULL ) ) < 0 ) { mlt_log_error( NULL, "%s:%d: sws_init_context failed, ret=%d\n", __FUNCTION__, __LINE__, ret ); sws_freeContext( sws ); return 0; } mlt_set_luma_transfer( sws, ctx->src_colorspace, ctx->dst_colorspace, ctx->src_full_range, ctx->dst_full_range ); #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(55, 0, 100) #define PIX_DESC_BPP(DESC) (DESC.step_minus1 + 1) #else #define PIX_DESC_BPP(DESC) (DESC.step) #endif for( i = 0; i < 4; i++ ) { int in_offset = (AV_PIX_FMT_FLAG_PLANAR & ctx->src_desc->flags) ? ( ( 1 == i || 2 == i ) ? ( slice_x >> ctx->src_desc->log2_chroma_w ) : slice_x ) : ( ( 0 == i ) ? slice_x : 0 ); int out_offset = (AV_PIX_FMT_FLAG_PLANAR & ctx->dst_desc->flags) ? ( ( 1 == i || 2 == i ) ? ( slice_x >> ctx->dst_desc->log2_chroma_w ) : slice_x ) : ( ( 0 == i ) ? slice_x : 0 ); in_offset *= PIX_DESC_BPP(ctx->src_desc->comp[i]); out_offset *= PIX_DESC_BPP(ctx->dst_desc->comp[i]); in_stride[i] = ctx->frame->linesize[i] * mul; out_stride[i] = ctx->out_stride[i] * mul; in[i] = ctx->frame->data[i] + ctx->frame->linesize[i] * field + in_offset; out[i] = ctx->out_data[i] + ctx->out_stride[i] * field + out_offset; } sws_scale( sws, in, in_stride, 0, h, out, out_stride ); sws_freeContext( sws ); return 0; } #endif // returns resulting YUV colorspace static int convert_image( producer_avformat self, AVFrame *frame, uint8_t *buffer, int pix_fmt, mlt_image_format *format, int width, int height, uint8_t **alpha ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self->parent ) ); int result = self->yuv_colorspace; mlt_log_timings_begin(); mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "%s @ %dx%d space %d->%d\n", mlt_image_format_name( *format ), width, height, self->yuv_colorspace, profile->colorspace ); // extract alpha from planar formats if ( ( pix_fmt == AV_PIX_FMT_YUVA420P #if defined(FFUDIV) || pix_fmt == AV_PIX_FMT_YUVA444P #endif ) && *format != mlt_image_rgb24a && *format != mlt_image_opengl && frame->data[3] && frame->linesize[3] ) { int i; uint8_t *src, *dst; dst = *alpha = mlt_pool_alloc( width * height ); src = frame->data[3]; for ( i = 0; i < height; dst += width, src += frame->linesize[3], i++ ) memcpy( dst, src, FFMIN( width, frame->linesize[3] ) ); } int src_pix_fmt = pix_fmt; pick_av_pixel_format( &src_pix_fmt ); if ( *format == mlt_image_yuv420p ) { // This is a special case. Movit wants the full range, if available. // Thankfully, there is not much other use of yuv420p except consumer // avformat with no filters and explicitly requested. #if defined(FFUDIV) int flags = mlt_get_sws_flags(width, height, src_pix_fmt, width, height, AV_PIX_FMT_YUV420P); struct SwsContext *context = sws_getContext(width, height, src_pix_fmt, width, height, AV_PIX_FMT_YUV420P, flags, NULL, NULL, NULL); #else int dst_pix_fmt = self->full_luma ? AV_PIX_FMT_YUVJ420P : AV_PIX_FMT_YUV420P; int flags = mlt_get_sws_flags(width, height, pix_fmt, width, height, dst_pix_fmt); struct SwsContext *context = sws_getContext( width, height, pix_fmt, width, height, dst_pix_fmt, flags, NULL, NULL, NULL); #endif uint8_t *out_data[4]; int out_stride[4]; out_data[0] = buffer; out_data[1] = buffer + width * height; out_data[2] = buffer + ( 5 * width * height ) / 4; out_stride[0] = width; out_stride[1] = width >> 1; out_stride[2] = width >> 1; if ( !mlt_set_luma_transfer( context, self->yuv_colorspace, profile->colorspace, self->full_luma, self->full_luma ) ) result = profile->colorspace; sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, out_data, out_stride); sws_freeContext( context ); } else if ( *format == mlt_image_rgb24 ) { int flags = mlt_get_sws_flags(width, height, src_pix_fmt, width, height, AV_PIX_FMT_RGB24); struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, width, height, AV_PIX_FMT_RGB24, flags, NULL, NULL, NULL); uint8_t *out_data[4]; int out_stride[4]; av_image_fill_arrays(out_data, out_stride, buffer, AV_PIX_FMT_RGB24, width, height, IMAGE_ALIGN); // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. mlt_set_luma_transfer( context, self->yuv_colorspace, 601, self->full_luma, 0 ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, out_data, out_stride); sws_freeContext( context ); } else if ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { int flags = mlt_get_sws_flags(width, height, src_pix_fmt, width, height, AV_PIX_FMT_RGBA); struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, width, height, AV_PIX_FMT_RGBA, flags, NULL, NULL, NULL); uint8_t *out_data[4]; int out_stride[4]; av_image_fill_arrays(out_data, out_stride, buffer, AV_PIX_FMT_RGBA, width, height, IMAGE_ALIGN); // libswscale wants the RGB colorspace to be SWS_CS_DEFAULT, which is = SWS_CS_ITU601. mlt_set_luma_transfer( context, self->yuv_colorspace, 601, self->full_luma, 0 ); sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, out_data, out_stride); sws_freeContext( context ); } else #if defined(FFUDIV) && (LIBSWSCALE_VERSION_INT >= ((3<<16)+(1<<8)+101)) { int i, c; struct sliced_pix_fmt_conv_t ctx = { .width = width, .height = height, .frame = frame, .dst_format = AV_PIX_FMT_YUYV422, .src_colorspace = self->yuv_colorspace, .dst_colorspace = profile->colorspace, .src_full_range = self->full_luma, .dst_full_range = 0, }; ctx.src_format = (self->full_luma && src_pix_fmt == AV_PIX_FMT_YUV422P) ? AV_PIX_FMT_YUVJ422P : src_pix_fmt; ctx.src_desc = av_pix_fmt_desc_get( ctx.src_format ); ctx.dst_desc = av_pix_fmt_desc_get( ctx.dst_format ); ctx.flags = mlt_get_sws_flags(width, height, ctx.src_format, width, height, ctx.dst_format); av_image_fill_arrays(ctx.out_data, ctx.out_stride, buffer, ctx.dst_format, width, height, IMAGE_ALIGN); int sliced = !getenv("MLT_AVFORMAT_SLICED_PIXFMT_DISABLE"); if ( sliced ) { ctx.slice_w = ( width < 1000 ) ? ( 256 >> frame->interlaced_frame ) : ( 512 >> frame->interlaced_frame ); } else { ctx.slice_w = width; } c = ( width + ctx.slice_w - 1 ) / ctx.slice_w; int last_slice_w = width - ctx.slice_w * (c - 1); if ( sliced && (last_slice_w % 8) == 0 && !(ctx.src_format == AV_PIX_FMT_YUV422P && last_slice_w % 16) ) { c *= frame->interlaced_frame ? 2 : 1; mlt_slices_run_normal( c, sliced_h_pix_fmt_conv_proc, &ctx ); } else { c = frame->interlaced_frame ? 2 : 1; ctx.slice_w = width; for ( i = 0 ; i < c; i++ ) sliced_h_pix_fmt_conv_proc( i, i, c, &ctx ); } result = profile->colorspace; } #else { #if defined(FFUDIV) int flags = mlt_get_sws_flags(width, height, src_pix_fmt, width, height, AV_PIX_FMT_YUYV422); struct SwsContext *context = sws_getContext( width, height, src_pix_fmt, width, height, AV_PIX_FMT_YUYV422, flags, NULL, NULL, NULL); #else int flags = mlt_get_sws_flags(width, height, pix_fmt, width, height, AV_PIX_FMT_YUYV422); struct SwsContext *context = sws_getContext( width, height, pix_fmt, width, height, AV_PIX_FMT_YUYV422, flags, NULL, NULL, NULL); #endif AVPicture output; avpicture_fill( &output, buffer, AV_PIX_FMT_YUYV422, width, height ); if ( !mlt_set_luma_transfer( context, self->yuv_colorspace, profile->colorspace, self->full_luma, 0 ) ) result = profile->colorspace; sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); } #endif mlt_log_timings_end( NULL, __FUNCTION__ ); return result; } static void set_image_size( producer_avformat self, int *width, int *height ) { double dar = mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE(self->parent) ) ); double theta = self->autorotate? get_rotation( self->video_format->streams[self->video_index] ) : 0.0; if ( fabs(theta - 90.0) < 1.0 || fabs(theta - 270.0) < 1.0 ) { *height = self->video_codec->width; // Workaround 1088 encodings missing cropping info. if ( self->video_codec->height == 1088 && dar == 16.0/9.0 ) *width = 1080; else *width = self->video_codec->height; } else { *width = self->video_codec->width; // Workaround 1088 encodings missing cropping info. if ( self->video_codec->height == 1088 && dar == 16.0/9.0 ) *height = 1080; else *height = self->video_codec->height; } } /** Allocate the image buffer and set it on the frame. */ static int allocate_buffer( mlt_frame frame, 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; size = mlt_image_format_size( format, width, height, NULL ); *buffer = mlt_pool_alloc( size ); if ( *buffer ) mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); 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 producer producer_avformat self = mlt_frame_pop_service( frame ); mlt_producer producer = self->parent; // 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_frame_original_position( frame ); // Get the producer properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); pthread_mutex_lock( &self->video_mutex ); uint8_t *alpha = NULL; int got_picture = 0; int image_size = 0; // Fetch the video format context AVFormatContext *context = self->video_format; if ( !context ) goto exit_get_image; // Get the video stream AVStream *stream = context->streams[ self->video_index ]; // Get codec context AVCodecContext *codec_context = stream->codec; mlt_log_timings_begin(); // Always use the image cache for album art. int is_album_art = (codec_context->codec_id == AV_CODEC_ID_MJPEG && mlt_properties_get_int(properties, "meta.media.frame_rate_num") == 90000 && mlt_properties_get_int(properties, "meta.media.frame_rate_den") == 1); if (is_album_art) position = 0; // Get the image cache if ( ! self->image_cache ) { // if cache size supplied by environment variable int cache_supplied = getenv( "MLT_AVFORMAT_CACHE" ) != NULL; int cache_size = cache_supplied? atoi( getenv( "MLT_AVFORMAT_CACHE" ) ) : 0; // cache size supplied via property if ( mlt_properties_get( properties, "cache" ) ) { cache_supplied = 1; cache_size = mlt_properties_get_int( properties, "cache" ); } if ( mlt_properties_get_int( properties, "noimagecache" ) ) { cache_supplied = 1; cache_size = 0; } // create cache if not disabled if ( !cache_supplied || cache_size > 0 ) self->image_cache = mlt_cache_init(); // set cache size if supplied if ( self->image_cache && cache_supplied ) mlt_cache_set_size( self->image_cache, cache_size ); } if ( self->image_cache ) { mlt_frame original = mlt_cache_get_frame( self->image_cache, position ); if ( original ) { mlt_properties orig_props = MLT_FRAME_PROPERTIES( original ); int size = 0; *buffer = mlt_properties_get_data( orig_props, "alpha", &size ); if (*buffer) mlt_frame_set_alpha( frame, *buffer, size, NULL ); *buffer = mlt_properties_get_data( orig_props, "image", &size ); mlt_frame_set_image( frame, *buffer, size, NULL ); mlt_properties_set_data( frame_properties, "avformat.image_cache", original, 0, (mlt_destructor) mlt_frame_close, NULL ); *format = mlt_properties_get_int( orig_props, "format" ); set_image_size( self, width, height ); mlt_properties_pass_property(frame_properties, orig_props, "colorspace"); got_picture = 1; goto exit_get_image; } } // Cache miss // We may want to use the source fps if available double source_fps = mlt_properties_get_double( properties, "meta.media.frame_rate_num" ) / mlt_properties_get_double( properties, "meta.media.frame_rate_den" ); // This is the physical frame position in the source int64_t req_position = ( int64_t )( position / mlt_producer_get_fps( producer ) * source_fps + 0.5 ); // Determines if we have to decode all frames in a sequence - when there temporal compression is used. const AVCodecDescriptor *descriptor = codec_context->codec? avcodec_descriptor_get( codec_context->codec->id ) : NULL; int must_decode = descriptor && !( descriptor->props & AV_CODEC_PROP_INTRA_ONLY ); double delay = mlt_properties_get_double( properties, "video_delay" ); // Seek if necessary double speed = mlt_producer_get_speed(producer); int preseek = must_decode && codec_context->has_b_frames && speed >= 0.0 && speed <= 1.0; int paused = seek_video( self, position, req_position, preseek ); // Seek might have reopened the file context = self->video_format; stream = context->streams[ self->video_index ]; codec_context = stream->codec; if ( *format == mlt_image_none || *format == mlt_image_glsl || codec_context->pix_fmt == AV_PIX_FMT_ARGB || codec_context->pix_fmt == AV_PIX_FMT_RGBA || codec_context->pix_fmt == AV_PIX_FMT_ABGR || codec_context->pix_fmt == AV_PIX_FMT_BGRA ) *format = pick_image_format( codec_context->pix_fmt ); #if defined(FFUDIV) else if ( codec_context->pix_fmt == AV_PIX_FMT_BAYER_RGGB16LE ) { if ( *format == mlt_image_yuv422 ) *format = mlt_image_yuv420p; else if ( *format == mlt_image_rgb24a ) *format = mlt_image_rgb24; } #endif else if ( codec_context->pix_fmt == AV_PIX_FMT_YUVA444P10LE #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(56,0,0) || codec_context->pix_fmt == AV_PIX_FMT_GBRAP10LE || codec_context->pix_fmt == AV_PIX_FMT_GBRAP12LE #endif ) *format = mlt_image_rgb24a; // Duplicate the last image if necessary if ( self->video_frame && self->video_frame->linesize[0] && (self->pkt.stream_index == self->video_index ) && ( paused || self->current_position >= req_position ) ) { // Duplicate it set_image_size( self, width, height ); if ( ( image_size = allocate_buffer( frame, codec_context, buffer, *format, *width, *height ) ) ) { int yuv_colorspace; #ifdef VDPAU if ( self->vdpau && self->vdpau->buffer ) { AVPicture picture; picture.data[0] = self->vdpau->buffer; picture.data[2] = self->vdpau->buffer + codec_context->width * codec_context->height; picture.data[1] = self->vdpau->buffer + codec_context->width * codec_context->height * 5 / 4; picture.linesize[0] = codec_context->width; picture.linesize[1] = codec_context->width / 2; picture.linesize[2] = codec_context->width / 2; yuv_colorspace = convert_image( self, (AVFrame*) &picture, *buffer, AV_PIX_FMT_YUV420P, format, *width, *height, &alpha ); } else #endif yuv_colorspace = convert_image( self, self->video_frame, *buffer, codec_context->pix_fmt, format, *width, *height, &alpha ); mlt_properties_set_int( frame_properties, "colorspace", yuv_colorspace ); got_picture = 1; } } else { int ret = 0; int64_t int_position = 0; int decode_errors = 0; // Construct an AVFrame for YUV422 conversion if ( !self->video_frame ) self->video_frame = av_frame_alloc(); while( ret >= 0 && !got_picture ) { // Read a packet if ( self->pkt.stream_index == self->video_index ) av_free_packet( &self->pkt ); av_init_packet( &self->pkt ); pthread_mutex_lock( &self->packets_mutex ); if ( mlt_deque_count( self->vpackets ) ) { AVPacket *tmp = (AVPacket*) mlt_deque_pop_front( self->vpackets ); self->pkt = *tmp; free( tmp ); } else { ret = av_read_frame( context, &self->pkt ); if ( ret >= 0 && !self->video_seekable && self->pkt.stream_index == self->audio_index ) { if ( !av_dup_packet( &self->pkt ) ) { AVPacket *tmp = malloc( sizeof(AVPacket) ); *tmp = self->pkt; mlt_deque_push_back( self->apackets, tmp ); } } else if ( ret < 0 ) { if ( ret != AVERROR_EOF ) mlt_log_verbose( MLT_PRODUCER_SERVICE(producer), "av_read_frame returned error %d inside get_image\n", ret ); if ( !self->video_seekable && mlt_properties_get_int( properties, "reconnect" ) ) { // Try to reconnect to live sources by closing context and codecs, // and letting next call to get_frame() reopen. prepare_reopen( self ); pthread_mutex_unlock( &self->packets_mutex ); goto exit_get_image; } if ( !self->video_seekable && mlt_properties_get_int( properties, "exit_on_disconnect" ) ) { mlt_log_fatal( MLT_PRODUCER_SERVICE(producer), "Exiting with error due to disconnected source.\n" ); exit( EXIT_FAILURE ); } // Send null packets to drain decoder. self->pkt.size = 0; self->pkt.data = NULL; } } pthread_mutex_unlock( &self->packets_mutex ); // We only deal with video from the selected video_index if ( self->pkt.stream_index == self->video_index ) { int64_t pts = best_pts( self, self->pkt.pts, self->pkt.dts ); if ( pts != AV_NOPTS_VALUE ) { if ( !self->video_seekable && self->first_pts == AV_NOPTS_VALUE ) self->first_pts = pts; if ( self->first_pts != AV_NOPTS_VALUE ) pts -= self->first_pts; else if ( context->start_time != AV_NOPTS_VALUE ) pts -= context->start_time; int_position = ( int64_t )( ( av_q2d( self->video_time_base ) * pts + delay ) * source_fps + 0.5 ); if ( int_position == self->last_position ) int_position = self->last_position + 1; } mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "V pkt.pts %"PRId64" pkt.dts %"PRId64" req_pos %"PRId64" cur_pos %"PRId64" pkt_pos %"PRId64"\n", self->pkt.pts, self->pkt.dts, req_position, self->current_position, int_position ); // Make a dumb assumption on streams that contain wild timestamps if ( llabs( req_position - int_position ) > 999 ) { mlt_log_verbose( MLT_PRODUCER_SERVICE(producer), " WILD TIMESTAMP: " "pkt.pts=[%"PRId64"], pkt.dts=[%"PRId64"], req_position=[%"PRId64"], " "current_position=[%"PRId64"], int_position=[%"PRId64"], pts=[%"PRId64"] \n", self->pkt.pts, self->pkt.dts, req_position, self->current_position, int_position, pts ); int_position = req_position; } self->last_position = int_position; // Decode the image if ( must_decode || int_position >= req_position || !self->pkt.data ) { #ifdef VDPAU if ( self->vdpau ) { if ( self->vdpau->decoder == VDP_INVALID_HANDLE ) { vdpau_decoder_init( self ); } self->vdpau->is_decoded = 0; } #endif codec_context->reordered_opaque = int_position; if ( int_position >= req_position ) codec_context->skip_loop_filter = AVDISCARD_NONE; ret = avcodec_decode_video2( codec_context, self->video_frame, &got_picture, &self->pkt ); mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "decoded packet with size %d => %d\n", self->pkt.size, ret ); // Note: decode may fail at the beginning of MPEGfile (B-frames referencing before first I-frame), so allow a few errors. if ( ret < 0 ) { if ( ++decode_errors <= 10 ) { ret = 0; } else { mlt_log_warning( MLT_PRODUCER_SERVICE(producer), "video decoding error %d\n", ret ); self->last_good_position = POSITION_INVALID; } } else { decode_errors = 0; } } if ( got_picture ) { // Get position of reordered frame int_position = self->video_frame->reordered_opaque; pts = best_pts( self, self->video_frame->pkt_pts, self->video_frame->pkt_dts ); if ( pts != AV_NOPTS_VALUE ) { // Some streams are not marking their key frames even though // there are I frames, and find_first_pts() fails as a result. // Try to set first_pts here after getting pict_type. if ( self->first_pts == AV_NOPTS_VALUE && (self->video_frame->key_frame || self->video_frame->pict_type == AV_PICTURE_TYPE_I) ) self->first_pts = pts; if ( self->first_pts != AV_NOPTS_VALUE ) pts -= self->first_pts; else if ( context->start_time != AV_NOPTS_VALUE ) pts -= context->start_time; int_position = ( int64_t )( ( av_q2d( self->video_time_base ) * pts + delay ) * source_fps + 0.5 ); } if ( int_position < req_position ) got_picture = 0; else if ( int_position >= req_position ) codec_context->skip_loop_filter = AVDISCARD_NONE; } else if ( !self->pkt.data ) // draining decoder with null packets { ret = -1; } mlt_log_debug( MLT_PRODUCER_SERVICE(producer), " got_pic %d key %d ret %d pkt_pos %"PRId64"\n", got_picture, self->pkt.flags & AV_PKT_FLAG_KEY, ret, int_position ); } // Now handle the picture if we have one if ( got_picture ) { #ifdef AVFILTER if (self->autorotate && self->vfilter_graph) { ret = av_buffersrc_add_frame(self->vfilter_in, self->video_frame); if (ret < 0) { got_picture = 0; break; } while (ret >= 0) { ret = av_buffersink_get_frame_flags(self->vfilter_out, self->video_frame, 0); if (ret < 0) { ret = 0; break; } } } #endif set_image_size( self, width, height ); if ( ( image_size = allocate_buffer( frame, codec_context, buffer, *format, *width, *height ) ) ) { int yuv_colorspace; #ifdef VDPAU if ( self->vdpau ) { if ( self->vdpau->is_decoded ) { struct vdpau_render_state *render = (struct vdpau_render_state*) self->video_frame->data[0]; void *planes[3]; uint32_t pitches[3]; VdpYCbCrFormat dest_format = VDP_YCBCR_FORMAT_YV12; if ( !self->vdpau->buffer ) self->vdpau->buffer = mlt_pool_alloc( codec_context->width * codec_context->height * 3 / 2 ); self->video_frame->data[0] = planes[0] = self->vdpau->buffer; self->video_frame->data[2] = planes[1] = self->vdpau->buffer + codec_context->width * codec_context->height; self->video_frame->data[1] = planes[2] = self->vdpau->buffer + codec_context->width * codec_context->height * 5 / 4; self->video_frame->linesize[0] = pitches[0] = codec_context->width; self->video_frame->linesize[1] = pitches[1] = codec_context->width / 2; self->video_frame->linesize[2] = pitches[2] = codec_context->width / 2; VdpStatus status = vdp_surface_get_bits( render->surface, dest_format, planes, pitches ); if ( status == VDP_STATUS_OK ) { yuv_colorspace = convert_image( self, self->video_frame, *buffer, AV_PIX_FMT_YUV420P, format, *width, *height, &alpha ); mlt_properties_set_int( frame_properties, "colorspace", yuv_colorspace ); } else { mlt_log_error( MLT_PRODUCER_SERVICE(producer), "VDPAU Error: %s\n", vdp_get_error_string( status ) ); image_size = self->vdpau->is_decoded = 0; } } else { mlt_log_error( MLT_PRODUCER_SERVICE(producer), "VDPAU error in VdpDecoderRender\n" ); image_size = got_picture = 0; self->last_good_position = POSITION_INVALID; } } else #endif yuv_colorspace = convert_image( self, self->video_frame, *buffer, codec_context->pix_fmt, format, *width, *height, &alpha ); mlt_properties_set_int( frame_properties, "colorspace", yuv_colorspace ); self->top_field_first |= self->video_frame->top_field_first; self->top_field_first |= codec_context->field_order == AV_FIELD_TT; self->top_field_first |= codec_context->field_order == AV_FIELD_TB; self->current_position = int_position; } else { got_picture = 0; } } // Free packet data if not video and not live audio packet if ( self->pkt.stream_index != self->video_index && !( !self->video_seekable && self->pkt.stream_index == self->audio_index ) ) av_free_packet( &self->pkt ); } } // set alpha if ( alpha ) mlt_frame_set_alpha( frame, alpha, (*width) * (*height), mlt_pool_release ); if ( image_size > 0 ) { mlt_properties_set_int( frame_properties, "format", *format ); // Cache the image for rapid repeated access. if ( self->image_cache ) { if (is_album_art) { mlt_position original_pos = mlt_frame_original_position( frame ); mlt_properties_set_position(frame_properties, "original_position", 0); mlt_cache_put_frame( self->image_cache, frame ); mlt_properties_set_position(frame_properties, "original_position", original_pos); } else { mlt_cache_put_frame( self->image_cache, frame ); } } // Clone frame for error concealment. if ( self->current_position >= self->last_good_position ) { self->last_good_position = self->current_position; if ( self->last_good_frame ) mlt_frame_close( self->last_good_frame ); self->last_good_frame = mlt_frame_clone( frame, 1 ); } } else if ( self->last_good_frame ) { // Use last known good frame if there was a decoding failure. mlt_frame original = mlt_frame_clone( self->last_good_frame, 1 ); mlt_properties orig_props = MLT_FRAME_PROPERTIES( original ); int size = 0; *buffer = mlt_properties_get_data( orig_props, "alpha", &size ); if (*buffer) mlt_frame_set_alpha( frame, *buffer, size, NULL ); *buffer = mlt_properties_get_data( orig_props, "image", &size ); mlt_frame_set_image( frame, *buffer, size, NULL ); mlt_properties_set_data( frame_properties, "avformat.conceal_error", original, 0, (mlt_destructor) mlt_frame_close, NULL ); *format = mlt_properties_get_int( orig_props, "format" ); set_image_size( self, width, height ); got_picture = 1; } // Regardless of speed, we expect to get the next frame (cos we ain't too bright) self->video_expected = position + 1; exit_get_image: pthread_mutex_unlock( &self->video_mutex ); // Set the progressive flag if ( mlt_properties_get( properties, "force_progressive" ) ) { mlt_properties_set_int( frame_properties, "progressive", !!mlt_properties_get_int( properties, "force_progressive" ) ); } else if ( self->video_frame ) { mlt_properties_set_int( frame_properties, "progressive", !self->video_frame->interlaced_frame && (codec_context->field_order == AV_FIELD_PROGRESSIVE || codec_context->field_order == AV_FIELD_UNKNOWN) ); } // Set the field order property for this frame if ( mlt_properties_get( properties, "force_tff" ) ) mlt_properties_set_int( frame_properties, "top_field_first", !!mlt_properties_get_int( properties, "force_tff" ) ); else mlt_properties_set_int( frame_properties, "top_field_first", self->top_field_first ); // Set immutable properties of the selected track's (or overridden) source attributes. mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); mlt_properties_set_int( properties, "meta.media.top_field_first", self->top_field_first ); mlt_properties_set_int( properties, "meta.media.progressive", mlt_properties_get_int( frame_properties, "progressive" ) ); mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); mlt_log_timings_end( NULL, __FUNCTION__ ); return !got_picture; } /** 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 ); int search_flags = AV_OPT_SEARCH_CHILDREN; const AVOption *opt = av_opt_find( obj, opt_name, NULL, flags, search_flags ); if ( opt_name && mlt_properties_get( properties, opt_name ) ) { if ( opt ) av_opt_set( obj, opt_name, mlt_properties_get( properties, opt_name), search_flags ); } } } /** Initialize the video codec context. */ static int video_codec_init( producer_avformat self, int index, mlt_properties properties ) { // Initialise the codec if necessary if ( !self->video_codec ) { // Get the video stream AVStream *stream = self->video_format->streams[ index ]; // Get codec context AVCodecContext *codec_context = stream->codec; // Find the codec AVCodec *codec = avcodec_find_decoder( codec_context->codec_id ); if ( mlt_properties_get( properties, "vcodec" ) ) { if ( !( codec = avcodec_find_decoder_by_name( mlt_properties_get( properties, "vcodec" ) ) ) ) codec = avcodec_find_decoder( codec_context->codec_id ); } else if ( codec_context->codec_id == AV_CODEC_ID_VP9 ) { if ( !( codec = avcodec_find_decoder_by_name( "libvpx-vp9" ) ) ) codec = avcodec_find_decoder( codec_context->codec_id ); } else if ( codec_context->codec_id == AV_CODEC_ID_VP8 ) { if ( !( codec = avcodec_find_decoder_by_name( "libvpx" ) ) ) codec = avcodec_find_decoder( codec_context->codec_id ); } #ifdef VDPAU if ( codec_context->codec_id == AV_CODEC_ID_H264 ) { if ( ( codec = avcodec_find_decoder_by_name( "h264_vdpau" ) ) ) { if ( vdpau_init( self ) ) { self->video_codec = codec_context; if ( !vdpau_decoder_init( self ) ) vdpau_fini( self ); } } if ( !self->vdpau ) codec = avcodec_find_decoder( codec_context->codec_id ); } #endif // 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 >= 0 ) codec_context->thread_count = thread_count; // If we don't have a codec and we can't initialise it, we can't do much more... pthread_mutex_lock( &self->open_mutex ); if ( codec && avcodec_open2( codec_context, codec, NULL ) >= 0 ) { // Switch to the native vp8/vp9 decoder if not yuva420p if ( codec_context->pix_fmt != AV_PIX_FMT_YUVA420P && !mlt_properties_get( properties, "vcodec" ) && ( !strcmp(codec->name, "libvpx") || !strcmp(codec->name, "libvpx-vp9") ) ) { codec = avcodec_find_decoder( codec_context->codec_id ); if ( codec && avcodec_open2( codec_context, codec, NULL ) < 0 ) { self->video_index = -1; pthread_mutex_unlock( &self->open_mutex ); return 0; } } // Now store the codec with its destructor self->video_codec = codec_context; } else { // Remember that we can't use this later self->video_index = -1; pthread_mutex_unlock( &self->open_mutex ); return 0; } pthread_mutex_unlock( &self->open_mutex ); // Process properties as AVOptions apply_properties( codec_context, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM ); if ( codec->priv_class && codec_context->priv_data ) apply_properties( codec_context->priv_data, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM ); // Reset some image properties mlt_properties_set_int( properties, "width", self->video_codec->width ); mlt_properties_set_int( properties, "height", self->video_codec->height ); get_aspect_ratio( properties, stream, self->video_codec ); // Start with the muxer frame rate. AVRational frame_rate = stream->avg_frame_rate; double fps = av_q2d( frame_rate ); #if defined(FFUDIV) // Verify and sanitize the muxer frame rate. if ( isnan( fps ) || isinf( fps ) || fps == 0 ) { frame_rate = stream->r_frame_rate; fps = av_q2d( frame_rate ); } // With my samples when r_frame_rate != 1000 but avg_frame_rate is valid, // avg_frame_rate gives some approximate value that does not well match the media. // Also, on my sample where r_frame_rate = 1000, using avg_frame_rate directly // results in some very choppy output, but some value slightly different works // great. if ( av_q2d( stream->r_frame_rate ) >= 1000 && av_q2d( stream->avg_frame_rate ) > 0 ) { frame_rate = av_d2q( av_q2d( stream->avg_frame_rate ), 1024 ); fps = av_q2d( frame_rate ); } #endif // XXX frame rates less than 1 fps are not considered sane if ( isnan( fps ) || isinf( fps ) || fps < 1.0 ) { // Get the frame rate from the codec. frame_rate.num = self->video_codec->time_base.den; frame_rate.den = self->video_codec->time_base.num * self->video_codec->ticks_per_frame; fps = av_q2d( frame_rate ); } if ( isnan( fps ) || isinf( fps ) || fps < 1.0 ) { // Use the profile frame rate if all else fails. mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self->parent ) ); frame_rate.num = profile->frame_rate_num; frame_rate.den = profile->frame_rate_den; } self->video_time_base = stream->time_base; if ( mlt_properties_get( properties, "force_fps" ) ) { AVRational force_fps = av_d2q( mlt_properties_get_double( properties, "force_fps" ), 1024 ); self->video_time_base = av_mul_q( stream->time_base, av_div_q( frame_rate, force_fps ) ); frame_rate = force_fps; } mlt_properties_set_int( properties, "meta.media.frame_rate_num", frame_rate.num ); mlt_properties_set_int( properties, "meta.media.frame_rate_den", frame_rate.den ); // MP3 album art is a single JPEG at 90000 fps, which is not seekable. if ( codec->id == AV_CODEC_ID_MJPEG && frame_rate.num == 90000 && frame_rate.den == 1 ) self->video_seekable = 0; // Set the YUV colorspace from override or detect self->yuv_colorspace = mlt_properties_get_int( properties, "force_colorspace" ); if ( ! self->yuv_colorspace ) { switch ( self->video_codec->colorspace ) { case AVCOL_SPC_SMPTE240M: self->yuv_colorspace = 240; break; case AVCOL_SPC_BT470BG: case AVCOL_SPC_SMPTE170M: self->yuv_colorspace = 601; break; case AVCOL_SPC_BT709: self->yuv_colorspace = 709; break; default: // This is a heuristic Charles Poynton suggests in "Digital Video and HDTV" self->yuv_colorspace = self->video_codec->width * self->video_codec->height > 750000 ? 709 : 601; break; } } // Let apps get chosen colorspace mlt_properties_set_int( properties, "meta.media.colorspace", self->yuv_colorspace ); // Get the color transfer characteristic (gamma). self->color_trc = mlt_properties_get_int( properties, "force_color_trc" ); if ( !self->color_trc ) self->color_trc = self->video_codec->color_trc; mlt_properties_set_int( properties, "meta.media.color_trc", self->color_trc ); // Get the RGB color primaries. switch ( self->video_codec->color_primaries ) { case AVCOL_PRI_BT470BG: self->color_primaries = 601625; break; case AVCOL_PRI_SMPTE170M: case AVCOL_PRI_SMPTE240M: self->color_primaries = 601525; break; case AVCOL_PRI_BT709: case AVCOL_PRI_UNSPECIFIED: default: self->color_primaries = 709; break; } self->full_luma = 0; mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "color_range %d\n", codec_context->color_range ); if ( codec_context->color_range == AVCOL_RANGE_JPEG ) self->full_luma = 1; if ( mlt_properties_get( properties, "set.force_full_luma" ) ) self->full_luma = mlt_properties_get_int( properties, "set.force_full_luma" ); } return self->video_index > -1; } /** Set up video handling. */ static void producer_set_up_video( producer_avformat self, mlt_frame frame ) { // Get the producer mlt_producer producer = self->parent; // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Fetch the video format context AVFormatContext *context = self->video_format; // Get the video_index int index = mlt_properties_get_int( properties, "video_index" ); int unlock_needed = 0; // Reopen the file if necessary if ( !context && index > -1 ) { unlock_needed = 1; pthread_mutex_lock( &self->video_mutex ); producer_open( self, mlt_service_profile( MLT_PRODUCER_SERVICE(producer) ), mlt_properties_get( properties, "resource" ), 0, 0 ); context = self->video_format; } // 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 != AVMEDIA_TYPE_VIDEO; index-- ); mlt_properties_set_int( properties, "video_index", index ); } if ( context && index > -1 && context->streams[ index ]->codec->codec_type != AVMEDIA_TYPE_VIDEO ) { // Invalidate the video stream index = -1; mlt_properties_set_int( properties, "video_index", index ); } // Update the video properties if the index changed if ( context && index > -1 && index != self->video_index ) { // Reset the video properties if the index changed self->video_index = index; pthread_mutex_lock( &self->open_mutex ); if ( self->video_codec ) avcodec_close( self->video_codec ); self->video_codec = NULL; pthread_mutex_unlock( &self->open_mutex ); } // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); // Get the codec if ( context && index > -1 && video_codec_init( self, index, properties ) ) { // Set the frame properties 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" ); // Set the width and height double dar = mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ); double theta = self->autorotate? get_rotation( self->video_format->streams[index] ) : 0.0; if ( fabs(theta - 90.0) < 1.0 || fabs(theta - 270.0) < 1.0 ) { // Workaround 1088 encodings missing cropping info. if ( self->video_codec->height == 1088 && dar == 16.0/9.0 ) { mlt_properties_set_int( frame_properties, "width", 1080 ); mlt_properties_set_int( properties, "meta.media.width", 1080 ); } else { mlt_properties_set_int( frame_properties, "width", self->video_codec->height ); mlt_properties_set_int( properties, "meta.media.width", self->video_codec->height ); } mlt_properties_set_int( frame_properties, "height", self->video_codec->width ); mlt_properties_set_int( properties, "meta.media.height", self->video_codec->width ); aspect_ratio = ( force_aspect_ratio > 0.0 ) ? force_aspect_ratio : 1.0 / aspect_ratio; mlt_properties_set_double( frame_properties, "aspect_ratio", 1.0/aspect_ratio ); } else { mlt_properties_set_int( frame_properties, "width", self->video_codec->width ); mlt_properties_set_int( properties, "meta.media.width", self->video_codec->width ); // Workaround 1088 encodings missing cropping info. if ( self->video_codec->height == 1088 && dar == 16.0/9.0 ) { mlt_properties_set_int( frame_properties, "height", 1080 ); mlt_properties_set_int( properties, "meta.media.height", 1080 ); } else { mlt_properties_set_int( frame_properties, "height", self->video_codec->height ); mlt_properties_set_int( properties, "meta.media.height", self->video_codec->height ); } mlt_properties_set_double( frame_properties, "aspect_ratio", aspect_ratio ); } mlt_properties_set_int( frame_properties, "colorspace", self->yuv_colorspace ); mlt_properties_set_int( frame_properties, "color_trc", self->color_trc ); mlt_properties_set_int( frame_properties, "color_primaries", self->color_primaries ); mlt_properties_set_int( frame_properties, "full_luma", self->full_luma ); mlt_properties_set( properties, "meta.media.color_range", self->full_luma? "full" : "mpeg" ); // Add our image operation mlt_frame_push_service( frame, self ); mlt_frame_push_get_image( frame, producer_get_image ); } else { // If something failed, use test card image mlt_properties_set_int( frame_properties, "test_image", 1 ); } if ( unlock_needed ) pthread_mutex_unlock( &self->video_mutex ); } static int seek_audio( producer_avformat self, mlt_position position, double timecode ) { int paused = 0; pthread_mutex_lock( &self->packets_mutex ); // Seek if necessary if ( self->seekable && ( position != self->audio_expected || self->last_position < 0 ) ) { if ( self->last_position == POSITION_INITIAL ) { int video_index = self->video_index; if ( video_index == -1 ) video_index = first_video_index( self ); if ( self->first_pts == AV_NOPTS_VALUE && video_index >= 0 ) find_first_pts( self, video_index ); } if ( position + 1 == self->audio_expected && mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( self->parent ), "mute_on_pause" ) ) { // We're paused - silence required paused = 1; } else if ( position < self->audio_expected || position - self->audio_expected >= 12 ) { AVFormatContext *context = self->audio_format; int64_t timestamp = llrint( timecode * AV_TIME_BASE ); 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 int i = MAX_AUDIO_STREAMS + 1; while ( --i ) self->audio_used[i - 1] = 0; } } pthread_mutex_unlock( &self->packets_mutex ); return paused; } static int sample_bytes( AVCodecContext *context ) { return av_get_bytes_per_sample( context->sample_fmt ); } static void planar_to_interleaved( uint8_t *dest, AVFrame *src, int samples, int channels, int bytes_per_sample ) { int s, c; for ( s = 0; s < samples; s++ ) { for ( c = 0; c < channels; c++ ) { if ( c < AV_NUM_DATA_POINTERS ) memcpy( dest, &src->data[c][s * bytes_per_sample], bytes_per_sample ); dest += bytes_per_sample; } } } static int decode_audio( producer_avformat self, int *ignore, AVPacket pkt, int samples, double timecode, double fps ) { // Fetch the audio_format AVFormatContext *context = self->audio_format; // Get the current stream index int index = pkt.stream_index; // Get codec context AVCodecContext *codec_context = self->audio_codec[ index ]; // Obtain the audio buffers uint8_t *audio_buffer = self->audio_buffer[ index ]; int channels = codec_context->channels; int audio_used = self->audio_used[ index ]; int ret = 0; int discarded = 1; while ( pkt.data && pkt.size > 0 ) { int sizeof_sample = sample_bytes( codec_context ); int got_frame = 0; // Decode the audio if ( !self->audio_frame ) self->audio_frame = av_frame_alloc(); else av_frame_unref( self->audio_frame ); ret = avcodec_decode_audio4( codec_context, self->audio_frame, &got_frame, &pkt ); if ( ret < 0 ) { mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "audio decoding error %d\n", ret ); break; } // Consume (sometimes partial) data in the packet. pkt.size -= ret; pkt.data += ret; // If decoded successfully if ( got_frame ) { // Figure out how many samples will be needed after resampling int convert_samples = self->audio_frame->nb_samples; channels = codec_context->channels; // Resize audio buffer to prevent overflow if ( ( audio_used + convert_samples ) * channels * sizeof_sample > self->audio_buffer_size[ index ] ) { self->audio_buffer_size[ index ] = ( audio_used + convert_samples * 2 ) * channels * sizeof_sample; audio_buffer = self->audio_buffer[ index ] = mlt_pool_realloc( audio_buffer, self->audio_buffer_size[ index ] ); } uint8_t *dest = &audio_buffer[ audio_used * channels * sizeof_sample ]; switch ( codec_context->sample_fmt ) { case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_FLTP: planar_to_interleaved( dest, self->audio_frame, convert_samples, channels, sizeof_sample ); break; default: { int data_size = av_samples_get_buffer_size( NULL, channels, self->audio_frame->nb_samples, codec_context->sample_fmt, 1 ); // Straight copy to audio buffer memcpy( dest, self->audio_frame->data[0], data_size ); } } audio_used += convert_samples; discarded = 0; } // Handle ignore if ( *ignore > 0 && audio_used ) { int n = FFMIN( audio_used, *ignore ); *ignore -= n; audio_used -= n; memmove( audio_buffer, &audio_buffer[ n * channels * sizeof_sample ], audio_used * channels * sizeof_sample ); } } // If we're behind, ignore this packet // Skip this on non-seekable, audio-only inputs. if ( !discarded && pkt.pts >= 0 && ( self->seekable || self->video_format ) && *ignore == 0 && audio_used > samples / 2 ) { int64_t pts = pkt.pts; if ( self->first_pts != AV_NOPTS_VALUE ) pts -= self->first_pts; else if ( context->start_time != AV_NOPTS_VALUE && self->video_index != -1 ) pts -= context->start_time; double timebase = av_q2d( context->streams[ index ]->time_base ); int64_t int_position = llrint( timebase * pts * fps ); int64_t req_position = llrint( timecode * fps ); int64_t req_pts = llrint( timecode / timebase ); mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "A pkt.pts %"PRId64" pkt.dts %"PRId64" req_pos %"PRId64" cur_pos %"PRId64" pkt_pos %"PRId64"\n", pkt.pts, pkt.dts, req_position, self->current_position, int_position ); if ( self->seekable || int_position > 0 ) { if ( req_position > int_position ) { // We are behind, so skip some *ignore = lrint( timebase * (req_pts - pts) * codec_context->sample_rate ); } else if ( self->audio_index != INT_MAX && int_position > req_position + 2 && !self->is_audio_synchronizing ) { // We are ahead, so seek backwards some more. // Supply -1 as the position to defeat the checks needed by for the other // call to seek_audio() at the beginning of producer_get_audio(). Otherwise, // more often than not, req_position will equal audio_expected. seek_audio( self, -1, timecode - 1.0 ); self->is_audio_synchronizing = 1; } } // Cancel the find_first_pts() in seek_audio() if ( self->video_index == -1 && self->last_position == POSITION_INITIAL ) self->last_position = int_position; } self->audio_used[ index ] = audio_used; return ret; } /** Get the audio from a frame. */ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Get the producer producer_avformat self = mlt_frame_pop_audio( frame ); pthread_mutex_lock( &self->audio_mutex ); // Obtain the frame number of this frame mlt_position position = mlt_frame_original_position( frame ); // Calculate the real time code double real_timecode = producer_time_of_frame( self->parent, position ); // Get the producer fps double fps = mlt_producer_get_fps( self->parent ); if ( mlt_properties_get( MLT_FRAME_PROPERTIES(frame), "producer_consumer_fps" ) ) fps = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "producer_consumer_fps" ); // Number of frames to ignore (for ffwd) int ignore[ MAX_AUDIO_STREAMS ] = { 0 }; // Flag for paused (silence) double timecode = self->audio_expected > 0 ? real_timecode : FFMAX(real_timecode - 0.25, 0.0); int paused = seek_audio( self, position, timecode ); // Initialize ignore for all streams from the seek return value int i = MAX_AUDIO_STREAMS; while ( i-- ) ignore[i] = ignore[0]; // Fetch the audio_format AVFormatContext *context = self->audio_format; if ( !context ) goto exit_get_audio; int sizeof_sample = sizeof( int16_t ); // Determine the tracks to use int index = self->audio_index; int index_max = self->audio_index + 1; if ( self->audio_index == INT_MAX ) { index = 0; index_max = FFMIN( MAX_AUDIO_STREAMS, context->nb_streams ); *channels = self->total_channels; *samples = mlt_sample_calculator( fps, self->max_frequency, position ); *frequency = self->max_frequency; } // Initialize the buffers for ( ; index < index_max && index < MAX_AUDIO_STREAMS; index++ ) { // Get codec context AVCodecContext *codec_context = self->audio_codec[ index ]; if ( codec_context && !self->audio_buffer[ index ] ) { if ( self->audio_index != INT_MAX && !mlt_properties_get( MLT_PRODUCER_PROPERTIES(self->parent), "request_channel_layout" ) ) codec_context->request_channel_layout = av_get_default_channel_layout( *channels ); sizeof_sample = sample_bytes( codec_context ); // Check for audio buffer and create if necessary self->audio_buffer_size[ index ] = MAX_AUDIO_FRAME_SIZE * sizeof_sample; self->audio_buffer[ index ] = mlt_pool_alloc( self->audio_buffer_size[ index ] ); // Check for decoder buffer and create if necessary self->decode_buffer[ index ] = av_malloc( self->audio_buffer_size[ index ] ); } } // Get the audio if required if ( !paused && *frequency > 0 ) { int ret = 0; int got_audio = 0; AVPacket pkt; mlt_channel_layout layout; av_init_packet( &pkt ); // Caller requested number samples based on requested sample rate. if ( self->audio_index != INT_MAX ) *samples = mlt_sample_calculator( fps, self->audio_codec[ self->audio_index ]->sample_rate, position ); while ( ret >= 0 && !got_audio ) { // Check if the buffer already contains the samples required if ( self->audio_index != INT_MAX && self->audio_used[ self->audio_index ] >= *samples && ignore[ self->audio_index ] == 0 ) { got_audio = 1; break; } else if ( self->audio_index == INT_MAX ) { // Check if there is enough audio for all streams got_audio = 1; for ( index = 0; got_audio && index < index_max; index++ ) if ( ( self->audio_codec[ index ] && self->audio_used[ index ] < *samples ) || ignore[ index ] ) got_audio = 0; if ( got_audio ) break; } // Read a packet pthread_mutex_lock( &self->packets_mutex ); if ( mlt_deque_count( self->apackets ) ) { AVPacket *tmp = (AVPacket*) mlt_deque_pop_front( self->apackets ); pkt = *tmp; free( tmp ); } else { ret = av_read_frame( context, &pkt ); if ( ret >= 0 && !self->seekable && pkt.stream_index == self->video_index ) { if ( !av_dup_packet( &pkt ) ) { AVPacket *tmp = malloc( sizeof(AVPacket) ); *tmp = pkt; mlt_deque_push_back( self->vpackets, tmp ); } } else if ( ret < 0 ) { mlt_producer producer = self->parent; mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); if ( ret != AVERROR_EOF ) mlt_log_verbose( MLT_PRODUCER_SERVICE(producer), "av_read_frame returned error %d inside get_audio\n", ret ); if ( !self->seekable && mlt_properties_get_int( properties, "reconnect" ) ) { // Try to reconnect to live sources by closing context and codecs, // and letting next call to get_frame() reopen. prepare_reopen( self ); pthread_mutex_unlock( &self->packets_mutex ); goto exit_get_audio; } if ( !self->seekable && mlt_properties_get_int( properties, "exit_on_disconnect" ) ) { mlt_log_fatal( MLT_PRODUCER_SERVICE(producer), "Exiting with error due to disconnected source.\n" ); exit( EXIT_FAILURE ); } } } pthread_mutex_unlock( &self->packets_mutex ); // We only deal with audio from the selected audio index index = pkt.stream_index; if ( index < MAX_AUDIO_STREAMS && ret >= 0 && pkt.data && pkt.size > 0 && ( index == self->audio_index || ( self->audio_index == INT_MAX && context->streams[ index ]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) ) ) { ret = decode_audio( self, &ignore[index], pkt, *samples, real_timecode, fps ); } if ( self->seekable || index != self->video_index ) av_free_packet( &pkt ); } self->is_audio_synchronizing = 0; // Set some additional return values *format = mlt_audio_s16; if ( self->audio_index != INT_MAX ) { index = self->audio_index; *channels = self->audio_codec[ index ]->channels; *frequency = self->audio_codec[ index ]->sample_rate; *format = pick_audio_format( self->audio_codec[ index ]->sample_fmt ); sizeof_sample = sample_bytes( self->audio_codec[ index ] ); if( self->audio_codec[ index ]->channel_layout == 0 ) layout = av_channel_layout_to_mlt( av_get_default_channel_layout( self->audio_codec[ index ]->channels ) ); else layout = av_channel_layout_to_mlt( self->audio_codec[ index ]->channel_layout ); } else if ( self->audio_index == INT_MAX ) { layout = mlt_channel_independent; for ( index = 0; index < index_max; index++ ) if ( self->audio_codec[ index ] ) { // XXX: This only works if all audio tracks have the same sample format. *format = pick_audio_format( self->audio_codec[ index ]->sample_fmt ); sizeof_sample = sample_bytes( self->audio_codec[ index ] ); break; } } mlt_properties_set( MLT_FRAME_PROPERTIES(frame), "channel_layout", mlt_channel_layout_name( layout ) ); // Allocate and set the frame's audio buffer int size = mlt_audio_format_size( *format, *samples, *channels ); *buffer = mlt_pool_alloc( size ); mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); // Interleave tracks if audio_index=all if ( self->audio_index == INT_MAX ) { uint8_t *dest = *buffer; int i; for ( i = 0; i < *samples; i++ ) { for ( index = 0; index < index_max; index++ ) if ( self->audio_codec[ index ] ) { int current_channels = self->audio_codec[ index ]->channels; uint8_t *src = self->audio_buffer[ index ] + i * current_channels * sizeof_sample; memcpy( dest, src, current_channels * sizeof_sample ); dest += current_channels * sizeof_sample; } } for ( index = 0; index < index_max; index++ ) if ( self->audio_codec[ index ] && self->audio_used[ index ] >= *samples ) { int current_channels = self->audio_codec[ index ]->channels; uint8_t *src = self->audio_buffer[ index ] + *samples * current_channels * sizeof_sample; self->audio_used[index] -= *samples; memmove( self->audio_buffer[ index ], src, self->audio_used[ index ] * current_channels * sizeof_sample ); } } // Copy a single track to the output buffer else { index = self->audio_index; // Now handle the audio if we have enough if ( self->audio_used[ index ] > 0 ) { uint8_t *src = self->audio_buffer[ index ]; // copy samples from audio_buffer size = self->audio_used[ index ] < *samples ? self->audio_used[ index ] : *samples; memcpy( *buffer, src, size * *channels * sizeof_sample ); // supply the remaining requested samples as silence if ( *samples > self->audio_used[ index ] ) memset( *buffer + size * *channels * sizeof_sample, 0, ( *samples - self->audio_used[ index ] ) * *channels * sizeof_sample ); // reposition the samples within audio_buffer self->audio_used[ index ] -= size; memmove( src, src + size * *channels * sizeof_sample, self->audio_used[ index ] * *channels * sizeof_sample ); } else { // Otherwise fill with silence memset( *buffer, 0, *samples * *channels * sizeof_sample ); } } } else { exit_get_audio: // 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 ) self->audio_expected = position + 1; pthread_mutex_unlock( &self->audio_mutex ); return 0; } /** Initialize the audio codec context. */ static int audio_codec_init( producer_avformat self, int index, mlt_properties properties ) { // Initialise the codec if necessary if ( !self->audio_codec[ index ] ) { // Get codec context AVCodecContext *codec_context = self->audio_format->streams[index]->codec; // Find the codec AVCodec *codec = avcodec_find_decoder( codec_context->codec_id ); if ( mlt_properties_get( properties, "acodec" ) ) { if ( !( codec = avcodec_find_decoder_by_name( mlt_properties_get( properties, "acodec" ) ) ) ) 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... pthread_mutex_lock( &self->open_mutex ); if ( codec && avcodec_open2( codec_context, codec, NULL ) >= 0 ) { // Now store the codec with its destructor if ( self->audio_codec[ index ] ) avcodec_close( self->audio_codec[ index ] ); self->audio_codec[ index ] = codec_context; } else { // Remember that we can't use self later self->audio_index = -1; } pthread_mutex_unlock( &self->open_mutex ); // Process properties as AVOptions apply_properties( codec_context, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM ); if ( codec && codec->priv_class && codec_context->priv_data ) apply_properties( codec_context->priv_data, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM ); } return self->audio_codec[ index ] && self->audio_index > -1; } /** Set up audio handling. */ static void producer_set_up_audio( producer_avformat self, mlt_frame frame ) { // Get the producer mlt_producer producer = self->parent; // Get the properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Fetch the audio format context AVFormatContext *context = self->audio_format; mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); // Get the audio_index int index = mlt_properties_get_int( properties, "audio_index" ); // Handle all audio tracks if ( self->audio_index > -1 && mlt_properties_get( properties, "audio_index" ) && !strcmp( mlt_properties_get( properties, "audio_index" ), "all" ) ) index = INT_MAX; // Reopen the file if necessary if ( !context && self->audio_index > -1 && index > -1 ) { producer_open( self, mlt_service_profile( MLT_PRODUCER_SERVICE(producer) ), mlt_properties_get( properties, "resource" ), 1, 0 ); context = self->audio_format; } // Exception handling for audio_index if ( context && index >= (int) context->nb_streams && index < INT_MAX ) { for ( index = context->nb_streams - 1; index >= 0 && context->streams[ index ]->codec->codec_type != AVMEDIA_TYPE_AUDIO; index-- ); mlt_properties_set_int( properties, "audio_index", index ); } if ( context && index > -1 && index < INT_MAX && context->streams[ index ]->codec->codec_type != AVMEDIA_TYPE_AUDIO ) { index = self->audio_index; mlt_properties_set_int( properties, "audio_index", index ); } if ( context && index > -1 && index < INT_MAX && pick_audio_format( context->streams[ index ]->codec->sample_fmt ) == mlt_audio_none ) { index = -1; } // Update the audio properties if the index changed if ( context && self->audio_index < context->nb_streams && index > -1 && self->audio_index > -1 && index != self->audio_index ) { pthread_mutex_lock( &self->open_mutex ); if ( self->audio_codec[ self->audio_index ] ) avcodec_close( self->audio_codec[ self->audio_index ] ); self->audio_codec[ self->audio_index ] = NULL; pthread_mutex_unlock( &self->open_mutex ); } if ( self->audio_index != -1 ) self->audio_index = index; else index = -1; // Get the codec(s) if ( context && index == INT_MAX ) { mlt_properties_set_int( frame_properties, "audio_frequency", self->max_frequency ); mlt_properties_set_int( frame_properties, "audio_channels", self->total_channels ); for ( index = 0; index < context->nb_streams && index < MAX_AUDIO_STREAMS; index++ ) { if ( context->streams[ index ]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) audio_codec_init( self, index, properties ); } } else if ( context && index > -1 && index < MAX_AUDIO_STREAMS && audio_codec_init( self, index, properties ) ) { mlt_properties_set_int( frame_properties, "audio_frequency", self->audio_codec[ index ]->sample_rate ); mlt_properties_set_int( frame_properties, "audio_channels", self->audio_codec[ index ]->channels ); } if ( context && index > -1 ) { // Add our audio operation mlt_frame_push_audio( frame, self ); mlt_frame_push_audio( frame, producer_get_audio ); } } /** Our get frame implementation. */ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { // Access the private data mlt_service service = MLT_PRODUCER_SERVICE( producer ); mlt_cache_item cache_item = mlt_service_cache_get( service, "producer_avformat" ); producer_avformat self = mlt_cache_item_data( cache_item, NULL ); // If cache miss if ( !self ) { self = calloc( 1, sizeof( struct producer_avformat_s ) ); producer->child = self; self->parent = producer; mlt_service_cache_put( service, "producer_avformat", self, 0, (mlt_destructor) producer_avformat_close ); cache_item = mlt_service_cache_get( service, "producer_avformat" ); } // Create an empty frame *frame = mlt_frame_init( service); if ( *frame ) { mlt_properties_set_data( MLT_FRAME_PROPERTIES(*frame), "avformat_cache", cache_item, 0, (mlt_destructor) mlt_cache_item_close, NULL ); } else { mlt_cache_item_close( cache_item ); return 1; } // Update timecode on the frame we're creating mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Set up the video producer_set_up_video( self, *frame ); // Set up the audio producer_set_up_audio( self, *frame ); // Set the position of this producer mlt_position position = mlt_producer_frame( producer ); mlt_properties_set_position( MLT_FRAME_PROPERTIES( *frame ), "original_position", position ); // Calculate the next timecode mlt_producer_prepare_next( producer ); return 0; } static void producer_avformat_close( producer_avformat self ) { mlt_log_debug( NULL, "producer_avformat_close\n" ); // Cleanup av contexts av_free_packet( &self->pkt ); av_free( self->video_frame ); av_free( self->audio_frame ); if ( self->is_mutex_init ) pthread_mutex_lock( &self->open_mutex ); int i; for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) { mlt_pool_release( self->audio_buffer[i] ); av_free( self->decode_buffer[i] ); if ( self->audio_codec[i] ) avcodec_close( self->audio_codec[i] ); self->audio_codec[i] = NULL; } if ( self->video_codec ) avcodec_close( self->video_codec ); self->video_codec = NULL; // Close the file if ( self->dummy_context ) avformat_close_input( &self->dummy_context ); if ( self->seekable && self->audio_format ) avformat_close_input( &self->audio_format ); if ( self->video_format ) avformat_close_input( &self->video_format ); if ( self->is_mutex_init ) pthread_mutex_unlock( &self->open_mutex ); #ifdef VDPAU vdpau_producer_close( self ); #endif #ifdef AVFILTER avfilter_graph_free(&self->vfilter_graph); #endif // Cleanup caches. mlt_cache_close( self->image_cache ); if ( self->last_good_frame ) mlt_frame_close( self->last_good_frame ); // Cleanup the mutexes if ( self->is_mutex_init ) { pthread_mutex_destroy( &self->audio_mutex ); pthread_mutex_destroy( &self->video_mutex ); pthread_mutex_destroy( &self->packets_mutex ); pthread_mutex_destroy( &self->open_mutex ); } // Cleanup the packet queues AVPacket *pkt; if ( self->apackets ) { while ( ( pkt = mlt_deque_pop_back( self->apackets ) ) ) { av_free_packet( pkt ); free( pkt ); } mlt_deque_close( self->apackets ); self->apackets = NULL; } if ( self->vpackets ) { while ( ( pkt = mlt_deque_pop_back( self->vpackets ) ) ) { av_free_packet( pkt ); free( pkt ); } mlt_deque_close( self->vpackets ); self->vpackets = NULL; } free( self ); } static void producer_close( mlt_producer parent ) { // Remove this instance from the cache mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) ); // Close the parent parent->close = NULL; mlt_producer_close( parent ); // Free the memory free( parent ); } mlt-6.20.0/src/modules/avformat/producer_avformat.yml000066400000000000000000000172261362234133600227310ustar00rootroot00000000000000schema_version: 0.3 type: producer identifier: avformat title: FFmpeg Reader version: 2 copyright: Copyright (C) 2003-2019 Meltytech, LLC license: LGPL language: en url: http://www.ffmpeg.org/ creator: Charles Yates contributor: - Dan Dennedy tags: - Audio - Video description: Read an audio and/or video file using FFmpeg. notes: > This service uses mlt_cache to prevent many simultaneous, open instances of libavformat and libavcodec contexts, file handles, and threads in memory at the same time. Not only does it save on RAM usage, but kernels enforce maximum open file handles and threads per process. Without caching, large projects could easily run into these limits. The default producer cache size is in mlt_cache at size 4. When using mlt_multitrack, the size is adjusted automatically to be the number of tracks plus one if at least 4. This makes it rather dynamic and automatic; however, there are some service graph configurations or authoring scenarios that do not exclusively use the multitrack for multi-layer operations and may need a larger avformat producer cache size. One can set the environment variable MLT_AVFORMAT_PRODUCER_CACHE to a number to override and increase the size of this cache (or to lower it for limited use cases and seeking to minimize RAM). bugs: - 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. - > More than 2 channels of audio more than 16 bits is not supported. parameters: - identifier: resource argument: yes title: File/URL type: string description: | A file name specification or URL in the form: [{protocol}|{format}]:{resource}[?{format-parameter}[&{format-parameter}...]] For example, video4linux2:/dev/video1?width=320&height=240 Note: on the bash command line, & must be escaped as '\&'. 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 description: > Choose the index of audio stream to use (-1 is off). When this value is equal to the maximum size of a 32-bit signed integer or the string "all" then all audio tracks are coalesced into a bundle of channels on one audio track. readonly: no mutable: no minimum: -1 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: 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: 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: seekable title: Supports seeking type: boolean description: if the resource can seek readonly: yes - identifier: width title: Width type: integer unit: pixels readonly: yes - identifier: height title: Height type: integer unit: pixels readonly: yes - identifier: noimagecache title: Disable image caching type: boolean widget: checkbox - identifier: cache title: Number of images cache type: integer description: > By default, this producer caches images to facilitate YADIF deinterlace, which needs previous and next frames. Also, caching helps with frame- stepping within a player. The default number of images cached is supplied by the MLT framework, which is currently 4, but you can override it with this property. You can also disable caching by setting it to 0. If you are using parallel processing with YADIF deinterlacing, then you might need to increase caching to prevent inadvertent backward seeks. One can also set this value globally for all instances of avformat by setting the environment variable MLT_AVFORMAT_CACHE. - identifier: force_progressive title: Force progressive description: When provided, this overrides the detection of progressive video. type: boolean widget: checkbox - identifier: force_tff title: Force top field first description: When provided, this overrides the detected field order of interlaced video. type: boolean widget: checkbox - identifier: force_fps title: Force frame rate description: When provided, this attempts to override the detected frame rate of the video. type: float widget: checkbox - identifier: force_colorspace title: Force colorspace description: When provided, this overrides the detected colorspace of the video (Y'CbCr only). type: integer values: - 240 # SMPTE 240M - 601 # ITU-R BT.601 - 709 # ITU-R BT.709 - identifier: force_color_trc title: Force transfer characteristic description: > When provided, this overrides the detected gamma transfer of the video. See libavcodec AVColorTransferCharacteristic for values. type: integer - identifier: video_delay title: Video delay description: > Manually adjust A/V synchronization. A negative value advances the video instead of delaying it. type: float default: 0 unit: seconds widget: timecode - identifier: reconnect title: Automatically reconnect? description: > Whether to attempt to automatically reconnect to a live source when a read failure occurs. type: boolean widget: checkbox - identifier: exit_on_disconnect title: Exit upon disconnection? description: > When this is set, the program will terminate with an error if a live source becomes disconnected. type: boolean widget: checkbox - identifier: mute_on_pause title: Mute on Pause description: > Mute the audio when the same frame is requested twice in a row. type: boolean default: 1 widget: checkbox - identifier: seek_threshold title: Seek Threshold description: > Number of frames required to trigger a seek forward rather than continuous read when reading forward. This can be useful to optimize some applications which rely on accelerated reading of a media file or in cases where lack of I-frames cause libavformat to face issues in seeking and where user tries to minimize the number of seek calls. type: integer unit: frames - identifier: autorotate title: Auto-rotate? type: boolean description: > Whether to automatically compensate image orientation if the file is tagged with appropriate metadata and this resource has video/images. default: 1 mlt-6.20.0/src/modules/avformat/resolution_scale.yml000066400000000000000000000011071362234133600225500ustar00rootroot00000000000000# This files contains a list of plugin parameters that are sensitive to the # image resolution. This only works for parameters with type F0R_PARAM_DOUBLE. # plugin: # parameter#: scale factor in addition to mlt_frame_resolution_scale delogo: x: 1.0 y: 1.0 w: 1.0 h: 1.0 floodfill: x: 1.0 y: 1.0 pad: x: 1.0 y: 1.0 width: 1.0 height: 1.0 perspective: x0: 1.0 y0: 1.0 x1: 1.0 y1: 1.0 x2: 1.0 y2: 1.0 x3: 1.0 y3: 1.0 swaprect: w: 1.0 h: 1.0 x1: 1.0 y1: 1.0 x2: 1.0 y2: 1.0 vignette: x0: 1.0 y0: 1.0 zoompan: x: 1.0 y: 1.0 mlt-6.20.0/src/modules/avformat/vdpau.c000066400000000000000000000252311362234133600177420ustar00rootroot00000000000000/* * producer_avformat/vdpau.c -- VDPAU functions for the avformat producer * Copyright (C) 2009-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include extern pthread_mutex_t mlt_sdl_mutex; static VdpDeviceCreateX11 *vdpau_device_create_x11; static VdpDeviceDestroy *vdp_device_destroy; static VdpGetProcAddress *vdp_get_proc_address; static VdpGetErrorString *vdp_get_error_string; static VdpGetApiVersion *vdp_get_api_version; static VdpGetInformationString *vdp_get_information_string; static VdpVideoSurfaceCreate *vdp_surface_create; static VdpVideoSurfaceDestroy *vdp_surface_destroy; static VdpVideoSurfaceGetBitsYCbCr *vdp_surface_get_bits; static VdpDecoderCreate *vdp_decoder_create; static VdpDecoderDestroy *vdp_decoder_destroy; static VdpDecoderRender *vdp_decoder_render; // TODO: Shouldn't these be protected by a mutex? static int vdpau_init_done = 0; static int vdpau_supported = 1; /** VDPAUD functions */ static void vdpau_fini( producer_avformat self ) { if ( !self->vdpau ) return; mlt_log_debug( NULL, "vdpau_fini (%x)\n", self->vdpau->device ); if ( self->vdpau->decoder != VDP_INVALID_HANDLE ) vdp_decoder_destroy( self->vdpau->decoder ); if ( self->vdpau->device != VDP_INVALID_HANDLE ) vdp_device_destroy( self->vdpau->device ); free( self->vdpau ); self->vdpau = NULL; } static int vdpau_init( producer_avformat self ) { if ( !vdpau_supported ) return 0; mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_init\n" ); int success = 0; mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent ); Display *display = XOpenDisplay( NULL ); if ( !display || mlt_properties_get_int( properties, "novdpau" ) || ( getenv( "MLT_NO_VDPAU" ) && strcmp( getenv( "MLT_NO_VDPAU" ), "1" ) == 0 ) ) return success; void *object = NULL; if ( !vdpau_init_done ) { int flags = RTLD_NOW; object = dlopen( "/usr/lib/libvdpau.so", flags ); #ifdef ARCH_X86_64 if ( !object ) object = dlopen( "/usr/lib64/libvdpau.so", flags ); if ( !object ) object = dlopen( "/usr/lib/x86_64-linux-gnu/libvdpau.so.1", flags ); #elif ARCH_X86 if ( !object ) object = dlopen( "/usr/lib/i386-linux-gnu/libvdpau.so.1", flags ); #endif if ( !object ) object = dlopen( "/usr/local/lib/libvdpau.so", flags ); if ( object ) vdpau_device_create_x11 = dlsym( object, "vdp_device_create_x11" ); else { mlt_log( MLT_PRODUCER_SERVICE(self->parent), MLT_LOG_WARNING, "%s: failed to dlopen libvdpau.so\n (%s)\n", __FUNCTION__, dlerror() ); // Don't try again. vdpau_supported = 0; return success; } } if ( vdpau_device_create_x11 ) { int screen = mlt_properties_get_int( properties, "x11_screen" ); self->vdpau = calloc( 1, sizeof( *self->vdpau ) ); self->vdpau->device = VDP_INVALID_HANDLE; self->vdpau->decoder = VDP_INVALID_HANDLE; mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "X11 Display = %p\n", display ); if ( VDP_STATUS_OK == vdpau_device_create_x11( display, screen, &self->vdpau->device, &vdp_get_proc_address ) ) { if ( !vdpau_init_done ) { vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_ERROR_STRING, (void**) &vdp_get_error_string ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_API_VERSION, (void**) &vdp_get_api_version ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_GET_INFORMATION_STRING, (void**) &vdp_get_information_string ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**) &vdp_surface_create ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, (void**) &vdp_surface_destroy ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**) &vdp_surface_get_bits ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_CREATE, (void**) &vdp_decoder_create ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_DESTROY, (void**) &vdp_decoder_destroy ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DECODER_RENDER, (void**) &vdp_decoder_render ); vdp_get_proc_address( self->vdpau->device, VDP_FUNC_ID_DEVICE_DESTROY, (void**) &vdp_device_destroy ); vdpau_init_done = 1; } success = 1; } } if ( !success ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "VDPAU failed to initialize device\n" ); if ( object ) dlclose( object ); free( self->vdpau ); self->vdpau = NULL; } return success; } static enum AVPixelFormat vdpau_get_format( struct AVCodecContext *s, const enum AVPixelFormat *fmt ) { return AV_PIX_FMT_VDPAU_H264; } static int vdpau_get_buffer( AVCodecContext *codec_context, AVFrame *frame ) { int error = 0; producer_avformat self = codec_context->opaque; mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_get_buffer\n" ); if ( self->vdpau && mlt_deque_count( self->vdpau->deque ) ) { struct vdpau_render_state *render = mlt_deque_pop_front( self->vdpau->deque ); if ( render ) { frame->data[0] = (uint8_t*) render; frame->data[1] = (uint8_t*) render; frame->data[2] = (uint8_t*) render; frame->linesize[0] = 0; frame->linesize[1] = 0; frame->linesize[2] = 0; frame->type = FF_BUFFER_TYPE_USER; render->state = FF_VDPAU_STATE_USED_FOR_REFERENCE; frame->reordered_opaque = codec_context->reordered_opaque; if ( frame->reference ) { self->vdpau->ip_age[0] = self->vdpau->ip_age[1] + 1; self->vdpau->ip_age[1] = 1; self->vdpau->b_age++; } else { self->vdpau->ip_age[0] ++; self->vdpau->ip_age[1] ++; self->vdpau->b_age = 1; } } else { mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "VDPAU surface underrun\n" ); error = -1; } } else { mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "VDPAU surface underrun\n" ); error = -1; } return error; } static void vdpau_release_buffer( AVCodecContext *codec_context, AVFrame *frame ) { producer_avformat self = codec_context->opaque; if ( self->vdpau ) { struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0]; mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_release_buffer (%x)\n", render->surface ); int i; render->state &= ~FF_VDPAU_STATE_USED_FOR_REFERENCE; for ( i = 0; i < 4; i++ ) frame->data[i] = NULL; mlt_deque_push_back( self->vdpau->deque, render ); } } static void vdpau_draw_horiz( AVCodecContext *codec_context, const AVFrame *frame, int offset[4], int y, int type, int height ) { producer_avformat self = codec_context->opaque; if ( self->vdpau ) { struct vdpau_render_state *render = (struct vdpau_render_state*) frame->data[0]; VdpVideoSurface surface = render->surface; VdpStatus status = vdp_decoder_render( self->vdpau->decoder, surface, (void*) &render->info, render->bitstream_buffers_used, render->bitstream_buffers ); if ( status != VDP_STATUS_OK ) { self->vdpau->is_decoded = 0; mlt_log_warning( MLT_PRODUCER_SERVICE(self->parent), "VDPAU failed to decode (%s)\n", vdp_get_error_string( status ) ); } else { self->vdpau->is_decoded = 1; } } } static int vdpau_decoder_init( producer_avformat self ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_decoder_init\n" ); int success = 1; self->video_codec->opaque = self; self->video_codec->get_format = vdpau_get_format; self->video_codec->get_buffer = vdpau_get_buffer; self->video_codec->release_buffer = vdpau_release_buffer; self->video_codec->draw_horiz_band = vdpau_draw_horiz; self->video_codec->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; self->video_codec->pix_fmt = AV_PIX_FMT_VDPAU_H264; VdpDecoderProfile profile = VDP_DECODER_PROFILE_H264_HIGH; uint32_t max_references = self->video_codec->refs; pthread_mutex_lock( &mlt_sdl_mutex ); VdpStatus status = vdp_decoder_create( self->vdpau->device, profile, self->video_codec->width, self->video_codec->height, max_references, &self->vdpau->decoder ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( status == VDP_STATUS_OK ) { int i, n = FFMIN( self->video_codec->refs + 2, MAX_VDPAU_SURFACES ); self->vdpau->deque = mlt_deque_init(); for ( i = 0; i < n; i++ ) { if ( VDP_STATUS_OK == vdp_surface_create( self->vdpau->device, VDP_CHROMA_TYPE_420, self->video_codec->width, self->video_codec->height, &self->vdpau->render_states[i].surface ) ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "successfully created VDPAU surface %x\n", self->vdpau->render_states[i].surface ); mlt_deque_push_back( self->vdpau->deque, &self->vdpau->render_states[i] ); } else { mlt_log_info( MLT_PRODUCER_SERVICE(self->parent), "failed to create VDPAU surface %dx%d\n", self->video_codec->width, self->video_codec->height ); while ( mlt_deque_count( self->vdpau->deque ) ) { struct vdpau_render_state *render = mlt_deque_pop_front( self->vdpau->deque ); vdp_surface_destroy( render->surface ); } mlt_deque_close( self->vdpau->deque ); success = 0; break; } } if ( self->vdpau ) self->vdpau->b_age = self->vdpau->ip_age[0] = self->vdpau->ip_age[1] = 256*256*256*64; // magic from Avidemux } else { success = 0; self->vdpau->decoder = VDP_INVALID_HANDLE; mlt_log_error( MLT_PRODUCER_SERVICE(self->parent), "VDPAU failed to initialize decoder (%s)\n", vdp_get_error_string( status ) ); } return success; } static void vdpau_producer_close( producer_avformat self ) { if ( self->vdpau ) { mlt_log_debug( MLT_PRODUCER_SERVICE(self->parent), "vdpau_producer_close\n" ); int i; for ( i = 0; i < MAX_VDPAU_SURFACES; i++ ) { if ( self->vdpau->render_states[i].surface != VDP_INVALID_HANDLE ) vdp_surface_destroy( self->vdpau->render_states[i].surface ); self->vdpau->render_states[i].surface = VDP_INVALID_HANDLE; } mlt_deque_close( self->vdpau->deque ); if ( self->vdpau->buffer ) mlt_pool_release( self->vdpau->buffer ); self->vdpau->buffer = NULL; vdpau_fini( self ); } } mlt-6.20.0/src/modules/configure000077500000000000000000000017041362234133600165460ustar00rootroot00000000000000#!/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 -a ! -f $i/deprecated \) \) ] then if [ "$gpl" = "true" -o ! -f $i/gpl -o "$help" = "1" ] 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 mlt-6.20.0/src/modules/core/000077500000000000000000000000001362234133600155655ustar00rootroot00000000000000mlt-6.20.0/src/modules/core/CMakeLists.txt000066400000000000000000000010531362234133600203240ustar00rootroot00000000000000file(GLOB mltcore_src *.c) set(mltcore_inc "") if(WIN32) list(APPEND mltcore_src ${CMAKE_SOURCE_DIR}/src/win32/fnmatch.c) list(APPEND mltcore_inc ${CMAKE_SOURCE_DIR}/src/win32) endif() add_library(mltcore MODULE ${mltcore_src}) target_include_directories(mltcore PRIVATE ${mltcore_inc}) target_link_libraries(mltcore mlt Threads::Threads) install(TARGETS mltcore LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES data_fx.properties loader.dict loader.ini ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/core) mlt-6.20.0/src/modules/core/Makefile000066400000000000000000000040461362234133600172310ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lm -lpthread include ../../../config.mak TARGET = ../libmltcore$(LIBSUF) OBJS = factory.o \ producer_colour.o \ producer_consumer.o \ producer_hold.o \ producer_loader.o \ producer_melt.o \ producer_noise.o \ producer_timewarp.o \ producer_tone.o \ filter_audiochannels.o \ filter_audiomap.o \ filter_audioconvert.o \ filter_audiowave.o \ filter_brightness.o \ filter_channelcopy.o \ filter_crop.o \ filter_data_feed.o \ filter_data_show.o \ filter_fieldorder.o \ filter_gamma.o \ filter_greyscale.o \ filter_imageconvert.o \ filter_luma.o \ filter_mask_apply.o \ filter_mask_start.o \ filter_mirror.o \ filter_mono.o \ filter_obscure.o \ filter_panner.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 \ transition_matte.o \ consumer_multi.o \ consumer_null.o ifdef SSE2_FLAGS ifdef ARCH_X86_64 OBJS += composite_line_yuv_sse2_simple.o endif endif ASM_OBJS = SRCS := $(OBJS:.o=.c) ifeq ($(targetos), MinGW) CFLAGS += -I../../win32 OBJS += ../../win32/fnmatch.o SRCS += ../../win32/fnmatch.c endif 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/core" install -m 644 data_fx.properties "$(DESTDIR)$(mltdatadir)/core" install -m 644 loader.dict "$(DESTDIR)$(mltdatadir)/core" install -m 644 loader.ini "$(DESTDIR)$(mltdatadir)/core" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/core" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/core/composite_line_yuv_mmx.S000066400000000000000000000066661362234133600225240ustar00rootroot00000000000000 .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 mlt-6.20.0/src/modules/core/composite_line_yuv_sse2_simple.c000066400000000000000000000302221362234133600241510ustar00rootroot00000000000000/* * composite_line_yuv_sse2_simple.c * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include const static unsigned char const1[] = { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00 }; const static unsigned char const2[] = { 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; #define LOAD_CONSTS \ "pxor %%xmm0, %%xmm0 \n\t" /* clear zero register */ \ "movdqu (%[const1]), %%xmm9 \n\t" /* load const1 */ \ "movdqu (%[const2]), %%xmm10 \n\t" /* load const2 */ #define LOAD_WEIGHT \ "movd %[weight], %%xmm1 \n\t" /* load weight and decompose */ \ "movlhps %%xmm1, %%xmm1 \n\t" \ "pshuflw $0, %%xmm1, %%xmm1 \n\t" \ "pshufhw $0, %%xmm1, %%xmm1 \n\t" #define LOAD_SRC_A \ "movq (%[src_a]), %%xmm2 \n\t" /* load source alpha */ \ "punpcklbw %%xmm0, %%xmm2 \n\t" /* unpack alpha 8 8-bits alphas to 8 16-bits values */ #define SRC_A_PREMUL \ "pmullw %%xmm1, %%xmm2 \n\t" /* premultiply source alpha */ \ "psrlw $8, %%xmm2 \n\t" # define DST_A_CALC \ /* DSTa = DSTa + (SRCa * (0xFF - DSTa)) >> 8 */ \ "movq (%[dest_a]), %%xmm3 \n\t" /* load dst alpha */ \ "punpcklbw %%xmm0, %%xmm3 \n\t" /* unpack dst 8 8-bits alphas to 8 16-bits values */ \ "movdqa %%xmm9, %%xmm4 \n\t" \ "psubw %%xmm3, %%xmm4 \n\t" \ "pmullw %%xmm2, %%xmm4 \n\t" \ "movdqa %%xmm4, %%xmm5 \n\t" \ "psrlw $8, %%xmm4 \n\t" \ "paddw %%xmm5, %%xmm4 \n\t" \ "paddw %%xmm10, %%xmm4 \n\t" \ "psrlw $8, %%xmm4 \n\t" \ "paddw %%xmm4, %%xmm3 \n\t" \ "packuswb %%xmm0, %%xmm3 \n\t" \ "movq %%xmm3, (%[dest_a]) \n\t" /* save dst alpha */ #define DST_PIX_CALC \ "movdqu (%[src]), %%xmm3 \n\t" /* load src */ \ "movdqu (%[dest]), %%xmm4 \n\t" /* load dst */ \ "movdqa %%xmm3, %%xmm5 \n\t" /* dub src */ \ "movdqa %%xmm4, %%xmm6 \n\t" /* dub dst */ \ "punpcklbw %%xmm0, %%xmm5 \n\t" /* unpack src low */ \ "punpcklbw %%xmm0, %%xmm6 \n\t" /* unpack dst low */ \ "punpckhbw %%xmm0, %%xmm3 \n\t" /* unpack src high */ \ "punpckhbw %%xmm0, %%xmm4 \n\t" /* unpack dst high */ \ "movdqa %%xmm2, %%xmm7 \n\t" /* dub alpha */ \ "movdqa %%xmm2, %%xmm8 \n\t" /* dub alpha */ \ "movlhps %%xmm7, %%xmm7 \n\t" /* dub low */ \ "movhlps %%xmm8, %%xmm8 \n\t" /* dub high */ \ "pshuflw $0x50, %%xmm7, %%xmm7 \n\t" \ "pshuflw $0x50, %%xmm8, %%xmm8 \n\t" \ "pshufhw $0xFA, %%xmm7, %%xmm7 \n\t" \ "pshufhw $0xFA, %%xmm8, %%xmm8 \n\t" \ "psubw %%xmm4, %%xmm3 \n\t" /* src = src - dst */ \ "psubw %%xmm6, %%xmm5 \n\t" \ "pmullw %%xmm8, %%xmm3 \n\t" /* src = src * alpha */ \ "pmullw %%xmm7, %%xmm5 \n\t" \ "pmullw %%xmm9, %%xmm4 \n\t" /* dst = dst * 0xFF */ \ "pmullw %%xmm9, %%xmm6 \n\t" \ "paddw %%xmm3, %%xmm4 \n\t" /* dst = dst + src */ \ "paddw %%xmm5, %%xmm6 \n\t" \ "movdqa %%xmm4, %%xmm3 \n\t" /* dst = ((dst >> 8) + dst + 128) >> 8 */ \ "movdqa %%xmm6, %%xmm5 \n\t" \ "psrlw $8, %%xmm4 \n\t" \ "psrlw $8, %%xmm6 \n\t" \ "paddw %%xmm3, %%xmm4 \n\t" \ "paddw %%xmm5, %%xmm6 \n\t" \ "paddw %%xmm10, %%xmm4 \n\t" \ "paddw %%xmm10, %%xmm6 \n\t" \ "psrlw $8, %%xmm4 \n\t" \ "psrlw $8, %%xmm6 \n\t" \ "packuswb %%xmm4, %%xmm6 \n\t" \ "movdqu %%xmm6, (%[dest]) \n\t" /* store dst */ #define PIX_POINTER_INC \ "add $0x10, %[src] \n\t" \ "add $0x10, %[dest] \n\t" \ "dec %[width] \n\t" #define CLOBBER_XMM \ "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "%xmm9", "%xmm10" static void blend_case7(uint8_t *dest, uint8_t *src, int width, uint8_t *src_a, uint8_t *dest_a, int weight) { int w8 = width / 8; __asm__ volatile ( LOAD_CONSTS LOAD_WEIGHT "loop_start7: \n\t" LOAD_SRC_A SRC_A_PREMUL DST_A_CALC DST_PIX_CALC "add $0x08, %[src_a] \n\t" "add $0x08, %[dest_a] \n\t" PIX_POINTER_INC "jnz loop_start7 \n\t" : [weight] "+r" (weight), [src_a] "+r" (src_a), [src] "+r" (src), [dest] "+r" (dest), [dest_a] "+r" (dest_a), [width] "+r" (w8) : [const1] "r" (const1), [const2] "r" (const2) : CLOBBER_XMM ); }; // | 3 | dest_a == NULL | src_a != NULL | weight != 256 | blend: premultiply src alpha static void blend_case3(uint8_t *dest, uint8_t *src, int width, uint8_t *src_a, int weight) { int w8 = width / 8; __asm__ volatile ( LOAD_CONSTS LOAD_WEIGHT "loop_start3: \n\t" LOAD_SRC_A SRC_A_PREMUL DST_PIX_CALC "add $0x08, %[src_a] \n\t" PIX_POINTER_INC "jnz loop_start3 \n\t" : [weight] "+r" (weight), [src_a] "+r" (src_a), [src] "+r" (src), [dest] "+r" (dest), [width] "+r" (w8) : [const1] "r" (const1), [const2] "r" (const2) : CLOBBER_XMM ); }; // | 2 | dest_a == NULL | src_a != NULL | weight == 255 | blend: only src alpha static void blend_case2(uint8_t *dest, uint8_t *src, int width, uint8_t *src_a) { int w8 = width / 8; __asm__ volatile ( LOAD_CONSTS "loop_start2: \n\t" LOAD_SRC_A DST_PIX_CALC "add $0x08, %[src_a] \n\t" PIX_POINTER_INC "jnz loop_start2 \n\t" : [src_a] "+r" (src_a), [src] "+r" (src), [dest] "+r" (dest), [width] "+r" (w8) : [const1] "r" (const1), [const2] "r" (const2) : CLOBBER_XMM ); }; // | 1 | dest_a == NULL | src_a == NULL | weight != 256 | blend: with given alpha static void blend_case1(uint8_t *dest, uint8_t *src, int width, int weight) { int w8 = width / 8; __asm__ volatile ( LOAD_CONSTS LOAD_WEIGHT "loop_start1: \n\t" "movdqa %%xmm1, %%xmm2 \n\t" /* src alpha cames from weight */ DST_PIX_CALC PIX_POINTER_INC "jnz loop_start1 \n\t" : [weight] "+r" (weight), [src] "+r" (src), [dest] "+r" (dest), [width] "+r" (w8) : [const1] "r" (const1), [const2] "r" (const2) : CLOBBER_XMM ); }; // | 5 | dest_a != NULL | src_a == NULL | weight != 256 | blend: with given alpha static void blend_case5(uint8_t *dest, uint8_t *src, int width, uint8_t *dest_a, int weight) { int w8 = width / 8; __asm__ volatile ( LOAD_CONSTS LOAD_WEIGHT "loop_start5: \n\t" "movdqa %%xmm1, %%xmm2 \n\t" /* source alpha comes from weight */ DST_A_CALC DST_PIX_CALC "add $0x08, %[dest_a] \n\t" PIX_POINTER_INC "jnz loop_start5 \n\t" : [weight] "+r" (weight), [src] "+r" (src), [dest] "+r" (dest), [dest_a] "+r" (dest_a), [width] "+r" (w8) : [const1] "r" (const1), [const2] "r" (const2) : CLOBBER_XMM ); }; // | 6 | dest_a != NULL | src_a != NULL | weight == 256 | blend: full blend without src alpha premutiply static void blend_case6(uint8_t *dest, uint8_t *src, int width, uint8_t *src_a, uint8_t *dest_a) { int w8 = width / 8; __asm__ volatile ( LOAD_CONSTS "loop_start6: \n\t" LOAD_SRC_A DST_A_CALC DST_PIX_CALC "add $0x08, %[src_a] \n\t" "add $0x08, %[dest_a] \n\t" PIX_POINTER_INC "jnz loop_start6 \n\t" : [src_a] "+r" (src_a), [src] "+r" (src), [dest] "+r" (dest), [dest_a] "+r" (dest_a), [width] "+r" (w8) : [const1] "r" (const1), [const2] "r" (const2) : CLOBBER_XMM ); }; void composite_line_yuv_sse2_simple(uint8_t *dest, uint8_t *src, int width, uint8_t *src_a, uint8_t *dest_a, int weight) { weight >>= 8; /* | 0 | dest_a == NULL | src_a == NULL | weight == 256 | blit | 1 | dest_a == NULL | src_a == NULL | weight != 256 | blend: with given alpha | 2 | dest_a == NULL | src_a != NULL | weight == 256 | blend: only src alpha | 3 | dest_a == NULL | src_a != NULL | weight != 256 | blend: premultiply src alpha | 4 | dest_a != NULL | src_a == NULL | weight == 256 | blit: blit and set dst alpha to FF | 5 | dest_a != NULL | src_a == NULL | weight != 256 | blend: with given alpha | 6 | dest_a != NULL | src_a != NULL | weight == 256 | blend: full blend without src alpha premutiply | 7 | dest_a != NULL | src_a != NULL | weight != 256 | blend: full (origin version) */ int cond = ((dest_a != NULL)?4:0) + ((src_a != NULL)?2:0) + ((weight != 256)?1:0); switch(cond) { case 0: memcpy(dest, src, 2 * width); break; case 1: blend_case1(dest, src, width, weight); break; case 2: blend_case2(dest, src, width, src_a); break; case 3: blend_case3(dest, src, width, src_a, weight); break; case 4: memcpy(dest, src, 2 * width); memset(dest_a, 0xFF, width); break; case 5: blend_case5(dest, src, width, dest_a, weight); break; case 6: blend_case6(dest, src, width, src_a, dest_a); break; case 7: blend_case7(dest, src, width, src_a, dest_a, weight); break; }; }; mlt-6.20.0/src/modules/core/consumer_multi.c000066400000000000000000000472511362234133600210070ustar00rootroot00000000000000/* * Copyright (C) 2011-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include // Forward references static int start( mlt_consumer consumer ); static int stop( mlt_consumer consumer ); static int is_stopped( mlt_consumer consumer ); static void *consumer_thread( void *arg ); static void consumer_close( mlt_consumer consumer ); static void purge( mlt_consumer consumer ); static mlt_properties normalisers = NULL; /** Initialise the consumer. */ mlt_consumer consumer_multi_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_consumer consumer = mlt_consumer_new( profile ); if ( consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); // Set defaults mlt_properties_set( properties, "resource", arg ); mlt_properties_set_int( properties, "real_time", -1 ); mlt_properties_set_int( properties, "terminate_on_pause", 1 ); // Init state mlt_properties_set_int( properties, "joined", 1 ); // Assign callbacks consumer->close = consumer_close; consumer->start = start; consumer->stop = stop; consumer->is_stopped = is_stopped; consumer->purge = purge; } return consumer; } static mlt_consumer create_consumer( mlt_profile profile, char *id, char *arg ) { char *myid = id ? strdup( id ) : NULL; char *myarg = ( myid && !arg ) ? strchr( myid, ':' ) : NULL; if ( myarg ) *myarg ++ = '\0'; else myarg = arg; mlt_consumer consumer = mlt_factory_consumer( profile, myid, myarg ); free( myid ); return consumer; } static void create_filter( mlt_profile profile, mlt_service service, char *effect, int *created ) { char *id = strdup( effect ); char *arg = strchr( id, ':' ); if ( arg != NULL ) *arg ++ = '\0'; // We cannot use GLSL-based filters here. if ( strncmp( effect, "movit.", 6 ) && strncmp( effect, "glsl.", 5 ) ) { mlt_filter filter; // The swscale and avcolor_space filters require resolution as arg to test compatibility if ( strncmp( effect, "swscale", 7 ) == 0 || strncmp( effect, "avcolo", 6 ) == 0 ) { int width = mlt_properties_get_int( MLT_SERVICE_PROPERTIES( service ), "meta.media.width" ); filter = mlt_factory_filter( profile, id, &width ); } else { filter = mlt_factory_filter( profile, id, arg ); } if ( filter ) { mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 ); mlt_service_attach( service, filter ); mlt_filter_close( filter ); *created = 1; } } free( id ); } static void attach_normalisers( mlt_profile profile, mlt_service service ) { // 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 ]; snprintf( temp, sizeof(temp), "%s/core/loader.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, service, mlt_tokeniser_get_string( tokeniser, j ), &created ); } // Close the tokeniser mlt_tokeniser_close( tokeniser ); // Attach the audio and video format converters int created = 0; // movit.convert skips setting the frame->convert_image pointer if GLSL cannot be used. mlt_filter filter = mlt_factory_filter( profile, "movit.convert", NULL ); if ( filter != NULL ) { mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 ); mlt_service_attach( service, filter ); mlt_filter_close( filter ); created = 1; } // avcolor_space and imageconvert only set frame->convert_image if it has not been set. create_filter( profile, service, "avcolor_space", &created ); if ( !created ) create_filter( profile, service, "imageconvert", &created ); create_filter( profile, service, "audioconvert", &created ); } static void on_frame_show( void *dummy, mlt_properties properties, mlt_frame frame ) { mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } static mlt_consumer generate_consumer( mlt_consumer consumer, mlt_properties props, int index ) { mlt_profile profile = NULL; if ( mlt_properties_get( props, "mlt_profile" ) ) profile = mlt_profile_init( mlt_properties_get( props, "mlt_profile" ) ); if ( !profile ) profile = mlt_profile_clone( mlt_service_profile( MLT_CONSUMER_SERVICE(consumer) ) ); mlt_consumer nested = create_consumer( profile, mlt_properties_get( props, "mlt_service" ), mlt_properties_get( props, "target" ) ); if ( nested ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested); char key[30]; snprintf( key, sizeof(key), "%d.consumer", index ); mlt_properties_set_data( properties, key, nested, 0, (mlt_destructor) mlt_consumer_close, NULL ); snprintf( key, sizeof(key), "%d.profile", index ); mlt_properties_set_data( properties, key, profile, 0, (mlt_destructor) mlt_profile_close, NULL ); mlt_properties_set_int( nested_props, "put_mode", 1 ); mlt_properties_pass_list( nested_props, properties, "terminate_on_pause" ); mlt_properties_set( props, "consumer", NULL ); // set mlt_profile before other properties to facilitate presets mlt_properties_pass_list( nested_props, props, "mlt_profile" ); mlt_properties_inherit( nested_props, props ); attach_normalisers( profile, MLT_CONSUMER_SERVICE(nested) ); // Relay the first available consumer-frame-show event mlt_event event = mlt_properties_get_data( properties, "frame-show-event", NULL ); if ( !event ) { event = mlt_events_listen( nested_props, properties, "consumer-frame-show", (mlt_listener) on_frame_show ); mlt_properties_set_data( properties, "frame-show-event", event, 0, /*mlt_event_close*/ NULL, NULL ); } } else { mlt_profile_close( profile ); } return nested; } static void foreach_consumer_init( mlt_consumer consumer ) { const char *resource = mlt_properties_get( MLT_CONSUMER_PROPERTIES(consumer), "resource" ); mlt_properties properties = mlt_properties_parse_yaml( resource ); char key[20]; int index = 0; if ( mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "0", NULL ) ) { // Properties set directly by application mlt_properties p; if ( properties ) mlt_properties_close( properties ); properties = MLT_CONSUMER_PROPERTIES(consumer); do { snprintf( key, sizeof(key), "%d", index ); if ( ( p = mlt_properties_get_data( properties, key, NULL ) ) ) generate_consumer( consumer, p, index++ ); } while ( p ); } else if ( properties && mlt_properties_get_data( properties, "0", NULL ) ) { // YAML file supplied mlt_properties p; do { snprintf( key, sizeof(key), "%d", index ); if ( ( p = mlt_properties_get_data( properties, key, NULL ) ) ) generate_consumer( consumer, p, index++ ); } while ( p ); mlt_properties_close( properties ); } else { // properties file supplied or properties on this consumer const char *s; if ( properties ) mlt_properties_close( properties ); if ( resource ) properties = mlt_properties_load( resource ); else properties = MLT_CONSUMER_PROPERTIES( consumer ); do { snprintf( key, sizeof(key), "%d", index ); if ( ( s = mlt_properties_get( properties, key ) ) ) { mlt_properties p = mlt_properties_new(); if ( !p ) break; // Terminate mlt_service value at the argument delimiter if supplied. // Needed here instead of just relying upon create_consumer() so that // a properties preset is picked up correctly. char *service = strdup( mlt_properties_get( properties, key ) ); char *arg = strchr( service, ':' ); if ( arg ) { *arg ++ = '\0'; mlt_properties_set( p, "target", arg ); } mlt_properties_set( p, "mlt_service", service ); free( service ); snprintf( key, sizeof(key), "%d.", index ); int i, count; count = mlt_properties_count( properties ); for ( i = 0; i < count; i++ ) { char *name = mlt_properties_get_name( properties, i ); if ( !strncmp( name, key, strlen(key) ) ) mlt_properties_set( p, name + strlen(key), mlt_properties_get_value( properties, i ) ); } generate_consumer( consumer, p, index++ ); mlt_properties_close( p ); } } while ( s ); if ( resource ) mlt_properties_close( properties ); } } static void foreach_consumer_start( mlt_consumer consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf( key, sizeof(key), "%d.consumer", index++ ); nested = mlt_properties_get_data( properties, key, NULL ); if ( nested ) { mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested); mlt_properties_set_position( nested_props, "_multi_position", mlt_properties_get_position( properties, "in" ) ); mlt_properties_set_data( nested_props, "_multi_audio", NULL, 0, NULL, NULL ); mlt_properties_set_int( nested_props, "_multi_samples", 0 ); mlt_consumer_start( nested ); } } while ( nested ); } static void foreach_consumer_refresh( mlt_consumer consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf( key, sizeof(key), "%d.consumer", index++ ); nested = mlt_properties_get_data( properties, key, NULL ); if ( nested ) mlt_properties_set_int( MLT_CONSUMER_PROPERTIES(nested), "refresh", 1 ); } while ( nested ); } // Update certain properties on this consumer from the child consumers. static void foreach_consumer_update( mlt_consumer consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf( key, sizeof(key), "%d.consumer", index++ ); nested = mlt_properties_get_data( properties, key, NULL ); if ( nested ) mlt_properties_pass_list( properties, MLT_CONSUMER_PROPERTIES(nested), "color_trc" ); } while ( nested ); } static void foreach_consumer_put( mlt_consumer consumer, mlt_frame frame ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf( key, sizeof(key), "%d.consumer", index++ ); nested = mlt_properties_get_data( properties, key, NULL ); if ( nested ) { mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested); double self_fps = mlt_properties_get_double( properties, "fps" ); double nested_fps = mlt_properties_get_double( nested_props, "fps" ); mlt_position nested_pos = mlt_properties_get_position( nested_props, "_multi_position" ); mlt_position self_pos = mlt_frame_get_position( frame ); double self_time = self_pos / self_fps; double nested_time = nested_pos / nested_fps; // get the audio for the current frame uint8_t *buffer = NULL; mlt_audio_format format = mlt_audio_s16; int channels = mlt_properties_get_int( properties, "channels" ); int frequency = mlt_properties_get_int( properties, "frequency" ); int current_samples = mlt_sample_calculator( self_fps, frequency, self_pos ); mlt_frame_get_audio( frame, (void**) &buffer, &format, &frequency, &channels, ¤t_samples ); int current_size = mlt_audio_format_size( format, current_samples, channels ); // get any leftover audio int prev_size = 0; uint8_t *prev_buffer = mlt_properties_get_data( nested_props, "_multi_audio", &prev_size ); uint8_t *new_buffer = NULL; if ( prev_size > 0 ) { new_buffer = mlt_pool_alloc( prev_size + current_size ); memcpy( new_buffer, prev_buffer, prev_size ); memcpy( new_buffer + prev_size, buffer, current_size ); buffer = new_buffer; } current_size += prev_size; current_samples += mlt_properties_get_int( nested_props, "_multi_samples" ); while ( nested_time <= self_time ) { // put ideal number of samples into cloned frame int deeply = index > 1 ? 1 : 0; mlt_frame clone_frame = mlt_frame_clone( frame, deeply ); mlt_properties clone_props = MLT_FRAME_PROPERTIES( clone_frame ); int nested_samples = mlt_sample_calculator( nested_fps, frequency, nested_pos ); // -10 is an optimization to avoid tiny amounts of leftover samples nested_samples = nested_samples > current_samples - 10 ? current_samples : nested_samples; int nested_size = mlt_audio_format_size( format, nested_samples, channels ); if ( nested_size > 0 ) { prev_buffer = mlt_pool_alloc( nested_size ); memcpy( prev_buffer, buffer, nested_size ); } else { prev_buffer = NULL; nested_size = 0; } mlt_frame_set_audio( clone_frame, prev_buffer, format, nested_size, mlt_pool_release ); mlt_properties_set_int( clone_props, "audio_samples", nested_samples ); mlt_properties_set_int( clone_props, "audio_frequency", frequency ); mlt_properties_set_int( clone_props, "audio_channels", channels ); // chomp the audio current_samples -= nested_samples; current_size -= nested_size; buffer += nested_size; // Fix some things mlt_properties_set_int( clone_props, "meta.media.width", mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "width" ) ); mlt_properties_set_int( clone_props, "meta.media.height", mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "height" ) ); // send frame to nested consumer mlt_consumer_put_frame( nested, clone_frame ); mlt_properties_set_position( nested_props, "_multi_position", ++nested_pos ); nested_time = nested_pos / nested_fps; } // save any remaining audio if ( current_size > 0 ) { prev_buffer = mlt_pool_alloc( current_size ); memcpy( prev_buffer, buffer, current_size ); } else { prev_buffer = NULL; current_size = 0; } mlt_pool_release( new_buffer ); mlt_properties_set_data( nested_props, "_multi_audio", prev_buffer, current_size, mlt_pool_release, NULL ); mlt_properties_set_int( nested_props, "_multi_samples", current_samples ); } } while ( nested ); } static void foreach_consumer_stop( mlt_consumer consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_consumer nested = NULL; char key[30]; int index = 0; struct timespec tm = { 0, 1000 * 1000 }; do { snprintf( key, sizeof(key), "%d.consumer", index++ ); nested = mlt_properties_get_data( properties, key, NULL ); if ( nested ) { // Let consumer with terminate_on_pause stop on their own if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES(nested), "terminate_on_pause" ) ) { // Send additional dummy frame to unlatch nested consumer's threads mlt_consumer_put_frame( nested, mlt_frame_init( MLT_CONSUMER_SERVICE(consumer) ) ); // wait for stop while ( !mlt_consumer_is_stopped( nested ) ) nanosleep( &tm, NULL ); } else { mlt_consumer_stop( nested ); } } } while ( nested ); } /** Start the consumer. */ static int start( mlt_consumer consumer ) { // Check that we're not already running if ( is_stopped( consumer ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); pthread_t *thread = calloc( 1, sizeof( pthread_t ) ); // Assign the thread to properties with automatic dealloc 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 ); // Construct and start nested consumers if ( !mlt_properties_get_data( properties, "0.consumer", NULL ) ) foreach_consumer_init( consumer ); foreach_consumer_start( consumer ); // Create the thread pthread_create( thread, NULL, consumer_thread, consumer ); } return 0; } /** Stop the consumer. */ static int stop( mlt_consumer consumer ) { // Check that we're running if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES(consumer), "joined" ) ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); pthread_t *thread = mlt_properties_get_data( properties, "thread", NULL ); // Stop the thread mlt_properties_set_int( properties, "running", 0 ); // Wait for termination if ( thread ) { foreach_consumer_refresh( consumer ); pthread_join( *thread, NULL ); } mlt_properties_set_int( properties, "joined", 1 ); // Stop nested consumers foreach_consumer_stop( consumer ); } return 0; } /** Determine if the consumer is stopped. */ static int is_stopped( mlt_consumer consumer ) { return !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "running" ); } /** Purge each of the child consumers. */ static void purge( mlt_consumer consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); if ( mlt_properties_get_int( properties, "running" ) ) { mlt_consumer nested = NULL; char key[30]; int index = 0; do { snprintf( key, sizeof(key), "%d.consumer", index++ ); nested = mlt_properties_get_data( properties, key, NULL ); mlt_consumer_purge( nested ); } while ( nested ); } } /** The main thread - the argument is simply the consumer. */ static void *consumer_thread( void *arg ) { mlt_consumer consumer = arg; mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_frame frame = NULL; // Determine whether to stop at end-of-media int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); int terminated = 0; foreach_consumer_update( consumer ); // Loop while running while ( !terminated && !is_stopped( consumer ) ) { // Get the next frame frame = mlt_consumer_rt_frame( consumer ); // Check for termination if ( terminate_on_pause && frame ) terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0; // Check that we have a frame to work with if ( frame && !terminated && !is_stopped( consumer ) ) { if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered" ) ) { if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "_speed" ) == 0 ) foreach_consumer_refresh( consumer ); foreach_consumer_put( consumer, frame ); } else { int dropped = mlt_properties_get_int( properties, "_dropped" ); mlt_log_info( MLT_CONSUMER_SERVICE(consumer), "dropped frame %d\n", ++dropped ); mlt_properties_set_int( properties, "_dropped", dropped ); } mlt_frame_close( frame ); } else { if ( frame && terminated ) { // Send this termination frame to nested consumers for their cancellation foreach_consumer_put( consumer, frame ); } if ( frame ) mlt_frame_close( frame ); terminated = 1; } } // Indicate that the consumer is stopped mlt_consumer_stopped( consumer ); return NULL; } /** Close the consumer. */ static void consumer_close( mlt_consumer consumer ) { mlt_consumer_stop( consumer ); // Close the parent mlt_consumer_close( consumer ); free( consumer ); } mlt-6.20.0/src/modules/core/consumer_multi.yml000066400000000000000000000026451362234133600213640ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: multi title: Multiple outputs version: 1 copyright: Copyright (C) 2011-2014 Meltytech, LLC license: LGPL language: en creator: Dan Dennedy tags: - Audio - Video description: Use multiple consumers with the same producer. notes: | There are a few ways of defining each of the outputs and their properties. One form is a flat set of properties on this consumer that follows the pattern: = [.=]* For example, 0=sdl 0.rescale=bilinear 1=avformat 1.target=foo.dv ... To change the profile for a particular output set the property "mlt_profile." You can put these into a MLT properties file and supply that to this consumer. Another way is to create a separate properties list for each output and set that on the consumer with a numeric name starting with zero: = ... In this format, to specify the service, use the property name "mlt_service" and, again, to specify the profile, use "mlt_profile." You can put these into a YAML Tiny file and supply that to this consumer. This is also the recommended way for applications to interact with this consumer, which is how melt and the XML producer support multiple consumers. parameters: - identifier: argument title: File type: string description: > A properties or YAML file specifying multiple consumers and their properties. required: no mlt-6.20.0/src/modules/core/consumer_null.c000066400000000000000000000112651362234133600206230ustar00rootroot00000000000000/* * consumer_null.c -- a null consumer * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // mlt Header files #include #include // System header files #include #include #include #include // Forward references. static int consumer_start( mlt_consumer consumer ); static int consumer_stop( mlt_consumer consumer ); static int consumer_is_stopped( mlt_consumer consumer ); static void *consumer_thread( void *arg ); static void consumer_close( mlt_consumer consumer ); /** 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 consumer = mlt_consumer_new( profile ); // If memory allocated and initialises without error if ( consumer != NULL ) { // Assign close callback consumer->close = consumer_close; // Set up start/stop/terminated callbacks consumer->start = consumer_start; consumer->stop = consumer_stop; consumer->is_stopped = consumer_is_stopped; } // Return consumer return consumer; } /** Start the consumer. */ static int consumer_start( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // 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, consumer ); } return 0; } /** Stop the consumer. */ static int consumer_stop( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // 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 if ( thread ) pthread_join( *thread, NULL ); } return 0; } /** Determine if the consumer is stopped. */ static int consumer_is_stopped( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); 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 consumer = arg; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // 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( consumer ); // 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( consumer ); return NULL; } /** Close the consumer. */ static void consumer_close( mlt_consumer consumer ) { // Stop the consumer mlt_consumer_stop( consumer ); // Close the parent mlt_consumer_close( consumer ); // Free the memory free( consumer ); } mlt-6.20.0/src/modules/core/data_fx.properties000066400000000000000000000174101362234133600213140ustar00rootroot00000000000000# 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.family=San .filter[1].producer.size=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.family=San .filter[1].producer.size=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.family=San .filter[1].producer.size=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.family=San .filter[1].producer.size=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.family=San .filter[1].producer.size=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.family=San .filter[1].producer.size=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.family=San .filter[1].producer.size=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.family=San .filter[1].producer.size=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[1].producer.family=San .filter[1].producer.size=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% mlt-6.20.0/src/modules/core/factory.c000066400000000000000000000271401362234133600174040ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include extern mlt_consumer consumer_multi_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_consumer consumer_null_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_audiochannels_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_audioconvert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_audiomap_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_audiowave_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_fieldorder_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_imageconvert_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_mask_apply_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_mask_start_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_panner_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_hold_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_loader_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_melt_file_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_melt_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv ); extern mlt_producer producer_noise_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_timewarp_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_tone_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 ); extern mlt_transition transition_matte_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #include "transition_region.h" static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/core/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "multi", consumer_multi_init ); MLT_REGISTER( consumer_type, "null", consumer_null_init ); MLT_REGISTER( filter_type, "audiochannels", filter_audiochannels_init ); MLT_REGISTER( filter_type, "audioconvert", filter_audioconvert_init ); MLT_REGISTER( filter_type, "audiomap", filter_audiomap_init ); MLT_REGISTER( filter_type, "audiowave", filter_audiowave_init ); MLT_REGISTER( filter_type, "brightness", filter_brightness_init ); MLT_REGISTER( filter_type, "channelcopy", filter_channelcopy_init ); MLT_REGISTER( filter_type, "channelswap", 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, "fieldorder", filter_fieldorder_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, "imageconvert", filter_imageconvert_init ); MLT_REGISTER( filter_type, "luma", filter_luma_init ); MLT_REGISTER( filter_type, "mask_apply", filter_mask_apply_init ); MLT_REGISTER( filter_type, "mask_start", filter_mask_start_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, "panner", filter_panner_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, "abnormal", producer_loader_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, "hold", producer_hold_init ); MLT_REGISTER( producer_type, "loader", producer_loader_init ); MLT_REGISTER( producer_type, "melt", producer_melt_init ); MLT_REGISTER( producer_type, "melt_file", producer_melt_file_init ); MLT_REGISTER( producer_type, "noise", producer_noise_init ); MLT_REGISTER( producer_type, "timewarp", producer_timewarp_init ); MLT_REGISTER( producer_type, "tone", producer_tone_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, "matte", transition_matte_init ); MLT_REGISTER( transition_type, "region", transition_region_init ); MLT_REGISTER_METADATA( consumer_type, "multi", metadata, "consumer_multi.yml" ); MLT_REGISTER_METADATA( filter_type, "audiomap", metadata, "filter_audiomap.yml" ); MLT_REGISTER_METADATA( filter_type, "audiowave", metadata, "filter_audiowave.yml" ); MLT_REGISTER_METADATA( filter_type, "brightness", metadata, "filter_brightness.yml" ); MLT_REGISTER_METADATA( filter_type, "channelcopy", metadata, "filter_channelcopy.yml" ); MLT_REGISTER_METADATA( filter_type, "channelswap", metadata, "filter_channelcopy.yml" ); MLT_REGISTER_METADATA( filter_type, "crop", metadata, "filter_crop.yml" ); MLT_REGISTER_METADATA( filter_type, "data_show", metadata, "filter_data_show.yml" ); MLT_REGISTER_METADATA( filter_type, "fieldorder", metadata, "filter_fieldorder.yml" ); MLT_REGISTER_METADATA( filter_type, "gamma", metadata, "filter_gamma.yml" ); MLT_REGISTER_METADATA( filter_type, "greyscale", metadata, "filter_greyscale.yml" ); MLT_REGISTER_METADATA( filter_type, "grayscale", metadata, "filter_greyscale.yml" ); MLT_REGISTER_METADATA( filter_type, "luma", metadata, "filter_luma.yml" ); MLT_REGISTER_METADATA( filter_type, "mask_apply", metadata, "filter_mask_apply.yml" ); MLT_REGISTER_METADATA( filter_type, "mask_start", metadata, "filter_mask_start.yml" ); MLT_REGISTER_METADATA( filter_type, "mirror", metadata, "filter_mirror.yml" ); MLT_REGISTER_METADATA( filter_type, "mono", metadata, "filter_mono.yml" ); MLT_REGISTER_METADATA( filter_type, "obscure", metadata, "filter_obscure.yml" ); MLT_REGISTER_METADATA( filter_type, "panner", metadata, "filter_panner.yml" ); MLT_REGISTER_METADATA( filter_type, "region", metadata, "filter_region.yml" ); MLT_REGISTER_METADATA( filter_type, "rescale", metadata, "filter_rescale.yml" ); MLT_REGISTER_METADATA( filter_type, "resize", metadata, "filter_resize.yml" ); MLT_REGISTER_METADATA( filter_type, "transition", metadata, "filter_transition.yml" ); MLT_REGISTER_METADATA( filter_type, "watermark", metadata, "filter_watermark.yml" ); MLT_REGISTER_METADATA( producer_type, "colour", metadata, "producer_colour.yml" ); MLT_REGISTER_METADATA( producer_type, "color", metadata, "producer_colour.yml" ); MLT_REGISTER_METADATA( producer_type, "consumer", metadata, "producer_consumer.yml" ); MLT_REGISTER_METADATA( producer_type, "hold", metadata, "producer_hold.yml" ); MLT_REGISTER_METADATA( producer_type, "loader", metadata, "producer_loader.yml" ); MLT_REGISTER_METADATA( producer_type, "melt", metadata, "producer_melt.yml" ); MLT_REGISTER_METADATA( producer_type, "melt_file", metadata, "producer_melt_file.yml" ); MLT_REGISTER_METADATA( producer_type, "noise", metadata, "producer_noise.yml" ); MLT_REGISTER_METADATA( producer_type, "timewarp", metadata, "producer_timewarp.yml" ); MLT_REGISTER_METADATA( producer_type, "tone", metadata, "producer_tone.yml" ); MLT_REGISTER_METADATA( transition_type, "composite", metadata, "transition_composite.yml" ); MLT_REGISTER_METADATA( transition_type, "luma", metadata, "transition_luma.yml" ); MLT_REGISTER_METADATA( transition_type, "mix", metadata, "transition_mix.yml" ); MLT_REGISTER_METADATA( transition_type, "matte", metadata, "transition_matte.yml" ); MLT_REGISTER_METADATA( transition_type, "region", metadata, "transition_region.yml" ); } mlt-6.20.0/src/modules/core/filter_audiochannels.c000066400000000000000000000167551362234133600221310ustar00rootroot00000000000000/* * filter_audiochannels.c -- convert from one audio format to another * Copyright (C) 2009-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Used to return number of channels in the source int channels_avail = *channels; // Get the producer's audio int error = mlt_frame_get_audio( frame, buffer, format, frequency, &channels_avail, samples ); if ( error ) return error; if ( channels_avail < *channels ) { int size = mlt_audio_format_size( *format, *samples, *channels ); int16_t *new_buffer = mlt_pool_alloc( size ); // Duplicate the existing channels if ( *format == mlt_audio_s16 ) { int i, j, k = 0; for ( i = 0; i < *samples; i++ ) { for ( j = 0; j < *channels; j++ ) { new_buffer[ ( i * *channels ) + j ] = ((int16_t*)(*buffer))[ ( i * channels_avail ) + k ]; k = ( k + 1 ) % channels_avail; } } } else if ( *format == mlt_audio_s32le || *format == mlt_audio_f32le ) { int32_t *p = (int32_t*) new_buffer; int i, j, k = 0; for ( i = 0; i < *samples; i++ ) { for ( j = 0; j < *channels; j++ ) { p[ ( i * *channels ) + j ] = ((int32_t*)(*buffer))[ ( i * channels_avail ) + k ]; k = ( k + 1 ) % channels_avail; } } } else if ( *format == mlt_audio_u8 ) { uint8_t *p = (uint8_t*) new_buffer; int i, j, k = 0; for ( i = 0; i < *samples; i++ ) { for ( j = 0; j < *channels; j++ ) { p[ ( i * *channels ) + j ] = ((uint8_t*)(*buffer))[ ( i * channels_avail ) + k ]; k = ( k + 1 ) % channels_avail; } } } else { // non-interleaved - s32 or float int size_avail = mlt_audio_format_size( *format, *samples, channels_avail ); int32_t *p = (int32_t*) new_buffer; int i = *channels / channels_avail; while ( i-- ) { memcpy( p, *buffer, size_avail ); p += size_avail / sizeof(*p); } i = *channels % channels_avail; if ( i ) { size_avail = mlt_audio_format_size( *format, *samples, i ); memcpy( p, *buffer, size_avail ); } } // Update the audio buffer now - destroys the old mlt_frame_set_audio( frame, new_buffer, *format, size, mlt_pool_release ); *buffer = new_buffer; } else if ( channels_avail == 6 && *channels == 2 ) { // Downmix 5.1 audio to stereo. // Mix levels taken from ATSC A/52 assuming maximum center and surround // mix levels. #define MIX(front, center, surr) (front + (0.707 * center) + (0.5 * surr)) // Convert to a supported format if necessary mlt_audio_format new_format = *format; switch( *format ) { default: // Unknown. Try to convert to float anyway. mlt_log_error( NULL, "[audiochannels] Unknown format %d\n", *format ); case mlt_audio_float: case mlt_audio_f32le: new_format = mlt_audio_float; break; case mlt_audio_s32le: case mlt_audio_s32: new_format = mlt_audio_s32; break; case mlt_audio_s16: case mlt_audio_u8: new_format = mlt_audio_s16; break; case mlt_audio_none: new_format = mlt_audio_none; break; } if ( *format != new_format && frame->convert_audio ) frame->convert_audio( frame, buffer, format, new_format ); // Perform the downmix. Operate on the buffer in place to avoid realloc. if ( *format == mlt_audio_s16 ) { int16_t* in = *buffer; int16_t* out = *buffer; int i; for ( i = 0; i < *samples; i++ ) { float fl = in[0]; float fr = in[1]; float c = in[2]; // in[3] is LFE float sl = in[4]; float sr = in[5]; *out++ = CLAMP( MIX(fl, c, sl), INT16_MIN, INT16_MAX ); // Left *out++ = CLAMP( MIX(fr, c, sr), INT16_MIN, INT16_MAX ); // Right in +=6; } } else if ( *format == mlt_audio_s32 ) { int32_t* flin = *buffer; int32_t* frin = *buffer + (*samples * sizeof(float)); int32_t* cin = *buffer + (2 * *samples * sizeof(float)); int32_t* slin = *buffer + (4 * *samples * sizeof(float)); int32_t* srin = *buffer + (5 * *samples * sizeof(float)); int32_t* lout = *buffer; int32_t* rout = *buffer + (*samples * sizeof(float)); int i; for ( i = 0; i < *samples; i++ ) { double fl = *flin++; double fr = *frin++; double c = *cin++; double sl = *slin++; double sr = *srin++; *lout++ = CLAMP( MIX(fl, c, sl), INT32_MIN, INT32_MAX ); *rout++ = CLAMP( MIX(fr, c, sr), INT32_MIN, INT32_MAX ); } } else if ( *format == mlt_audio_float ) { float* flin = *buffer; float* frin = *buffer + (*samples * sizeof(float)); float* cin = *buffer + (2 * *samples * sizeof(float)); float* slin = *buffer + (4 * *samples * sizeof(float)); float* srin = *buffer + (5 * *samples * sizeof(float)); float* lout = *buffer; float* rout = *buffer + (*samples * sizeof(float)); int i; for ( i = 0; i < *samples; i++ ) { float fl = *flin++; float fr = *frin++; float c = *cin++; float sl = *slin++; float sr = *srin++; *lout++ = MIX(fl, c, sl); *rout++ = MIX(fr, c, sr); } } else { mlt_log_error( NULL, "[audiochannels] Unable to mix format %d\n", *format ); } } else if ( channels_avail > *channels ) { int size = mlt_audio_format_size( *format, *samples, *channels ); int16_t *new_buffer = mlt_pool_alloc( size ); int i, j; // Drop all but the first *channels if ( *format == mlt_audio_s16 ) { for ( i = 0; i < *samples; i++ ) for ( j = 0; j < *channels; j++ ) new_buffer[ ( i * *channels ) + j ] = ((int16_t*)(*buffer))[ ( i * channels_avail ) + j ]; } else if ( *format == mlt_audio_s32le || *format == mlt_audio_f32le ) { int32_t *p = (int32_t*) new_buffer; for ( i = 0; i < *samples; i++ ) for ( j = 0; j < *channels; j++ ) p[ ( i * *channels ) + j ] = ((int32_t*)(*buffer))[ ( i * channels_avail ) + j ]; } else if ( *format == mlt_audio_u8 ) { uint8_t *p = (uint8_t*) new_buffer; for ( i = 0; i < *samples; i++ ) for ( j = 0; j < *channels; j++ ) p[ ( i * *channels ) + j ] = ((uint8_t*)(*buffer))[ ( i * channels_avail ) + j ]; } else { // non-interleaved - s32 or float memcpy( new_buffer, *buffer, size ); } // Update the audio buffer now - destroys the old mlt_frame_set_audio( frame, new_buffer, *format, size, mlt_pool_release ); *buffer = new_buffer; } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter_get_audio ); return frame; } /** Constructor for the filter. */ mlt_filter filter_audiochannels_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); if ( filter ) filter->process = filter_process; return filter; } mlt-6.20.0/src/modules/core/filter_audioconvert.c000066400000000000000000000317501362234133600220060ustar00rootroot00000000000000/* * filter_audioconvert.c -- convert from one audio format to another * Copyright (C) 2009-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int convert_audio( mlt_frame frame, void **audio, mlt_audio_format *format, mlt_audio_format requested_format ) { int error = 1; mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int channels = mlt_properties_get_int( properties, "audio_channels" ); int samples = mlt_properties_get_int( properties, "audio_samples" ); int size = mlt_audio_format_size( requested_format, samples, channels ); if ( *format != requested_format ) { mlt_log_debug( NULL, "[filter audioconvert] %s -> %s %d channels %d samples\n", mlt_audio_format_name( *format ), mlt_audio_format_name( requested_format ), channels, samples ); switch ( *format ) { case mlt_audio_s16: switch ( requested_format ) { case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; int c; for ( c = 0; c < channels; c++ ) { int16_t *q = (int16_t*) *audio + c; int i = samples + 1; while ( --i ) { *p++ = (int32_t) *q << 16; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; int c; for ( c = 0; c < channels; c++ ) { int16_t *q = (int16_t*) *audio + c; int i = samples + 1; while ( --i ) { *p++ = (float)( *q ) / 32768.0; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; int16_t *q = (int16_t*) *audio; int i = samples * channels + 1; while ( --i ) *p++ = (int32_t) *q++ << 16; *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; int16_t *q = (int16_t*) *audio; int i = samples * channels + 1; while ( --i ) { float f = (float)( *q++ ) / 32768.0; *p++ = CLAMP( f, -1.0f, 1.0f ); } *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc( size ); uint8_t *p = buffer; int16_t *q = (int16_t*) *audio; int i = samples * channels + 1; while ( --i ) *p++ = ( *q++ >> 8 ) + 128; *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_s32: switch ( requested_format ) { case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc( size ); int16_t *p = buffer; int32_t *q = (int32_t*) *audio; int s, c; for ( s = 0; s < samples; s++ ) for ( c = 0; c < channels; c++ ) *p++ = *( q + c * samples + s ) >> 16; *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; int32_t *q = (int32_t*) *audio; int i = samples * channels + 1; while ( --i ) *p++ = (float)( *q++ ) / 2147483648.0; *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; int32_t *q = (int32_t*) *audio; int s, c; for ( s = 0; s < samples; s++ ) for ( c = 0; c < channels; c++ ) *p++ = *( q + c * samples + s ); *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; int32_t *q = (int32_t*) *audio; int s, c; for ( s = 0; s < samples; s++ ) for ( c = 0; c < channels; c++ ) { float f = (float)( *( q + c * samples + s ) ) / 2147483648.0; *p++ = CLAMP( f, -1.0f, 1.0f ); } *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc( size ); uint8_t *p = buffer; int32_t *q = (int32_t*) *audio; int s, c; for ( s = 0; s < samples; s++ ) for ( c = 0; c < channels; c++ ) *p++ = ( q[c * samples + s] >> 24 ) + 128; *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_float: switch ( requested_format ) { case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc( size ); int16_t *p = buffer; float *q = (float*) *audio; int s, c; for ( s = 0; s < samples; s++ ) for ( c = 0; c < channels; c++ ) { float f = *( q + c * samples + s ); f = CLAMP( f, -1.0f, 1.0f ); *p++ = 32767 * f; } *audio = buffer; error = 0; break; } case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; float *q = (float*) *audio; int i = samples * channels + 1; while ( --i ) { float f = *q++; f = CLAMP( f, -1.0f, 1.0f ); int64_t pcm = ( f > 0.0f ? 2147483647LL : 2147483648LL ) * f; *p++ = CLAMP( pcm, -2147483648LL, 2147483647LL ); } *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; float *q = (float*) *audio; int s, c; for ( s = 0; s < samples; s++ ) for ( c = 0; c < channels; c++ ) { float f = *( q + c * samples + s ); f = CLAMP( f, -1.0f, 1.0f ); int64_t pcm = ( f > 0.0f ? 2147483647LL : 2147483648LL ) * f; *p++ = CLAMP( pcm, -2147483648LL, 2147483647LL ); } *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; float *q = (float*) *audio; int s, c; for ( s = 0; s < samples; s++ ) for ( c = 0; c < channels; c++ ) *p++ = *( q + c * samples + s ); *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc( size ); uint8_t *p = buffer; float *q = (float*) *audio; int s, c; for ( s = 0; s < samples; s++ ) for ( c = 0; c < channels; c++ ) { float f = *( q + c * samples + s ); f = CLAMP( f, -1.0f, 1.0f ); *p++ = ( 127 * f ) + 128; } *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_s32le: switch ( requested_format ) { case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc( size ); int16_t *p = buffer; int32_t *q = (int32_t*) *audio; int i = samples * channels + 1; while ( --i ) *p++ = *q++ >> 16; *audio = buffer; error = 0; break; } case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; int c; for ( c = 0; c < channels; c++ ) { int32_t *q = (int32_t*) *audio + c; int i = samples + 1; while ( --i ) { *p++ = *q; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; int c; for ( c = 0; c < channels; c++ ) { int32_t *q = (int32_t*) *audio + c; int i = samples + 1; while ( --i ) { *p++ = (float)( *q ) / 2147483648.0; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; int32_t *q = (int32_t*) *audio; int i = samples * channels + 1; while ( --i ) *p++ = (float)( *q++ ) / 2147483648.0; *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc( size ); uint8_t *p = buffer; int32_t *q = (int32_t*) *audio; int i = samples * channels + 1; while ( --i ) *p++ = ( *q++ >> 24 ) + 128; *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_f32le: switch ( requested_format ) { case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc( size ); int16_t *p = buffer; float *q = (float*) *audio; int i = samples * channels + 1; while ( --i ) { float f = *q++; f = CLAMP( f, -1.0f , 1.0f ); *p++ = 32767 * f; } *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; int c; for ( c = 0; c < channels; c++ ) { float *q = (float*) *audio + c; int i = samples + 1; while ( --i ) { *p++ = *q; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; int c; for ( c = 0; c < channels; c++ ) { float *q = (float*) *audio + c; int i = samples + 1; while ( --i ) { float f = *q; f = CLAMP( f, -1.0f , 1.0f ); int64_t pcm = ( f > 0.0f ? 2147483647LL : 2147483648LL ) * f; *p++ = CLAMP( pcm, -2147483648LL, 2147483647LL ); q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; float *q = (float*) *audio; int i = samples * channels + 1; while ( --i ) { float f = *q++; f = CLAMP( f, -1.0f , 1.0f ); int64_t pcm = ( f > 0.0f ? 2147483647LL : 2147483648LL ) * f; *p++ = CLAMP( pcm, -2147483648LL, 2147483647LL ); } *audio = buffer; error = 0; break; } case mlt_audio_u8: { uint8_t *buffer = mlt_pool_alloc( size ); uint8_t *p = buffer; float *q = (float*) *audio; int i = samples * channels + 1; while ( --i ) { float f = *q++; f = CLAMP( f, -1.0f , 1.0f ); *p++ = ( 127 * f ) + 128; } *audio = buffer; error = 0; break; } default: break; } break; case mlt_audio_u8: switch ( requested_format ) { case mlt_audio_s32: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; int c; for ( c = 0; c < channels; c++ ) { uint8_t *q = (uint8_t*) *audio + c; int i = samples + 1; while ( --i ) { *p++ = ( (int32_t) *q - 128 ) << 24; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_float: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; int c; for ( c = 0; c < channels; c++ ) { uint8_t *q = (uint8_t*) *audio + c; int i = samples + 1; while ( --i ) { *p++ = ( (float) *q - 128 ) / 256.0f; q += channels; } } *audio = buffer; error = 0; break; } case mlt_audio_s16: { int16_t *buffer = mlt_pool_alloc( size ); int16_t *p = buffer; uint8_t *q = (uint8_t*) *audio; int i = samples * channels + 1; while ( --i ) *p++ = ( (int16_t) *q++ - 128 ) << 8; *audio = buffer; error = 0; break; } case mlt_audio_s32le: { int32_t *buffer = mlt_pool_alloc( size ); int32_t *p = buffer; uint8_t *q = (uint8_t*) *audio; int i = samples * channels + 1; while ( --i ) *p++ = ( (int32_t) *q++ - 128 ) << 24; *audio = buffer; error = 0; break; } case mlt_audio_f32le: { float *buffer = mlt_pool_alloc( size ); float *p = buffer; uint8_t *q = (uint8_t*) *audio; int i = samples * channels + 1; while ( --i ) { float f = ( (float) *q++ - 128 ) / 256.0f; *p++ = CLAMP( f, -1.0f, 1.0f ); } *audio = buffer; error = 0; break; } default: break; } break; default: break; } } if ( !error ) { mlt_frame_set_audio( frame, *audio, requested_format, size, mlt_pool_release ); *format = requested_format; } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { frame->convert_audio = convert_audio; return frame; } /** Constructor for the filter. */ mlt_filter filter_audioconvert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = calloc( 1, sizeof( struct mlt_filter_s ) ); if ( mlt_filter_init( filter, filter ) == 0 ) filter->process = filter_process; return filter; } mlt-6.20.0/src/modules/core/filter_audiomap.c000066400000000000000000000052451362234133600211030ustar00rootroot00000000000000/* * filter_audiomap.c -- remap audio channels * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #define MAX_CHANNELS 32 static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { char prop_name[32], *prop_val; int i, j, l, m[MAX_CHANNELS]; mlt_filter filter = mlt_frame_pop_audio(frame); // Get the producer's audio int error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); if ( error ) return error; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); /* find samples length */ int len = mlt_audio_format_size( *format, 1, 1 ); /* pcm samples buffer */ uint8_t *pcm = *buffer; /* build matrix */ for ( i = 0; i < MAX_CHANNELS; i++ ) { m[i] = i; snprintf( prop_name, sizeof(prop_name), "%d", i ); if ( ( prop_val = mlt_properties_get( properties, prop_name ) ) ) { j = atoi( prop_val ); if( j >= 0 && j < MAX_CHANNELS ) m[i] = j; } } /* process samples */ for ( i = 0; i < *samples; i++ ) { uint8_t tmp[MAX_CHANNELS * 4]; for ( j = 0; j < MAX_CHANNELS && j < *channels; j++ ) for(l = 0; l < len; l++) tmp[j * len + l] = pcm[m[j] * len + l]; for ( j = 0; j < MAX_CHANNELS && j < *channels; j++ ) for ( l = 0; l < len; l++ ) pcm[j * len + l] = tmp[j * len + l]; pcm += len * (*channels); } return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; } /** Constructor for the filter. */ mlt_filter filter_audiomap_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); if ( filter ) filter->process = filter_process; return filter; } mlt-6.20.0/src/modules/core/filter_audiomap.yml000066400000000000000000000107451362234133600214630ustar00rootroot00000000000000schema_version: 0.2 type: filter identifier: audiomap title: Remap Channels version: 1 copyright: Meltytech, LLC creator: Maksym Veremeyenko license: LGPLv2.1 language: en tags: - Audio description: Copy/swap channels according to given mapping. parameters: - identifier: '0' title: Source of channel 0 type: integer mutable: yes minimum: 0 maximum: 31 default: 0 - identifier: '1' title: Source of channel 1 type: integer mutable: yes minimum: 0 maximum: 31 default: 1 - identifier: '2' title: Source of channel 2 type: integer mutable: yes minimum: 0 maximum: 31 default: 2 - identifier: '3' title: Source of channel 3 type: integer mutable: yes minimum: 0 maximum: 31 default: 3 - identifier: '4' title: Source of channel 4 type: integer mutable: yes minimum: 0 maximum: 31 default: 4 - identifier: '5' title: Source of channel 5 type: integer mutable: yes minimum: 0 maximum: 31 default: 5 - identifier: '6' title: Source of channel 6 type: integer mutable: yes minimum: 0 maximum: 31 default: 6 - identifier: '7' title: Source of channel 7 type: integer mutable: yes minimum: 0 maximum: 31 default: 7 - identifier: '8' title: Source of channel 8 type: integer mutable: yes minimum: 0 maximum: 31 default: 8 - identifier: '9' title: Source of channel 9 type: integer mutable: yes minimum: 0 maximum: 31 default: 9 - identifier: '10' title: Source of channel 10 type: integer mutable: yes minimum: 0 maximum: 31 default: 10 - identifier: '11' title: Source of channel 11 type: integer mutable: yes minimum: 0 maximum: 31 default: 11 - identifier: '12' title: Source of channel 12 type: integer mutable: yes minimum: 0 maximum: 31 default: 12 - identifier: '13' title: Source of channel 13 type: integer mutable: yes minimum: 0 maximum: 31 default: 13 - identifier: '14' title: Source of channel 14 type: integer mutable: yes minimum: 0 maximum: 31 default: 14 - identifier: '15' title: Source of channel 15 type: integer mutable: yes minimum: 0 maximum: 31 default: 15 - identifier: '16' title: Source of channel 16 type: integer mutable: yes minimum: 0 maximum: 31 default: 16 - identifier: '17' title: Source of channel 17 type: integer mutable: yes minimum: 0 maximum: 31 default: 17 - identifier: '18' title: Source of channel 18 type: integer mutable: yes minimum: 0 maximum: 31 default: 18 - identifier: '19' title: Source of channel 19 type: integer mutable: yes minimum: 0 maximum: 31 default: 19 - identifier: '20' title: Source of channel 20 type: integer mutable: yes minimum: 0 maximum: 31 default: 20 - identifier: '21' title: Source of channel 21 type: integer mutable: yes minimum: 0 maximum: 31 default: 21 - identifier: '22' title: Source of channel 22 type: integer mutable: yes minimum: 0 maximum: 31 default: 22 - identifier: '23' title: Source of channel 23 type: integer mutable: yes minimum: 0 maximum: 31 default: 23 - identifier: '24' title: Source of channel 24 type: integer mutable: yes minimum: 0 maximum: 31 default: 24 - identifier: '25' title: Source of channel 25 type: integer mutable: yes minimum: 0 maximum: 31 default: 25 - identifier: '26' title: Source of channel 26 type: integer mutable: yes minimum: 0 maximum: 31 default: 26 - identifier: '27' title: Source of channel 27 type: integer mutable: yes minimum: 0 maximum: 31 default: 27 - identifier: '28' title: Source of channel 28 type: integer mutable: yes minimum: 0 maximum: 31 default: 28 - identifier: '29' title: Source of channel 29 type: integer mutable: yes minimum: 0 maximum: 31 default: 29 - identifier: '30' title: Source of channel 30 type: integer mutable: yes minimum: 0 maximum: 31 default: 30 - identifier: '31' title: Source of channel 31 type: integer mutable: yes minimum: 0 maximum: 31 default: 31 mlt-6.20.0/src/modules/core/filter_audiowave.c000066400000000000000000000036501362234133600212660ustar00rootroot00000000000000/* * filter_audiowave.c -- display audio waveform * Copyright (C) 2010-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #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 ) { int size = *width * *height * 2; *format = mlt_image_yuv422; *image = mlt_pool_alloc( size ); mlt_frame_set_image( frame, *image, size, mlt_pool_release ); uint8_t *wave = mlt_frame_get_waveform( frame, *width, *height ); if ( wave ) { uint8_t *p = *image; uint8_t *q = *image + *width * *height * 2; uint8_t *s = wave; while ( p != q ) { *p ++ = *s ++; *p ++ = 128; } } return ( wave == NULL ); } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ mlt_filter filter_audiowave_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if ( filter != NULL ) filter->process = filter_process; return filter; } mlt-6.20.0/src/modules/core/filter_audiowave.yml000066400000000000000000000007341362234133600216450ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: audiowave title: Audio Waveform version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en description: Generate audio waveforms. tags: - Video bugs: - > This does not work alone on audio-only clips. It must have video to overwrite. A workaround is to apply this to a multitrack with a color generator. - The quality of the waveforms is not so good especially for high definition video. mlt-6.20.0/src/modules/core/filter_brightness.c000066400000000000000000000100131362234133600214410ustar00rootroot00000000000000/* * filter_brightness.c -- brightness, fade, and opacity filter * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); double level = 1.0; // Use animated "level" property only if it has been set since init char* level_property = mlt_properties_get( properties, "level" ); if ( level_property != NULL ) { level = mlt_properties_anim_get_double( properties, "level", position, length ); } else { // Get level using old "start,"end" mechanics // Get the starting brightness level level = fabs( mlt_properties_get_double( properties, "start" ) ); // If there is an end adjust gain to the range if ( mlt_properties_get( properties, "end" ) != NULL ) { // Determine the time position of this frame in the transition duration double end = fabs( mlt_properties_get_double( properties, "end" ) ); level += ( end - level ) * mlt_filter_get_progress( filter, frame ); } } // Do not cause an image conversion unless there is real work to do. if ( level != 1.0 ) *format = mlt_image_yuv422; // Get the image int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error. if ( error == 0 ) { // Only process if level is something other than 1 if ( level != 1.0 && *format == mlt_image_yuv422 ) { 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; } } // Process the alpha channel if requested. if ( mlt_properties_get( properties, "alpha" ) ) { double alpha = mlt_properties_anim_get_double( properties, "alpha", position, length ); alpha = alpha >= 0.0 ? alpha : level; if ( alpha != 1.0 ) { int32_t m = alpha * ( 1 << 16 ); int i = *width * *height + 1; if ( *format == mlt_image_rgb24a ) { uint8_t *p = *image + 3; for ( ; --i; p += 4 ) p[0] = ( p[0] * m ) >> 16; } else { uint8_t *p = mlt_frame_get_alpha_mask( frame ); for ( ; --i; ++p ) p[0] = ( p[0] * m ) >> 16; } } } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "start", arg == NULL ? "1" : arg ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "level", NULL ); } return filter; } mlt-6.20.0/src/modules/core/filter_brightness.yml000066400000000000000000000020571362234133600220310ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: brightness title: Brightness version: 3 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en description: Adjust the brightness and opacity of the image. tags: - Video parameters: - identifier: argument title: Start level type: float minimum: 0.0 maximum: 15.0 default: 1.0 - identifier: start title: Start level type: float minimum: 0.0 maximum: 15.0 default: 1.0 - identifier: end title: End level type: float minimum: 0.0 maximum: 15.0 default: 1.0 - identifier: level title: Level type: float minimum: 0.0 maximum: 15.0 - identifier: alpha title: Alpha factor description: > When this is less than zero, the alpha factor follows the level property. Otherwise, you can set this to another value to adjust the alpha component independently. No alpha channel adjustment occurs if this is not set or it equals 1. type: float minimum: -1 maximum: 1 mutable: yes mlt-6.20.0/src/modules/core/filter_channelcopy.c000066400000000000000000000110161362234133600216000ustar00rootroot00000000000000/* * filter_channelcopy.c -- copy one audio channel to another * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /** Get the audio. */ static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Get the filter service mlt_filter filter = mlt_frame_pop_audio( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); int from = mlt_properties_get_int( properties, "from" ); int to = mlt_properties_get_int( properties, "to" ); int swap = mlt_properties_get_int( properties, "swap" ); // Get the producer's audio mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); // Copy channels as necessary if ( from != to) switch ( *format ) { case mlt_audio_u8: { uint8_t *f = (uint8_t*) *buffer + from; uint8_t *t = (uint8_t*) *buffer + to; uint8_t x; int i; if ( swap ) for ( i = 0; i < *samples; i++, f += *channels, t += *channels ) { x = *t; *t = *f; *f = x; } else for ( i = 0; i < *samples; i++, f += *channels, t += *channels ) *t = *f; break; } case mlt_audio_s16: { int16_t *f = (int16_t*) *buffer + from; int16_t *t = (int16_t*) *buffer + to; int16_t x; int i; if ( swap ) for ( i = 0; i < *samples; i++, f += *channels, t += *channels ) { x = *t; *t = *f; *f = x; } else for ( i = 0; i < *samples; i++, f += *channels, t += *channels ) *t = *f; break; } case mlt_audio_s32: { int32_t *f = (int32_t*) *buffer + from * *samples; int32_t *t = (int32_t*) *buffer + to * *samples; if ( swap ) { int32_t *x = malloc( *samples * sizeof(int32_t) ); memcpy( x, t, *samples * sizeof(int32_t) ); memcpy( t, f, *samples * sizeof(int32_t) ); memcpy( f, x, *samples * sizeof(int32_t) ); free( x ); } else { memcpy( t, f, *samples * sizeof(int32_t) ); } break; } case mlt_audio_s32le: case mlt_audio_f32le: { int32_t *f = (int32_t*) *buffer + from; int32_t *t = (int32_t*) *buffer + to; int32_t x; int i; if ( swap ) for ( i = 0; i < *samples; i++, f += *channels, t += *channels ) { x = *t; *t = *f; *f = x; } else for ( i = 0; i < *samples; i++, f += *channels, t += *channels ) *t = *f; break; } case mlt_audio_float: { float *f = (float*) *buffer + from * *samples; float *t = (float*) *buffer + to * *samples; if ( swap ) { float *x = malloc( *samples * sizeof(float) ); memcpy( x, t, *samples * sizeof(float) ); memcpy( t, f, *samples * sizeof(float) ); memcpy( f, x, *samples * sizeof(float) ); free( x ); } else { memcpy( t, f, *samples * sizeof(float) ); } break; } default: mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid audio format\n" ); break; } return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Override the get_audio method mlt_frame_push_audio( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; if ( arg != NULL ) mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "to", atoi( arg ) ); else mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "to", 1 ); if ( strcmp(id, "channelswap") == 0 ) mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "swap", 1 ); } return filter; } mlt-6.20.0/src/modules/core/filter_channelcopy.yml000066400000000000000000000012271362234133600221620ustar00rootroot00000000000000schema_version: 0.2 type: filter identifier: channelcopy title: Copy Channels version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: Copy one audio channel to another. parameters: - identifier: to title: To type: integer argument: true minimum: 0 maximum: 15 default: 1 - identifier: from title: From type: integer minimum: 0 maximum: 15 default: 0 - identifier: swap title: Swap description: Swap the two channels instead of duplicating the source channel. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/core/filter_crop.c000066400000000000000000000172631362234133600202520ustar00rootroot00000000000000/* * filter_crop.c -- cropping filter * Copyright (C) 2009-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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 src_stride = ( width ) * bpp; int dest_stride = ( width - left - right ) * bpp; int y = height - top - bottom + 1; src += top * src_stride + left * bpp; while ( --y ) { memcpy( dest, src, dest_stride ); dest += dest_stride; src += src_stride; } } /** Do it :-). */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_profile profile = mlt_frame_pop_service( frame ); // Get the properties from the frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Correct Width/height if necessary if ( *width == 0 || *height == 0 ) { *width = profile->width; *height = profile->height; } 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" ); // Request the image at its original resolution if ( left || right || top || bottom ) { mlt_properties_set_int( properties, "rescale_width", mlt_properties_get_int( properties, "crop.original_width" ) ); mlt_properties_set_int( properties, "rescale_height", mlt_properties_get_int( properties, "crop.original_height" ) ); } // Now get the image error = mlt_frame_get_image( frame, image, format, width, height, writable ); int owidth = *width - left - right; int oheight = *height - top - bottom; owidth = owidth < 0 ? 0 : owidth; oheight = oheight < 0 ? 0 : oheight; if ( ( owidth != *width || oheight != *height ) && error == 0 && *image != NULL && owidth > 0 && oheight > 0 ) { int bpp; // Subsampled YUV is messy and less precise. if ( *format == mlt_image_yuv422 && frame->convert_image && ( left & 1 ) ) { mlt_image_format requested_format = mlt_image_rgb24; frame->convert_image( frame, image, format, requested_format ); } mlt_log_debug( NULL, "[filter crop] %s %dx%d -> %dx%d\n", mlt_image_format_name(*format), *width, *height, owidth, oheight); if ( top % 2 ) mlt_properties_set_int( properties, "top_field_first", !mlt_properties_get_int( properties, "top_field_first" ) ); // Create the output image int size = mlt_image_format_size( *format, owidth, oheight, &bpp ); uint8_t *output = mlt_pool_alloc( size ); if ( output ) { // Call the generic resize crop( *image, output, bpp, *width, *height, left, right, top, bottom ); // Now update the frame mlt_frame_set_image( frame, output, size, mlt_pool_release ); *image = output; } // We should resize the alpha too uint8_t *alpha = mlt_frame_get_alpha( frame ); int alpha_size = 0; mlt_properties_get_data( properties, "alpha", &alpha_size ); if ( alpha && alpha_size >= ( *width * *height ) ) { uint8_t *newalpha = mlt_pool_alloc( owidth * oheight ); if ( newalpha ) { crop( alpha, newalpha, 1, *width, *height, left, right, top, bottom ); mlt_frame_set_alpha( frame, newalpha, owidth * oheight, mlt_pool_release ); } } *width = owidth; *height = oheight; } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "active" ) ) { // Push the get_image method on to the stack mlt_frame_push_service( frame, mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ); mlt_frame_push_get_image( frame, filter_get_image ); } else { mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); 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, "meta.media.width" ); int height = mlt_properties_get_int( frame_props, "meta.media.height" ); int use_profile = mlt_properties_get_int( filter_props, "use_profile" ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); if ( use_profile ) { top = top * height / profile->height; bottom = bottom * height / profile->height; left = left * width / profile->width; right = right * width / profile->width; } if ( mlt_properties_get_int( filter_props, "center" ) ) { double aspect_ratio = mlt_frame_get_aspect_ratio( frame ); if ( aspect_ratio == 0.0 ) aspect_ratio = mlt_profile_sar( profile ); double input_ar = aspect_ratio * width / height; double output_ar = mlt_profile_dar( mlt_service_profile( MLT_FILTER_SERVICE(filter) ) ); int bias = mlt_properties_get_int( filter_props, "center_bias" ); if ( input_ar > output_ar ) { left = right = ( width - rint( output_ar * height / aspect_ratio ) ) / 2; if ( abs(bias) > left ) bias = bias < 0 ? -left : left; else if ( use_profile ) bias = bias * width / profile->width; left -= bias; right += bias; } else { top = bottom = ( height - rint( aspect_ratio * width / output_ar ) ) / 2; if ( abs(bias) > top ) bias = bias < 0 ? -top : top; else if ( use_profile ) bias = bias * height / profile->height; top -= bias; bottom += bias; } } // Coerce the output to an even width because subsampled YUV with odd widths is too // risky for downstream processing to handle correctly. left += ( width - left - right ) & 1; if ( width - left - right < 8 ) left = right = 0; if ( height - top - bottom < 8 ) top = bottom = 0; 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, "crop.original_width", width ); mlt_properties_set_int( frame_props, "crop.original_height", height ); mlt_properties_set_int( frame_props, "meta.media.width", width - left - right ); mlt_properties_set_int( frame_props, "meta.media.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 filter = calloc( 1, sizeof( struct mlt_filter_s ) ); if ( mlt_filter_init( filter, filter ) == 0 ) { filter->process = filter_process; if ( arg ) mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "active", atoi( arg ) ); } return filter; } mlt-6.20.0/src/modules/core/filter_crop.yml000066400000000000000000000036371362234133600206310ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: crop title: Crop version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en description: Remove pixels from the edges of the video. tags: - Video notes: > This filter is meant to be included as a normalizing filter attached automatically by the loader so that cropping occurs early in the processing stack and can request the full resolution of the source image. Then, a second instance of the filter may be applied to set the parameters of the crop operation. parameters: - identifier: argument title: Active description: Whether to do the processing (1) or simply set the parameters. type: integer minimum: 0 maximum: 1 default: 0 - identifier: left title: Left type: integer minimum: 0 default: 0 unit: pixels - identifier: right title: Right type: integer minimum: 0 default: 0 unit: pixels - identifier: top title: Top type: integer minimum: 0 default: 0 unit: pixels - identifier: bottom title: Bottom type: integer minimum: 0 default: 0 unit: pixels - identifier: center title: Center crop description: Whether to automatically crop whatever is needed to fill the output frame and prevent padding. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: center_bias title: Center balance description: When center crop is enabled, offset the center point. type: integer minimum: 0 default: 0 unit: pixels - identifier: use_profile title: Use profile resolution description: > This is useful for proxy editing. Normally all crop values are expressed in terms of pixels of the source footage, but this option makes them relative to the profile resolution. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/core/filter_data_feed.c000066400000000000000000000132471362234133600212010ustar00rootroot00000000000000/* * filter_data_feed.c -- data feed filter * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include /** This filter should be used in conjunction 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 filter, mlt_frame frame ) { // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); // 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( filter ); int out = mlt_filter_get_out( filter ); // 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 filter = mlt_filter_new( ); // Initialise it if ( filter != NULL ) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Assign the argument (default to titles) mlt_properties_set( properties, "type", arg == NULL ? "titles" : arg ); // Specify the processing method filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/core/filter_data_show.c000066400000000000000000000257311362234133600212570ustar00rootroot00000000000000/* * filter_data_show.c -- data feed filter * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include /** Handle the profile. */ static mlt_filter obtain_filter( mlt_filter filter, char *type ) { // Result to return mlt_filter result = NULL; // Miscellaneous 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 { strncpy( temp, profile, sizeof( temp ) ); temp[ sizeof( temp ) - 1 ] = '\0'; } // 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, double fps) { if (fps == 0) return strdup("-"); char *res = malloc(12); int seconds = frames / fps; frames = frames % lrint( 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, sizeof( result ) - strlen( result ) - 2 ); strcat( result, "#" ); ct++; } else { strncat( result, keywords, sizeof( result ) - strlen( result ) - 1 ); } } else if ( !strcmp( keywords, "timecode" ) ) { // special case: replace #timecode# with current frame timecode mlt_position frames = mlt_properties_get_position( feed, "position" ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); char *s = mlt_properties_frames_to_time( properties, frames, mlt_time_smpte_df ); if ( s ) strncat( result, s, sizeof( result ) - strlen( result ) - 1 ); } else if ( !strcmp( keywords, "frame" ) ) { // special case: replace #frame# with current frame number int pos = mlt_properties_get_int( feed, "position" ); char s[12]; snprintf( s, sizeof(s) - 1, "%d", pos ); s[sizeof( s ) - 1] = '\0'; strncat( result, s, sizeof( result ) - strlen( result ) - 1 ); } else { // replace keyword with metadata value char *metavalue = metadata_value( MLT_FRAME_PROPERTIES( frame ), keywords ); strncat( result, metavalue ? metavalue : "-", sizeof( result ) - strlen( result ) -1 ); } 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 ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // 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 ); mlt_service_unlock( MLT_FILTER_SERVICE( 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 filter, mlt_frame frame ) { // Push the filter mlt_frame_push_service( frame, filter ); // 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 filter = mlt_filter_new( ); // Initialise it if ( filter != NULL ) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Assign the argument (default to titles) mlt_properties_set( properties, "resource", arg == NULL ? NULL : arg ); // Specify the processing method filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/core/filter_data_show.yml000066400000000000000000000030171362234133600216270ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: data_show title: Template version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Show data based on properties of the producer. notes: | The data show filter uses data provided by the data feed filter. The producer properties must supply: > * The keyword text to be inserted in the form of: meta.attr.[keyword].markup=[text to insert] * The name of the properties to be used from the feed file in the form of: meta.attr.[name]=1 * The text to be displayed in the form of: meta.attr.[name].markup=[some text #keyword#] (Keyword text is enclosed between #s.) e.g. melt file.dv meta.attr.sometext.markup="this is some text" meta.attr.titles=1 meta.attr.titles.markup=#sometext# -filter data_show dynamic=1 > Two special keywords exist * #timecode# shows the frame position as a timecode. * #frame# shows the frame position as an integer. e.g. melt file.dv meta.attr.timecode=1 meta.attr.timecode.markup=#timecode# -attach data_feed:attr_check -attach data_show:custom_file.properties dynamic=1 (where the file "custom_file" contains a filter definition by the name of "timecode") parameters: - identifier: argument title: Feed Properties File type: string required: no readonly: no default: data_fx.properties widget: fileopen - identifier: dynamic title: Dynamic type: integer default: 0 minimum: 0 maximum: 1 widget: checkbox mlt-6.20.0/src/modules/core/filter_fieldorder.c000066400000000000000000000106261362234133600214220ustar00rootroot00000000000000/* * filter_fieldorder.c -- change field dominance * Copyright (C) 2011-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the properties from the frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Get the input image, width and height int error = mlt_frame_get_image( frame, image, format, width, height, writable ); if ( !error && *image ) { int tff = mlt_properties_get_int( properties, "consumer_tff" ); // 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_log_debug( NULL, "TFF in %d out %d\n", mlt_properties_get_int( properties, "top_field_first" ), tff ); if ( mlt_properties_get_int( properties, "meta.swap_fields" ) && mlt_properties_get( properties, "progressive" ) && mlt_properties_get_int( properties, "progressive" ) == 0 ) { // We only work with non-planar formats if ( *format == mlt_image_yuv420p && frame->convert_image ) error = frame->convert_image( frame, image, format, mlt_image_yuv422 ); // Make a new image int bpp; int size = mlt_image_format_size( *format, *width, *height, &bpp ); uint8_t *new_image = mlt_pool_alloc( size ); int stride = *width * bpp; int i = *height + 1; uint8_t *src = *image; // Set the new image mlt_frame_set_image( frame, new_image, size, mlt_pool_release ); *image = new_image; while ( --i ) { memcpy( new_image, src + stride * !(i % 2), stride ); new_image += stride; src += stride * (i % 2) * 2; } } // Correct field order if needed if ( tff != -1 && mlt_properties_get_int( properties, "top_field_first" ) != tff && mlt_properties_get( properties, "progressive" ) && mlt_properties_get_int( properties, "progressive" ) == 0 ) { mlt_log_timings_begin(); // We only work with non-vertical luma/chroma scaled if ( *format == mlt_image_yuv420p ) { *format = mlt_image_yuv422; mlt_frame_get_image( frame, image, format, width, height, writable ); } // Shift the entire image down by one line int p, strides[4], size; uint8_t *new_planes[4], *old_planes[4], *new_image; size = mlt_image_format_size( *format, *width, *height, NULL ); new_image = mlt_pool_alloc( size ); mlt_image_format_planes( *format, *width, *height, new_image, new_planes, strides ); mlt_image_format_planes( *format, *width, *height, *image, old_planes, strides ); for( p = 0; p < 4; p++ ) { if( !new_planes[p] ) continue; memcpy( new_planes[p], old_planes[p], strides[p] ); memcpy( new_planes[p] + strides[p], old_planes[p], strides[p] * ( *height - 1 ) ); } // Set the new image mlt_frame_set_image( frame, new_image, size, mlt_pool_release ); *image = new_image; mlt_log_timings_end( NULL, "shifting_fields" ); } // Set the normalised field order mlt_properties_set_int( properties, "top_field_first", tff ); mlt_properties_set_int( properties, "meta.top_field_first", tff ); } return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_get_image( frame, get_image ); return frame; } mlt_filter filter_fieldorder_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = calloc( 1, sizeof( *filter ) ); if ( mlt_filter_init( filter, NULL ) == 0 ) { filter->process = process; } return filter; } mlt-6.20.0/src/modules/core/filter_fieldorder.yml000066400000000000000000000013711362234133600217760ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: fieldorder title: Field order description: Correct the field order of interlaced video. version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video - Hidden notes: > This filter is automatically invoked by the loader as part of image normalisation. It compares the frame property top_field_first to the consumer property with the same name to determine if correction is needed. It performs field order correction by shifting the image down by one line. If you set the property meta.swap_fields=1 on the producer, then this filter swaps the fields of an interlaced frame in addition to any field order correction by shifting the image. mlt-6.20.0/src/modules/core/filter_gamma.c000066400000000000000000000050041362234133600203570ustar00rootroot00000000000000/* * filter_gamma.c -- gamma filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 ) { // Get the gamma value double gamma = mlt_properties_anim_get_double( properties, "gamma", position, length ); 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 filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "gamma", arg == NULL ? "1" : arg ); } return filter; } mlt-6.20.0/src/modules/core/filter_gamma.yml000066400000000000000000000007751362234133600207500ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: gamma title: Gamma version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Adjust image luma using a non-linear power-law curve. parameters: - identifier: argument title: Gamma type: float description: The exponential factor of the power-law curve default: 1.0 - identifier: gamma title: Gamma type: float description: See "argument" mutable: yes default: 1.0 mlt-6.20.0/src/modules/core/filter_greyscale.c000066400000000000000000000033661362234133600212640ustar00rootroot00000000000000/* * filter_greyscale.c -- greyscale filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #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 ) { *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 ) { 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 filter, 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 filter = mlt_filter_new( ); if ( filter != NULL ) filter->process = filter_process; return filter; } mlt-6.20.0/src/modules/core/filter_greyscale.yml000066400000000000000000000003421362234133600216320ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: greyscale title: Greyscale version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Convert colour image to greyscale mlt-6.20.0/src/modules/core/filter_imageconvert.c000066400000000000000000000216031362234133600217630ustar00rootroot00000000000000/* * filter_imageconvert.c -- colorspace and pixel format converter * Copyright (C) 2009-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /** This macro converts a YUV value to the RGB color space. */ #define RGB2YUV_601_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_601_UNSCALED( y, u, v, r, g, b ) \ r = ((1024 * y + 1404 * ( v - 128 ) ) >> 10 ); \ g = ((1024 * y - 715 * ( v - 128 ) - 345 * ( u - 128 ) ) >> 10 ); \ b = ((1024 * y + 1774 * ( 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; #define SCALED 1 #if SCALED #define RGB2YUV_601 RGB2YUV_601_SCALED #define YUV2RGB_601 YUV2RGB_601_SCALED #else #define RGB2YUV_601 RGB2YUV_601_UNSCALED #define YUV2RGB_601 YUV2RGB_601_UNSCALED #endif static int convert_yuv422_to_rgb24a( uint8_t *yuv, uint8_t *rgba, uint8_t *alpha, int width, int height ) { int ret = 0; int yy, uu, vv; int r,g,b; int total = width * height / 2 + 1; while ( --total ) { yy = yuv[0]; uu = yuv[1]; vv = yuv[3]; YUV2RGB_601( yy, uu, vv, r, g, b ); rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = *alpha++; yy = yuv[2]; YUV2RGB_601( yy, uu, vv, r, g, b ); rgba[4] = r; rgba[5] = g; rgba[6] = b; rgba[7] = *alpha++; yuv += 4; rgba += 8; } return ret; } static int convert_yuv422_to_rgb24( uint8_t *yuv, uint8_t *rgb, uint8_t *alpha, int width, int height ) { int ret = 0; int yy, uu, vv; int r,g,b; int total = width * height / 2 + 1; while ( --total ) { yy = yuv[0]; uu = yuv[1]; vv = yuv[3]; YUV2RGB_601( yy, uu, vv, r, g, b ); rgb[0] = r; rgb[1] = g; rgb[2] = b; yy = yuv[2]; YUV2RGB_601( yy, uu, vv, r, g, b ); rgb[3] = r; rgb[4] = g; rgb[5] = b; yuv += 4; rgb += 6; } return ret; } static int convert_rgb24a_to_yuv422( uint8_t *rgba, uint8_t *yuv, uint8_t *alpha, int width, int height ) { int ret = 0; int stride = width * 4; int y0, y1, u0, u1, v0, v1; int r, g, b; uint8_t *s, *d = yuv; int i, j, n = width / 2 + 1; if ( alpha ) for ( i = 0; i < height; i++ ) { s = rgba + ( stride * i ); j = n; while ( --j ) { r = *s++; g = *s++; b = *s++; *alpha++ = *s++; RGB2YUV_601( r, g, b, y0, u0 , v0 ); r = *s++; g = *s++; b = *s++; *alpha++ = *s++; RGB2YUV_601( 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_601( r, g, b, y0, u0 , v0 ); *d++ = y0; *d++ = u0; } } else for ( i = 0; i < height; i++ ) { s = rgba + ( stride * i ); j = n; while ( --j ) { r = *s++; g = *s++; b = *s++; s++; RGB2YUV_601( r, g, b, y0, u0 , v0 ); r = *s++; g = *s++; b = *s++; s++; RGB2YUV_601( 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_601( r, g, b, y0, u0 , v0 ); *d++ = y0; *d++ = u0; } } return ret; } static int convert_rgb24_to_yuv422( uint8_t *rgb, uint8_t *yuv, uint8_t *alpha, int width, int height ) { int ret = 0; int stride = width * 3; int y0, y1, u0, u1, v0, v1; int r, g, b; uint8_t *s, *d = yuv; int i, j, n = width / 2 + 1; for ( i = 0; i < height; i++ ) { s = rgb + ( stride * i ); j = n; while ( --j ) { r = *s++; g = *s++; b = *s++; RGB2YUV_601( r, g, b, y0, u0 , v0 ); r = *s++; g = *s++; b = *s++; RGB2YUV_601( 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_601( r, g, b, y0, u0 , v0 ); *d++ = y0; *d++ = u0; } } return ret; } static int convert_yuv420p_to_yuv422( uint8_t *yuv420p, uint8_t *yuv, uint8_t *alpha, int width, int height ) { int ret = 0; int i, j; int half = width >> 1; uint8_t *Y = yuv420p; uint8_t *U = Y + width * height; uint8_t *V = U + width * height / 4; uint8_t *d = yuv; for ( i = 0; i < height; i++ ) { uint8_t *u = U + ( i / 2 ) * ( half ); uint8_t *v = V + ( i / 2 ) * ( half ); j = half + 1; while ( --j ) { *d ++ = *Y ++; *d ++ = *u ++; *d ++ = *Y ++; *d ++ = *v ++; } } return ret; } static int convert_rgb24_to_rgb24a( uint8_t *rgb, uint8_t *rgba, uint8_t *alpha, int width, int height ) { uint8_t *s = rgb; uint8_t *d = rgba; int total = width * height + 1; while ( --total ) { *d++ = s[0]; *d++ = s[1]; *d++ = s[2]; *d++ = 0xff; s += 3; } return 0; } static int convert_rgb24a_to_rgb24( uint8_t *rgba, uint8_t *rgb, uint8_t *alpha, int width, int height ) { uint8_t *s = rgba; uint8_t *d = rgb; int total = width * height + 1; while ( --total ) { *d++ = s[0]; *d++ = s[1]; *d++ = s[2]; *alpha++ = s[3]; s += 4; } return 0; } typedef int ( *conversion_function )( uint8_t *yuv, uint8_t *rgba, uint8_t *alpha, int width, int height ); static conversion_function conversion_matrix[ mlt_image_invalid - 1 ][ mlt_image_invalid - 1 ] = { { NULL, convert_rgb24_to_rgb24a, convert_rgb24_to_yuv422, NULL, convert_rgb24_to_rgb24a, NULL }, { convert_rgb24a_to_rgb24, NULL, convert_rgb24a_to_yuv422, NULL, NULL, NULL }, { convert_yuv422_to_rgb24, convert_yuv422_to_rgb24a, NULL, NULL, convert_yuv422_to_rgb24a, NULL }, { NULL, NULL, convert_yuv420p_to_yuv422, NULL, NULL, NULL }, { convert_rgb24a_to_rgb24, NULL, convert_rgb24a_to_yuv422, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL, NULL }, }; static uint8_t bpp_table[4] = { 3, 4, 2, 0 }; static int convert_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, mlt_image_format requested_format ) { int error = 0; mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); if ( *format != requested_format ) { conversion_function converter = conversion_matrix[ *format - 1 ][ requested_format - 1 ]; mlt_log_debug( NULL, "[filter imageconvert] %s -> %s @ %dx%d\n", mlt_image_format_name( *format ), mlt_image_format_name( requested_format ), width, height ); if ( converter ) { int size = width * height * bpp_table[ requested_format - 1 ]; int alpha_size = width * height; uint8_t *image = mlt_pool_alloc( size ); uint8_t *alpha = ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) ? mlt_pool_alloc( width * height ) : NULL; if ( requested_format == mlt_image_rgb24a || requested_format == mlt_image_opengl ) { if ( alpha ) mlt_pool_release( alpha ); alpha = mlt_frame_get_alpha_mask( frame ); mlt_properties_get_data( properties, "alpha", &alpha_size ); } if ( !( error = converter( *buffer, image, alpha, width, height ) ) ) { mlt_frame_set_image( frame, image, size, mlt_pool_release ); if ( alpha && ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ) ) mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release ); *buffer = image; *format = requested_format; } else { mlt_pool_release( image ); if ( alpha ) mlt_pool_release( alpha ); } } else { error = 1; } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { if ( !frame->convert_image ) frame->convert_image = convert_image; return frame; } /** Constructor for the filter. */ mlt_filter filter_imageconvert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = calloc( 1, sizeof( struct mlt_filter_s ) ); if ( mlt_filter_init( filter, filter ) == 0 ) { filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/core/filter_luma.c000066400000000000000000000124471362234133600202440ustar00rootroot00000000000000/* * filter_luma.c -- luma filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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 ) { int error = 0; mlt_filter filter = mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_service_lock( MLT_FILTER_SERVICE( 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" ); int cycle = mlt_properties_get_int( properties, "cycle" ); int duration = mlt_properties_get_int( properties, "duration" ); mlt_position position = mlt_filter_get_position( filter, frame ); out = out? out + 1 : 25; if ( cycle ) out = cycle; if ( duration < 1 || duration > out ) duration = out; *format = mlt_image_yuv422; 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", duration - 1 ); mlt_properties_set_int( luma_properties, "reverse", 1 ); mlt_properties_set_data( properties, "luma", luma, 0, ( mlt_destructor )mlt_transition_close, NULL ); } } mlt_position modulo_pos = MLT_POSITION_MOD(position, out); mlt_log_debug( MLT_FILTER_SERVICE(filter), "pos " MLT_POSITION_FMT " mod period " MLT_POSITION_FMT "\n", position, modulo_pos ); if ( luma != NULL && ( mlt_properties_get( properties, "blur" ) != NULL || ( position >= duration && modulo_pos < duration - 1 ) ) ) { mlt_properties luma_properties = MLT_TRANSITION_PROPERTIES( luma ); mlt_properties_pass( luma_properties, properties, "luma." ); int in = position / out * out + mlt_frame_get_position( frame ) - position; mlt_properties_set_int( luma_properties, "in", in ); mlt_properties_set_int( luma_properties, "out", in + duration - 1 ); mlt_transition_process( luma, frame, b_frame ); } error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // We only need a copy of the last frame in the cycle, but we could miss it // with realtime frame-dropping, so we copy the last several frames of the cycle. if ( error == 0 && modulo_pos > out - duration ) { mlt_properties a_props = MLT_FRAME_PROPERTIES( frame ); 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_log_debug( MLT_FILTER_SERVICE(filter), "copying frame " MLT_POSITION_FMT "\n", modulo_pos ); mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); memcpy( dst, src, size ); mlt_frame_set_image( b_frame, dst, size, mlt_pool_release ); mlt_properties_set_int( b_props, "width", *width ); mlt_properties_set_int( b_props, "height", *height ); mlt_properties_set_int( b_props, "format", *format ); } } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the filter on to the stack mlt_frame_push_service( frame, filter ); // 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 filter = mlt_filter_new( ); if ( filter != NULL ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); filter->process = filter_process; if ( arg != NULL ) mlt_properties_set( properties, "resource", arg ); } return filter; } mlt-6.20.0/src/modules/core/filter_luma.yml000066400000000000000000000020671362234133600206200ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: luma title: Wipe version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Applies a luma transition between the current and next frames. Useful for transitions from a slideshow created using producer pixbuf. parameters: - identifier: argument title: File type: string description: The luma map file to be used for the transition - identifier: cycle title: Period type: integer description: > The duration between iterations of the transition. For best results set this to a multiple of ttl used in pixbuf. mutable: yes default: 25 - identifier: duration title: Duration type: integer description: The length of the transition. mutable: yes default: 25 - identifier: luma.* title: Luma Properties description: > All properties beginning with "luma." are passed to the encapsulated luma transition. For example, luma.out controls the duration of the wipe. mutable: yes mlt-6.20.0/src/modules/core/filter_mask_apply.c000066400000000000000000000107331362234133600214420ustar00rootroot00000000000000/* * filter_mask_apply.c -- composite atop a cloned frame made by mask_start * Copyright (C) 2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include static int dummy_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); *image = mlt_properties_get_data(properties, "image", NULL); *format = mlt_properties_get_int(properties, "format"); *width = mlt_properties_get_int(properties, "width"); *height = mlt_properties_get_int(properties, "height"); return 0; } static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_transition transition = mlt_frame_pop_service(frame); *format = mlt_frame_pop_service_int(frame); int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error) { mlt_properties properties = MLT_FRAME_PROPERTIES(frame); mlt_frame clone = mlt_properties_get_data(properties, "mask frame", NULL); if (clone) { mlt_frame_push_get_image(frame, dummy_get_image); mlt_service_lock(MLT_TRANSITION_SERVICE(transition)); mlt_transition_process(transition, clone, frame); mlt_service_unlock(MLT_TRANSITION_SERVICE(transition)); error = mlt_frame_get_image(clone, image, format, width, height, writable); if (!error) { int size = mlt_image_format_size(*format, *width, *height, NULL); mlt_frame_set_image(frame, *image, size, NULL); } } } return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_transition transition = mlt_properties_get_data(properties, "instance", NULL); char *name = mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "transition"); if (!name || !strcmp("", name)) return frame; // Create the transition if needed. if (!transition || !mlt_properties_get(MLT_FILTER_PROPERTIES(transition), "mlt_service") || strcmp(name, mlt_properties_get(MLT_FILTER_PROPERTIES(transition), "mlt_service"))) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); transition = mlt_factory_transition(profile, name, NULL); mlt_properties_set_data(MLT_FILTER_PROPERTIES(filter), "instance", transition, 0, (mlt_destructor) mlt_transition_close, NULL); } if (transition) { mlt_properties transition_props = MLT_TRANSITION_PROPERTIES(transition); int type = mlt_properties_get_int(transition_props, "_transition_type"); int hide = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "hide"); mlt_properties_pass_list(transition_props, properties, "in out"); mlt_properties_pass(transition_props, properties, "transition." ); // Only if video transition on visible track. if ((type & 1) && !mlt_frame_is_test_card(frame) && !(hide & 1)) { mlt_frame_push_service_int(frame, mlt_image_format_id(mlt_properties_get(properties, "mlt_image_format"))); mlt_frame_push_service(frame, transition); mlt_frame_push_get_image(frame, get_image); } if (type == 0) mlt_properties_debug(transition_props, "unknown transition type", stderr); } else { mlt_properties_debug(properties, "mask_failed to create transition", stderr ); } return frame; } mlt_filter filter_mask_apply_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new( ); if (filter) { mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "transition", arg? arg : "frei0r.composition"); mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "mlt_image_format", "rgb24a"); filter->process = process; } return filter; } mlt-6.20.0/src/modules/core/filter_mask_apply.yml000066400000000000000000000020011362234133600220060ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: mask_apply title: Apply a filter mask version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > This filter works in conjunction with the mask_start filter, which makes a snapshot of the frame. There can be other filters between the two, which are masked by the alpha channel via this filter's compositing transition. parameters: - identifier: transition title: Transition description: The name of a compositing or blending transition type: string argument: yes default: frei0r.composition mutable: yes - identifier: transition.* type: properties description: > Properties to set on the encapsulated transition - identifier: mlt_image_format title: MLT image format type: string description: Set to the same image format that the transition needs. values: mutable: yes default: rgb24a values: - yuv422 - rgb24 - rgb24a mlt-6.20.0/src/modules/core/filter_mask_start.c000066400000000000000000000057231362234133600214550ustar00rootroot00000000000000/* * filter_mask_start.c -- clone a frame before invoking a filter * Copyright (C) 2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int 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); int error = mlt_frame_get_image(frame, image, format, width, height, writable); if (!error) { mlt_frame clone = mlt_frame_clone(frame, 1); clone->convert_audio = frame->convert_audio; clone->convert_image = frame->convert_image; mlt_properties_set_data(properties, "mask frame", clone, 0, (mlt_destructor) mlt_frame_close, NULL); } return error; } static mlt_frame process(mlt_filter filter, mlt_frame frame) { mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_filter instance = mlt_properties_get_data(properties, "instance", NULL); const char* name = mlt_properties_get(properties, "filter"); if (!name || !strcmp("", name)) return frame; // Create the filter if needed. if (!instance || !mlt_properties_get(MLT_FILTER_PROPERTIES(instance), "mlt_service") || strcmp(name, mlt_properties_get(MLT_FILTER_PROPERTIES(instance), "mlt_service"))) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); instance = mlt_factory_filter(profile, name, NULL); mlt_properties_set_data(properties, "instance", instance, 0, (mlt_destructor) mlt_filter_close, NULL); } if (instance) { mlt_properties instance_props = MLT_FILTER_PROPERTIES(instance); mlt_properties_pass_list(instance_props, properties, "in out"); mlt_properties_pass(instance_props, properties, "filter."); mlt_frame_push_get_image(frame, get_image); return mlt_filter_process(instance, frame); } else { mlt_properties_debug(properties, "failed to create filter", stderr); return frame; } } mlt_filter filter_mask_start_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { mlt_filter filter = mlt_filter_new(); if (filter) { mlt_properties_set(MLT_FILTER_PROPERTIES(filter), "filter", arg? arg : "frei0r.alphaspot"); filter->process = process; } return filter; } mlt-6.20.0/src/modules/core/filter_mask_start.yml000066400000000000000000000016561362234133600220350ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: mask_start title: Setup a filter mask version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > This filter works in conjunction with the mask_apply filter to make filter masking easier. This filter makes a snapshop of the frame before applying a filter, typically one to affect the alpha channel defining the mask. Then, the mask_apply filter uses a transition to composite the current frame's image over the snapshot. The typical use case is to add filters in the following sequence: mask_start, zero or more filters, mask_apply. parameters: - identifier: filter title: Filter description: The name of a filter type: string argument: yes default: frei0r.alphaspot mutable: yes - identifier: filter.* type: properties description: > Properties to set on the encapsulated filter mlt-6.20.0/src/modules/core/filter_mirror.c000066400000000000000000000167071362234133600206230ustar00rootroot00000000000000/* * filter_mirror.c -- mirror filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 filter = mlt_frame_pop_service( frame ); // Get the mirror type mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // 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 *format = mlt_image_yuv422; 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 ) { // 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 filter, mlt_frame frame ) { // Push the service on to the stack mlt_frame_push_service( frame, filter ); // 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 filter = mlt_filter_new( ); // If we have a filter, initialise it if ( filter != NULL ) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Set the default mirror type mlt_properties_set_or_default( properties, "mirror", arg, "horizontal" ); // Assign the process method filter->process = filter_process; } // Return the filter return filter; } mlt-6.20.0/src/modules/core/filter_mirror.yml000066400000000000000000000011531362234133600211670ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: mirror title: Mirror version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Provides various mirror and image reversing effects. parameters: - identifier: argument title: File type: string description: Choose the type of mirror operation. values: - horizontal - vertical - diagonal - xdiagonal - flip - flop - identifier: reverse title: Reverse type: integer mutable: no default: 0 minimum: 0 maximum: 1 widget: checkbox mlt-6.20.0/src/modules/core/filter_mono.c000066400000000000000000000116761362234133600202610ustar00rootroot00000000000000/* * filter_mono.c -- mix all channels to a mono signal across n channels * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /** Get the audio. */ static int filter_get_audio( mlt_frame frame, void **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; // Get the producer's audio mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); if ( channels_out == -1 ) channels_out = *channels; size = mlt_audio_format_size( *format, *samples, channels_out ); switch ( *format ) { case mlt_audio_u8: { uint8_t *new_buffer = mlt_pool_alloc( size ); for ( i = 0; i < *samples; i++ ) { uint8_t mixdown = 0; for ( j = 0; j < *channels; j++ ) mixdown += ((uint8_t*) *buffer)[ ( i * *channels ) + j ]; for ( j = 0; j < channels_out; j++ ) new_buffer[ ( i * channels_out ) + j ] = mixdown; } *buffer = new_buffer; break; } case mlt_audio_s16: { int16_t *new_buffer = mlt_pool_alloc( size ); for ( i = 0; i < *samples; i++ ) { int16_t mixdown = 0; for ( j = 0; j < *channels; j++ ) mixdown += ((int16_t*) *buffer)[ ( i * *channels ) + j ]; for ( j = 0; j < channels_out; j++ ) new_buffer[ ( i * channels_out ) + j ] = mixdown; } *buffer = new_buffer; break; } case mlt_audio_s32le: { int32_t *new_buffer = mlt_pool_alloc( size ); for ( i = 0; i < *samples; i++ ) { int32_t mixdown = 0; for ( j = 0; j < *channels; j++ ) mixdown += ((int32_t*) *buffer)[ ( i * *channels ) + j ]; for ( j = 0; j < channels_out; j++ ) new_buffer[ ( i * channels_out ) + j ] = mixdown; } *buffer = new_buffer; break; } case mlt_audio_f32le: { float *new_buffer = mlt_pool_alloc( size ); for ( i = 0; i < *samples; i++ ) { float mixdown = 0; for ( j = 0; j < *channels; j++ ) mixdown += ((float*) *buffer)[ ( i * *channels ) + j ]; for ( j = 0; j < channels_out; j++ ) new_buffer[ ( i * channels_out ) + j ] = mixdown; } *buffer = new_buffer; break; } case mlt_audio_s32: { int32_t *new_buffer = mlt_pool_alloc( size ); for ( i = 0; i < *samples; i++ ) { int32_t mixdown = 0; for ( j = 0; j < *channels; j++ ) mixdown += ((int32_t*) *buffer)[ ( j * *channels ) + i ]; for ( j = 0; j < channels_out; j++ ) new_buffer[ ( j * *samples ) + i ] = mixdown; } *buffer = new_buffer; break; } case mlt_audio_float: { float *new_buffer = mlt_pool_alloc( size ); for ( i = 0; i < *samples; i++ ) { float mixdown = 0; for ( j = 0; j < *channels; j++ ) mixdown += ((float*) *buffer)[ ( j * *channels ) + i ]; for ( j = 0; j < channels_out; j++ ) new_buffer[ ( j * *samples ) + i ] = mixdown; } *buffer = new_buffer; break; } default: mlt_log_error( NULL, "[filter mono] Invalid audio format\n" ); break; } if ( size > *samples * channels_out ) { mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); *channels = channels_out; } return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame ); // Propagate 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; if ( arg != NULL ) mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "channels", atoi( arg ) ); else mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "channels", -1 ); } return filter; } mlt-6.20.0/src/modules/core/filter_mono.yml000066400000000000000000000007461362234133600206340ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: mono title: Mixdown version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: > Mix all channels of audio into a mono signal and output it as N channels. parameters: - identifier: argument title: channels type: integer description: > Set the number of output channels. The default is automatic based on consumer request. required: no minimum: 1 mlt-6.20.0/src/modules/core/filter_obscure.c000066400000000000000000000160201362234133600207370ustar00rootroot00000000000000/* * filter_obscure.c -- obscure filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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 == '%' || ( 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 geometry 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 ( upper > lower && 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 = lerp( in->mask_w + ( out->mask_w - in->mask_w ) * position, 1, -1 ); output->mask_h = lerp( in->mask_h + ( out->mask_h - in->mask_h ) * position, 1, -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 ) { // Pop the top of stack now mlt_filter filter = mlt_frame_pop_service( frame ); // Get the image from the frame *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Get the image from the frame if ( error == 0 ) { if ( filter != NULL ) { // Get the filter properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); // Structures for geometry struct geometry_s result; struct geometry_s start; struct geometry_s end; // Retrieve the position float position = mlt_filter_get_progress( filter, frame ); // Now parse the geometries geometry_parse( &start, NULL, mlt_properties_get( properties, "start" ), profile->width, profile->height ); geometry_parse( &end, &start, mlt_properties_get( properties, "end" ), profile->width, profile->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 filter, mlt_frame frame ) { // Push this on to the service stack mlt_frame_push_service( frame, filter ); // 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 filter = mlt_filter_new( ); if ( filter != NULL ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); filter->process = filter_process; mlt_properties_set( properties, "start", arg != NULL ? arg : "0%/0%:100%x100%" ); mlt_properties_set( properties, "end", "" ); } return filter; } mlt-6.20.0/src/modules/core/filter_obscure.yml000066400000000000000000000011741362234133600213220ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: obscure title: Obscure version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Obscuring filter. parameters: - identifier: argument title: Start type: string description: > The starting rectangle is given in the format X/Y:WxH[:PWxPY] where PWxPY is the size of the averaging region in pixels. - identifier: end title: End type: string description: > The ending rectangle is given in the format X/Y:WxH[:PWxPY] where PWxPY is the size of the averaging region in pixels. mlt-6.20.0/src/modules/core/filter_panner.c000066400000000000000000000244411362234133600205660ustar00rootroot00000000000000/* * filter_panner.c --pan/balance audio channels * Copyright (C) 2010-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include /** Get the audio. */ static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_properties properties = mlt_frame_pop_audio( frame ); mlt_filter filter = mlt_frame_pop_audio( frame ); mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame ); // We can only mix interleaved 32-bit float. *format = mlt_audio_f32le; mlt_frame_get_audio( frame, (void**) buffer, format, frequency, channels, samples ); // Apply silence int silent = mlt_properties_get_int( frame_props, "silent_audio" ); mlt_properties_set_int( frame_props, "silent_audio", 0 ); if ( silent ) memset( *buffer, 0, *samples * *channels * sizeof( float ) ); int src_size = 0; float *src = mlt_properties_get_data( filter_props, "scratch_buffer", &src_size ); float *dest = *buffer; double v; // sample accumulator int i, out, in; double factors[6][6]; // mixing weights [in][out] double mix_start = 0.5, mix_end = 0.5; if ( mlt_properties_get( properties, "previous_mix" ) != NULL ) mix_start = mlt_properties_get_double( properties, "previous_mix" ); if ( mlt_properties_get( properties, "mix" ) != NULL ) mix_end = mlt_properties_get_double( properties, "mix" ); double weight = mix_start; double weight_step = ( mix_end - mix_start ) / *samples; int active_channel = mlt_properties_get_int( properties, "channel" ); int gang = mlt_properties_get_int( properties, "gang" ) ? 2 : 1; // Setup or resize a scratch buffer if ( !src || src_size < *samples * *channels * sizeof(*src) ) { // We allocate 4 more samples than we need to deal with jitter in the sample count per frame. src_size = ( *samples + 4 ) * *channels * sizeof(*src); src = mlt_pool_alloc( src_size ); if ( !src ) return 0; mlt_properties_set_data( filter_props, "scratch_buffer", src, src_size, mlt_pool_release, NULL ); } // We must use a pristine copy as the source memcpy( src, *buffer, *samples * *channels * sizeof(*src) ); // Initialize the mix factors for ( i = 0; i < 6; i++ ) for ( out = 0; out < 6; out++ ) factors[i][out] = 0.0; for ( i = 0; i < *samples; i++ ) { // Recompute the mix factors switch ( active_channel ) { case -1: // Front L/R balance case -2: // Rear L/R balance { // Gang front/rear balance if requested int g, active = active_channel; for ( g = 0; g < gang; g++, active-- ) { int left = active == -1 ? 0 : 2; int right = left + 1; if ( weight < 0.0 ) { factors[left][left] = 1.0; factors[right][right] = weight + 1.0 < 0.0 ? 0.0 : weight + 1.0; } else { factors[left][left] = 1.0 - weight < 0.0 ? 0.0 : 1.0 - weight; factors[right][right] = 1.0; } } break; } case -3: // Left fade case -4: // right fade { // Gang left/right fade if requested int g, active = active_channel; for ( g = 0; g < gang; g++, active-- ) { int front = active == -3 ? 0 : 1; int rear = front + 2; if ( weight < 0.0 ) { factors[front][front] = 1.0; factors[rear][rear] = weight + 1.0 < 0.0 ? 0.0 : weight + 1.0; } else { factors[front][front] = 1.0 - weight < 0.0 ? 0.0 : 1.0 - weight; factors[rear][rear] = 1.0; } } break; } case 0: // left case 2: { int left = active_channel; int right = left + 1; factors[right][right] = 1.0; if ( weight < 0.0 ) // output left toward left { factors[left][left] = 0.5 - weight * 0.5; factors[left][right] = ( 1.0 + weight ) * 0.5; } else // output left toward right { factors[left][left] = ( 1.0 - weight ) * 0.5; factors[left][right] = 0.5 + weight * 0.5; } break; } case 1: // right case 3: { int right = active_channel; int left = right - 1; factors[left][left] = 1.0; if ( weight < 0.0 ) // output right toward left { factors[right][left] = 0.5 - weight * 0.5; factors[right][right] = ( 1.0 + weight ) * 0.5; } else // output right toward right { factors[right][left] = ( 1.0 - weight ) * 0.5; factors[right][right] = 0.5 + weight * 0.5; } break; } } // Do the mixing for ( out = 0; out < *channels && out < 6; out++ ) { v = 0; for ( in = 0; in < *channels && in < 6; in++ ) v += factors[in][out] * src[ i * *channels + in ]; dest[ i * *channels + out ] = v; } weight += weight_step; } return 0; } /** Pan filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame ); mlt_properties instance_props = mlt_properties_new(); // 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 filter duration mlt_properties props = mlt_properties_get_data( frame_props, "_producer", NULL ); int always_active = mlt_properties_get_int( properties, "always_active" ); mlt_position in = !always_active ? mlt_filter_get_in( filter ) : mlt_properties_get_int( props, "in" ); mlt_position out = !always_active ? mlt_filter_get_out( filter ) : mlt_properties_get_int( props, "out" ); int length = mlt_properties_get_int( properties, "length" ); mlt_position time = !always_active ? mlt_frame_get_position( frame ) : mlt_properties_get_int( props, "_frame" ); double mix = ( double )( time - in ) / ( double )( out - in + 1 ); 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; } // Use constant mix level if only start else if ( mlt_properties_get( properties, "start" ) != NULL ) { mix = mlt_properties_get_double( properties, "start" ); } // Use animated property "split" to get mix level if property is set char* split_property = mlt_properties_get( properties, "split" ); if ( split_property ) { mlt_position pos = mlt_filter_get_position( filter, frame ); mlt_position len = mlt_filter_get_length2( filter, frame ); mix = mlt_properties_anim_get_double( properties, "split", pos, len ); } // Convert it from [0, 1] to [-1, 1] mix = mix * 2.0 - 1.0; // Finally, set the mix property on the frame mlt_properties_set_double( instance_props, "mix", mix ); // Initialise filter previous mix value to prevent an inadvertent jump from 0 mlt_position last_position = mlt_properties_get_position( properties, "_last_position" ); mlt_position current_position = mlt_frame_get_position( frame ); mlt_properties_set_position( properties, "_last_position", current_position ); if ( mlt_properties_get( properties, "_previous_mix" ) == NULL || current_position != last_position + 1 ) mlt_properties_set_double( properties, "_previous_mix", mix ); // Tell the frame what the previous mix level was mlt_properties_set_double( instance_props, "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", mix ); } 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 *= ( 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( instance_props, "previous_mix", mix_start ); mlt_properties_set_double( instance_props, "mix", mix_end ); } mlt_properties_set_int( instance_props, "channel", mlt_properties_get_int( properties, "channel" ) ); mlt_properties_set_int( instance_props, "gang", mlt_properties_get_int( properties, "gang" ) ); } char label[64]; snprintf( label, sizeof(label), "panner %s", mlt_properties_get( properties, "_unique_id" ) ); mlt_properties_set_data( frame_props, label, instance_props, 0, (mlt_destructor) mlt_properties_close, NULL ); // Override the get_audio method mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, instance_props ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; } /** Constructor for the filter. */ mlt_filter filter_panner_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = calloc( 1, sizeof( struct mlt_filter_s ) ); if ( filter != NULL && mlt_filter_init( filter, NULL ) == 0 ) { filter->process = filter_process; if ( arg != NULL ) mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "start", atof( arg ) ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "channel", -1 ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "split", NULL ); } return filter; } mlt-6.20.0/src/modules/core/filter_panner.yml000066400000000000000000000040661362234133600211460ustar00rootroot00000000000000schema_version: 0.2 type: filter identifier: panner title: Audio Pan version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: Pan an audio channel, adjust balance, or adjust fade. notes: Only handles up to 6 channels. Needs more work balance for surround and other channel layouts. parameters: - identifier: start title: Start description: > The position of the audio relative to its neighbor channel. For example, when channel is set to 0 for left, then start 0 is full left, 0.5 is center, and 1.0 is full right. If value for property "split" is set value of this property is discarded. type: float argument: true mutable: yes minimum: 0 maximum: 1 - identifier: end title: End description: > The ending value of the audio position. It will be interpolated from start to end over the in-out range. If value for property "split" is set value of this property is discarded. type: float mutable: yes minimum: 0 maximum: 1 - identifier: channel title: Channel type: integer description: > For stereo: 0 for front left, 1 for front right, -1 for front balance. For quad: 2 for back left, 3 for back right, -2 for rear balance, -3 for left fade, -4 for right fade. minimum: -4 maximum: 5 default: -1 - identifier: gang title: Gang description: > Whether to gang together the front and back when using balance or to gang together the left and right when using fade. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: split title: Split description: > The animated position of the audio relative to its neighbor channel. For example, when channel is set to 0 for left, then start 0 is full left, 0.5 is center, and 1.0 is full right. If this value is set, values for properties "start" and "end" are discarded. type: float mutable: yes minimum: 0 maximum: 1 mlt-6.20.0/src/modules/core/filter_region.c000066400000000000000000000067301362234133600205670ustar00rootroot00000000000000/* * filter_region.c -- region filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "transition_region.h" #include #include #include #include #include /** Filter processing. */ 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 properties of the filter mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // 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( filter ) ); 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", filter, 0, NULL, NULL ); } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Pass all properties down mlt_properties_inherit( MLT_TRANSITION_PROPERTIES( transition ), properties ); // Make the frame's position relative to this filter's in point mlt_frame_set_position( frame, mlt_filter_get_position( filter, frame ) ); // Process the frame mlt_transition_process( transition, frame, NULL ); int result = mlt_frame_get_image( frame, image, format, width, height, writable ); // Restore the frame's position. mlt_frame_set_position( frame, mlt_frame_original_position( frame ) ); return result; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** 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 filter = mlt_filter_new( ); // Further initialisation if ( filter != NULL ) { // Get the properties from the filter mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Assign the filter process method filter->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 filter; } mlt-6.20.0/src/modules/core/filter_region.yml000066400000000000000000000016041362234133600211410ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: region title: Regionalize version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Apply one or more filters to a region of the video image. The region can be shaped as well using the alpha channel of another producer. bugs: - Circle is unpredictable in the absence of the librsvg pixbuf loader. parameters: - identifier: argument title: File type: string description: > A file whose alpha channel will "shape" the region. The string "circle" is a shortcut but it requires pixbuf with the librsvg loader. The circle is automatically stretched to the region to create an ellipse. - identifier: region.* title: Region description: > Properties may be set on the encapsulated region transition. See "region" transition for details. mlt-6.20.0/src/modules/core/filter_rescale.c000066400000000000000000000215671362234133600207270ustar00rootroot00000000000000/* * filter_rescale.c -- scale the producer video frame size to match the consumer * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include /** virtual function declaration for an image scaler * * image scaler implementations are expected to support the following in and out formats: * yuv422 -> yuv422 * rgb24 -> rgb24 * rgb24a -> rgb24a * rgb24 -> yuv422 * rgb24a -> yuv422 */ typedef int ( *image_scaler )( mlt_frame frame, uint8_t **image, mlt_image_format *format, int iwidth, int iheight, int owidth, int oheight ); static int filter_scale( mlt_frame frame, uint8_t **image, mlt_image_format *format, int iwidth, int iheight, int owidth, int 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 = *image + 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_frame_set_image( frame, output, owidth * ( oheight + 1 ) * 2, mlt_pool_release ); *image = output; return 0; } static void scale_alpha( mlt_frame frame, int iwidth, int iheight, int owidth, int oheight ) { // Scale the alpha uint8_t *output = NULL; uint8_t *input = mlt_frame_get_alpha( frame ); if ( input != NULL ) { uint8_t *out_line, *in_line; register int i, j, x, y; register int ox = ( iwidth << 16 ) / owidth; register int oy = ( iheight << 16 ) / oheight; output = mlt_pool_alloc( owidth * oheight ); out_line = output; // Loop for the entirety of our output height. for ( i = 0, y = (oy >> 1); i < oheight; i++, y += oy ) { in_line = &input[ (y >> 16) * iwidth ]; for ( j = 0, x = (ox >> 1); j < owidth; j++, x += ox ) *out_line ++ = in_line[ x >> 16 ]; } // Set it back on the frame mlt_frame_set_alpha( frame, output, owidth * oheight, mlt_pool_release ); } } /** Do it :-). */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; // Get the frame properties mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Get the filter from the stack mlt_filter filter = mlt_frame_pop_service( frame ); // 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 ) { mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); *width = profile->width; *height = profile->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" ); if ( mlt_properties_get( filter_properties, "factor" ) ) { double factor = mlt_properties_get_double( filter_properties, "factor" ); owidth *= factor; oheight *= factor; } // Default from the scaler if not specified on the frame if ( interps == NULL ) { interps = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "interpolation" ); mlt_properties_set( properties, "rescale.interp", interps ); } // If meta.media.width/height exist, we want that as minimum information if ( mlt_properties_get_int( properties, "meta.media.width" ) ) { iwidth = mlt_properties_get_int( properties, "meta.media.width" ); iheight = mlt_properties_get_int( properties, "meta.media.height" ); } // Let the producer know what we are actually requested to obtain if ( 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 ); // Convert the image to yuv422 when using the local scaler if ( scaler_method == filter_scale ) *format = mlt_image_yuv422; // Get the image as requested mlt_frame_get_image( frame, 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 && strcmp( interps, "none" ) && ( iwidth != owidth || iheight != oheight ) ) { mlt_log_debug( MLT_FILTER_SERVICE( filter ), "%dx%d -> %dx%d (%s) %s\n", iwidth, iheight, owidth, oheight, mlt_image_format_name( *format ), interps ); // If valid colorspace if ( *format == mlt_image_yuv422 || *format == mlt_image_rgb24 || *format == mlt_image_rgb24a || *format == mlt_image_opengl ) { // Call the virtual function scaler_method( frame, image, format, iwidth, iheight, owidth, oheight ); *width = owidth; *height = oheight; } // 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 ) && alpha_size != ( owidth * ( oheight + 1 ) ) ) scale_alpha( frame, iwidth, iheight, owidth, oheight ); } else { *width = iwidth; *height = iheight; } } else { error = 1; } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the filter mlt_frame_push_service( frame, filter ); // 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 filter = mlt_filter_new( ); // If successful, then initialise it if ( filter != NULL ) { // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Set the process method filter->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 filter; } mlt-6.20.0/src/modules/core/filter_rescale.yml000066400000000000000000000016601362234133600212760ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: rescale title: Rescale version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video - Hidden description: > Scale the producer video frame size to match the consumer. This filter is designed for use as a normaliser for the loader producer. notes: > 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. bugs: - > It only implements a nearest neighbour scaling - it is used as the base class for the gtkrescale and mcrescale filters. mlt-6.20.0/src/modules/core/filter_resize.c000066400000000000000000000230311362234133600205760ustar00rootroot00000000000000/* * filter_resize.c -- resizing filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include static uint8_t *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; } static void resize_image( uint8_t *output, int owidth, int oheight, uint8_t *input, int iwidth, int iheight, int bpp, mlt_image_format format, uint8_t alpha_value ) { // Calculate strides int istride = iwidth * bpp; int ostride = owidth * bpp; int offset_x = ( owidth - iwidth ) / 2 * bpp; 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 || iheight <= 6 ) ) { return; } else if ( iwidth == owidth && iheight == oheight ) { memcpy( output, input, iheight * istride ); return; } if ( format == mlt_image_rgb24a ) { while ( size -- ) { *p ++ = 0; *p ++ = 0; *p ++ = 0; *p ++ = alpha_value; } } else if ( bpp == 2 ) { while( size -- ) { *p ++ = 16; *p ++ = 128; } offset_x -= offset_x % 4; } else { size *= bpp; while ( size-- ) *p ++ = 0; } 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, istride ); // Move to next input line in_line += istride; // Move to next output line out_line += ostride; } } /** A padding function for frames - this does not rescale, but simply resizes. */ static uint8_t *frame_resize_image( mlt_frame frame, int owidth, int oheight, mlt_image_format format ) { // Get properties mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Get the input image, width and height uint8_t *input = mlt_properties_get_data( properties, "image", NULL ); uint8_t *alpha = mlt_frame_get_alpha( frame ); int alpha_size = 0; mlt_properties_get_data( properties, "alpha", &alpha_size ); int bpp = 0; mlt_image_format_size( format, owidth, oheight, &bpp ); 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 ) * bpp ); // Call the generic resize resize_image( output, owidth, oheight, input, iwidth, iheight, bpp, format, alpha_value ); // Now update the frame mlt_frame_set_image( frame, output, owidth * ( oheight + 1 ) * bpp, mlt_pool_release ); // We should resize the alpha too if ( format != mlt_image_rgb24a && alpha && alpha_size >= iwidth * iheight ) { alpha = resize_alpha( alpha, owidth, oheight, iwidth, iheight, alpha_value ); if ( alpha ) mlt_frame_set_alpha( frame, alpha, owidth * oheight, mlt_pool_release ); } // Return the output return output; } // No change, return input return input; } /** Do it :-). */ static int filter_get_image( mlt_frame frame, 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( frame ); // Pop the top of stack now mlt_filter filter = mlt_frame_pop_service( frame ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); // Retrieve the aspect ratio double aspect_ratio = mlt_deque_pop_back_double( MLT_FRAME_IMAGE_STACK( frame ) ); double consumer_aspect = mlt_profile_sar( mlt_service_profile( MLT_FILTER_SERVICE( filter ) ) ); // Correct Width/height if necessary if ( *width == 0 || *height == 0 ) { *width = profile->width; *height = profile->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 = consumer_aspect; // Reset the aspect ratio mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio ); // XXX: This is a hack, but it forces the force_full_luma to apply by doing a RGB // conversion because range scaling only occurs on YUV->RGB. And we do it here, // after the deinterlace filter, which only operates in YUV to avoid a YUV->RGB->YUV->?. // Instead, it will go YUV->RGB->?. if ( mlt_properties_get_int( properties, "force_full_luma" ) ) *format = mlt_image_rgb24a; // Hmmm... char *rescale = mlt_properties_get( properties, "rescale.interp" ); if ( rescale != NULL && !strcmp( rescale, "none" ) ) return mlt_frame_get_image( frame, image, format, width, height, writable ); if ( mlt_properties_get_int( properties, "distort" ) == 0 ) { // Normalise the input and out display aspect int normalised_width = profile->width; int normalised_height = profile->height; int real_width = mlt_properties_get_int( properties, "meta.media.width" ); int real_height = mlt_properties_get_int( properties, "meta.media.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 = consumer_aspect * 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( frame, consumer_aspect ); } 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 ); // If there will be padding, then we need packed image format. if ( *format == mlt_image_yuv420p ) { int iwidth = mlt_properties_get_int( properties, "width" ); int iheight = mlt_properties_get_int( properties, "height" ); if ( iwidth < owidth || iheight < oheight ) *format = mlt_image_yuv422; } // Now get the image if ( *format == mlt_image_yuv422 ) owidth -= owidth % 2; error = mlt_frame_get_image( frame, image, format, &owidth, &oheight, writable ); if ( error == 0 && *image && *format != mlt_image_yuv420p ) { *image = frame_resize_image( frame, *width, *height, *format ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, 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, filter ); // 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 filter = calloc( 1, sizeof( struct mlt_filter_s ) ); if ( mlt_filter_init( filter, filter ) == 0 ) { filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/core/filter_resize.yml000066400000000000000000000010651362234133600211600ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: resize title: Pad version: 2 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video - Hidden description: Pad an image with black to fulfill the requested image size. notes: > 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 is automatically invoked by the loader as part of image normalisation. mlt-6.20.0/src/modules/core/filter_transition.c000066400000000000000000000104751362234133600214770ustar00rootroot00000000000000/* * filter_transition.c -- Convert any transition into a filter * Copyright (C) 2005-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_transition transition = mlt_frame_pop_service( frame ); if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "image_count" ) >= 1 ) mlt_transition_process( transition, frame, frame ); return mlt_frame_get_image( frame, 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 frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Obtain the transition instance mlt_transition transition = mlt_frame_pop_audio( frame ); mlt_transition_process( transition, frame, frame ); return mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Obtain the transition instance mlt_transition transition = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), "instance", NULL ); // If we haven't created the instance, do it now if ( transition == NULL ) { char *name = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "transition" ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); transition = mlt_factory_transition( profile, name, NULL ); mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "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( filter ), "in" ) ); mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "out", mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "out" ) ); // Refresh with current user values mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), MLT_FILTER_PROPERTIES( filter ), "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( filter ), "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 filter = mlt_filter_new( ); if ( filter != NULL ) { mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "transition", arg ); filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/core/filter_transition.yml000066400000000000000000000012301362234133600220430ustar00rootroot00000000000000schema_version: 0.2 type: filter identifier: transition title: Transition as Filter version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video description: > Use a transition as a filter. The filters supplies the same frame as both the A and B clip to the transition. parameters: - identifier: transition argument: yes type: string title: Transition description: The MLT name of a transition. required: yes - identifier: transition.* type: properties description: > Properties may be set on the encapsulated composite transition. e.g.: transition.valign=c mlt-6.20.0/src/modules/core/filter_watermark.c000066400000000000000000000220651362234133600213000ustar00rootroot00000000000000/* * filter_watermark.c -- watermark filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 filter = mlt_frame_pop_service( frame ); // Get the properties of the filter mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // 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( filter ) ); 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( filter ) ); 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." ); } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Process all remaining filters first *format = mlt_image_yuv422; error = mlt_frame_get_image( frame, image, format, width, height, 0 ); // Only continue if we have both producer and composite if ( !error && composite != NULL && producer != NULL ) { // Get the service of the producer mlt_service service = MLT_PRODUCER_SERVICE( producer ); // Create a temporary frame so the original stays in tact. mlt_frame a_frame = mlt_frame_clone( frame, 0 ); // We will get the 'b frame' from the producer mlt_frame b_frame = NULL; // Get the original producer position mlt_position position = mlt_filter_get_position( filter, frame ); // 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( a_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( a_frame ); mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); mlt_profile profile = mlt_service_profile( service ); // 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_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) || mlt_properties_get_int( properties, "deinterlace" ) ); // Check for the special case - no aspect ratio means no problem :-) if ( mlt_frame_get_aspect_ratio( b_frame ) == 0 ) mlt_frame_set_aspect_ratio( b_frame, mlt_profile_sar( profile ) ); if ( mlt_frame_get_aspect_ratio( a_frame ) == 0 ) mlt_frame_set_aspect_ratio( a_frame, mlt_profile_sar( profile ) ); 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 ); } *format = mlt_image_yuv422; 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( filter ), b_frame, 0 ); // Process the frame mlt_transition_process( composite, a_frame, b_frame ); // Get the image error = mlt_frame_get_image( a_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, a_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( filter ), b_frame, 0 ); error = mlt_frame_get_image( b_frame, image, format, width, height, 1 ); alpha = mlt_frame_get_alpha_mask( b_frame ); mlt_frame_set_image( frame, *image, *width * *height * 2, NULL ); mlt_frame_set_alpha( frame, alpha, *width * *height, 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 temporary frames mlt_frame_close( a_frame ); mlt_frame_close( b_frame ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Get the properties of the frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // Assign the frame out point to the filter (just in case we need it later) mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_out", mlt_properties_get_int( properties, "out" ) ); // Push the filter on to the stack mlt_frame_push_service( frame, filter ); // 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 filter = mlt_filter_new( ); if ( filter != NULL ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); filter->process = filter_process; mlt_properties_set( properties, "factory", mlt_environment( "MLT_PRODUCER" ) ); 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 filter; } mlt-6.20.0/src/modules/core/filter_watermark.yml000066400000000000000000000041731362234133600216570ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: watermark title: Overlay version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Overlay text or images onto the video notes: > The watermark filter combines a frame producer and a composite transition to overlay the specified text or image onto the video. Supplying a filename with extension ".txt" causes the loader to load a pango producer. Supplying a file name with an extension supported by gtk-pixbuf causes the loader to load a pixbuf producer. See the pango and pixbuf producers for details. Note: If the filename begins with "+" the pango producer interprets the filename as pango text. Text Example: melt colour:red -filter watermark:"+First Line~Second Line.txt" composite.progressive=1 producer.align=centre composite.valign=c composite.halign=c Image Example: melt clip.dv -filter watermark:logo.png parameters: - identifier: argument title: File/URL type: string description: The file to overlay. required: no readonly: no widget: fileopen - identifier: distort title: Allow distorted scaling type: integer default: 0 minimum: 0 maximum: 1 widget: checkbox - identifier: producer.* title: Producer description: | Properties may be set on the encapsulated producer. e.g.: producer.align=centre See "pango" and "pixbuf" producers for details. readonly: no - identifier: composite.* title: Composite description: | Properties may be set on the encapsulated composite. e.g.: composite.valign=c See "composite" transition for details. readonly: no - identifier: reverse title: Reverse type: integer description: Overlay the video to which the filter is applied atop the supplied file. minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: deinterlace description: Force the supplied file to be be deinterlaced if it is interlaced. type: integer description: minimum: 0 maximum: 1 mutable: yes widget: checkbox mlt-6.20.0/src/modules/core/loader.dict000066400000000000000000000026441362234133600177060ustar00rootroot00000000000000http://*=avformat,webvfx:plain: https://*=webvfx:plain: plain:http://*=webvfx:plain: plain:https://*=webvfx:plain: #include #include #include #include #include #include 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 || !strcmp( colour, "" ) ) ? "0x000000ff" : colour ); mlt_properties_set( properties, "_resource", "" ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); return producer; } free( producer ); return 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 ); // Obtain the producer for this frame mlt_producer producer = mlt_frame_pop_service( frame ); mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); // 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 int size = 0; 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" ); mlt_image_format current_format = mlt_properties_get_int( producer_props, "_format" ); // Parse the colour if ( now && strchr( now, '/' ) ) { now = strdup( strrchr( now, '/' ) + 1 ); mlt_properties_set( producer_props, "resource", now ); free( now ); now = mlt_properties_get( producer_props, "resource" ); } mlt_color color = mlt_properties_get_color( producer_props, "resource" ); if ( mlt_properties_get( producer_props, "mlt_image_format") ) *format = mlt_image_format_id( mlt_properties_get( producer_props, "mlt_image_format") ); // Choose suitable out values if nothing specific requested if ( *format == mlt_image_none || *format == mlt_image_glsl ) *format = mlt_image_rgb24a; if ( *width <= 0 ) *width = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->width; if ( *height <= 0 ) *height = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->height; // Choose default image format if specific request is unsupported if (*format!=mlt_image_yuv420p && *format!=mlt_image_yuv422 && *format!=mlt_image_rgb24 && *format!= mlt_image_glsl && *format!= mlt_image_glsl_texture) *format = mlt_image_rgb24a; // See if we need to regenerate if ( !now || ( then && strcmp( now, then ) ) || *width != current_width || *height != current_height || *format != current_format ) { // Color the image int i = *width * *height + 1; int bpp; // Allocate the image size = mlt_image_format_size( *format, *width, *height, &bpp ); uint8_t *p = 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_int( producer_props, "_format", *format ); mlt_properties_set( producer_props, "_resource", now ); mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); switch ( *format ) { case mlt_image_yuv420p: { int plane_size = *width * *height; uint8_t y, u, v; RGB2YUV_601_SCALED( color.r, color.g, color.b, y, u, v ); memset(p + 0, y, plane_size); memset(p + plane_size, u, plane_size/4); memset(p + plane_size + plane_size/4, v, plane_size/4); mlt_properties_set_int( properties, "colorspace", 601 ); break; } case mlt_image_yuv422: { int uneven = *width % 2; int count = ( *width - uneven ) / 2 + 1; uint8_t y, u, v; RGB2YUV_601_SCALED( color.r, color.g, color.b, y, u, v ); i = *height + 1; while ( --i ) { int j = count; while ( --j ) { *p ++ = y; *p ++ = u; *p ++ = y; *p ++ = v; } if ( uneven ) { *p ++ = y; *p ++ = u; } } mlt_properties_set_int( properties, "colorspace", 601 ); break; } case mlt_image_rgb24: while ( --i ) { *p ++ = color.r; *p ++ = color.g; *p ++ = color.b; } break; case mlt_image_glsl: case mlt_image_glsl_texture: memset(p, 0, size); break; case mlt_image_rgb24a: while ( --i ) { *p ++ = color.r; *p ++ = color.g; *p ++ = color.b; *p ++ = color.a; } break; default: mlt_log_error( MLT_PRODUCER_SERVICE( producer ), "invalid image format %s\n", mlt_image_format_name( *format ) ); } } else { mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); } // Create the alpha channel int alpha_size = 0; uint8_t *alpha = NULL; // Initialise the alpha if (color.a < 255 || *format == mlt_image_rgb24a) { alpha_size = *width * *height; alpha = mlt_pool_alloc( alpha_size ); if ( alpha ) memset( alpha, color.a, alpha_size ); else alpha_size = 0; } // Clone our image if (buffer && image && size > 0) { *buffer = mlt_pool_alloc( size ); memcpy( *buffer, image, size ); } // Now update properties so we free the copy after mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release ); mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_props, "aspect_ratio" ) ); mlt_properties_set_int( properties, "meta.media.width", *width ); mlt_properties_set_int( properties, "meta.media.height", *height ); 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 ); // 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_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); mlt_properties_set_int( properties, "meta.media.width", profile->width ); mlt_properties_set_int( properties, "meta.media.height", profile->height ); // 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_service( *frame, producer ); 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-6.20.0/src/modules/core/producer_colour.yml000066400000000000000000000016761362234133600215300ustar00rootroot00000000000000schema_version: 0.3 type: producer identifier: colour title: Color version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: A simple color generator. parameters: - identifier: resource argument: yes title: Color description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. type: string required: no readonly: no default: black widget: color - identifier: mlt_image_format title: MLT image format type: string description: > Force to generate image in the specified format. The default behavior is to supply whatever was requested by the connected consumer. mutable: yes values: - yuv420p - yuv422 - rgb24 - rgb24a mlt-6.20.0/src/modules/core/producer_consumer.c000066400000000000000000000237401362234133600214750ustar00rootroot00000000000000/* * producer_consumer.c -- produce as a consumer of an encapsulated producer * Copyright (C) 2008-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #define CONSUMER_PROPERTIES_PREFIX "consumer." #define PRODUCER_PROPERTIES_PREFIX "producer." #include #include #include #include struct context_s { mlt_producer self; mlt_producer producer; mlt_consumer consumer; mlt_profile profile; int64_t audio_counter; mlt_position audio_position; }; 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 = mlt_image_format_size( *format, *width, *height, NULL ); uint8_t *new_image = mlt_pool_alloc( size ); // Update the frame mlt_properties properties = mlt_frame_properties( frame ); mlt_frame_set_image( frame, new_image, size, mlt_pool_release ); memcpy( new_image, *image, size ); mlt_properties_set( properties, "progressive", mlt_properties_get( MLT_FRAME_PROPERTIES(nested_frame), "progressive" ) ); *image = new_image; // Copy the alpha channel uint8_t *alpha = mlt_properties_get_data( MLT_FRAME_PROPERTIES( nested_frame ), "alpha", &size ); if ( alpha && size > 0 ) { new_image = mlt_pool_alloc( size ); memcpy( new_image, alpha, size ); mlt_frame_set_alpha( frame, new_image, size, mlt_pool_release ); } return result; } static int get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { context cx = mlt_frame_pop_audio( frame ); mlt_frame nested_frame = mlt_frame_pop_audio( frame ); int result = 0; // if not repeating last frame if ( mlt_frame_get_position( nested_frame ) != cx->audio_position ) { double fps = mlt_profile_fps( cx->profile ); if ( mlt_producer_get_fps( cx->self ) < fps ) { fps = mlt_producer_get_fps( cx->self ); mlt_properties_set_double( MLT_FRAME_PROPERTIES(nested_frame), "producer_consumer_fps", fps ); } *samples = mlt_sample_calculator( fps, *frequency, cx->audio_counter++ ); result = mlt_frame_get_audio( nested_frame, buffer, format, frequency, channels, samples ); int size = mlt_audio_format_size( *format, *samples, *channels ); int16_t *new_buffer = mlt_pool_alloc( size ); mlt_frame_set_audio( frame, new_buffer, *format, size, mlt_pool_release ); memcpy( new_buffer, *buffer, size ); *buffer = new_buffer; cx->audio_position = mlt_frame_get_position( nested_frame ); } else { // otherwise return no samples *samples = 0; *buffer = NULL; } return result; } static void property_changed( mlt_properties owner, mlt_consumer self, char *name ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); context cx = mlt_properties_get_data( properties, "context", NULL ); if ( !cx ) return; if ( name == strstr( name, CONSUMER_PROPERTIES_PREFIX ) ) mlt_properties_set(MLT_CONSUMER_PROPERTIES( cx->consumer ), name + strlen( CONSUMER_PROPERTIES_PREFIX ), mlt_properties_get( properties, name )); if ( name == strstr( name, PRODUCER_PROPERTIES_PREFIX ) ) mlt_properties_set(MLT_PRODUCER_PROPERTIES( cx->producer ), name + strlen( PRODUCER_PROPERTIES_PREFIX ), mlt_properties_get( properties, name )); } static int get_frame( mlt_producer self, mlt_frame_ptr frame, int index ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES(self); context cx = mlt_properties_get_data( properties, "context", NULL ); if ( !cx ) { // Allocate and initialize our context cx = mlt_pool_alloc( sizeof( struct context_s ) ); memset( cx, 0, sizeof( *cx ) ); mlt_properties_set_data( properties, "context", cx, 0, mlt_pool_release, NULL ); cx->self = self; char *profile_name = mlt_properties_get( properties, "profile" ); if ( !profile_name ) profile_name = mlt_properties_get( properties, "mlt_profile" ); mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( self ) ); if ( profile_name ) { cx->profile = mlt_profile_init( profile_name ); cx->profile->is_explicit = 1; } else { cx->profile = mlt_profile_clone( profile ); cx->profile->is_explicit = 0; } // Encapsulate a real producer for the resource cx->producer = mlt_factory_producer( cx->profile, NULL, mlt_properties_get( properties, "resource" ) ); if ( ( profile_name && !strcmp( profile_name, "auto" ) ) || mlt_properties_get_int( properties, "autoprofile" ) ) { mlt_profile_from_producer( cx->profile, cx->producer ); mlt_producer_close( cx->producer ); cx->producer = mlt_factory_producer( cx->profile, NULL, mlt_properties_get( properties, "resource" ) ); } // Since we control the seeking, prevent it from seeking on its own mlt_producer_set_speed( cx->producer, 0 ); cx->audio_position = -1; // 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, deinterlace_method, rescale" ); mlt_properties_pass( MLT_CONSUMER_PROPERTIES( cx->consumer ), properties, CONSUMER_PROPERTIES_PREFIX ); mlt_properties_pass( MLT_PRODUCER_PROPERTIES( cx->producer ), properties, PRODUCER_PROPERTIES_PREFIX ); mlt_events_listen( properties, self, "property-changed", ( mlt_listener )property_changed ); // 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( self ) ); if ( *frame ) { // Seek the producer to the correct place // Calculate our positions double actual_position = (double) mlt_producer_frame( self ); if ( mlt_producer_get_speed( self ) != 0 ) actual_position *= mlt_producer_get_speed( self ); mlt_position need_first = floor( actual_position ); mlt_producer_seek( cx->producer, lrint( need_first * mlt_profile_fps( cx->profile ) / mlt_producer_get_fps( self ) ) ); // 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, cx ); mlt_frame_push_audio( *frame, get_audio ); // Give the returned frame temporal identity mlt_frame_set_position( *frame, mlt_producer_position( self ) ); // Store the nested frame on the produced frame for destruction mlt_properties frame_props = MLT_FRAME_PROPERTIES( *frame ); mlt_properties_set_data( frame_props, "_producer_consumer.frame", nested_frame, 0, (mlt_destructor) mlt_frame_close, NULL ); // Inform the normalizers about our video properties 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, "meta.media.width", cx->profile->width ); mlt_properties_set_int( frame_props, "meta.media.height", cx->profile->height ); mlt_properties_set_int( frame_props, "progressive", cx->profile->progressive ); } // Calculate the next timecode mlt_producer_prepare_next( self ); return 0; } static void producer_close( mlt_producer self ) { context cx = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES( self ), "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 ); mlt_profile_close( cx->profile ); } self->close = NULL; mlt_producer_close( self ); free( self ); } mlt_producer producer_consumer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_producer self = mlt_producer_new( profile ); // Encapsulate the real producer mlt_profile temp_profile = mlt_profile_clone( profile ); temp_profile->is_explicit = 0; mlt_producer real_producer = mlt_factory_producer( temp_profile, NULL, arg ); if ( self && real_producer ) { // Override some producer methods self->close = ( mlt_destructor )producer_close; self->get_frame = get_frame; // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); 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 ( self ) mlt_producer_close( self ); if ( real_producer ) mlt_producer_close( real_producer ); self = NULL; } mlt_profile_close( temp_profile ); return self; } mlt-6.20.0/src/modules/core/producer_consumer.yml000066400000000000000000000022571362234133600220540ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: consumer title: Consumer as Producer version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video parameters: - identifier: argument title: File/URL type: string description: A file name, URL, or producer name. required: yes - identifier: profile title: Profile type: string description: > The name of a MLT profile with which to load the resource. This defaults to the composition's profile, but could be overridden by the profile in MLT XML. Also, the value "auto" triggers profile detection. - identifier: autoprofile title: Auto-profile type: integer description: Generate a new, custom profile from the encapsulated resource. minimum: 0 maximum: 1 default: 0 - identifier: producer.* title: Producer properties description: A property and its value to apply to the encapsulated producer. type: properties mutable: yes - identifier: consumer.* title: Consumer properties description: A property and its value to apply to the encapsulated consumer. type: properties mutable: yes mlt-6.20.0/src/modules/core/producer_hold.c000066400000000000000000000151121362234133600205620ustar00rootroot00000000000000/* * producer_hold.c -- frame holding producer * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include // Forward references static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); static void producer_close( mlt_producer producer ); /** 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 self = mlt_producer_new( profile ); // Construct the requested producer via loader mlt_producer producer = mlt_factory_producer( profile, NULL, arg ); // Initialise the frame holding capabilities if ( self != NULL && producer != NULL ) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( self ); // 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, "out", 25 ); mlt_properties_set( properties, "resource", arg ); mlt_properties_set( properties, "method", "onefield" ); // Override the get_frame method self->get_frame = producer_get_frame; self->close = ( mlt_destructor )producer_close; } else { // Clean up (not sure which one failed, can't be bothered to find out, so close both) if ( self ) mlt_producer_close( self ); if ( producer ) mlt_producer_close( producer ); // Make sure we return NULL self = NULL; } // Return this producer return self; } 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_frame_set_image( frame, *buffer, size, mlt_pool_release ); } else { // Pass the current image as is mlt_frame_set_image( frame, *buffer, size, 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 producer, mlt_frame_ptr frame, int index ) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Construct a new frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); // If we have a frame, then stack the producer itself and the get_image method if ( *frame ) { // 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 uint8_t *image = mlt_properties_get_data( MLT_FRAME_PROPERTIES( real_frame ), "image", NULL ); mlt_frame_set_image( *frame, image, 0, 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( producer ); return 0; } static void producer_close( mlt_producer producer ) { producer->close = NULL; mlt_producer_close( producer ); free( producer ); } mlt-6.20.0/src/modules/core/producer_hold.yml000066400000000000000000000011001362234133600211310ustar00rootroot00000000000000schema_version: 0.2 type: producer identifier: hold title: Still version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: Repeat the same frame for the entirety of the clip. parameters: - identifier: resource argument: yes type: string title: File/URL description: A file name specification, URL, or producer name:argument. required: yes - identifier: frame type: integer title: Frame number description: The frame number of the frame to repeat default: 0 minimum: 0 mlt-6.20.0/src/modules/core/producer_loader.c000066400000000000000000000173461362234133600211150ustar00rootroot00000000000000/* * producer_loader.c -- auto-load producer by file name extension * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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'; // If the service name has a colon as field delimiter, then treat the // second field as a prefix for the file/url. char *prefix = strchr( service, ':' ); if ( prefix ) { *prefix ++ = '\0'; char* prefix_file = calloc( 1, strlen( file ) + strlen( prefix ) + 1 ); strcpy( prefix_file, prefix ); strcat( prefix_file, file ); producer = mlt_factory_producer( profile, service, prefix_file ); free( prefix_file ); } else { 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 // And ignore drive letters on Win32 - no single char services supported. if ( strchr( file, ':' ) > file + 1 ) { 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; // Make backup of profile for determining if we need to use 'consumer' producer. mlt_profile backup_profile = mlt_profile_clone( profile ); // We only need to load the dictionary once if ( dictionary == NULL ) { char temp[ 1024 ]; sprintf( temp, "%s/core/loader.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 ++; } // Chop off the query string p = strrchr( lookup, '?' ); if ( p ) p[0] = '\0'; // Strip file:// prefix p = lookup; if ( strncmp( lookup, "file://", 7 ) == 0 ) p += 7; // 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, p, 0 ) == 0 ) result = create_from( profile, file, mlt_properties_get_value( dictionary, i ) ); } // Check if the producer changed the profile - xml does this. // The consumer producer does not handle frame rate differences. if ( result && backup_profile->is_explicit && ( profile->width != backup_profile->width || profile->height != backup_profile->height || profile->sample_aspect_num != backup_profile->sample_aspect_num || profile->sample_aspect_den != backup_profile->sample_aspect_den || profile->colorspace != backup_profile->colorspace ) ) { // Restore the original profile attributes. profile->display_aspect_den = backup_profile->display_aspect_den; profile->display_aspect_num = backup_profile->display_aspect_num; profile->frame_rate_den = backup_profile->frame_rate_den; profile->frame_rate_num = backup_profile->frame_rate_num; profile->height = backup_profile->height; profile->progressive = backup_profile->progressive; profile->sample_aspect_den = backup_profile->sample_aspect_den; profile->sample_aspect_num = backup_profile->sample_aspect_num; profile->width = backup_profile->width; // Use the 'consumer' producer. mlt_producer_close( result ); result = mlt_factory_producer( profile, "consumer", file ); } mlt_profile_close( backup_profile ); 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 ) { mlt_filter filter; char *id = strdup( effect ); char *arg = strchr( id, ':' ); if ( arg != NULL ) *arg ++ = '\0'; filter = mlt_factory_filter( profile, id, arg ); if ( filter ) { mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 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/core/loader.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_loader_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 xml'd :-) if ( producer && strcmp( id, "abnormal" ) && strncmp( arg, "abnormal:", 9 ) && mlt_properties_get( properties, "xml" ) == NULL && mlt_properties_get( properties, "_xml" ) == NULL && mlt_properties_get( properties, "loader_normalised" ) == NULL ) attach_normalisers( profile, producer ); if ( producer ) { // Always let the image and audio be converted int created = 0; // movit.convert skips setting the frame->convert_image pointer if GLSL cannot be used. create_filter( profile, producer, "movit.convert", &created ); // avcolor_space and imageconvert only set frame->convert_image if it has not been set. create_filter( profile, producer, "avcolor_space", &created ); if ( !created ) create_filter( profile, producer, "imageconvert", &created ); create_filter( profile, producer, "audioconvert", &created ); } // 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; } mlt-6.20.0/src/modules/core/producer_loader.yml000066400000000000000000000014021362234133600214560ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: loader title: Loader version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video - Hidden description: > 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. parameters: - identifier: argument title: File/URL type: string description: The file for the producer to be based on. required: no readonly: no widget: fileopen mlt-6.20.0/src/modules/core/producer_melt.c000066400000000000000000000425131362234133600206020ustar00rootroot00000000000000/* * producer_melt.c -- load from melt command line syntax * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #define MELT_FILE_MAX_LINES (100000) #define MELT_FILE_MAX_LENGTH (2048) mlt_producer producer_melt_init( mlt_profile profile, mlt_service_type type, const char *id, char **argv ); mlt_producer producer_melt_file_init( mlt_profile profile, mlt_service_type type, const char *id, char *file ) { FILE *input = mlt_fopen( file, "r" ); char **args = calloc( sizeof( char * ), MELT_FILE_MAX_LINES ); int count = 0; char temp[ MELT_FILE_MAX_LENGTH ]; if ( input != NULL ) { while( fgets( temp, MELT_FILE_MAX_LENGTH, input ) && count < MELT_FILE_MAX_LINES ) { if ( temp[ strlen( temp ) - 1 ] != '\n' ) mlt_log_warning( NULL, "Exceeded maximum line length (%d) while reading a melt file.\n", MELT_FILE_MAX_LENGTH ); temp[ strlen( temp ) - 1 ] = '\0'; if ( strcmp( temp, "" ) ) args[ count ++ ] = strdup( temp ); } fclose( input ); if ( count == MELT_FILE_MAX_LINES ) mlt_log_warning( NULL, "Reached the maximum number of lines (%d) while reading a melt file.\n" "Consider using MLT XML.\n", MELT_FILE_MAX_LINES ); } mlt_producer result = producer_melt_init( profile, type, id, args ); if ( result != NULL ) { mlt_properties_set( MLT_PRODUCER_PROPERTIES( result ), "resource", file ); mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( result ), "loader_normalised", 1); } 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, NULL, 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 *temp = strdup( id ); char *arg = strchr( temp, ':' ); if ( arg != NULL ) *arg ++ = '\0'; mlt_transition transition = mlt_factory_transition( profile, temp, arg ); if ( transition != NULL ) { mlt_field_plant_transition( field, transition, track, track + 1 ); track_service( field, transition, ( mlt_destructor )mlt_transition_close ); } free( temp ); return transition; } mlt_producer producer_melt_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_new( profile ); 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 ), "_melt_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 ( argv[ i + 1 ] == NULL && ( !strcmp( argv[ i ], "-attach" ) || !strcmp( argv[ i ], "-attach-cut" ) || !strcmp( argv[ i ], "-attach-track" ) || !strcmp( argv[ i ], "-attach-clip" ) || !strcmp( argv[ i ], "-repeat" ) || !strcmp( argv[ i ], "-split" ) || !strcmp( argv[ i ], "-join" ) || !strcmp( argv[ i ], "-mixer" ) || !strcmp( argv[ i ], "-mix" ) || !strcmp( argv[ i ], "-filter" ) || !strcmp( argv[ i ], "-transition" ) || !strcmp( argv[ i ], "-blank" ) ) ) { fprintf( stderr, "Argument missing for %s.\n", argv[ i ] ); break; } 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; if ( strchr( argv[ i + 1 ], ':' ) ) mlt_playlist_blank_time( playlist, argv[ ++ i ] ); else // support for legacy where plain int is an out point instead of length 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 ), "_melt_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_new( profile ); } 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 ], " strchr( argv[ i ], '=' ) ) ) { mlt_properties_parse( properties, argv[ i ] ); } else if ( argv[ i ][ 0 ] != '-' ) { if ( producer != NULL && !mlt_producer_is_cut( producer ) ) mlt_playlist_append( playlist, producer ); if ( title == NULL && strstr( argv[ i ], " strchr( argv[ i ], '=' ) ) ) { i ++; backtrack = 1; } if ( backtrack ) i --; } } // Connect last producer to playlist if ( producer != NULL && !mlt_producer_is_cut( producer ) ) mlt_playlist_append( playlist, producer ); // Track the last playlist too track_service( field, playlist, ( mlt_destructor )mlt_playlist_close ); // We must have a playlist to connect if ( playlist && ( !mlt_properties_get_int( MLT_PLAYLIST_PROPERTIES( playlist ), "_melt_first" ) || mlt_producer_get_playtime( MLT_PLAYLIST_PRODUCER( playlist ) ) > 0 ) ) mlt_multitrack_connect( multitrack, MLT_PLAYLIST_PRODUCER( playlist ), track ); mlt_producer prod = MLT_TRACTOR_PRODUCER( tractor ); mlt_producer_optimise( prod ); mlt_properties props = MLT_TRACTOR_PROPERTIES( tractor ); mlt_properties_set_data( props, "group", group, 0, ( mlt_destructor )mlt_properties_close, NULL ); mlt_properties_set_position( props, "length", mlt_producer_get_out( MLT_MULTITRACK_PRODUCER( multitrack ) ) + 1 ); mlt_producer_set_in_and_out( prod, 0, mlt_producer_get_out( MLT_MULTITRACK_PRODUCER( multitrack ) ) ); if ( title != NULL ) mlt_properties_set( props, "title", strchr( title, '/' ) ? strrchr( title, '/' ) + 1 : title ); // If the last producer has a consumer property connect it tractor if ( producer ) { mlt_consumer consumer = mlt_properties_get_data( MLT_PRODUCER_PROPERTIES(producer), "consumer", NULL ); if ( consumer ) mlt_consumer_connect( consumer, MLT_PRODUCER_SERVICE(prod) ); } return prod; } mlt-6.20.0/src/modules/core/producer_melt.yml000066400000000000000000000003351362234133600211550ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: melt title: Melt description: Process melt command line. version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video mlt-6.20.0/src/modules/core/producer_melt_file.yml000066400000000000000000000007331362234133600221560ustar00rootroot00000000000000schema_version: 0.2 type: producer identifier: melt_file title: Melt description: Process file containing melt command line syntax. version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video notes: Limited to files with 100000 or less lines and maximum line size of 2048 bytes. parameters: - identifier: resource argument: yes title: File description: The name of the melt text file. required: yes mlt-6.20.0/src/modules/core/producer_noise.c000066400000000000000000000127711362234133600207610ustar00rootroot00000000000000/* * producer_noise.c -- noise generating producer * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /** Random number generator */ typedef struct { unsigned int x; unsigned int y; } rand_seed; static void init_seed( rand_seed* seed, int init ) { // Use the initial value to initialize the seed to arbitrary values. // This causes the algorithm to produce consistent results each time for the same frame number. seed->x = 521288629 + init - ( init << 16 ); seed->y = 362436069 - init + ( init << 16 ); } static inline unsigned int fast_rand( rand_seed* seed ) { 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 ) ); } // Forward declarations static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); static void producer_close( mlt_producer producer ); /** 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 producer = mlt_producer_new( profile ); // Initialise the producer if ( producer != NULL ) { // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; } return producer; } static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { // Choose suitable out values if nothing specific requested if ( *width <= 0 ) *width = mlt_service_profile( MLT_PRODUCER_SERVICE( mlt_frame_get_original_producer( frame ) ) )->width; if ( *height <= 0 ) *height = mlt_service_profile( MLT_PRODUCER_SERVICE( mlt_frame_get_original_producer( frame ) ) )->height; // 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_frame_set_image( frame, *buffer, size, mlt_pool_release ); // 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; // Initialize seed from the frame number. rand_seed seed; init_seed( &seed, mlt_frame_get_position( frame ) ); // Generate random noise while ( p != *buffer ) { value = fast_rand( &seed ) & 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 ) { int size = 0; // Correct the returns if necessary *samples = *samples <= 0 ? 1920 : *samples; *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; *format = mlt_audio_s16; // 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; rand_seed seed; init_seed( &seed, mlt_frame_get_position( frame ) ); while ( p != *buffer ) { int16_t val = (int16_t)fast_rand( &seed ); *( -- p ) = val; } } // Set the buffer for destruction mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); 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 ) ); // 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_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); // 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( producer ) ); // 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( producer ); return 0; } static void producer_close( mlt_producer producer ) { producer->close = NULL; mlt_producer_close( producer ); free( producer ); } mlt-6.20.0/src/modules/core/producer_noise.yml000066400000000000000000000003311362234133600213250ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: noise title: Noise version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video description: White noise producer mlt-6.20.0/src/modules/core/producer_timewarp.c000066400000000000000000000321431362234133600214670ustar00rootroot00000000000000/* * producer_timewarp.c -- modify speed and direction of a clip * Copyright (C) 2015-2019 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include // Private Types typedef struct { int first_frame; double speed; int reverse; mlt_producer clip_producer; mlt_profile clip_profile; mlt_properties clip_parameters; mlt_filter pitch_filter; } private_data; // Private Functions static void timewarp_property_changed( mlt_service owner, mlt_producer producer, char *name ) { private_data* pdata = (private_data*)producer->child; if ( mlt_properties_get_int( pdata->clip_parameters, name ) || !strcmp( name, "length" ) || !strcmp( name, "in" ) || !strcmp( name, "out" ) || !strcmp( name, "ignore_points" ) || !strcmp( name, "eof" ) || !strncmp( name, "meta.", 5 ) ) { // Pass parameter changes from this producer to the encapsulated clip // producer. mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES( pdata->clip_producer ); mlt_events_block( clip_properties, producer ); mlt_properties_pass_property( clip_properties, producer_properties, name ); mlt_events_unblock( clip_properties, producer ); } } static void clip_property_changed( mlt_service owner, mlt_producer producer, char *name ) { private_data* pdata = (private_data*)producer->child; if ( mlt_properties_get_int( pdata->clip_parameters, name ) || !strcmp( name, "length" ) || !strcmp( name, "in" ) || !strcmp( name, "out" ) || !strcmp( name, "ignore_points" ) || !strcmp( name, "eof" ) || !strncmp( name, "meta.", 5 ) ) { // The encapsulated clip producer might change its own parameters. // Pass those changes to this producer. mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES( pdata->clip_producer ); mlt_events_block( producer_properties, producer ); mlt_properties_pass_property( producer_properties, clip_properties, name ); mlt_events_unblock( producer_properties, producer ); } } static int producer_get_audio( mlt_frame frame, void** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_producer producer = mlt_frame_pop_audio( frame ); private_data* pdata = (private_data*)producer->child; int error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); // Scale the frequency to account for the speed change. // The resample normalizer will convert it to the requested frequency *frequency = (double)*frequency * fabs(pdata->speed); if( pdata->speed < 0.0 ) { // Reverse the audio in this frame int c = 0; switch ( *format ) { // Interleaved 8bit formats case mlt_audio_u8: { int8_t tmp; for ( c = 0; c < *channels; c++ ) { // Pointer to first sample int8_t* a = (int8_t*)*buffer + c; // Pointer to last sample int8_t* b = (int8_t*)*buffer + ((*samples - 1) * *channels) + c; while( a < b ) { tmp = *a; *a = *b; *b = tmp; a += *channels; b -= *channels; } } break; } // Interleaved 16bit formats case mlt_audio_s16: { int16_t tmp; for ( c = 0; c < *channels; c++ ) { // Pointer to first sample int16_t *a = (int16_t*)*buffer + c; // Pointer to last sample int16_t *b = (int16_t*)*buffer + ((*samples - 1) * *channels) + c; while( a < b ) { tmp = *a; *a = *b; *b = tmp; a += *channels; b -= *channels; } } break; } // Interleaved 32bit formats case mlt_audio_s32le: case mlt_audio_f32le: { int32_t tmp; for ( c = 0; c < *channels; c++ ) { // Pointer to first sample int32_t *a = (int32_t*)*buffer + c; // Pointer to last sample int32_t *b = (int32_t*)*buffer + ((*samples - 1)* *channels) + c; while( a < b ) { tmp = *a; *a = *b; *b = tmp; a += *channels; b -= *channels; } } break; } // Non-Interleaved 32bit formats case mlt_audio_s32: case mlt_audio_float: { int32_t tmp; for ( c = 0; c < *channels; c++ ) { // Pointer to first sample int32_t *a = (int32_t*)*buffer + (c * *samples); // Pointer to last sample int32_t *b = (int32_t*)*buffer + ((c + 1) * *samples) - 1; while( a < b ) { tmp = *a; *a = *b; *b = tmp; a++; b--; } } break; } case mlt_audio_none: break; default: mlt_log_error( MLT_PRODUCER_SERVICE(producer), "Unknown Audio Format %s\n", mlt_audio_format_name( *format ) ); break; } } return error; } static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { private_data* pdata = (private_data*)producer->child; mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); if( pdata->first_frame && pdata->clip_producer ) { // Pass parameters from this producer to the clip producer // Properties that are set after initialization are not always caught by // the property_changed event - so do this once after init. int n = mlt_properties_count( pdata->clip_parameters ); int i = 0; mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES( pdata->clip_producer ); mlt_events_block( clip_properties, producer ); for ( i = 0; i < n; i++ ) { char* name = mlt_properties_get_name( pdata->clip_parameters, i ); if( mlt_properties_get_int( clip_properties, name ) && mlt_properties_get( producer_properties, name ) && strcmp("resource", name) ) { mlt_properties_pass_property( clip_properties, producer_properties, name ); } } mlt_events_unblock( clip_properties, producer ); pdata->first_frame = 0; } if( pdata->clip_producer ) { // Seek the clip producer to the appropriate position mlt_position clip_position = mlt_producer_position( producer ); if( pdata->speed < 0.0 ) { clip_position = mlt_properties_get_int( producer_properties, "out" ) - clip_position; } mlt_producer_seek( pdata->clip_producer, clip_position ); // Get the frame from the clip producer mlt_service_get_frame( MLT_PRODUCER_SERVICE( pdata->clip_producer), frame, index ); // Configure callbacks if ( !mlt_frame_is_test_audio( *frame ) ) { mlt_frame_push_audio( *frame, producer ); mlt_frame_push_audio( *frame, producer_get_audio ); if ( mlt_properties_get_int( producer_properties, "warp_pitch" ) ) { if ( !pdata->pitch_filter ) { pdata->pitch_filter = mlt_factory_filter( mlt_service_profile( MLT_PRODUCER_SERVICE(producer)), "rbpitch", NULL ); } if ( pdata->pitch_filter ) { mlt_properties_set_double( MLT_FILTER_PROPERTIES(pdata->pitch_filter), "pitchscale", 1.0 / fabs(pdata->speed) ); mlt_filter_process( pdata->pitch_filter, *frame ); } } } } else { *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); } // Set the correct position on the frame mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Calculate the next time code mlt_producer_prepare_next( producer ); return 0; } static void producer_close( mlt_producer producer ) { private_data* pdata = (private_data*)producer->child; if ( pdata ) { mlt_producer_close( pdata->clip_producer ); mlt_profile_close( pdata->clip_profile ); mlt_properties_close( pdata->clip_parameters ); mlt_filter_close( pdata->pitch_filter ); free( pdata ); } producer->child = NULL; producer->close = NULL; mlt_producer_close( producer ); free( producer ); } mlt_producer producer_timewarp_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create a new producer object mlt_producer producer = mlt_producer_new( profile ); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); if ( arg && producer && pdata ) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); // Initialize the producer mlt_properties_set( producer_properties, "resource", arg ); producer->child = pdata; producer->get_frame = producer_get_frame; producer->close = (mlt_destructor)producer_close; // Get the resource to be passed to the clip producer char* resource = strchr( arg, ':' ); if ( resource == NULL ) resource = arg; // Apparently speed was not specified. else resource++; // move past the delimiter. // Initialize private data pdata->first_frame = 1; pdata->speed = atof( arg ); if( pdata->speed == 0.0 ) { pdata->speed = 1.0; } pdata->clip_profile = NULL; pdata->clip_parameters = NULL; pdata->clip_producer = NULL; pdata->pitch_filter = NULL; // Create a false profile to be used by the clip producer. pdata->clip_profile = mlt_profile_clone( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ); // Frame rate must be recalculated for the clip profile to change the time base. if( pdata->clip_profile->frame_rate_num < 1000 ) { // Scale the frame rate fraction so we keep more accuracy when // the speed is factored in. pdata->clip_profile->frame_rate_num *= 1000; pdata->clip_profile->frame_rate_den *= 1000; } pdata->clip_profile->frame_rate_num = (double)pdata->clip_profile->frame_rate_num / fabs(pdata->speed); // Create a producer for the clip using the false profile. pdata->clip_producer = mlt_factory_producer( pdata->clip_profile, "abnormal", resource ); if( pdata->clip_producer ) { mlt_properties clip_properties = MLT_PRODUCER_PROPERTIES( pdata->clip_producer ); int n = 0; int i = 0; // Set the speed to 0 since we will control the seeking mlt_producer_set_speed( pdata->clip_producer, 0 ); // Create a list of all parameters used by the clip producer so that // they can be passed between the clip producer and this producer. pdata->clip_parameters = mlt_properties_new(); mlt_repository repository = mlt_factory_repository(); mlt_properties clip_metadata = mlt_repository_metadata( repository, producer_type, mlt_properties_get( clip_properties, "mlt_service" ) ); if ( clip_metadata ) { mlt_properties params = (mlt_properties) mlt_properties_get_data( clip_metadata, "parameters", NULL ); if ( params ) { n = mlt_properties_count( params ); for ( i = 0; i < n; i++ ) { mlt_properties param = (mlt_properties) mlt_properties_get_data( params, mlt_properties_get_name( params, i ), NULL ); char* identifier = mlt_properties_get( param, "identifier" ); if ( identifier ) { // Set the value to 1 to indicate the parameter exists. mlt_properties_set_int( pdata->clip_parameters, identifier, 1 ); } } // Explicitly exclude the "resource" parameter since it needs to be different. mlt_properties_set_int( pdata->clip_parameters, "resource", 0 ); } } // Pass parameters and properties from the clip producer to this producer. // Some properties may have been set during initialization. n = mlt_properties_count( clip_properties ); for ( i = 0; i < n; i++ ) { char* name = mlt_properties_get_name( clip_properties, i ); if ( mlt_properties_get_int( pdata->clip_parameters, name ) || !strcmp( name, "length" ) || !strcmp( name, "in" ) || !strcmp( name, "out" ) || !strncmp( name, "meta.", 5 ) ) { mlt_properties_pass_property( producer_properties, clip_properties, name ); } } // Initialize warp producer properties mlt_properties_set_double( producer_properties, "warp_speed", pdata->speed ); mlt_properties_set( producer_properties, "warp_resource", mlt_properties_get( clip_properties, "resource" ) ); // Monitor property changes from both producers so that the clip // parameters can be passed back and forth. mlt_events_listen( clip_properties, producer, "property-changed", ( mlt_listener )clip_property_changed ); mlt_events_listen( producer_properties, producer, "property-changed", ( mlt_listener )timewarp_property_changed ); } } if ( !producer || !pdata || !pdata->clip_producer ) { if ( pdata ) { mlt_producer_close( pdata->clip_producer ); mlt_profile_close( pdata->clip_profile ); mlt_properties_close( pdata->clip_parameters ); free( pdata ); } if ( producer ) { producer->child = NULL; producer->close = NULL; mlt_producer_close( producer ); free( producer ); producer = NULL; } } return producer; } mlt-6.20.0/src/modules/core/producer_timewarp.yml000066400000000000000000000046421362234133600220510ustar00rootroot00000000000000schema_version: 0.3 type: producer identifier: timewarp title: Time Warp version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Audio - Video description: > Timewarp is a wrapper producer that allows temporal effects on an encapsulated producer. The encapsulated producer can be modified to change the speed (faster/slower) or direction (forward/reverse) In addition to the parameters listed below, this producer inherits all parameters of the encapsulated producer. The encapsulated producer parameters can be accessed directly (without any prefix) by getting and setting those parameters on the timewarp producer. In addition to modifying the speed of the video, the audio is also slowed down or sped up to match the video. parameters: - identifier: resource title: Speed and Resource type: string argument: yes description: | The speed factor and the producer resource in the form: [speed:resource] The speed can be any decimal number between 20 and 0.01. Negative speed values cause the file to be played backwards. The resource can be a file name or any producer service name. The resource will be passed to the loader to create the encapsulated producer. Examples: File opened for 2x speed forward: "2.0:example.mp4" File opened for 2x speed reverse: "-2.0:example.mp4" File opened for 1x speed reverse: "-1.0:example.mp4" File opened for 0.25x speed (slow motion) forward: "0.25:example.mp4" The most common use for this producer is to change the speed of a file. However, any arbitrary producer can be specified. E.g.: "2.0:colour:red" readonly: no required: yes mutable: no - identifier: warp_pitch title: Pitch Compensation type: boolean description: Enable or disable pitch compensation readonly: no mutable: no default: 0 - identifier: warp_speed title: Warp Speed type: float description: > A convenience parameter to access the speed that was passed as part of the argument. readonly: yes - identifier: warp_resource title: Warp Producer type: string description: > A convenience parameter to access the resource that was passed as part of the argument readonly: yes mlt-6.20.0/src/modules/core/producer_tone.c000066400000000000000000000077151362234133600206130ustar00rootroot00000000000000/* * producer_tone.c -- audio tone generating producer * Copyright (C) 2014 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_producer producer = mlt_frame_pop_audio( frame ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); double fps = mlt_producer_get_fps( producer ); mlt_position position = mlt_frame_get_position( frame ); mlt_position length = mlt_producer_get_length( producer ); // Correct the returns if necessary *format = mlt_audio_float; *frequency = *frequency <= 0 ? 48000 : *frequency; *channels = *channels <= 0 ? 2 : *channels; *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, position ) : *samples; // Allocate the buffer int size = *samples * *channels * sizeof( float ); *buffer = mlt_pool_alloc( size ); // Fill the buffer int s = 0; int c = 0; long double first_sample = mlt_sample_calculator_to_now( fps, *frequency, position ); float a = mlt_properties_anim_get_double( producer_properties, "level", position, length ); long double f = mlt_properties_anim_get_double( producer_properties, "frequency", position, length ); long double p = mlt_properties_anim_get_double( producer_properties, "phase", position, length ); p = (M_PI / 180) * p; // Convert from degrees to radians a = pow( 10, a / 20.0 ); // Convert from dB to amplitude for( s = 0; s < *samples; s++ ) { long double t = (first_sample + s) / *frequency; float value = a * sin( 2*M_PI*f*t + p ); for( c = 0; c < *channels; c++ ) { float* sample_ptr = ((float*)*buffer) + (c * *samples) + s; *sample_ptr = value; } } // Set the buffer for destruction mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); 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 ) { // Update time code on the frame mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Configure callbacks mlt_frame_push_audio( *frame, producer ); mlt_frame_push_audio( *frame, producer_get_audio ); } // Calculate the next time code mlt_producer_prepare_next( producer ); return 0; } static void producer_close( mlt_producer this ) { this->close = NULL; mlt_producer_close( this ); free( this ); } mlt_producer producer_tone_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create a new producer object mlt_producer producer = mlt_producer_new( profile ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); // Initialize the producer if ( producer ) { mlt_properties_set_double( producer_properties, "frequency", 1000.0 ); mlt_properties_set_double( producer_properties, "phase", 0.0 ); mlt_properties_set_double( producer_properties, "level", 0.0 ); // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; } return producer; } mlt-6.20.0/src/modules/core/producer_tone.yml000066400000000000000000000014511362234133600211610ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: tone title: Tone version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Audio description: > Generate audio tones. parameters: - identifier: frequency title: Frequency type: float description: > The frequency of the tone. unit: Hz default: 1000.0 readonly: no mutable: yes widget: spinner - identifier: phase title: Phase type: float description: > The phase of the tone. unit: degrees default: 0.0 readonly: no mutable: yes widget: spinner - identifier: level title: Level type: float description: > The peak level of the tone. unit: dB default: 0.0 readonly: no mutable: yes widget: spinner mlt-6.20.0/src/modules/core/transition_composite.c000066400000000000000000001261511362234133600222130ustar00rootroot00000000000000/* * transition_composite.c -- compose one image over another using alpha channel * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "transition_composite.h" #include #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, uint32_t step ); /** 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 self, struct geometry_s *output, double position ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( self ); 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 self, 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( self ); // Create an empty geometries object mlt_geometry geometry = mlt_geometry_init( ); // Get the duration mlt_position length = mlt_transition_get_length( self ); 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 ); mlt_geometry_interpolate( geometry ); } 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 self, mlt_position position ) { // Get the in and out position mlt_position in = mlt_transition_get_in( self ); // Now do the calcs return position - in; } /** Calculate the field delta for this frame - position between two frames. */ 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 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; } static inline int calculate_mix( uint16_t *luma, int j, int softness, int weight, int alpha, uint32_t step ) { return ( ( luma ? smoothstep( luma[ j ], luma[ j ] + softness, step ) : weight ) * ( alpha + 1 ) ) >> 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 */ #if defined(USE_SSE) && defined(ARCH_X86_64) void composite_line_yuv_sse2_simple(uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, int weight); #endif 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, uint32_t step ) { register int j = 0; register int mix; #if defined(USE_SSE) && defined(ARCH_X86_64) if ( !luma && width > 7 ) { composite_line_yuv_sse2_simple(dest, src, width, alpha_b, alpha_a, weight); j = width - width % 8; dest += j * 2; src += j * 2; if ( alpha_a ) alpha_a += j; if ( alpha_b ) alpha_b += j; } #endif for ( ; j < width; j ++ ) { mix = calculate_mix( luma, j, soft, weight, alpha_b? *alpha_b : 255, step ); *dest = sample_mix( *dest, *src++, mix ); dest++; *dest = sample_mix( *dest, *src++, mix ); dest++; if ( alpha_a ) { *alpha_a = ( mix >> 8 ) | *alpha_a; alpha_a ++; } if ( alpha_b ) alpha_b ++; } } 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, uint32_t step ) { register int j; register int mix; for ( j = 0; j < width; j ++ ) { mix = calculate_mix( luma, j, soft, weight, (alpha_b? *alpha_b : 255) | (alpha_a? *alpha_a : 255), step ); *dest = sample_mix( *dest, *src++, mix ); dest++; *dest = sample_mix( *dest, *src++, mix ); dest++; if (alpha_a) *alpha_a ++ = mix >> 8; if (alpha_b) alpha_b++; } } 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, uint32_t step ) { register int j; register int mix; for ( j = 0; j < width; j ++ ) { mix = calculate_mix( luma, j, soft, weight, (alpha_b? *alpha_b : 255) & (alpha_a? *alpha_a : 255), step ); *dest = sample_mix( *dest, *src++, mix ); dest++; *dest = sample_mix( *dest, *src++, mix ); dest++; if (alpha_a) *alpha_a ++ = mix >> 8; if (alpha_b) alpha_b++; } } 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, uint32_t step ) { register int j; register int mix; for ( j = 0; j < width; j ++ ) { mix = calculate_mix( luma, j, soft, weight, (alpha_b? *alpha_b : 255) ^ (alpha_a? *alpha_a : 255), step ); *dest = sample_mix( *dest, *src++, mix ); dest++; *dest = sample_mix( *dest, *src++, mix ); dest++; if (alpha_a) *alpha_a ++ = mix >> 8; if (alpha_b) alpha_b++; } } struct sliced_composite_desc { int height_src; int step; uint8_t* p_dest; uint8_t* p_src; int width_src; uint8_t* alpha_b; uint8_t* alpha_a; int weight; uint16_t* p_luma; int i_softness; uint32_t luma_step; int stride_src; int stride_dest; int alpha_b_stride; int alpha_a_stride; composite_line_fn line_fn; }; static int sliced_composite_proc( int id, int idx, int jobs, void* cookie ) { struct sliced_composite_desc ctx = *((struct sliced_composite_desc*)cookie); int i, hs = (ctx.height_src + jobs / 2) / jobs, ho = hs * idx; for ( i = 0; i < ctx.height_src; i += ctx.step ) { if ( i >= ho && i < ( ho + hs ) ) ctx.line_fn( ctx.p_dest, ctx.p_src, ctx.width_src, ctx.alpha_b, ctx.alpha_a, ctx.weight, ctx.p_luma, ctx.i_softness, ctx.luma_step ); ctx.p_src += ctx.stride_src; ctx.p_dest += ctx.stride_dest; if ( ctx.alpha_b ) ctx.alpha_b += ctx.alpha_b_stride; if ( ctx.alpha_a ) ctx.alpha_a += ctx.alpha_a_stride; if ( ctx.p_luma ) ctx.p_luma += ctx.alpha_b_stride; } return 0; } /** 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, double softness, composite_line_fn line_fn, int sliced ) { int ret = 0; int i; int x_src = -geometry.x_src, y_src = -geometry.y_src; int uneven_x_src = ( x_src % 2 ); int step = ( field > -1 ) ? 2 : 1; int bpp = 2; int stride_src = geometry.sw * bpp; int stride_dest = width_dest * bpp; int i_softness = ( 1 << 16 ) * softness; int weight = ( ( 1 << 16 ) * geometry.item.mix + 50 ) / 100; uint32_t luma_step = ( ( ( 1 << 16 ) - 1 ) * geometry.item.mix + 50 ) / 100 * ( 1.0 + softness ); // 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 * bpp + y * stride_dest; // offset pointer into alpha channel based upon cropping if ( alpha_b ) alpha_b += x_src + y_src * stride_src / bpp; if ( alpha_a ) 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; if ( alpha_b ) alpha_b += stride_src / bpp; if ( alpha_a ) 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; if ( alpha_b ) alpha_b += 1; } // now do the compositing only to cropped extents if ( !sliced ) { for ( i = 0; i < height_src; i += step ) { line_fn( p_dest, p_src, width_src, alpha_b, alpha_a, weight, p_luma, i_softness, luma_step ); p_src += stride_src; p_dest += stride_dest; if ( alpha_b ) alpha_b += alpha_b_stride; if ( alpha_a ) alpha_a += alpha_a_stride; if ( p_luma ) p_luma += alpha_b_stride; } } else { struct sliced_composite_desc s = { .height_src = height_src, .step = step, .p_dest = p_dest, .p_src = p_src, .width_src = width_src, .alpha_b = alpha_b, .alpha_a = alpha_a, .weight = weight, .p_luma = p_luma, .i_softness = i_softness, .luma_step = luma_step, .stride_src = stride_src, .stride_dest = stride_dest, .alpha_b_stride = alpha_b_stride, .alpha_a_stride = alpha_a_stride, .line_fn = line_fn, }; mlt_slices_run_normal(0, sliced_composite_proc, &s); } 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 self, 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 *orig_resource = resource; mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( self ) ); char temp[ 512 ]; if ( luma_width == 0 || luma_height == 0 ) { luma_width = width; luma_height = height; } if ( resource && resource[0] && strchr( resource, '%' ) ) { sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_profile_lumas_dir(profile), strchr( resource, '%' ) + 1 ); FILE *test = mlt_fopen(temp, "r"); if (!test) { strcat(temp, ".png"); test = mlt_fopen(temp, "r"); } if (test) { 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 ); mlt_properties_set_data( properties, "_luma.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 int lumaLoaded = 0; if ( extension != NULL && strcmp( extension, ".pgm" ) == 0 ) { // Load from PGM if (mlt_luma_map_from_pgm( resource, &orig_bitmap, &luma_width, &luma_height )) { // Failed to read file; generate it. mlt_luma_map luma = mlt_luma_map_new(orig_resource); if (profile) { luma->w = profile->width; luma->h = profile->height; } luma_bitmap = mlt_luma_map_render(luma); luma_width = luma->w; luma_height = luma->h; free(luma); } if ( luma_width > 0 && luma_height > 0 ) { // 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 ); lumaLoaded = 1; } } if ( !lumaLoaded ) { // Get the factory producer service char *factory = mlt_properties_get( properties, "factory" ); // Create the producer 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 ) mlt_luma_map_from_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 ); } else { luma_width = 0; luma_height = 0; } } } if ( luma_width > 0 && luma_height > 0 ) { // 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 self, mlt_frame b_frame, uint8_t **image, int *width, int *height, struct geometry_s *geometry ) { int error = 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( self ); uint8_t resize_alpha = mlt_properties_get_int( b_props, "resize_alpha" ); double output_ar = mlt_profile_sar( mlt_service_profile( MLT_TRANSITION_SERVICE(self) ) ); // 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, "meta.media.width", "width" ); int real_height = get_value( b_props, "meta.media.height", "height" ); double input_ar = mlt_properties_get_double( b_props, "aspect_ratio" ); 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; } else if ( mlt_properties_get_int( properties, "crop_to_fill" ) ) { int real_width = get_value( b_props, "meta.media.width", "width" ); int real_height = get_value( b_props, "meta.media.height", "height" ); double input_ar = mlt_properties_get_double( b_props, "aspect_ratio" ); int scaled_width = rint( ( input_ar == 0.0 ? output_ar : input_ar ) / output_ar * real_width ); int scaled_height = real_height; int normalised_width = geometry->item.w; int normalised_height = geometry->item.h; if ( scaled_height > 0 && scaled_width * normalised_height / scaled_height >= normalised_width ) { // crop left/right edges scaled_width = rint( scaled_width * normalised_height / scaled_height ); scaled_height = normalised_height; } else if ( scaled_width > 0 ) { // crop top/bottom edges scaled_height = rint( scaled_height * normalised_width / scaled_width ); scaled_width = normalised_width; } 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, "meta.media.width", "width" ); int real_height = get_value( b_props, "meta.media.height", "height" ); double input_ar = mlt_properties_get_double( b_props, "aspect_ratio" ); 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\n", __FILE__, // scaled_width, scaled_height, normalised_width, normalised_height, real_width, real_height, // 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 // ????: Shouldn'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 ); *width -= *width % 2; // coerce to even width for yuv422 *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); error = mlt_frame_get_image( b_frame, image, &format, width, height, 1 ); // composite_yuv uses geometry->sw to determine source stride, which // should equal the image width if not using crop property. if ( !mlt_properties_get( properties, "crop" ) ) geometry->sw = *width; // Set the frame back mlt_properties_set_int( b_props, "resize_alpha", resize_alpha ); return !error && image; } static void crop_calculate( mlt_transition self, 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 length = mlt_transition_get_length( self ); 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 self, struct geometry_s *result, mlt_frame a_frame, double position ) { // Get the properties from the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES( self ); // 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 mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( self ) ); int normalised_width = profile->width; int normalised_height = profile->height; char *name = mlt_properties_get( properties, "_unique_id" ); char key[ 256 ]; snprintf( key, sizeof(key), "composite %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( self, 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 { mlt_position length = mlt_transition_get_length( self ); 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( self, 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( self, properties, result, position ); return start; } mlt_frame composite_copy_region( mlt_transition self, mlt_frame a_frame, mlt_position frame_position ) { // Create a frame to return mlt_frame b_frame = mlt_frame_init( MLT_TRANSITION_SERVICE( self ) ); b_frame->convert_image = a_frame->convert_image; // 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( self, frame_position ); // Get the unique id of the transition char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( self ), "_unique_id" ); char key[ 256 ]; // Destination image uint8_t *dest = NULL; // Get the image and dimensions uint8_t *image = NULL; int width = mlt_properties_get_int( a_props, "width" ); int height = mlt_properties_get_int( a_props, "height" ); mlt_image_format format = mlt_image_yuv422; mlt_frame_get_image( a_frame, &image, &format, &width, &height, 0 ); if ( !image ) return b_frame; // 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( self, &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 snprintf( key, sizeof(key), "composite %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 ); snprintf( key, sizeof(key), "composite %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_frame_set_image( b_frame, dest, w * h * 2, mlt_pool_release ); 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 self = 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( self ); // 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 ); } if ( mlt_properties_get_int( properties, "invert" ) ) { mlt_frame c = a_frame; a_frame = b_frame; b_frame = c; } // 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 = mlt_transition_get_progress_delta( self, a_frame ); mlt_position length = mlt_transition_get_length( self ); // Get the image from the b frame uint8_t *image_b = NULL; mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( self ) ); int width_b = *width > 0 ? *width : profile->width; int height_b = *height > 0 ? *height : profile->height; // Vars for alphas uint8_t *alpha_a = NULL; uint8_t *alpha_b = NULL; // Do the calculation // NB: Locks needed here since the properties are being modified mlt_service_lock( MLT_TRANSITION_SERVICE( self ) ); composite_calculate( self, &result, a_frame, position ); mlt_service_unlock( MLT_TRANSITION_SERVICE( self ) ); // Manual option to deinterlace if ( mlt_properties_get_int( properties, "deinterlace" ) ) { mlt_properties_set_int( a_props, "consumer_deinterlace", 1 ); mlt_properties_set_int( b_props, "consumer_deinterlace", 1 ); } // 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( self, b_frame, &image_b, &width_b, &height_b, &result ); alpha_b = mlt_frame_get_alpha( 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( 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 || get_b_frame_image( self, b_frame, &image_b, &width_b, &height_b, &result ) ) ) { int progressive = mlt_properties_get_int( a_props, "consumer_deinterlace" ) || mlt_properties_get_int( properties, "progressive" ); int top_field_first = mlt_properties_get_int( a_props, "top_field_first" ); int field; int sliced = mlt_properties_get_int( properties, "sliced_composite" ); double luma_softness = mlt_properties_get_double( properties, "softness" ); mlt_service_lock( MLT_TRANSITION_SERVICE( self ) ); uint16_t *luma_bitmap = get_luma( self, properties, width_b, height_b ); mlt_service_unlock( MLT_TRANSITION_SERVICE( self ) ); char *operator = mlt_properties_get( properties, "operator" ); alpha_b = alpha_b == NULL ? mlt_frame_get_alpha( 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" ) && alpha_a ) memset( alpha_a, mlt_properties_get_int( properties, "alpha_a" ), *width * *height ); if ( mlt_properties_get( properties, "alpha_b" ) && 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 * length; int field_id = progressive ? -1 : ( top_field_first ? ( 1 - field ) : field ); // Do the calculation if we need to // NB: Locks needed here since the properties are being modified mlt_service_lock( MLT_TRANSITION_SERVICE( self ) ); composite_calculate( self, &result, a_frame, field_position ); mlt_service_unlock( MLT_TRANSITION_SERVICE( self ) ); 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 if ( mlt_properties_get_int( properties, "crop_to_fill" ) ) { if ( result.item.w < result.sw ) result.x_src = rint( ( result.item.w - result.sw ) * result.halign / 2 ); if ( result.item.h < result.sh ) result.y_src = rint( ( result.item.h - result.sh ) * result.valign / 2 ); // same as 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 mlt_log_timings_begin() composite_yuv( *image, *width, *height, image_b, width_b, height_b, alpha_b, alpha_a, result, field_id, luma_bitmap, luma_softness, line_fn, sliced ); mlt_log_timings_end( NULL, "composite_yuv" ) } } } else { mlt_frame_get_image( a_frame, image, format, width, height, 1 ); } return 0; } /** Composition transition processing. */ static mlt_frame composite_process( mlt_transition self, 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( self ), "always_active" ) == 0 ) { mlt_frame_push_service_int( a_frame, mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "in" ) ); mlt_frame_push_service_int( a_frame, mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "out" ) ); mlt_deque_push_back_double( MLT_FRAME_IMAGE_STACK( a_frame ), position_calculate( self, 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, self ); 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 self = calloc( 1, sizeof( struct mlt_transition_s ) ); if ( self != NULL && mlt_transition_init( self, NULL ) == 0 ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( self ); self->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", mlt_environment( "MLT_PRODUCER" ) ); // Use alignment (and hence alpha of b frame) mlt_properties_set_int( properties, "aligned", 1 ); // Default to progressive rendering mlt_properties_set_int( properties, "progressive", 1 ); // Inform apps and framework that this is a video only transition mlt_properties_set_int( properties, "_transition_type", 1 ); } return self; } mlt-6.20.0/src/modules/core/transition_composite.h000066400000000000000000000026471362234133600222230ustar00rootroot00000000000000/* * transition_composite.h -- compose one image over another using alpha channel * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 ); extern 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, uint32_t step ); #endif mlt-6.20.0/src/modules/core/transition_composite.yml000066400000000000000000000115351362234133600225710ustar00rootroot00000000000000schema_version: 0.3 type: transition identifier: composite title: Composite version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > A key-framable alpha-channel compositor for two frames. notes: > 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. bugs: - Assumes lower field first during field rendering. parameters: - identifier: factory title: Factory type: string description: > The name of a factory service used as a non-PGM producer loader. default: loader - identifier: geometry title: Geometry type: geometry description: > Key frame specification. This is a ";" delimited form of the deprecated start, key[n], end properties. mutable: yes - identifier: progressive title: Progressive description: > Enable or disable field-based rendering. type: integer minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: distort title: Allow distorted scaling description: > When set, causes the B frame image to fill the WxH completely with no regard to B's aspect ratio. type: integer default: 0 minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: crop_to_fill title: Fill by cropping description: > When set, causes the B frame image to fill the WxH completely by cropping edges in order to maintain B's aspect ratio. type: integer default: 0 minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: halign title: Horizontal alignment description: > When not distorting, set the horizontal alignment of B within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > When not distorting, set the vertical alignment of B within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo - identifier: luma title: Luma map description: > The luma map file name. If not supplied, a dissolve. type: string mutable: yes widget: fileopen - identifier: softness title: Softness description: > Only when using a luma map, how soft to make the edges between A and B. type: float default: 0.0 minimum: 0.0 maximum: 1.0 mutable: yes - identifier: luma.* title: Luma producer description: > Properties may be set on the encapsulated producer. Any property starting with "luma." is passed to the non-PGM luma producer. readonly: no mutable: yes - identifier: start title: Start geometry description: > (deprecated) 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. Mix is always a 2 digit percentage, defaults to 100. type: geometry default: "0%/0%:100%x100%" readonly: no mutable: yes - identifier: end title: End geometry description: > (deprecated) X/Y:WxH[:mix] - The end geometry specification (see "start"). type: geometry readonly: no mutable: yes - identifier: sliced_composite title: Use sliced compositing description: > Enabling this option will start sliced processing of picture compositing, i.e. some parts of picture processed in different thread type: boolean default: 0 mutable: yes widget: checkbox - identifier: key[F] title: Key frame geometry description: > (deprecated) 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. type: geometry readonly: no mutable: yes - identifier: fill title: Fill geometry description: > Determines whether the image will be scaled up to fill the geometry. Otherwise, if the B frame image fits within the geometry, it will not be scaled. If 0, and the B frame image exceeds the geometry, then it is scaled down to fit within the geometry. type: boolean default: 1 mutable: yes widget: checkbox - identifier: invert title: Invert description: Whether to swap the A and B clips type: boolean default: 1 mutable: yes widget: checkbox mlt-6.20.0/src/modules/core/transition_luma.c000066400000000000000000000454331362234133600211520ustar00rootroot00000000000000/* * transition_luma.c -- a generic dissolve/wipe processor * Copyright (C) 2003-2020 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "transition_composite.h" static inline int is_opaque( uint8_t *alpha_channel, int width, int height ) { int n = width * height + 1; while ( --n ) if ( *alpha_channel++ != 0xff ) return 0; return 1; } static inline float calculate_mix( float weight, float alpha ) { return weight * alpha / 255.f; } static inline uint8_t sample_mix( uint8_t dest, uint8_t src, float mix ) { return src * mix + dest * ( 1.f - mix ); } static void composite_line_yuv_float( uint8_t *dest, uint8_t *src, int width, uint8_t *alpha_b, uint8_t *alpha_a, float weight ) { register int j = 0; float mix_a, mix_b; for ( ; j < width; j ++ ) { mix_a = calculate_mix( 1.0f - weight, alpha_a? *alpha_a : 255 ); mix_b = calculate_mix( weight, alpha_b? *alpha_b : 255 ); if (alpha_a) { float mix2 = mix_b + mix_a - mix_b * mix_a; *alpha_a = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } *dest = sample_mix( *dest, *src++, mix_b ); dest++; *dest = sample_mix( *dest, *src++, mix_b ); dest++; if ( alpha_a ) alpha_a ++; if ( alpha_b ) alpha_b ++; } } struct dissolve_slice_context { uint8_t *dst_image; uint8_t *src_image; uint8_t *dst_alpha; uint8_t *src_alpha; int width; int height; float weight; }; static int dissolve_slice( int id, int index, int count, void *context ) { struct dissolve_slice_context ctx = *((struct dissolve_slice_context*) context); int stride = ctx.width * 2; int slice_height = (ctx.height + count - 1) / count; int i; ctx.dst_image += index * slice_height * stride; ctx.src_image += index * slice_height * stride; if (ctx.dst_alpha) ctx.dst_alpha += index * slice_height * ctx.width; if (ctx.src_alpha) ctx.src_alpha += index * slice_height * ctx.width; slice_height = MIN(slice_height, ctx.height - index * slice_height); for (i = 0; i < slice_height; i++) { composite_line_yuv_float( ctx.dst_image, ctx.src_image, ctx.width, ctx.src_alpha, ctx.dst_alpha, ctx.weight ); ctx.dst_image += stride; ctx.src_image += stride; if (ctx.dst_alpha) ctx.dst_alpha += ctx.width; if (ctx.src_alpha) ctx.src_alpha += ctx.width; } return 0; } static inline int dissolve_yuv( mlt_frame frame, mlt_frame that, float weight, int width, int height, int threads, int alpha_over ) { int ret = 0; int i = height + 1; int width_src = width, height_src = height; mlt_image_format format = mlt_image_yuv422; uint8_t *p_src, *p_dest; uint8_t *alpha_src; uint8_t *alpha_dst; int mix = weight * ( 1 << 16 ); if ( mlt_properties_get( &frame->parent, "distort" ) ) mlt_properties_set( &that->parent, "distort", mlt_properties_get( &frame->parent, "distort" ) ); mlt_frame_get_image( frame, &p_dest, &format, &width, &height, 1 ); alpha_dst = mlt_frame_get_alpha_mask( frame ); mlt_frame_get_image( that, &p_src, &format, &width_src, &height_src, 0 ); alpha_src = mlt_frame_get_alpha_mask( that ); int is_translucent = ( alpha_dst && !is_opaque(alpha_dst, width, height) ) || ( alpha_src && !is_opaque(alpha_src, width_src, height_src) ); // Pick the lesser of two evils ;-) width_src = width_src > width ? width : width_src; height_src = height_src > height ? height : height_src; if (is_translucent && alpha_over) { struct dissolve_slice_context context = { .dst_image = p_dest, .src_image = p_src, .dst_alpha = alpha_dst, .src_alpha = alpha_src, .width = width_src, .height = height_src, .weight = weight }; mlt_slices_run_normal(threads, dissolve_slice, &context); } else { while ( --i ) { composite_line_yuv( p_dest, p_src, width_src, alpha_src, alpha_dst, mix, NULL, 0, 0 ); p_src += width_src << 1; p_dest += width << 1; alpha_src += width_src; alpha_dst += width; } } return ret; } /** 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; } static float smoothstep_float( float edge1, float edge2, float a ) { if ( a < edge1 ) return 0.f; if ( a >= edge2 ) return 1.f; a = ( a - edge1 ) / ( edge2 - edge1 ); return ( a * a ) * ( 3 - ( 2 * a ) ); } /** 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 invert ) { 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; uint8_t *alpha_src, *alpha_dest; int i, j; int stride_src; int stride_dest; if ( mlt_properties_get( &a_frame->parent, "distort" ) ) mlt_properties_set( &b_frame->parent, "distort", mlt_properties_get( &a_frame->parent, "distort" ) ); mlt_frame_get_image( a_frame, &p_dest, &format_dest, &width_dest, &height_dest, 1 ); alpha_dest = mlt_frame_get_alpha_mask( a_frame ); mlt_frame_get_image( b_frame, &p_src, &format_src, &width_src, &height_src, 0 ); alpha_src = mlt_frame_get_alpha_mask( b_frame ); if ( *width == 0 || *height == 0 ) return; int is_translucent = ( alpha_dest && !is_opaque(alpha_dest, width_dest, height_dest) ) || ( alpha_src && !is_opaque(alpha_src, width_src, height_src ) ); // 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 ... float field_pos[ 2 ]; field_pos[ 0 ] = ( pos + ( ( field_order == 0 ? 1 : 0 ) * frame_delta * 0.5f ) ) * ( 1.f + softness ); field_pos[ 1 ] = ( pos + ( ( field_order == 0 ? 0 : 1 ) * frame_delta * 0.5f ) ) * ( 1.f + softness ); register uint8_t *p; register uint8_t *q; uint16_t *l; 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; uint32_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; float mix_a, mix_b; // 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; l = luma_bitmap + ( y_offset >> 16 ) * ( luma_width * field_count ); x_offset = 0; j = width_src; if (is_translucent) { while( j -- ) { float weight = l[ x_offset >> 16 ] / 65535.f; float value = smoothstep_float( weight, softness + weight, field_pos[ field ] ); mix_a = calculate_mix( 1.0f - value, alpha_dest? *alpha_dest : 255 ); mix_b = calculate_mix( value, alpha_src? *alpha_src : 255 ); if (invert && alpha_src) { float mix2 = mix_b + mix_a - mix_b * mix_a; *alpha_src = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } else if (!invert && alpha_dest) { float mix2 = mix_b + mix_a - mix_b * mix_a; *alpha_dest = 255 * mix2; if (mix2 != 0.f) mix_b /= mix2; } *q = sample_mix( *q, *p++, mix_b ); q++; *q = sample_mix( *q, *p++, mix_b ); q++; if ( alpha_dest ) alpha_dest ++; if ( alpha_src ) alpha_src ++; x_offset += x_diff; } } else { while( j -- ) { uint16_t weight = l[ x_offset >> 16 ]; uint32_t value = smoothstep( weight, i_softness + weight, (1 << 16) * field_pos[ field ] ); *q = ( *p++ * value + *q * ((1 << 16) - value) ) >> 16; q++; *q = ( *p++ * value + *q * ((1 << 16) - value) ) >> 16; q++; x_offset += x_diff; } } y_offset += y_diff; i += field_count; p_row += field_stride_src; q_row += field_stride_dest; } field ++; } } void yuv422_to_luma16(uint8_t *image, uint16_t **map, int width, int height, int full_range) { // allocate the luma bitmap *map = (uint16_t*) mlt_pool_alloc(width * height * sizeof(uint16_t)); if (!*map) return; int i; int n = width * height; uint8_t offset = full_range? 0 : 16; uint8_t max = full_range? 255 : 235 - offset; int factor = full_range? 256 : 299; // 299 = 65535 / 219 // process the image data into the luma bitmap for (i = 0; i < n; i++) { (*map)[i] = CLAMP(image[i << 1] - offset, 0, max) * factor; } } 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; mlt_service_lock( MLT_TRANSITION_SERVICE( transition ) ); // 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" ); mlt_producer producer = mlt_properties_get_data(properties, "producer", NULL); // 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 = *width; luma_height = *height; } if ( resource && ( producer || !current_resource || strcmp( resource, current_resource ) ) ) { char temp[ 512 ]; char *extension = strrchr( resource, '.' ); char *orig_resource = resource; mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) ); if ( strchr( resource, '%' ) ) { sprintf( temp, "%s/lumas/%s/%s", mlt_environment( "MLT_DATA" ), mlt_profile_lumas_dir(profile), strchr( resource, '%' ) + 1 ); FILE *test = mlt_fopen(temp, "r"); if (!test) { strcat(temp, ".png"); test = mlt_fopen(temp, "r"); } if (test) { fclose( test ); resource = temp; } extension = strrchr( resource, '.' ); } // See if it is a PGM if ( extension != NULL && strcmp( extension, ".pgm" ) == 0 ) { // Load from PGM luma_bitmap = NULL; if (mlt_luma_map_from_pgm(resource, &luma_bitmap, &luma_width, &luma_height)) { // Failed to read file; generate it. mlt_luma_map luma = mlt_luma_map_new(orig_resource); if (profile) { luma->w = profile->width; luma->h = profile->height; } luma_bitmap = mlt_luma_map_render(luma); luma_width = luma->w; luma_height = luma->h; free(luma); } // 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", orig_resource ); mlt_properties_set_data( properties, "bitmap", luma_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL ); mlt_properties_clear(properties, "producer"); } 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 ); mlt_properties_clear(properties, "producer"); } else { if (!producer || !current_resource || strcmp(resource, current_resource)) { // Get the factory producer service char *factory = mlt_properties_get( properties, "factory" ); // Create the producer producer = mlt_factory_producer( profile, factory, resource ); if (producer) mlt_properties_set(properties, "_resource", resource); } // If we have one if (producer) { // 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; // Determine if producer is a video clip or still image const char* service_name = mlt_properties_get(producer_properties, "mlt_service"); int is_clip = mlt_producer_get_length(producer) > 1 && mlt_properties_get_int(producer_properties, "video_index") >= 0 && service_name && !strncmp("avformat", service_name, 8); if (is_clip) { if (!mlt_properties_get(producer_properties, "producer.eof")) mlt_properties_set(producer_properties, "eof", "pause"); mlt_producer_seek(producer, mlt_transition_get_position(transition, a_frame)); } // 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_frame_get_image( luma_frame, &luma_image, &luma_format, &luma_width, &luma_height, 0 ); // Generate the luma map if (luma_image) { if (is_clip) { yuv422_to_luma16(luma_image, &luma_bitmap, luma_width, luma_height, mlt_properties_get_int(MLT_FRAME_PROPERTIES(luma_frame), "full_luma")); } else { mlt_luma_map_from_yuv422(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_data( properties, "bitmap", luma_bitmap, luma_width * luma_height * 2, mlt_pool_release, NULL ); // Cleanup the luma frame mlt_frame_close( luma_frame ); } if (is_clip) { // Save the producer for getting next frame mlt_properties_set_data(properties, "producer", producer, 0, (mlt_destructor) mlt_producer_close, NULL); } else { // Cleanup the luma producer mlt_producer_close(producer); producer = NULL; } } } } // Arbitrary composite defaults float mix = mlt_transition_get_progress( transition, a_frame ); float frame_delta = mlt_transition_get_progress_delta( 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" ); int threads = CLAMP(mlt_properties_get_int(properties, "threads"), 0, mlt_slices_count_normal()); int alpha_over = mlt_properties_get_int(properties, "alpha_over"); // Honour the reverse here if ( mix >= 1.0 ) mix -= floor( mix ); if ( mlt_properties_get( properties, "fixed" ) ) mix = mlt_properties_get_double( properties, "fixed" ); mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) ); if (producer) { invert = !invert; mix = 0.5f; } if ( luma_width > 0 && luma_height > 0 && luma_bitmap != NULL ) { reverse = invert ? !reverse : reverse; mix = reverse ? 1 - mix : mix; frame_delta *= reverse ? -1.0 : 1.0; // 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, invert ); } else { mix = ( reverse || invert ) ? 1 - mix : mix; invert = 0; // Dissolve the frames using the time offset for mix value dissolve_yuv( a_frame, b_frame, mix, *width, *height, threads, alpha_over ); } // 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 ) { // 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", mlt_environment( "MLT_PRODUCER" ) ); // 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; } mlt-6.20.0/src/modules/core/transition_luma.yml000066400000000000000000000036301362234133600215220ustar00rootroot00000000000000schema_version: 0.3 type: transition identifier: luma title: Wipe version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > A generic dissolve and wipe transition processor. "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. bugs: - Assumes lower field first output. parameters: - identifier: resource title: Luma map file type: string description: > Either PGM or any other producable video. If not supplied, performs a dissolve. argument: yes - identifier: factory title: Factory type: string description: > The name of a factory service used as a non-PGM producer loader. default: loader - identifier: softness title: Softness type: float mutable: yes description: > Only when using a luma map, how soft to make the edges between A and B. 0.0 = no softness. 1.0 = too soft. - identifier: reverse title: Reverse type: integer mutable: yes description: > Reverse the direction of the transition. default: 0 - identifier: producer.* title: Producer mutable: yes description: > Properties may be set on the encapsulated producer. Any property starting with "producer." is passed to the non-PGM luma producer. readonly: no - identifier: alpha_over title: Use over-blending on the alpha channel type: boolean default: 0 mutable: yes mlt-6.20.0/src/modules/core/transition_matte.c000066400000000000000000000150261362234133600213210ustar00rootroot00000000000000/* * transition_matte.c -- replace alpha channel of track * * Copyright (C) 2003-2014 Meltytech, LLC * Author: Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #if defined(USE_SSE) && defined(ARCH_X86_64) static void __attribute__((noinline)) copy_Y_to_A_scaled_luma_sse(uint8_t* alpha_a, uint8_t* image_b, int cnt) { const static unsigned char const1[] = { 43, 0, 43, 0, 43, 0, 43, 0, 43, 0, 43, 0, 43, 0, 43, 0 }; const static unsigned char const2[] = { 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0 }; const static unsigned char const3[] = { 235, 0, 235, 0, 235, 0, 235, 0, 235, 0, 235, 0, 235, 0, 235, 0 }; const static unsigned char const4[] = { 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0 }; __asm__ volatile ( "movdqu (%[equ43]), %%xmm7 \n\t" /* load multiplier 43 */ "movdqu (%[equ16]), %%xmm6 \n\t" /* load bottom value 16 */ "movdqu (%[equ235]), %%xmm5 \n\t" /* load bottom value 235 */ "movdqu (%[equ255]), %%xmm4 \n\t" /* load bottom value 0xff */ "loop_start: \n\t" /* load pixels block 1 */ "movdqu 0(%[image_b]), %%xmm0 \n\t" "add $0x10, %[image_b] \n\t" /* load pixels block 2 */ "movdqu 0(%[image_b]), %%xmm1 \n\t" "add $0x10, %[image_b] \n\t" /* leave only Y */ "pand %%xmm4, %%xmm0 \n\t" "pand %%xmm4, %%xmm1 \n\t" /* upper range clip */ "pminsw %%xmm5, %%xmm0 \n\t" "pminsw %%xmm5, %%xmm1 \n\t" /* upper range clip */ "pmaxsw %%xmm6, %%xmm0 \n\t" "pmaxsw %%xmm6, %%xmm1 \n\t" /* upper range clip */ "psubw %%xmm6, %%xmm0 \n\t" "psubw %%xmm6, %%xmm1 \n\t" /* duplicate values */ "movdqa %%xmm0,%%xmm2 \n\t" "movdqa %%xmm1,%%xmm3 \n\t" /* regA = regA << 8 */ "psllw $8, %%xmm0 \n\t" "psllw $8, %%xmm1 \n\t" /* regB = regB * 47 */ "pmullw %%xmm7, %%xmm2 \n\t" "pmullw %%xmm7, %%xmm3 \n\t" /* regA = regA + regB */ "paddw %%xmm2, %%xmm0 \n\t" "paddw %%xmm3, %%xmm1 \n\t" /* regA = regA >> 8 */ "psrlw $8, %%xmm0 \n\t" "psrlw $8, %%xmm1 \n\t" /* pack to 8 bit value */ "packuswb %%xmm1, %%xmm0 \n\t" /* store */ "movdqu %%xmm0, (%[alpha_a]) \n\t" "add $0x10, %[alpha_a] \n\t" /* loop if we done */ "dec %[cnt] \n\t" "jnz loop_start \n\t" : [cnt]"+r" (cnt), [alpha_a]"+r"(alpha_a), [image_b]"+r"(image_b) : [equ43]"r"(const1), [equ16]"r"(const2), [equ235]"r"(const3), [equ255]"r"(const4) : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" ); }; #endif static void copy_Y_to_A_scaled_luma(uint8_t* alpha_a, int stride_a, uint8_t* image_b, int stride_b, int width, int height) { int i, j; for(j = 0; j < height; j++) { i = 0; #if defined(USE_SSE) && defined(ARCH_X86_64) if(width >= 16) { copy_Y_to_A_scaled_luma_sse(alpha_a, image_b, width >> 4); i = (width >> 4) << 4; } #endif for(; i < width; i++) { unsigned int p = image_b[2*i]; if(p < 16) p = 16; if(p > 235) p = 235; /* p = (p - 16) * 255 / 219; */ p -= 16; p = ((p << 8) + (p * 43)) >> 8; alpha_a[i] = p; }; alpha_a += stride_a; image_b += stride_b; }; }; /** 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 ); mlt_frame_get_image( a_frame, image, format, width, height, 1 ); // 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 ); int width_a = mlt_properties_get_int( a_props, "width" ), width_b = width_a, height_a = mlt_properties_get_int( a_props, "height" ), height_b = height_a; uint8_t *alpha_a, *image_b; // This transition is yuv422 only *format = mlt_image_yuv422; // Get the image from the a frame mlt_frame_get_image( b_frame, &image_b, format, &width_b, &height_b, 1 ); alpha_a = mlt_frame_get_alpha_mask( a_frame ); // copy data copy_Y_to_A_scaled_luma ( alpha_a, width_a, image_b, width_b * 2, (width_a > width_b)?width_b:width_a, (height_a > height_b)?height_b:height_a ); // Extract the a_frame image info *width = mlt_properties_get_int( a_props, "width" ); *height = mlt_properties_get_int( a_props, "height" ); *image = mlt_properties_get_data( a_props, "image", NULL ); return 0; } /** Matte transition processing. */ static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { // 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_matte_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; // 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; } mlt-6.20.0/src/modules/core/transition_matte.yml000066400000000000000000000035541362234133600217030ustar00rootroot00000000000000schema_version: 0.1 type: transition identifier: matte title: Matte version: 1 copyright: Meltytech, LLC creator: Maksym Veremeyenko license: LGPLv2.1 language: en tags: - Video description: > Replace the alpha channel of track A with the luma channel from track B. notes: | Please note that the transition automatically detects if the frame from track B has scaled or non-scaled luma. The frame property "full_luma" should indicate it. If you need to prepare fill and key (matte) files, you can use melt or ffmpeg to extract alpha channel from existing video: melt sg_gm_2013_clip_title.avi -attach frei0r.alpha0ps 0=0.21 -consumer \ avformat:sg_gm_2013_clip_title.matte_scaled.mp4 crf=10 preset=placebo an=1 ffmpeg -i sg_gm_2013_clip_title.avi -vf "alphaextract" -pix_fmt \ yuv422p -preset placebo -crf 10 -y sg_gm_2013_clip_title.matte_scaled.mp4 Because the example above provides a scaled luma output, the transition performs scaling from [16,235] -> [0, 255]. It is possible to create unscaled (full) range: melt sg_gm_2013_clip_title.avi -attach frei0r.alpha0ps 0=0.21 -consumer \ avformat:sg_gm_2013_clip_title.matte_full.mp4 crf=10 preset=placebo an=1 \ mlt_image_format=rgb24a pix_fmt=yuvj422p ffmpeg -i sg_gm_2013_clip_title.avi -vf "alphaextract" -pix_fmt \ yuvj422p -preset placebo -crf 10 -y sg_gm_2013_clip_title.matte_full.mp4 The fill can be converted from rgba to yuv422: melt sg_gm_2013_clip_title.avi -consumer avformat:sg_gm_2013_clip_title.fill.mp4 \ crf=10 preset=placebo an=1 ffmpeg -i sg_gm_2013_clip_title.avi -pix_fmt yuv422p -preset placebo -crf 10 -y \ sg_gm_2013_clip_title.fill.mp4 Putting it all together: melt sg_gm_2013_clip_title.matte_full.mp4 -track noise: -track \ sg_gm_2013_clip_title.fill.mp4 -transition matte a_track=2 \ b_track=0 -transition composite a_track=1 b_track=2 mlt-6.20.0/src/modules/core/transition_mix.c000066400000000000000000000354171362234133600210120ustar00rootroot00000000000000/* * transition_mix.c -- mix two audio streams * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #define MAX_CHANNELS (6) #define MAX_SAMPLES (192000) #define SAMPLE_BYTES(samples, channels) ((samples) * (channels) * sizeof(float)) #define MAX_BYTES SAMPLE_BYTES( MAX_SAMPLES, MAX_CHANNELS ) typedef struct transition_mix_s { mlt_transition parent; float src_buffer[MAX_SAMPLES * MAX_CHANNELS]; float dest_buffer[MAX_SAMPLES * MAX_CHANNELS]; int src_buffer_count; int dest_buffer_count; } *transition_mix; static void mix_audio( double weight_start, double weight_end, float *buffer_a, float *buffer_b, int channels_a, int channels_b, int channels_out, int samples ) { int i, j; double a, b, v; // Compute a smooth ramp over start to end double mix = weight_start; double mix_step = ( weight_end - weight_start ) / samples; for ( i = 0; i < samples; i++ ) { for ( j = 0; j < channels_out; j++ ) { a = (double) buffer_a[ i * channels_a + j ]; b = (double) buffer_b[ i * channels_b + j ]; v = mix * b + (1.0 - mix) * a; buffer_a[ i * channels_a + j ] = v; } mix += mix_step; } } static void sum_audio( double weight_start, double weight_end, float *buffer_a, float *buffer_b, int channels_a, int channels_b, int channels_out, int samples ) { int i, j; double a, b; // Compute a smooth ramp over start to end double mix = weight_start; double mix_step = ( weight_end - weight_start ) / samples; for ( i = 0; i < samples; i++ ) { for ( j = 0; j < channels_out; j++ ) { a = (double) buffer_a[ i * channels_a + j ]; b = (double) buffer_b[ i * channels_b + j ]; buffer_a[ i * channels_a + j ] = mix * b + a; } mix += mix_step; } } // This filter uses an inline low pass filter to allow mixing without volume hacking. static void combine_audio( double weight, float *buffer_a, float *buffer_b, int channels_a, int channels_b, int channels_out, int samples ) { int i, j; double Fc = 0.5; double B = exp(-2.0 * M_PI * Fc); double A = 1.0 - B; double a, b, v; double v_prev[MAX_CHANNELS]; for ( j = 0; j < channels_out; j++ ) v_prev[j] = (double) buffer_a[j]; for ( i = 0; i < samples; i++ ) { for ( j = 0; j < channels_out; j++ ) { a = (double) buffer_a[ i * channels_a + j ]; b = (double) buffer_b[ i * channels_b + j ]; v = weight * a + b; v_prev[j] = buffer_a[ i * channels_a + j ] = v * A + v_prev[j] * B; } } } /** Get the audio. */ static int transition_get_audio( mlt_frame frame_a, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { int error = 0; // Get the b frame from the stack mlt_frame frame_b = mlt_frame_pop_audio( frame_a ); // Get the effect mlt_transition transition = mlt_frame_pop_audio( frame_a ); // Get the properties of the b frame mlt_properties b_props = MLT_FRAME_PROPERTIES( frame_b ); transition_mix self = transition->child; float *buffer_b, *buffer_a; int frequency_b = *frequency, frequency_a = *frequency; int channels_b = *channels, channels_a = *channels; int samples_b = *samples, samples_a = *samples; // We can only mix interleaved 32-bit float. *format = mlt_audio_f32le; mlt_frame_get_audio( frame_b, (void**) &buffer_b, format, &frequency_b, &channels_b, &samples_b ); mlt_frame_get_audio( frame_a, (void**) &buffer_a, format, &frequency_a, &channels_a, &samples_a ); // Prevent dividing by zero. if ( !channels_a || !channels_b ) return 1; if ( buffer_b == buffer_a ) { *samples = samples_b; *channels = channels_b; *buffer = buffer_b; *frequency = frequency_b; return error; } int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame_a ), "silent_audio" ); mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame_a ), "silent_audio", 0 ); if ( silent ) memset( buffer_a, 0, samples_a * channels_a * sizeof( float ) ); silent = mlt_properties_get_int( b_props, "silent_audio" ); mlt_properties_set_int( b_props, "silent_audio", 0 ); if ( silent ) memset( buffer_b, 0, samples_b * channels_b * sizeof( float ) ); // determine number of samples to process *samples = MIN( self->src_buffer_count + samples_b, self->dest_buffer_count + samples_a ); *channels = MIN( MIN( channels_b, channels_a ), MAX_CHANNELS ); *frequency = frequency_a; // Prevent src buffer overflow by discarding oldest samples. samples_b = MIN( samples_b, MAX_SAMPLES * MAX_CHANNELS / channels_b ); size_t bytes = SAMPLE_BYTES( samples_b, channels_b ); if ( SAMPLE_BYTES( self->src_buffer_count + samples_b, channels_b ) > MAX_BYTES ) { mlt_log_verbose( MLT_TRANSITION_SERVICE(transition), "buffer overflow: src_buffer_count %d\n", self->src_buffer_count ); self->src_buffer_count = MAX_SAMPLES * MAX_CHANNELS / channels_b - samples_b; memmove( self->src_buffer, &self->src_buffer[MAX_SAMPLES * MAX_CHANNELS - samples_b * channels_b], SAMPLE_BYTES( samples_b, channels_b ) ); } // Buffer new src samples. memcpy( &self->src_buffer[self->src_buffer_count * channels_b], buffer_b, bytes ); self->src_buffer_count += samples_b; buffer_b = self->src_buffer; // Prevent dest buffer overflow by discarding oldest samples. samples_a = MIN( samples_a, MAX_SAMPLES * MAX_CHANNELS / channels_a ); bytes = SAMPLE_BYTES( samples_a, channels_a ); if ( SAMPLE_BYTES( self->dest_buffer_count + samples_a, channels_a ) > MAX_BYTES ) { mlt_log_verbose( MLT_TRANSITION_SERVICE(transition), "buffer overflow: dest_buffer_count %d\n", self->dest_buffer_count ); self->dest_buffer_count = MAX_SAMPLES * MAX_CHANNELS / channels_a - samples_a; memmove( self->dest_buffer, &self->dest_buffer[MAX_SAMPLES * MAX_CHANNELS - samples_a * channels_a], SAMPLE_BYTES( samples_a, channels_a ) ); } // Buffer the new dest samples. memcpy( &self->dest_buffer[self->dest_buffer_count * channels_a], buffer_a, bytes ); self->dest_buffer_count += samples_a; buffer_a = self->dest_buffer; // Do the mixing. if ( mlt_properties_get_int( MLT_TRANSITION_PROPERTIES(transition), "sum" ) ) { double mix_start = 1.0, mix_end = 1.0; if ( mlt_properties_get( b_props, "audio.previous_mix" ) ) mix_start = mlt_properties_get_double( b_props, "audio.previous_mix" ); if ( mlt_properties_get( b_props, "audio.mix" ) ) mix_end = mlt_properties_get_double( b_props, "audio.mix" ); if ( mlt_properties_get_int( b_props, "audio.reverse" ) ) { mix_start = 1.0 - mix_start; mix_end = 1.0 - mix_end; } sum_audio( mix_start, mix_end, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples ); } else if ( mlt_properties_get_int( MLT_TRANSITION_PROPERTIES(transition), "combine" ) ) { double weight = 1.0; if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame_a ), "meta.mixdown" ) ) weight = 1.0 - mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame_a ), "meta.volume" ); combine_audio( weight, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples ); } else { double mix_start = 0.5, mix_end = 0.5; if ( mlt_properties_get( b_props, "audio.previous_mix" ) ) mix_start = mlt_properties_get_double( b_props, "audio.previous_mix" ); if ( mlt_properties_get( b_props, "audio.mix" ) ) mix_end = mlt_properties_get_double( b_props, "audio.mix" ); if ( mlt_properties_get_int( b_props, "audio.reverse" ) ) { mix_start = 1.0 - mix_start; mix_end = 1.0 - mix_end; } mix_audio( mix_start, mix_end, buffer_a, buffer_b, channels_a, channels_b, *channels, *samples ); } // Copy the audio into the frame. bytes = SAMPLE_BYTES( *samples, *channels ); *buffer = mlt_pool_alloc( bytes ); memcpy( *buffer, buffer_a, bytes ); mlt_frame_set_audio( frame_a, *buffer, *format, bytes, mlt_pool_release ); if ( mlt_properties_get_int( b_props, "_speed" ) == 0 ) { // Flush the buffer when paused and scrubbing. samples_b = self->src_buffer_count; samples_a = self->dest_buffer_count; } else { // Determine the maximum amount of latency permitted in the buffer. int max_latency = CLAMP( *frequency / 1000, 0, MAX_SAMPLES ); // samples in 1ms // samples_b becomes the new target src buffer count. samples_b = CLAMP( self->src_buffer_count - *samples, 0, max_latency ); // samples_b becomes the number of samples to consume: difference between actual and the target. samples_b = self->src_buffer_count - samples_b; // samples_a becomes the new target dest buffer count. samples_a = CLAMP( self->dest_buffer_count - *samples, 0, max_latency ); // samples_a becomes the number of samples to consume: difference between actual and the target. samples_a = self->dest_buffer_count - samples_a; } // Consume the src buffer. self->src_buffer_count -= samples_b; if ( self->src_buffer_count ) { memmove( self->src_buffer, &self->src_buffer[samples_b * channels_b], SAMPLE_BYTES( self->src_buffer_count, channels_b )); } // Consume the dest buffer. self->dest_buffer_count -= samples_a; if ( self->dest_buffer_count > 0 ) { memmove( self->dest_buffer, &self->dest_buffer[samples_a * channels_a], SAMPLE_BYTES( self->dest_buffer_count, channels_a )); } return error; } /** Mix transition processing. */ static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); 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" ) ) { // 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 ); mlt_position in = mlt_properties_get_int( props, "in" ); mlt_position out = mlt_properties_get_int( props, "out" ); int length = mlt_properties_get_int( properties, "length" ); mlt_position time = mlt_properties_get_int( props, "_frame" ); double mix = mlt_transition_get_progress( transition, b_frame ); if ( mlt_properties_get_int( properties, "always_active" ) ) 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" ) ) { 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 inadvertent jump from 0 mlt_position last_position = mlt_properties_get_position( properties, "_last_position" ); mlt_position current_position = mlt_frame_get_position( b_frame ); mlt_properties_set_position( properties, "_last_position", current_position ); if ( !mlt_properties_get( properties, "_previous_mix" ) || 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, transition ); mlt_frame_push_audio( a_frame, b_frame ); mlt_frame_push_audio( a_frame, transition_get_audio ); // Ensure transition_get_audio is called if test_audio=1. if ( mlt_properties_get_int( properties, "accepts_blanks" ) ) mlt_properties_set_int( MLT_FRAME_PROPERTIES(a_frame), "test_audio", 0 ); return a_frame; } static void transition_close( mlt_transition transition ) { free( transition->child ); transition->close = NULL; mlt_transition_close( transition ); } /** Constructor for the transition. */ mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { transition_mix mix = calloc( 1 , sizeof( struct transition_mix_s ) ); mlt_transition transition = calloc( 1, sizeof( struct mlt_transition_s ) ); if ( mix && transition && !mlt_transition_init( transition, mix ) ) { mix->parent = transition; transition->close = transition_close; transition->process = transition_process; if ( arg ) { mlt_properties_set_double( MLT_TRANSITION_PROPERTIES( transition ), "start", atof( arg ) ); if ( atof( arg ) < 0 ) mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "accepts_blanks", 1 ); } // Inform apps and framework that this is an audio only transition mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 2 ); } else { if ( transition ) mlt_transition_close( transition ); if ( mix ) free( mix ); } return transition; } mlt-6.20.0/src/modules/core/transition_mix.yml000066400000000000000000000035301362234133600213600ustar00rootroot00000000000000schema_version: 0.2 type: transition identifier: mix title: Mix version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: Mix two audio tracks. bugs: - Samples from the longer of the two frames are discarded. parameters: - identifier: start title: Start argument: yes type: float mutable: yes description: > The mix level to apply to the second frame. Any negative value causes an automatic crossfade from 0 to 1. - identifier: end title: End type: float mutable: yes description: > The ending value of the mix level. Mix level will be interpolated from start to end over the in-out range. - identifier: reverse title: Reverse type: boolean mutable: yes description: > Set to 1 to reverse the direction of the mix. default: 0 widget: checkbox - identifier: combine title: Use an alternative mixing algorithm description: > Mix using a low pass filter to prevent affecting audio levels. However, this may introduce slight artifacts. This is incompatible with start < 0. type: boolean default: 0 mutable: yes - identifier: sum title: Mix by simply adding samples description: > The default mixing algorithm halves the sample values before adding them to absolutely prevent clipping. However, that affects levels. This algorithm simply adds samples and may clip. In many real world scenarios, the signals being mixed typically have headroom in their level and are rarely correlated and thus often will not clip. Also, one can reduce the gain and add a limiter on the mixed output prior to integer quantization to prevent clipping. This mode is incompatible with start < 0. type: boolean default: 0 mutable: yes mlt-6.20.0/src/modules/core/transition_region.c000066400000000000000000000333151362234133600214730ustar00rootroot00000000000000/* * transition_region.c -- region transition * Copyright (C) 2003-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "transition_region.h" #include "transition_composite.h" #include #include #include #include static int create_instance( mlt_transition transition, 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( transition ) ); if ( type ) filter = mlt_factory_filter( profile, type, arg ); // If we have a filter, then initialise and store it if ( filter != NULL ) { // Properties of transition mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); // 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 ); mlt_properties_pass_list( MLT_FILTER_PROPERTIES( filter ), properties, "in, out, length" ); // 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 frame ) { uint8_t *alpha = NULL; // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); // 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( properties, "width" ); int region_height = mlt_properties_get_int( properties, "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 ); int size = region_width * region_height; uint8_t *alpha_duplicate = mlt_pool_alloc( size ); // Generate from the Y component of the image if no alpha available if ( alpha == NULL ) { alpha = alpha_duplicate; while ( size -- ) { *alpha ++ = ( int )( ( ( *image ++ - 16 ) * 299 ) / 255 ); image ++; } } else { memcpy( alpha_duplicate, alpha, size ); } mlt_frame_set_alpha( frame, alpha_duplicate, region_width * region_height, mlt_pool_release ); return alpha_duplicate; } /** 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 transition = mlt_frame_pop_service( 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( frame ); mlt_service_lock( MLT_TRANSITION_SERVICE( transition ) ); // 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 position mlt_position position = mlt_transition_get_position( transition, frame ); // 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( transition ) ); 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( transition, 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 ++; } } } } mlt_properties_set_int( a_props, "width", *width ); mlt_properties_set_int( a_props, "height", *height ); // 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 (region filter) if ( b_frame == NULL ) { // Copy the region b_frame = composite_copy_region( composite, frame, position ); // Ensure a destructor char name[64]; snprintf( name, sizeof(name), "region %s", mlt_properties_get( properties, "_unique_id" ) ); mlt_properties_set_data( a_props, name, b_frame, 0, ( mlt_destructor )mlt_frame_close, NULL ); } // Properties of the B frame mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame ); // filter_only prevents copying the alpha channel of the shape to the output frame // by compositing filtered frame over itself if ( mlt_properties_get_int( properties, "filter_only" ) ) { char name[64]; snprintf( name, sizeof(name), "region %s", mlt_properties_get( properties, "_unique_id" ) ); frame = composite_copy_region( composite, b_frame, position ); mlt_properties_set_data( b_props, name, frame, 0, ( mlt_destructor )mlt_frame_close, NULL ); } // 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( transition ) ); 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( b_props, "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 ); } mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) ); return error; } /** Filter processing. */ static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_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 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 transition = mlt_transition_new( ); // Further initialisation if ( transition != NULL ) { // Get the properties from the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); // Assign the transition process method transition->process = transition_process; // Default factory mlt_properties_set( properties, "factory", mlt_environment( "MLT_PRODUCER" ) ); // 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 transition; } mlt-6.20.0/src/modules/core/transition_region.h000066400000000000000000000020511362234133600214710ustar00rootroot00000000000000/* * transition_region.h -- region transition * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 mlt-6.20.0/src/modules/core/transition_region.yml000066400000000000000000000025631362234133600220530ustar00rootroot00000000000000schema_version: 0.1 type: transition identifier: region title: Regionalize version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video 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. parameters: - identifier: argument title: Shape producer type: string description: > The default shape is a rectangle, "circle" is a pixbuf-generated SVG circle, anything else is loaded by the factory. - identifier: factory title: Factory type: string description: > The service that creates the shape producer. default: loader - identifier: filter[N] title: Filter type: string description: > One or more filters to apply. All filter properties are passed using the same filter "key". - identifier: composite.* title: Composite type: properties service-name: transition.composite description: > Properties may be set on the encapsulated composite transition. e.g.: composite.valign=c See "composite" transition for details. readonly: no - identifier: filter_only title: Use region for filtering only type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/decklink/000077500000000000000000000000001362234133600164215ustar00rootroot00000000000000mlt-6.20.0/src/modules/decklink/CMakeLists.txt000066400000000000000000000014061362234133600211620ustar00rootroot00000000000000file(GLOB mltdecklink_src *.cpp) if(WIN32) list(APPEND mltdecklink_src win/DeckLinkAPI_i.cpp) set(mltdecklink_inc ${CMAKE_CURRENT_SOURCE_DIR}/win) elseif(APPLE) list(APPEND mltdecklink_src darwin/DeckLinkAPIDispatch.cpp) set(mltdecklink_inc ${CMAKE_CURRENT_SOURCE_DIR}/darwin) else() list(APPEND mltdecklink_src linux/DeckLinkAPIDispatch.cpp) set(mltdecklink_inc ${CMAKE_CURRENT_SOURCE_DIR}/linux) endif() add_library(mltdecklink MODULE ${mltdecklink_src}) target_link_libraries(mltdecklink mlt Threads::Threads) target_include_directories(mltdecklink PRIVATE ${mltdecklink_inc}) install(TARGETS mltdecklink LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/decklink) mlt-6.20.0/src/modules/decklink/Makefile000077500000000000000000000021601362234133600200630ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread include ../../../config.mak TARGET = ../libmltdecklink$(LIBSUF) OBJS = consumer_decklink.o \ producer_decklink.o \ common.o ifeq ($(targetos), MinGW) CFLAGS += -Iwin OBJS += win/DeckLinkAPI_i.o LDFLAGS += -lole32 -loleaut32 else ifeq ($(targetos), Darwin) CFLAGS += -Idarwin OBJS += darwin/DeckLinkAPIDispatch.o LDFLAGS += -framework CoreFoundation else CFLAGS += -Ilinux OBJS += linux/DeckLinkAPIDispatch.o endif endif SRCS := $(OBJS:.o=.cpp) CXXFLAGS += $(CFLAGS) -Wno-deprecated -Wno-multichar -fno-rtti LDFLAGS += $(LIBDL) all: $(TARGET) $(TARGET): $(OBJS) $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS) depend: $(SRCS) $(CXX) -MM $(CFLAGS) $^ 1>.depend distclean: clean rm -f .depend clean: rm -f $(OBJS) $(TARGET) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/decklink" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/decklink" uninstall: rm -f "$(DESTDIR)$(moduledir)/libmltdecklink$(LIBSUF)" rm -rf "$(DESTDIR)$(mltdatadir)/decklink" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/decklink/common.cpp000066400000000000000000000061221362234133600204160ustar00rootroot00000000000000/* * common.cpp -- Blackmagic Design DeckLink common functions * Copyright (C) 2012 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 consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #ifdef __APPLE__ char* getCString( DLString aDLString ) { char* CString = (char*) malloc( 64 ); CFStringGetCString( aDLString, CString, 64, kCFStringEncodingMacRoman ); return CString; } void freeCString( char* aCString ) { if ( aCString ) free( aCString ); } void freeDLString( DLString aDLString ) { if ( aDLString ) CFRelease( aDLString ); } #elif defined(_WIN32) char* getCString( DLString aDLString ) { char* CString = NULL; if ( aDLString ) { int size = WideCharToMultiByte( CP_UTF8, 0, aDLString, -1, NULL, 0, NULL, NULL ); if (size) { CString = new char[ size ]; size = WideCharToMultiByte( CP_UTF8, 0, aDLString, -1, CString, size, NULL, NULL ); if ( !size ) { delete[] CString; CString = NULL; } } } return CString; } void freeCString( char* aCString ) { delete[] aCString; } void freeDLString( DLString aDLString ) { SysFreeString( aDLString ); } #else char* getCString( DLString aDLString ) { return aDLString? (char*) aDLString : NULL; } void freeCString( char* aCString ) { } void freeDLString( DLString aDLString ) { if ( aDLString ) free( (void*) aDLString ); } #endif void swab2( const void *from, void *to, int n ) { #if defined(USE_SSE) #define SWAB_STEP 16 int cnt = n / SWAB_STEP; __asm__ volatile ( "loop_start: \n\t" /* load */ "movdqa 0(%[from]), %%xmm0 \n\t" "add $0x10, %[from] \n\t" /* duplicate to temp registers */ "movdqa %%xmm0, %%xmm1 \n\t" /* shift right temp register */ "psrlw $8, %%xmm1 \n\t" /* shift left main register */ "psllw $8, %%xmm0 \n\t" /* compose them back */ "por %%xmm0, %%xmm1 \n\t" /* save */ "movdqa %%xmm1, 0(%[to]) \n\t" "add $0x10, %[to] \n\t" "dec %[cnt] \n\t" "jnz loop_start \n\t" : [from]"+r"(from), [to]"+r"(to), [cnt]"+r"(cnt) : : "xmm0", "xmm1" ); from = (unsigned char*) from + n - (n % SWAB_STEP); to = (unsigned char*) to + n - (n % SWAB_STEP); n = (n % SWAB_STEP); #endif swab((char*) from, (char*) to, n); }; mlt-6.20.0/src/modules/decklink/common.h000066400000000000000000000025701362234133600200660ustar00rootroot00000000000000/* * common.h -- Blackmagic Design DeckLink common functions * Copyright (C) 2012 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 consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DECKLINK_COMMON_H #define DECKLINK_COMMON_H #ifdef _WIN32 # include # include "DeckLinkAPI_h.h" typedef BSTR DLString; #else # include "DeckLinkAPI.h" # ifdef __APPLE__ typedef CFStringRef DLString; # else typedef const char* DLString; # endif #endif #define SAFE_RELEASE(V) if (V) { V->Release(); V = NULL; } char* getCString( DLString aDLString ); void freeCString( char* aCString ); void freeDLString( DLString aDLString ); void swab2( const void *from, void *to, int n ); #endif // DECKLINK_COMMON_H mlt-6.20.0/src/modules/decklink/consumer_decklink.cpp000066400000000000000000000720471362234133600226360ustar00rootroot00000000000000/* * consumer_decklink.cpp -- output through Blackmagic Design DeckLink * Copyright (C) 2010-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_FORMAT_MACROS /* see inttypes.h */ #include #include #include #include #include #include #include #include #include "common.h" #define SWAB_SLICED_ALIGN_POW 5 static int swab_sliced( int id, int idx, int jobs, void* cookie ) { unsigned char** args = (unsigned char**)cookie; ssize_t sz = (ssize_t)args[2]; ssize_t bsz = ( ( sz / jobs + ( 1 << SWAB_SLICED_ALIGN_POW ) - 1 ) >> SWAB_SLICED_ALIGN_POW ) << SWAB_SLICED_ALIGN_POW; ssize_t offset = bsz * idx; if ( offset < sz ) { if ( ( offset + bsz ) > sz ) bsz = sz - offset; swab2( args[0] + offset, args[1] + offset, bsz ); } return 0; }; static const unsigned PREROLL_MINIMUM = 3; enum { OP_NONE = 0, OP_OPEN, OP_START, OP_STOP, OP_EXIT }; class DeckLinkConsumer : public IDeckLinkVideoOutputCallback , public IDeckLinkAudioOutputCallback { private: mlt_consumer_s m_consumer; IDeckLink* m_deckLink; IDeckLinkOutput* m_deckLinkOutput; IDeckLinkDisplayMode* m_displayMode; int m_width; int m_height; BMDTimeValue m_duration; BMDTimeScale m_timescale; double m_fps; uint64_t m_count; int m_outChannels; int m_inChannels; bool m_isAudio; int m_isKeyer; IDeckLinkKeyer* m_deckLinkKeyer; bool m_terminate_on_pause; uint32_t m_preroll; uint32_t m_reprio; mlt_deque m_aqueue; pthread_mutex_t m_aqueue_lock; mlt_deque m_frames; pthread_mutex_t m_op_lock; pthread_mutex_t m_op_arg_mutex; pthread_cond_t m_op_arg_cond; int m_op_id; int m_op_res; int m_op_arg; pthread_t m_op_thread; bool m_sliced_swab; uint8_t* m_buffer; IDeckLinkDisplayMode* getDisplayMode() { mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) ); IDeckLinkDisplayModeIterator* iter = NULL; IDeckLinkDisplayMode* mode = NULL; IDeckLinkDisplayMode* result = 0; if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK ) { while ( !result && iter->Next( &mode ) == S_OK ) { m_width = mode->GetWidth(); m_height = mode->GetHeight(); mode->GetFrameRate( &m_duration, &m_timescale ); m_fps = (double) m_timescale / m_duration; int p = mode->GetFieldDominance() == bmdProgressiveFrame; mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p ); if ( m_width == profile->width && p == profile->progressive && (int) m_fps == (int) mlt_profile_fps( profile ) && ( m_height == profile->height || ( m_height == 486 && profile->height == 480 ) ) ) result = mode; else SAFE_RELEASE( mode ); } SAFE_RELEASE( iter ); } return result; } public: mlt_consumer getConsumer() { return &m_consumer; } DeckLinkConsumer() { pthread_mutexattr_t mta; m_displayMode = NULL; m_deckLinkKeyer = NULL; m_deckLinkOutput = NULL; m_deckLink = NULL; m_aqueue = mlt_deque_init(); m_frames = mlt_deque_init(); m_buffer = NULL; // operation locks m_op_id = OP_NONE; m_op_arg = 0; pthread_mutexattr_init( &mta ); pthread_mutexattr_settype( &mta, PTHREAD_MUTEX_RECURSIVE ); pthread_mutex_init( &m_op_lock, &mta ); pthread_mutex_init( &m_op_arg_mutex, &mta ); pthread_mutex_init( &m_aqueue_lock, &mta ); pthread_mutexattr_destroy( &mta ); pthread_cond_init( &m_op_arg_cond, NULL ); pthread_create( &m_op_thread, NULL, op_main, this ); } virtual ~DeckLinkConsumer() { mlt_log_debug( getConsumer(), "%s: entering\n", __FUNCTION__ ); SAFE_RELEASE( m_displayMode ); SAFE_RELEASE( m_deckLinkKeyer ); SAFE_RELEASE( m_deckLinkOutput ); SAFE_RELEASE( m_deckLink ); mlt_deque_close( m_aqueue ); mlt_deque_close( m_frames ); op(OP_EXIT, 0); mlt_log_debug( getConsumer(), "%s: waiting for op thread\n", __FUNCTION__ ); pthread_join(m_op_thread, NULL); mlt_log_debug( getConsumer(), "%s: finished op thread\n", __FUNCTION__ ); pthread_mutex_destroy( &m_aqueue_lock ); pthread_mutex_destroy(&m_op_lock); pthread_mutex_destroy(&m_op_arg_mutex); pthread_cond_destroy(&m_op_arg_cond); mlt_log_debug( getConsumer(), "%s: exiting\n", __FUNCTION__ ); } int op(int op_id, int arg) { int r; // lock operation mutex pthread_mutex_lock(&m_op_lock); mlt_log_debug( getConsumer(), "%s: op_id=%d\n", __FUNCTION__, op_id ); // notify op id pthread_mutex_lock(&m_op_arg_mutex); m_op_id = op_id; m_op_arg = arg; pthread_cond_signal(&m_op_arg_cond); pthread_mutex_unlock(&m_op_arg_mutex); // wait op done pthread_mutex_lock(&m_op_arg_mutex); while(OP_NONE != m_op_id) pthread_cond_wait(&m_op_arg_cond, &m_op_arg_mutex); pthread_mutex_unlock(&m_op_arg_mutex); // save result r = m_op_res; mlt_log_debug( getConsumer(), "%s: r=%d\n", __FUNCTION__, r ); // unlock operation mutex pthread_mutex_unlock(&m_op_lock); return r; } protected: static void* op_main(void* thisptr) { DeckLinkConsumer* d = static_cast(thisptr); mlt_log_debug( d->getConsumer(), "%s: entering\n", __FUNCTION__ ); for (;;) { int o, r = 0; // wait op command pthread_mutex_lock ( &d->m_op_arg_mutex ); while ( OP_NONE == d->m_op_id ) pthread_cond_wait( &d->m_op_arg_cond, &d->m_op_arg_mutex ); pthread_mutex_unlock( &d->m_op_arg_mutex ); o = d->m_op_id; mlt_log_debug( d->getConsumer(), "%s:%d d->m_op_id=%d\n", __FUNCTION__, __LINE__, d->m_op_id ); switch ( d->m_op_id ) { case OP_OPEN: r = d->m_op_res = d->open( d->m_op_arg ); break; case OP_START: r = d->m_op_res = d->start( d->m_op_arg ); break; case OP_STOP: r = d->m_op_res = d->stop(); break; }; // notify op done pthread_mutex_lock( &d->m_op_arg_mutex ); d->m_op_id = OP_NONE; pthread_cond_signal( &d->m_op_arg_cond ); pthread_mutex_unlock( &d->m_op_arg_mutex ); // post for async if ( OP_START == o && r ) d->preroll(); if ( OP_EXIT == o ) { mlt_log_debug( d->getConsumer(), "%s: exiting\n", __FUNCTION__ ); return NULL; } }; return NULL; } bool open( unsigned card = 0 ) { unsigned i = 0; #ifdef _WIN32 IDeckLinkIterator* deckLinkIterator = NULL; HRESULT result = CoInitialize( NULL ); if ( FAILED( result ) ) { mlt_log_error( getConsumer(), "COM initialization failed\n" ); return false; } result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &deckLinkIterator ); if ( FAILED( result ) ) { mlt_log_warning( getConsumer(), "The DeckLink drivers not installed.\n" ); return false; } #else IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance(); if ( !deckLinkIterator ) { mlt_log_warning( getConsumer(), "The DeckLink drivers not installed.\n" ); return false; } #endif // Connect to the Nth DeckLink instance for ( i = 0; deckLinkIterator->Next( &m_deckLink ) == S_OK ; i++) { if( i == card ) break; else SAFE_RELEASE( m_deckLink ); } SAFE_RELEASE( deckLinkIterator ); if ( !m_deckLink ) { mlt_log_error( getConsumer(), "DeckLink card not found\n" ); return false; } // Obtain the audio/video output interface (IDeckLinkOutput) if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK ) { mlt_log_error( getConsumer(), "No DeckLink cards support output\n" ); SAFE_RELEASE( m_deckLink ); return false; } // Get the keyer interface IDeckLinkAttributes *deckLinkAttributes = 0; if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK ) { #ifdef _WIN32 BOOL flag = FALSE; #else bool flag = false; #endif if ( deckLinkAttributes->GetFlag( BMDDeckLinkSupportsInternalKeying, &flag ) == S_OK && flag ) { if ( m_deckLink->QueryInterface( IID_IDeckLinkKeyer, (void**) &m_deckLinkKeyer ) != S_OK ) { mlt_log_error( getConsumer(), "Failed to get keyer\n" ); SAFE_RELEASE( m_deckLinkOutput ); SAFE_RELEASE( m_deckLink ); return false; } } SAFE_RELEASE( deckLinkAttributes ); } // Provide this class as a delegate to the audio and video output interfaces m_deckLinkOutput->SetScheduledFrameCompletionCallback( this ); m_deckLinkOutput->SetAudioCallback( this ); return true; } int preroll() { mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); mlt_log_debug( getConsumer(), "%s: starting\n", __FUNCTION__ ); if ( !mlt_properties_get_int( properties, "running" ) ) return 0; mlt_log_verbose( getConsumer(), "preroll %u frames\n", m_preroll ); // preroll frames for ( unsigned i = 0; i < m_preroll ; i++ ) ScheduleNextFrame( true ); // start audio preroll if ( m_isAudio ) m_deckLinkOutput->BeginAudioPreroll( ); else m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 ); mlt_log_debug( getConsumer(), "%s: exiting\n", __FUNCTION__ ); return 0; } bool start( unsigned preroll ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); // Initialize members m_count = 0; m_buffer = NULL; preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll; m_inChannels = mlt_properties_get_int( properties, "channels" ); if( m_inChannels <= 2 ) { m_outChannels = 2; } else if( m_inChannels <= 8 ) { m_outChannels = 8; } else { m_outChannels = 16; } m_isAudio = !mlt_properties_get_int( properties, "audio_off" ); m_terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); m_displayMode = getDisplayMode(); if ( !m_displayMode ) { mlt_log_error( getConsumer(), "Profile is not compatible with decklink.\n" ); return false; } mlt_properties_set_int( properties, "top_field_first", m_displayMode->GetFieldDominance() == bmdUpperFieldFirst ); // Set the keyer if ( m_deckLinkKeyer && ( m_isKeyer = mlt_properties_get_int( properties, "keyer" ) ) ) { bool external = ( m_isKeyer == 2 ); double level = mlt_properties_get_double( properties, "keyer_level" ); if ( m_deckLinkKeyer->Enable( external ) != S_OK ) mlt_log_error( getConsumer(), "Failed to enable %s keyer\n", external ? "external" : "internal" ); m_deckLinkKeyer->SetLevel( level <= 1 ? ( level > 0 ? 255 * level : 255 ) : 255 ); } else if ( m_deckLinkKeyer ) { m_deckLinkKeyer->Disable(); } // Set the video output mode if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), (BMDVideoOutputFlags) (bmdVideoOutputFlagDefault | bmdVideoOutputRP188 | bmdVideoOutputVITC) ) ) { mlt_log_error( getConsumer(), "Failed to enable video output\n" ); return false; } // Set the audio output mode if ( m_isAudio && S_OK != m_deckLinkOutput->EnableAudioOutput( bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, m_outChannels, bmdAudioOutputStreamTimestamped ) ) { mlt_log_error( getConsumer(), "Failed to enable audio output\n" ); stop(); return false; } m_preroll = preroll; m_reprio = 2; for ( unsigned i = 0; i < ( m_preroll + 2 ) ; i++) { IDeckLinkMutableVideoFrame* frame; // Generate a DeckLink video frame if ( S_OK != m_deckLinkOutput->CreateVideoFrame( m_width, m_height, m_width * ( m_isKeyer? 4 : 2 ), m_isKeyer? bmdFormat8BitARGB : bmdFormat8BitYUV, bmdFrameFlagDefault, &frame ) ) { mlt_log_error( getConsumer(), "%s: CreateVideoFrame (%d) failed\n", __FUNCTION__, i ); return false; } mlt_deque_push_back( m_frames, frame ); } // Set the running state mlt_properties_set_int( properties, "running", 1 ); return true; } bool stop() { mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); mlt_log_debug( getConsumer(), "%s: starting\n", __FUNCTION__ ); // Stop the audio and video output streams immediately if ( m_deckLinkOutput ) { m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 ); m_deckLinkOutput->DisableAudioOutput(); m_deckLinkOutput->DisableVideoOutput(); } pthread_mutex_lock( &m_aqueue_lock ); while ( mlt_frame frame = (mlt_frame) mlt_deque_pop_back( m_aqueue ) ) mlt_frame_close( frame ); pthread_mutex_unlock( &m_aqueue_lock ); m_buffer = NULL; while ( IDeckLinkMutableVideoFrame* frame = (IDeckLinkMutableVideoFrame*) mlt_deque_pop_back( m_frames ) ) SAFE_RELEASE( frame ); // set running state is 0 mlt_properties_set_int( properties, "running", 0 ); mlt_consumer_stopped( getConsumer() ); mlt_log_debug( getConsumer(), "%s: exiting\n", __FUNCTION__ ); return true; } void renderAudio( mlt_frame frame ) { mlt_properties properties; properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set_int64( properties, "m_count", m_count); mlt_properties_inc_ref( properties ); pthread_mutex_lock( &m_aqueue_lock ); mlt_deque_push_back( m_aqueue, frame ); mlt_log_debug( getConsumer(), "%s:%d frame=%p, len=%d\n", __FUNCTION__, __LINE__, frame, mlt_deque_count( m_aqueue )); pthread_mutex_unlock( &m_aqueue_lock ); } void renderVideo( mlt_frame frame ) { HRESULT hr; mlt_image_format format = m_isKeyer? mlt_image_rgb24a : mlt_image_yuv422; uint8_t* image = 0; int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered"); mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); int stride = m_width * ( m_isKeyer? 4 : 2 ); int height = m_height; IDeckLinkMutableVideoFrame* decklinkFrame = static_cast( mlt_deque_pop_front( m_frames ) ); mlt_log_debug( getConsumer(), "%s: entering\n", __FUNCTION__ ); m_sliced_swab = mlt_properties_get_int( consumer_properties, "sliced_swab" ); if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &height, 0 ) ) { if ( decklinkFrame ) decklinkFrame->GetBytes( (void**) &m_buffer ); if ( m_buffer ) { // NTSC SDI is always 486 lines if ( m_height == 486 && height == 480 ) { // blank first 6 lines if ( m_isKeyer ) { memset( m_buffer, 0, stride * 6 ); m_buffer += stride * 6; } else for ( int i = 0; i < m_width * 6; i++ ) { *m_buffer++ = 128; *m_buffer++ = 16; } } if ( !m_isKeyer ) { unsigned char *arg[3] = { image, m_buffer }; ssize_t size = stride * height; // Normal non-keyer playout - needs byte swapping if ( !m_sliced_swab ) swab2( arg[0], arg[1], size ); else { arg[2] = (unsigned char*)size; mlt_slices_run_fifo( 0, swab_sliced, arg); } } else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) ) { // Normal keyer output int y = height + 1; uint32_t* s = (uint32_t*) image; uint32_t* d = (uint32_t*) m_buffer; // Need to relocate alpha channel RGBA => ARGB while ( --y ) { int x = m_width + 1; while ( --x ) { *d++ = ( *s << 8 ) | ( *s >> 24 ); s++; } } } else { // Keying blank frames - nullify alpha memset( m_buffer, 0, stride * height ); } } } else if ( decklinkFrame ) { uint8_t* buffer = NULL; decklinkFrame->GetBytes( (void**) &buffer ); if ( buffer ) memcpy( buffer, m_buffer, stride * height ); } if ( decklinkFrame ) { char* vitc; // set timecode vitc = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup" ); if( vitc ) { int h, m, s, f; if ( 4 == sscanf( vitc, "%d:%d:%d:%d", &h, &m, &s, &f ) ) decklinkFrame->SetTimecodeFromComponents(bmdTimecodeVITC, h, m, s, f, bmdTimecodeFlagDefault); } // set userbits vitc = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.userbits" ); if( vitc ) decklinkFrame->SetTimecodeUserBits(bmdTimecodeVITC, mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.userbits" )); hr = m_deckLinkOutput->ScheduleVideoFrame( decklinkFrame, m_count * m_duration, m_duration, m_timescale ); if ( S_OK != hr ) mlt_log_error( getConsumer(), "%s:%d: ScheduleVideoFrame failed, hr=%.8X \n", __FUNCTION__, __LINE__, unsigned(hr) ); else mlt_log_debug( getConsumer(), "%s: ScheduleVideoFrame SUCCESS\n", __FUNCTION__ ); } } HRESULT render( mlt_frame frame ) { HRESULT result = S_OK; // Get the audio double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" ); if ( m_isAudio && speed == 1.0 ) renderAudio( frame ); // Get the video renderVideo( frame ); ++m_count; return result; } void reprio( int target ) { int r; pthread_t thread; pthread_attr_t tattr; struct sched_param param; mlt_properties properties; if( m_reprio & target ) return; m_reprio |= target; properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); if ( !mlt_properties_get( properties, "priority" ) ) return; pthread_attr_init(&tattr); pthread_attr_setschedpolicy(&tattr, SCHED_FIFO); if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) ) param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1; else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) ) param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1; else param.sched_priority = mlt_properties_get_int( properties, "priority" ); pthread_attr_setschedparam(&tattr, ¶m); thread = pthread_self(); r = pthread_setschedparam(thread, SCHED_FIFO, ¶m); if( r ) mlt_log_error( getConsumer(), "%s: [%d] pthread_setschedparam returned %d\n", __FUNCTION__, target, r); else mlt_log_verbose( getConsumer(), "%s: [%d] param.sched_priority=%d\n", __FUNCTION__, target, param.sched_priority); } // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** // // IUnknown needs only a dummy implementation virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv ) { return E_NOINTERFACE; } virtual ULONG STDMETHODCALLTYPE AddRef() { return 1; } virtual ULONG STDMETHODCALLTYPE Release() { return 1; } /************************* DeckLink API Delegate Methods *****************************/ #ifdef _WIN32 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples ( BOOL preroll ) #else virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples ( bool preroll ) #endif { pthread_mutex_lock( &m_aqueue_lock ); mlt_log_debug( getConsumer(), "%s: ENTERING preroll=%d, len=%d\n", __FUNCTION__, (int)preroll, mlt_deque_count( m_aqueue )); mlt_frame frame = (mlt_frame) mlt_deque_pop_front( m_aqueue ); pthread_mutex_unlock( &m_aqueue_lock ); reprio( 2 ); if ( frame ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); uint64_t m_count = mlt_properties_get_int64( properties, "m_count" ); mlt_audio_format format = mlt_audio_s16; int frequency = bmdAudioSampleRate48kHz; int samples = mlt_sample_calculator( m_fps, frequency, m_count ); int16_t *pcm = 0; if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_inChannels, &samples ) ) { HRESULT hr; int16_t* outBuff = NULL; mlt_log_debug( getConsumer(), "%s:%d, samples=%d, channels=%d, freq=%d\n", __FUNCTION__, __LINE__, samples, m_inChannels, frequency ); if( m_inChannels != m_outChannels ) { int s = 0; int c = 0; int size = mlt_audio_format_size( format, samples, m_outChannels ); int16_t* src = pcm; int16_t* dst = (int16_t*)mlt_pool_alloc( size ); outBuff = dst; for( s = 0; s < samples; s++ ) { for( c = 0; c < m_outChannels; c++ ) { if( c < m_inChannels ) { *dst = *src; src++; } else { // Fill silence if there are more out channels than in channels. *dst = 0; } } for( c = 0; c < m_inChannels - m_outChannels; c++ ) { // Drop samples if there are more in channels than out channels. src++; } } pcm = outBuff; } #ifdef _WIN32 #define DECKLINK_UNSIGNED_FORMAT "%lu" unsigned long written = 0; #else #define DECKLINK_UNSIGNED_FORMAT "%u" uint32_t written = 0; #endif BMDTimeValue streamTime = m_count * frequency * m_duration / m_timescale; #ifdef _WIN32 hr = m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, (unsigned long*) &written ); #else hr = m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, &written ); #endif if ( S_OK != hr ) mlt_log_error( getConsumer(), "%s:%d ScheduleAudioSamples failed, hr=%.8X \n", __FUNCTION__, __LINE__, unsigned(hr) ); else mlt_log_debug( getConsumer(), "%s:%d ScheduleAudioSamples success " DECKLINK_UNSIGNED_FORMAT " samples\n", __FUNCTION__, __LINE__, written ); if ( written != (uint32_t) samples ) mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=" DECKLINK_UNSIGNED_FORMAT "\n", samples, written ); mlt_pool_release( outBuff ); } else mlt_log_error( getConsumer(), "%s:%d mlt_frame_get_audio failed\n", __FUNCTION__, __LINE__); mlt_frame_close( frame ); if ( !preroll ) RenderAudioSamples ( preroll ); } if ( preroll ) m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 ); return S_OK; } virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed ) { mlt_log_debug( getConsumer(), "%s: ENTERING\n", __FUNCTION__ ); mlt_deque_push_back( m_frames, completedFrame ); // change priority of video callback thread reprio( 1 ); // When a video frame has been released by the API, schedule another video frame to be output // ignore handler if frame was flushed if ( bmdOutputFrameFlushed == completed ) return S_OK; // schedule next frame ScheduleNextFrame( false ); // step forward frames counter if underrun if ( bmdOutputFrameDisplayedLate == completed ) { mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n" ); } if ( bmdOutputFrameDropped == completed ) { mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped == completed\n" ); m_count++; ScheduleNextFrame( false ); } return S_OK; } virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped() { return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK; } void ScheduleNextFrame( bool preroll ) { // get the consumer mlt_consumer consumer = getConsumer(); // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Frame and size mlt_frame frame = NULL; mlt_log_debug( getConsumer(), "%s:%d: preroll=%d\n", __FUNCTION__, __LINE__, preroll); while ( !frame && (mlt_properties_get_int( properties, "running" ) || preroll ) ) { mlt_log_timings_begin(); frame = mlt_consumer_rt_frame( consumer ); mlt_log_timings_end( NULL, "mlt_consumer_rt_frame" ); if ( frame ) { mlt_log_timings_begin(); render( frame ); mlt_log_timings_end( NULL, "render" ); mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); // terminate on pause if ( m_terminate_on_pause && mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0 ) stop(); mlt_frame_close( frame ); } else mlt_log_warning( getConsumer(), "%s: mlt_consumer_rt_frame return NULL\n", __FUNCTION__ ); } } }; /** Start the consumer. */ static int start( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child; return decklink->op( OP_START, mlt_properties_get_int( properties, "preroll" ) ) ? 0 : 1; } /** Stop the consumer. */ static int stop( mlt_consumer consumer ) { int r; mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__ ); // Get the properties DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child; r = decklink->op(OP_STOP, 0); mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__ ); return r; } /** Determine if the consumer is stopped. */ static int is_stopped( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); return !mlt_properties_get_int( properties, "running" ); } /** Close the consumer. */ static void close( mlt_consumer consumer ) { mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__ ); // Stop the consumer mlt_consumer_stop( consumer ); // Close the parent consumer->close = NULL; mlt_consumer_close( consumer ); // Free the memory delete (DeckLinkConsumer*) consumer->child; mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__ ); } extern "C" { // Listen for the list_devices property to be set static void on_property_changed( void*, mlt_properties properties, const char *name ) { IDeckLinkIterator* decklinkIterator = NULL; IDeckLink* decklink = NULL; IDeckLinkInput* decklinkOutput = NULL; int i = 0; if ( name && !strcmp( name, "list_devices" ) ) mlt_event_block( (mlt_event) mlt_properties_get_data( properties, "list-devices-event", NULL ) ); else return; #ifdef _WIN32 if ( FAILED( CoInitialize( NULL ) ) ) return; if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) ) return; #else if ( !( decklinkIterator = CreateDeckLinkIteratorInstance() ) ) return; #endif for ( ; decklinkIterator->Next( &decklink ) == S_OK; i++ ) { if ( decklink->QueryInterface( IID_IDeckLinkOutput, (void**) &decklinkOutput ) == S_OK ) { DLString name = NULL; if ( decklink->GetModelName( &name ) == S_OK ) { char *name_cstr = getCString( name ); const char *format = "device.%d"; char *key = (char*) calloc( 1, strlen( format ) + 1 ); sprintf( key, format, i ); mlt_properties_set( properties, key, name_cstr ); free( key ); freeDLString( name ); freeCString( name_cstr ); } SAFE_RELEASE( decklinkOutput ); } SAFE_RELEASE( decklink ); } SAFE_RELEASE( decklinkIterator ); mlt_properties_set_int( properties, "devices", i ); } /** Initialise the consumer. */ mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Allocate the consumer DeckLinkConsumer* decklink = new DeckLinkConsumer(); mlt_consumer consumer = NULL; // If allocated if ( !mlt_consumer_init( decklink->getConsumer(), decklink, profile ) ) { // If initialises without error if ( decklink->op( OP_OPEN, arg? atoi(arg) : 0 ) ) { consumer = decklink->getConsumer(); mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Setup callbacks consumer->close = close; consumer->start = start; consumer->stop = stop; consumer->is_stopped = is_stopped; mlt_properties_set( properties, "deinterlace_method", "onefield" ); mlt_event event = mlt_events_listen( properties, properties, "property-changed", (mlt_listener) on_property_changed ); mlt_properties_set_data( properties, "list-devices-event", event, 0, NULL, NULL ); } } // Return consumer return consumer; } extern mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties 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 producer_type: service_type = "producer"; break; default: return NULL; } snprintf( file, PATH_MAX, "%s/decklink/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "decklink", consumer_decklink_init ); MLT_REGISTER( producer_type, "decklink", producer_decklink_init ); MLT_REGISTER_METADATA( consumer_type, "decklink", metadata, NULL ); MLT_REGISTER_METADATA( producer_type, "decklink", metadata, NULL ); } } // extern C mlt-6.20.0/src/modules/decklink/consumer_decklink.yml000066400000000000000000000055711362234133600226530ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: decklink title: Blackmagic Design DeckLink Output version: 2 copyright: Copyright (C) 2010-2018 Meltytech, LLC license: LGPL language: en creator: Dan Dennedy tags: - Audio - Video description: > Output audio and video using Blackmagic Design DeckLink SDI or Intensity HDMI cards. notes: > Please ensure that you use a MLT profile that is compatible with a broadcast standard which the card you are using supports. bugs: - Only internal keying is supported at this time. - Only 8-bit Y'CbCr or RGBA (key) is supported at this time. parameters: - identifier: argument title: Card type: integer readonly: no required: no mutable: no default: 0 minimum: 0 widget: spinner - identifier: preroll title: Pre-roll Count type: integer description: > This controls the amount of buffering in the DeckLink driver/library. Increase this if you get video tearing or choppy audio. However, as you increase the amount, you increase the risk of audio and video becoming out of synchronization. readonly: no required: no mutable: no default: 3 minimum: 2 unit: frames widget: spinner - identifier: keyer title: Enable Keyer type: integer description: > Keying is the process of compositing MLT output over a live SDI input. The alpha channel of the MLT video controls the transparent areas, and the keyer supports alpha-blending. You can not control the compositing rectangle. Rather, the entire MLT output overlays the entire video input. Therefore, you must use MLT's compositing services to control the size and position. The value 1 enables the internal keyer, the value 2 enables the external keyer, and the value 0 disables it. readonly: no required: no mutable: no default: 0 minimum: 0 maximum: 2 - identifier: keyer_level title: Key Opacity type: float description: > This controls the level of blending between the key and the input video. 1 is fully opaque and something near 0 is transparent. However, absolute 0 is considered as "not supplied" and also fully opaque. 0.5 is an evenly balanced blending of the key and input video. readonly: no required: no mutable: no minimum: 0 maximum: 1 default: 1 widget: slider - identifier: devices title: Number of devices type: integer readonly: yes minimum: 0 - identifier: device.* title: Device model description: The model name of each device that provides output. type: string readonly: yes - identifier: sliced_swab title: Use sliced swab operation description: This option enables multithreaded parallel swab frame data operation type: boolean readonly: no minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/decklink/darwin/000077500000000000000000000000001362234133600177055ustar00rootroot00000000000000mlt-6.20.0/src/modules/decklink/darwin/DeckLinkAPI.h000077500000000000000000001505101362234133600221010ustar00rootroot00000000000000/* -LICENSE-START- ** Copyright (c) 2011 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* DeckLinkAPI.h */ #ifndef __DeckLink_API_h__ #define __DeckLink_API_h__ #include #include #include #define BLACKMAGIC_DECKLINK_API_MAGIC 1 // Type Declarations typedef int64_t BMDTimeValue; typedef int64_t BMDTimeScale; typedef uint32_t BMDTimecodeBCD; typedef uint32_t BMDTimecodeUserBits; // Interface ID Declarations #define IID_IDeckLinkVideoOutputCallback /* 20AA5225-1958-47CB-820B-80A8D521A6EE */ (REFIID){0x20,0xAA,0x52,0x25,0x19,0x58,0x47,0xCB,0x82,0x0B,0x80,0xA8,0xD5,0x21,0xA6,0xEE} #define IID_IDeckLinkInputCallback /* DD04E5EC-7415-42AB-AE4A-E80C4DFC044A */ (REFIID){0xDD,0x04,0xE5,0xEC,0x74,0x15,0x42,0xAB,0xAE,0x4A,0xE8,0x0C,0x4D,0xFC,0x04,0x4A} #define IID_IDeckLinkMemoryAllocator /* B36EB6E7-9D29-4AA8-92EF-843B87A289E8 */ (REFIID){0xB3,0x6E,0xB6,0xE7,0x9D,0x29,0x4A,0xA8,0x92,0xEF,0x84,0x3B,0x87,0xA2,0x89,0xE8} #define IID_IDeckLinkAudioOutputCallback /* 403C681B-7F46-4A12-B993-2BB127084EE6 */ (REFIID){0x40,0x3C,0x68,0x1B,0x7F,0x46,0x4A,0x12,0xB9,0x93,0x2B,0xB1,0x27,0x08,0x4E,0xE6} #define IID_IDeckLinkIterator /* 74E936FC-CC28-4A67-81A0-1E94E52D4E69 */ (REFIID){0x74,0xE9,0x36,0xFC,0xCC,0x28,0x4A,0x67,0x81,0xA0,0x1E,0x94,0xE5,0x2D,0x4E,0x69} #define IID_IDeckLinkAPIInformation /* 7BEA3C68-730D-4322-AF34-8A7152B532A4 */ (REFIID){0x7B,0xEA,0x3C,0x68,0x73,0x0D,0x43,0x22,0xAF,0x34,0x8A,0x71,0x52,0xB5,0x32,0xA4} #define IID_IDeckLinkDisplayModeIterator /* 9C88499F-F601-4021-B80B-032E4EB41C35 */ (REFIID){0x9C,0x88,0x49,0x9F,0xF6,0x01,0x40,0x21,0xB8,0x0B,0x03,0x2E,0x4E,0xB4,0x1C,0x35} #define IID_IDeckLinkDisplayMode /* 3EB2C1AB-0A3D-4523-A3AD-F40D7FB14E78 */ (REFIID){0x3E,0xB2,0xC1,0xAB,0x0A,0x3D,0x45,0x23,0xA3,0xAD,0xF4,0x0D,0x7F,0xB1,0x4E,0x78} #define IID_IDeckLink /* 62BFF75D-6569-4E55-8D4D-66AA03829ABC */ (REFIID){0x62,0xBF,0xF7,0x5D,0x65,0x69,0x4E,0x55,0x8D,0x4D,0x66,0xAA,0x03,0x82,0x9A,0xBC} #define IID_IDeckLinkOutput /* A3EF0963-0862-44ED-92A9-EE89ABF431C7 */ (REFIID){0xA3,0xEF,0x09,0x63,0x08,0x62,0x44,0xED,0x92,0xA9,0xEE,0x89,0xAB,0xF4,0x31,0xC7} #define IID_IDeckLinkInput /* 6D40EF78-28B9-4E21-990D-95BB7750A04F */ (REFIID){0x6D,0x40,0xEF,0x78,0x28,0xB9,0x4E,0x21,0x99,0x0D,0x95,0xBB,0x77,0x50,0xA0,0x4F} #define IID_IDeckLinkTimecode /* BC6CFBD3-8317-4325-AC1C-1216391E9340 */ (REFIID){0xBC,0x6C,0xFB,0xD3,0x83,0x17,0x43,0x25,0xAC,0x1C,0x12,0x16,0x39,0x1E,0x93,0x40} #define IID_IDeckLinkVideoFrame /* 3F716FE0-F023-4111-BE5D-EF4414C05B17 */ (REFIID){0x3F,0x71,0x6F,0xE0,0xF0,0x23,0x41,0x11,0xBE,0x5D,0xEF,0x44,0x14,0xC0,0x5B,0x17} #define IID_IDeckLinkMutableVideoFrame /* 69E2639F-40DA-4E19-B6F2-20ACE815C390 */ (REFIID){0x69,0xE2,0x63,0x9F,0x40,0xDA,0x4E,0x19,0xB6,0xF2,0x20,0xAC,0xE8,0x15,0xC3,0x90} #define IID_IDeckLinkVideoFrame3DExtensions /* DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7 */ (REFIID){0xDA,0x0F,0x7E,0x4A,0xED,0xC7,0x48,0xA8,0x9C,0xDD,0x2D,0xB5,0x1C,0x72,0x9C,0xD7} #define IID_IDeckLinkVideoInputFrame /* 05CFE374-537C-4094-9A57-680525118F44 */ (REFIID){0x05,0xCF,0xE3,0x74,0x53,0x7C,0x40,0x94,0x9A,0x57,0x68,0x05,0x25,0x11,0x8F,0x44} #define IID_IDeckLinkVideoFrameAncillary /* 732E723C-D1A4-4E29-9E8E-4A88797A0004 */ (REFIID){0x73,0x2E,0x72,0x3C,0xD1,0xA4,0x4E,0x29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04} #define IID_IDeckLinkAudioInputPacket /* E43D5870-2894-11DE-8C30-0800200C9A66 */ (REFIID){0xE4,0x3D,0x58,0x70,0x28,0x94,0x11,0xDE,0x8C,0x30,0x08,0x00,0x20,0x0C,0x9A,0x66} #define IID_IDeckLinkScreenPreviewCallback /* B1D3F49A-85FE-4C5D-95C8-0B5D5DCCD438 */ (REFIID){0xB1,0xD3,0xF4,0x9A,0x85,0xFE,0x4C,0x5D,0x95,0xC8,0x0B,0x5D,0x5D,0xCC,0xD4,0x38} #define IID_IDeckLinkCocoaScreenPreviewCallback /* D174152F-8F96-4C07-83A5-DD5F5AF0A2AA */ (REFIID){0xD1,0x74,0x15,0x2F,0x8F,0x96,0x4C,0x07,0x83,0xA5,0xDD,0x5F,0x5A,0xF0,0xA2,0xAA} #define IID_IDeckLinkGLScreenPreviewHelper /* 504E2209-CAC7-4C1A-9FB4-C5BB6274D22F */ (REFIID){0x50,0x4E,0x22,0x09,0xCA,0xC7,0x4C,0x1A,0x9F,0xB4,0xC5,0xBB,0x62,0x74,0xD2,0x2F} #define IID_IDeckLinkConfiguration /* C679A35B-610C-4D09-B748-1D0478100FC0 */ (REFIID){0xC6,0x79,0xA3,0x5B,0x61,0x0C,0x4D,0x09,0xB7,0x48,0x1D,0x04,0x78,0x10,0x0F,0xC0} #define IID_IDeckLinkAttributes /* ABC11843-D966-44CB-96E2-A1CB5D3135C4 */ (REFIID){0xAB,0xC1,0x18,0x43,0xD9,0x66,0x44,0xCB,0x96,0xE2,0xA1,0xCB,0x5D,0x31,0x35,0xC4} #define IID_IDeckLinkKeyer /* 89AFCAF5-65F8-421E-98F7-96FE5F5BFBA3 */ (REFIID){0x89,0xAF,0xCA,0xF5,0x65,0xF8,0x42,0x1E,0x98,0xF7,0x96,0xFE,0x5F,0x5B,0xFB,0xA3} #define IID_IDeckLinkVideoConversion /* 3BBCB8A2-DA2C-42D9-B5D8-88083644E99A */ (REFIID){0x3B,0xBC,0xB8,0xA2,0xDA,0x2C,0x42,0xD9,0xB5,0xD8,0x88,0x08,0x36,0x44,0xE9,0x9A} #define IID_IDeckLinkDeckControlStatusCallback /* E5F693C1-4283-4716-B18F-C1431521955B */ (REFIID){0xE5,0xF6,0x93,0xC1,0x42,0x83,0x47,0x16,0xB1,0x8F,0xC1,0x43,0x15,0x21,0x95,0x5B} #define IID_IDeckLinkDeckControl /* 522A9E39-0F3C-4742-94EE-D80DE335DA1D */ (REFIID){0x52,0x2A,0x9E,0x39,0x0F,0x3C,0x47,0x42,0x94,0xEE,0xD8,0x0D,0xE3,0x35,0xDA,0x1D} /* Enum BMDDisplayMode - Video display modes */ typedef uint32_t BMDDisplayMode; enum _BMDDisplayMode { /* SD Modes */ bmdModeNTSC = 'ntsc', bmdModeNTSC2398 = 'nt23', // 3:2 pulldown bmdModePAL = 'pal ', bmdModeNTSCp = 'ntsp', bmdModePALp = 'palp', /* HD 1080 Modes */ bmdModeHD1080p2398 = '23ps', bmdModeHD1080p24 = '24ps', bmdModeHD1080p25 = 'Hp25', bmdModeHD1080p2997 = 'Hp29', bmdModeHD1080p30 = 'Hp30', bmdModeHD1080i50 = 'Hi50', bmdModeHD1080i5994 = 'Hi59', bmdModeHD1080i6000 = 'Hi60', // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p50 = 'Hp50', bmdModeHD1080p5994 = 'Hp59', bmdModeHD1080p6000 = 'Hp60', // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ bmdModeHD720p50 = 'hp50', bmdModeHD720p5994 = 'hp59', bmdModeHD720p60 = 'hp60', /* 2k Modes */ bmdMode2k2398 = '2k23', bmdMode2k24 = '2k24', bmdMode2k25 = '2k25', /* DCI Modes (output only) */ bmdMode2kDCI2398 = '2d23', bmdMode2kDCI24 = '2d24', bmdMode2kDCI25 = '2d25', /* 4k Modes */ bmdMode4K2160p2398 = '4k23', bmdMode4K2160p24 = '4k24', bmdMode4K2160p25 = '4k25', bmdMode4K2160p2997 = '4k29', bmdMode4K2160p30 = '4k30', bmdMode4K2160p50 = '4k50', bmdMode4K2160p5994 = '4k59', bmdMode4K2160p60 = '4k60', /* DCI Modes (output only) */ bmdMode4kDCI2398 = '4d23', bmdMode4kDCI24 = '4d24', bmdMode4kDCI25 = '4d25', /* Special Modes */ bmdModeUnknown = 'iunk' }; /* Enum BMDFieldDominance - Video field dominance */ typedef uint32_t BMDFieldDominance; enum _BMDFieldDominance { bmdUnknownFieldDominance = 0, bmdLowerFieldFirst = 'lowr', bmdUpperFieldFirst = 'uppr', bmdProgressiveFrame = 'prog', bmdProgressiveSegmentedFrame = 'psf ' }; /* Enum BMDPixelFormat - Video pixel formats supported for output/input */ typedef uint32_t BMDPixelFormat; enum _BMDPixelFormat { bmdFormat8BitYUV = '2vuy', bmdFormat10BitYUV = 'v210', bmdFormat8BitARGB = 32, bmdFormat8BitBGRA = 'BGRA', bmdFormat10BitRGB = 'r210' // Big-endian RGB 10-bit per component with SMPTE video levels (64-960). Packed as 2:10:10:10 }; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ typedef uint32_t BMDDisplayModeFlags; enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = 1 << 0, bmdDisplayModeColorspaceRec601 = 1 << 1, bmdDisplayModeColorspaceRec709 = 1 << 2 }; /* Enum BMDVideoOutputFlags - Flags to control the output of ancillary data along with video. */ typedef uint32_t BMDVideoOutputFlags; enum _BMDVideoOutputFlags { bmdVideoOutputFlagDefault = 0, bmdVideoOutputVANC = 1 << 0, bmdVideoOutputVITC = 1 << 1, bmdVideoOutputRP188 = 1 << 2, bmdVideoOutputDualStream3D = 1 << 4 }; /* Enum BMDFrameFlags - Frame flags */ typedef uint32_t BMDFrameFlags; enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ bmdFrameHasNoInputSource = 1 << 31 }; /* Enum BMDVideoInputFlags - Flags applicable to video input */ typedef uint32_t BMDVideoInputFlags; enum _BMDVideoInputFlags { bmdVideoInputFlagDefault = 0, bmdVideoInputEnableFormatDetection = 1 << 0, bmdVideoInputDualStream3D = 1 << 1 }; /* Enum BMDVideoInputFormatChangedEvents - Bitmask passed to the VideoInputFormatChanged notification to identify the properties of the input signal that have changed */ typedef uint32_t BMDVideoInputFormatChangedEvents; enum _BMDVideoInputFormatChangedEvents { bmdVideoInputDisplayModeChanged = 1 << 0, bmdVideoInputFieldDominanceChanged = 1 << 1, bmdVideoInputColorspaceChanged = 1 << 2 }; /* Enum BMDDetectedVideoInputFormatFlags - Flags passed to the VideoInputFormatChanged notification to describe the detected video input signal */ typedef uint32_t BMDDetectedVideoInputFormatFlags; enum _BMDDetectedVideoInputFormatFlags { bmdDetectedVideoInputYCbCr422 = 1 << 0, bmdDetectedVideoInputRGB444 = 1 << 1 }; /* Enum BMDOutputFrameCompletionResult - Frame Completion Callback */ typedef uint32_t BMDOutputFrameCompletionResult; enum _BMDOutputFrameCompletionResult { bmdOutputFrameCompleted, bmdOutputFrameDisplayedLate, bmdOutputFrameDropped, bmdOutputFrameFlushed }; /* Enum BMDReferenceStatus - GenLock input status */ typedef uint32_t BMDReferenceStatus; enum _BMDReferenceStatus { bmdReferenceNotSupportedByHardware = 1 << 0, bmdReferenceLocked = 1 << 1 }; /* Enum BMDAudioSampleRate - Audio sample rates supported for output/input */ typedef uint32_t BMDAudioSampleRate; enum _BMDAudioSampleRate { bmdAudioSampleRate48kHz = 48000 }; /* Enum BMDAudioSampleType - Audio sample sizes supported for output/input */ typedef uint32_t BMDAudioSampleType; enum _BMDAudioSampleType { bmdAudioSampleType16bitInteger = 16, bmdAudioSampleType32bitInteger = 32 }; /* Enum BMDAudioOutputStreamType - Audio output stream type */ typedef uint32_t BMDAudioOutputStreamType; enum _BMDAudioOutputStreamType { bmdAudioOutputStreamContinuous, bmdAudioOutputStreamContinuousDontResample, bmdAudioOutputStreamTimestamped }; /* Enum BMDDisplayModeSupport - Output mode supported flags */ typedef uint32_t BMDDisplayModeSupport; enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, bmdDisplayModeSupported, bmdDisplayModeSupportedWithConversion }; /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ typedef uint32_t BMDTimecodeFormat; enum _BMDTimecodeFormat { bmdTimecodeRP188 = 'rp18', bmdTimecodeRP188Field2 = 'rp12', bmdTimecodeVITC = 'vitc', bmdTimecodeVITCField2 = 'vit2', bmdTimecodeSerial = 'seri' }; /* Enum BMDTimecodeFlags - Timecode flags */ typedef uint32_t BMDTimecodeFlags; enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0 }; /* Enum BMDVideoConnection - Video connection types */ typedef uint32_t BMDVideoConnection; enum _BMDVideoConnection { bmdVideoConnectionSDI = 1 << 0, bmdVideoConnectionHDMI = 1 << 1, bmdVideoConnectionOpticalSDI = 1 << 2, bmdVideoConnectionComponent = 1 << 3, bmdVideoConnectionComposite = 1 << 4, bmdVideoConnectionSVideo = 1 << 5 }; /* Enum BMDAnalogVideoFlags - Analog video display flags */ typedef uint32_t BMDAnalogVideoFlags; enum _BMDAnalogVideoFlags { bmdAnalogVideoFlagCompositeSetup75 = 1 << 0, bmdAnalogVideoFlagComponentBetacamLevels = 1 << 1 }; /* Enum BMDAudioConnection - Audio connection types */ typedef uint32_t BMDAudioConnection; enum _BMDAudioConnection { bmdAudioConnectionEmbedded = 'embd', bmdAudioConnectionAESEBU = 'aes ', bmdAudioConnectionAnalog = 'anlg' }; /* Enum BMDAudioOutputAnalogAESSwitch - Audio output Analog/AESEBU switch */ typedef uint32_t BMDAudioOutputAnalogAESSwitch; enum _BMDAudioOutputAnalogAESSwitch { bmdAudioOutputSwitchAESEBU = 'aes ', bmdAudioOutputSwitchAnalog = 'anlg' }; /* Enum BMDVideoOutputConversionMode - Video/audio conversion mode */ typedef uint32_t BMDVideoOutputConversionMode; enum _BMDVideoOutputConversionMode { bmdNoVideoOutputConversion = 'none', bmdVideoOutputLetterboxDownconversion = 'ltbx', bmdVideoOutputAnamorphicDownconversion = 'amph', bmdVideoOutputHD720toHD1080Conversion = '720c', bmdVideoOutputHardwareLetterboxDownconversion = 'HWlb', bmdVideoOutputHardwareAnamorphicDownconversion = 'HWam', bmdVideoOutputHardwareCenterCutDownconversion = 'HWcc', bmdVideoOutputHardware720p1080pCrossconversion = 'xcap', bmdVideoOutputHardwareAnamorphic720pUpconversion = 'ua7p', bmdVideoOutputHardwareAnamorphic1080iUpconversion = 'ua1i', bmdVideoOutputHardwareAnamorphic149To720pUpconversion = 'u47p', bmdVideoOutputHardwareAnamorphic149To1080iUpconversion = 'u41i', bmdVideoOutputHardwarePillarbox720pUpconversion = 'up7p', bmdVideoOutputHardwarePillarbox1080iUpconversion = 'up1i' }; /* Enum BMDVideoInputConversionMode - Video input conversion mode */ typedef uint32_t BMDVideoInputConversionMode; enum _BMDVideoInputConversionMode { bmdNoVideoInputConversion = 'none', bmdVideoInputLetterboxDownconversionFromHD1080 = '10lb', bmdVideoInputAnamorphicDownconversionFromHD1080 = '10am', bmdVideoInputLetterboxDownconversionFromHD720 = '72lb', bmdVideoInputAnamorphicDownconversionFromHD720 = '72am', bmdVideoInputLetterboxUpconversion = 'lbup', bmdVideoInputAnamorphicUpconversion = 'amup' }; /* Enum BMDVideo3DPackingFormat - Video 3D packing format */ typedef uint32_t BMDVideo3DPackingFormat; enum _BMDVideo3DPackingFormat { bmdVideo3DPackingSidebySideHalf = 'sbsh', bmdVideo3DPackingLinebyLine = 'lbyl', bmdVideo3DPackingTopAndBottom = 'tabo', bmdVideo3DPackingFramePacking = 'frpk', bmdVideo3DPackingLeftOnly = 'left', bmdVideo3DPackingRightOnly = 'righ' }; /* Enum BMDIdleVideoOutputOperation - Video output operation when not playing video */ typedef uint32_t BMDIdleVideoOutputOperation; enum _BMDIdleVideoOutputOperation { bmdIdleVideoOutputBlack = 'blac', bmdIdleVideoOutputLastFrame = 'lafa' }; /* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ typedef uint32_t BMDDeckLinkConfigurationID; enum _BMDDeckLinkConfigurationID { /* Serial port Flags */ bmdDeckLinkConfigSwapSerialRxTx = 'ssrt', /* Video Input/Output Flags */ bmdDeckLinkConfigUse1080pNotPsF = 'fpro', /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = '3dpf', bmdDeckLinkConfigBypass = 'byps', /* Audio Input/Output Flags */ bmdDeckLinkConfigAnalogAudioConsumerLevels = 'aacl', /* Video output flags */ bmdDeckLinkConfigFieldFlickerRemoval = 'fdfr', bmdDeckLinkConfigHD1080p24ToHD1080i5994Conversion = 'to59', bmdDeckLinkConfig444SDIVideoOutput = '444o', bmdDeckLinkConfig3GBpsVideoOutput = '3gbs', bmdDeckLinkConfigBlackVideoOutputDuringCapture = 'bvoc', bmdDeckLinkConfigLowLatencyVideoOutput = 'llvo', /* Video Output Integers */ bmdDeckLinkConfigVideoOutputConnection = 'vocn', bmdDeckLinkConfigVideoOutputConversionMode = 'vocm', bmdDeckLinkConfigAnalogVideoOutputFlags = 'avof', bmdDeckLinkConfigReferenceInputTimingOffset = 'glot', bmdDeckLinkConfigVideoOutputIdleOperation = 'voio', /* Video Output Floats */ bmdDeckLinkConfigVideoOutputComponentLumaGain = 'oclg', bmdDeckLinkConfigVideoOutputComponentChromaBlueGain = 'occb', bmdDeckLinkConfigVideoOutputComponentChromaRedGain = 'occr', bmdDeckLinkConfigVideoOutputCompositeLumaGain = 'oilg', bmdDeckLinkConfigVideoOutputCompositeChromaGain = 'oicg', bmdDeckLinkConfigVideoOutputSVideoLumaGain = 'oslg', bmdDeckLinkConfigVideoOutputSVideoChromaGain = 'oscg', /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = 'vicn', bmdDeckLinkConfigAnalogVideoInputFlags = 'avif', bmdDeckLinkConfigVideoInputConversionMode = 'vicm', bmdDeckLinkConfig32PulldownSequenceInitialTimecodeFrame = 'pdif', bmdDeckLinkConfigVANCSourceLine1Mapping = 'vsl1', bmdDeckLinkConfigVANCSourceLine2Mapping = 'vsl2', bmdDeckLinkConfigVANCSourceLine3Mapping = 'vsl3', /* Video Input Floats */ bmdDeckLinkConfigVideoInputComponentLumaGain = 'iclg', bmdDeckLinkConfigVideoInputComponentChromaBlueGain = 'iccb', bmdDeckLinkConfigVideoInputComponentChromaRedGain = 'iccr', bmdDeckLinkConfigVideoInputCompositeLumaGain = 'iilg', bmdDeckLinkConfigVideoInputCompositeChromaGain = 'iicg', bmdDeckLinkConfigVideoInputSVideoLumaGain = 'islg', bmdDeckLinkConfigVideoInputSVideoChromaGain = 'iscg', /* Audio Input Integers */ bmdDeckLinkConfigAudioInputConnection = 'aicn', /* Audio Input Floats */ bmdDeckLinkConfigAnalogAudioInputScaleChannel1 = 'ais1', bmdDeckLinkConfigAnalogAudioInputScaleChannel2 = 'ais2', bmdDeckLinkConfigAnalogAudioInputScaleChannel3 = 'ais3', bmdDeckLinkConfigAnalogAudioInputScaleChannel4 = 'ais4', bmdDeckLinkConfigDigitalAudioInputScale = 'dais', /* Audio Output Integers */ bmdDeckLinkConfigAudioOutputAESAnalogSwitch = 'aoaa', /* Audio Output Floats */ bmdDeckLinkConfigAnalogAudioOutputScaleChannel1 = 'aos1', bmdDeckLinkConfigAnalogAudioOutputScaleChannel2 = 'aos2', bmdDeckLinkConfigAnalogAudioOutputScaleChannel3 = 'aos3', bmdDeckLinkConfigAnalogAudioOutputScaleChannel4 = 'aos4', bmdDeckLinkConfigDigitalAudioOutputScale = 'daos' }; /* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */ typedef uint32_t BMDDeckLinkAttributeID; enum _BMDDeckLinkAttributeID { /* Flags */ BMDDeckLinkSupportsInternalKeying = 'keyi', BMDDeckLinkSupportsExternalKeying = 'keye', BMDDeckLinkSupportsHDKeying = 'keyh', BMDDeckLinkSupportsInputFormatDetection = 'infd', BMDDeckLinkHasReferenceInput = 'hrin', BMDDeckLinkHasSerialPort = 'hspt', BMDDeckLinkHasAnalogVideoOutputGain = 'avog', BMDDeckLinkCanOnlyAdjustOverallVideoOutputGain = 'ovog', BMDDeckLinkHasVideoInputAntiAliasingFilter = 'aafl', BMDDeckLinkHasBypass = 'byps', /* Integers */ BMDDeckLinkMaximumAudioChannels = 'mach', BMDDeckLinkNumberOfSubDevices = 'nsbd', BMDDeckLinkSubDeviceIndex = 'subi', BMDDeckLinkVideoOutputConnections = 'vocn', BMDDeckLinkVideoInputConnections = 'vicn', /* Floats */ BMDDeckLinkVideoInputGainMinimum = 'vigm', BMDDeckLinkVideoInputGainMaximum = 'vigx', BMDDeckLinkVideoOutputGainMinimum = 'vogm', BMDDeckLinkVideoOutputGainMaximum = 'vogx', /* Strings */ BMDDeckLinkSerialPortDeviceName = 'slpn' }; /* Enum BMDDeckLinkAPIInformationID - DeckLinkAPI information ID */ typedef uint32_t BMDDeckLinkAPIInformationID; enum _BMDDeckLinkAPIInformationID { BMDDeckLinkAPIVersion = 'vers' }; /* Enum BMDDeckControlMode - DeckControl mode */ typedef uint32_t BMDDeckControlMode; enum _BMDDeckControlMode { bmdDeckControlNotOpened = 'ntop', bmdDeckControlVTRControlMode = 'vtrc', bmdDeckControlExportMode = 'expm', bmdDeckControlCaptureMode = 'capm' }; /* Enum BMDDeckControlEvent - DeckControl event */ typedef uint32_t BMDDeckControlEvent; enum _BMDDeckControlEvent { bmdDeckControlAbortedEvent = 'abte', // This event is triggered when a capture or edit-to-tape operation is aborted. /* Export-To-Tape events */ bmdDeckControlPrepareForExportEvent = 'pfee', // This event is triggered a few frames before reaching the in-point. IDeckLinkInput::StartScheduledPlayback() should be called at this point. bmdDeckControlExportCompleteEvent = 'exce', // This event is triggered a few frames after reaching the out-point. At this point, it is safe to stop playback. /* Capture events */ bmdDeckControlPrepareForCaptureEvent = 'pfce', // This event is triggered a few frames before reaching the in-point. The serial timecode attached to IDeckLinkVideoInputFrames is now valid. bmdDeckControlCaptureCompleteEvent = 'ccev' // This event is triggered a few frames after reaching the out-point. }; /* Enum BMDDeckControlVTRControlState - VTR Control state */ typedef uint32_t BMDDeckControlVTRControlState; enum _BMDDeckControlVTRControlState { bmdDeckControlNotInVTRControlMode = 'nvcm', bmdDeckControlVTRControlPlaying = 'vtrp', bmdDeckControlVTRControlRecording = 'vtrr', bmdDeckControlVTRControlStill = 'vtra', bmdDeckControlVTRControlSeeking = 'vtrs', bmdDeckControlVTRControlStopped = 'vtro' }; /* Enum BMDDeckControlStatusFlags - Deck Control status flags */ typedef uint32_t BMDDeckControlStatusFlags; enum _BMDDeckControlStatusFlags { bmdDeckControlStatusDeckConnected = 1 << 0, bmdDeckControlStatusRemoteMode = 1 << 1, bmdDeckControlStatusRecordInhibited = 1 << 2, bmdDeckControlStatusCassetteOut = 1 << 3 }; /* Enum BMDDeckControlExportModeOpsFlags - Export mode flags */ typedef uint32_t BMDDeckControlExportModeOpsFlags; enum _BMDDeckControlExportModeOpsFlags { bmdDeckControlExportModeInsertVideo = 1 << 0, bmdDeckControlExportModeInsertAudio1 = 1 << 1, bmdDeckControlExportModeInsertAudio2 = 1 << 2, bmdDeckControlExportModeInsertAudio3 = 1 << 3, bmdDeckControlExportModeInsertAudio4 = 1 << 4, bmdDeckControlExportModeInsertAudio5 = 1 << 5, bmdDeckControlExportModeInsertAudio6 = 1 << 6, bmdDeckControlExportModeInsertAudio7 = 1 << 7, bmdDeckControlExportModeInsertAudio8 = 1 << 8, bmdDeckControlExportModeInsertAudio9 = 1 << 9, bmdDeckControlExportModeInsertAudio10 = 1 << 10, bmdDeckControlExportModeInsertAudio11 = 1 << 11, bmdDeckControlExportModeInsertAudio12 = 1 << 12, bmdDeckControlExportModeInsertTimeCode = 1 << 13, bmdDeckControlExportModeInsertAssemble = 1 << 14, bmdDeckControlExportModeInsertPreview = 1 << 15, bmdDeckControlUseManualExport = 1 << 16 }; /* Enum BMDDeckControlError - Deck Control error */ typedef uint32_t BMDDeckControlError; enum _BMDDeckControlError { bmdDeckControlNoError = 'noer', bmdDeckControlModeError = 'moer', bmdDeckControlMissedInPointError = 'mier', bmdDeckControlDeckTimeoutError = 'dter', bmdDeckControlCommandFailedError = 'cfer', bmdDeckControlDeviceAlreadyOpenedError = 'dalo', bmdDeckControlFailedToOpenDeviceError = 'fder', bmdDeckControlInLocalModeError = 'lmer', bmdDeckControlEndOfTapeError = 'eter', bmdDeckControlUserAbortError = 'uaer', bmdDeckControlNoTapeInDeckError = 'nter', bmdDeckControlNoVideoFromCardError = 'nvfc', bmdDeckControlNoCommunicationError = 'ncom', bmdDeckControlBufferTooSmallError = 'btsm', bmdDeckControlBadChecksumError = 'chks', bmdDeckControlUnknownError = 'uner' }; /* Enum BMD3DPreviewFormat - Linked Frame preview format */ typedef uint32_t BMD3DPreviewFormat; enum _BMD3DPreviewFormat { bmd3DPreviewFormatDefault = 'defa', bmd3DPreviewFormatLeftOnly = 'left', bmd3DPreviewFormatRightOnly = 'righ', bmd3DPreviewFormatSideBySide = 'side', bmd3DPreviewFormatTopBottom = 'topb' }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkVideoOutputCallback; class IDeckLinkInputCallback; class IDeckLinkMemoryAllocator; class IDeckLinkAudioOutputCallback; class IDeckLinkIterator; class IDeckLinkAPIInformation; class IDeckLinkDisplayModeIterator; class IDeckLinkDisplayMode; class IDeckLink; class IDeckLinkOutput; class IDeckLinkInput; class IDeckLinkTimecode; class IDeckLinkVideoFrame; class IDeckLinkMutableVideoFrame; class IDeckLinkVideoFrame3DExtensions; class IDeckLinkVideoInputFrame; class IDeckLinkVideoFrameAncillary; class IDeckLinkAudioInputPacket; class IDeckLinkScreenPreviewCallback; class IDeckLinkCocoaScreenPreviewCallback; class IDeckLinkGLScreenPreviewHelper; class IDeckLinkConfiguration; class IDeckLinkAttributes; class IDeckLinkKeyer; class IDeckLinkVideoConversion; class IDeckLinkDeckControlStatusCallback; class IDeckLinkDeckControl; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ class IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame *completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; virtual HRESULT ScheduledPlaybackHasStopped (void) = 0; protected: virtual ~IDeckLinkVideoOutputCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkInputCallback - Frame arrival callback. */ class IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT VideoInputFrameArrived (/* in */ IDeckLinkVideoInputFrame* videoFrame, /* in */ IDeckLinkAudioInputPacket* audioPacket) = 0; protected: virtual ~IDeckLinkInputCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkMemoryAllocator - Memory allocator for video frames. */ class IDeckLinkMemoryAllocator : public IUnknown { public: virtual HRESULT AllocateBuffer (/* in */ uint32_t bufferSize, /* out */ void **allocatedBuffer) = 0; virtual HRESULT ReleaseBuffer (/* in */ void *buffer) = 0; virtual HRESULT Commit (void) = 0; virtual HRESULT Decommit (void) = 0; }; /* Interface IDeckLinkAudioOutputCallback - Optional callback to allow audio samples to be pulled as required. */ class IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT RenderAudioSamples (/* in */ bool preroll) = 0; }; /* Interface IDeckLinkIterator - enumerates installed DeckLink hardware */ class IDeckLinkIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink **deckLinkInstance) = 0; }; /* Interface IDeckLinkAPIInformation - DeckLinkAPI attribute interface */ class IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ bool *value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ int64_t *value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ double *value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ CFStringRef *value) = 0; protected: virtual ~IDeckLinkAPIInformation () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDisplayModeIterator - enumerates over supported input/output display modes. */ class IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode **deckLinkDisplayMode) = 0; protected: virtual ~IDeckLinkDisplayModeIterator () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDisplayMode - represents a display mode */ class IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT GetName (/* out */ CFStringRef *name) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; virtual long GetWidth (void) = 0; virtual long GetHeight (void) = 0; virtual HRESULT GetFrameRate (/* out */ BMDTimeValue *frameDuration, /* out */ BMDTimeScale *timeScale) = 0; virtual BMDFieldDominance GetFieldDominance (void) = 0; virtual BMDDisplayModeFlags GetFlags (void) = 0; protected: virtual ~IDeckLinkDisplayMode () {}; // call Release method to drop reference count }; /* Interface IDeckLink - represents a DeckLink device */ class IDeckLink : public IUnknown { public: virtual HRESULT GetModelName (/* out */ CFStringRef *modelName) = 0; }; /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ class IDeckLinkOutput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; /* Video Output */ virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0; virtual HRESULT DisableVideoOutput (void) = 0; virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame **outFrame) = 0; virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback *theCallback) = 0; virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t *bufferedFrameCount) = 0; /* Audio Output */ virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT DisableAudioOutput (void) = 0; virtual HRESULT WriteAudioSamplesSync (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t *sampleFramesWritten) = 0; virtual HRESULT BeginAudioPreroll (void) = 0; virtual HRESULT EndAudioPreroll (void) = 0; virtual HRESULT ScheduleAudioSamples (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t *sampleFramesWritten) = 0; virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t *bufferedSampleFrameCount) = 0; virtual HRESULT FlushBufferedAudioSamples (void) = 0; virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback *theCallback) = 0; /* Output Control */ virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0; virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue *actualStopTime, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool *active) = 0; virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *streamTime, /* out */ double *playbackSpeed) = 0; virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus *referenceStatus) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0; protected: virtual ~IDeckLinkOutput () {}; // call Release method to drop reference count }; /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ class IDeckLinkInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; /* Video Input */ virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0; virtual HRESULT DisableVideoInput (void) = 0; virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t *availableFrameCount) = 0; /* Audio Input */ virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0; virtual HRESULT DisableAudioInput (void) = 0; virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t *availableSampleFrameCount) = 0; /* Input Control */ virtual HRESULT StartStreams (void) = 0; virtual HRESULT StopStreams (void) = 0; virtual HRESULT PauseStreams (void) = 0; virtual HRESULT FlushStreams (void) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback *theCallback) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0; protected: virtual ~IDeckLinkInput () {}; // call Release method to drop reference count }; /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ class IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; virtual HRESULT GetComponents (/* out */ uint8_t *hours, /* out */ uint8_t *minutes, /* out */ uint8_t *seconds, /* out */ uint8_t *frames) = 0; virtual HRESULT GetString (/* out */ CFStringRef *timecode) = 0; virtual BMDTimecodeFlags GetFlags (void) = 0; virtual HRESULT GetTimecodeUserBits (/* out */ BMDTimecodeUserBits *userBits) = 0; protected: virtual ~IDeckLinkTimecode () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ class IDeckLinkVideoFrame : public IUnknown { public: virtual long GetWidth (void) = 0; virtual long GetHeight (void) = 0; virtual long GetRowBytes (void) = 0; virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDFrameFlags GetFlags (void) = 0; virtual HRESULT GetBytes (/* out */ void **buffer) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) = 0; virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; protected: virtual ~IDeckLinkVideoFrame () {}; // call Release method to drop reference count }; /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ class IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0; virtual HRESULT SetTimecode (/* in */ BMDTimecodeFormat format, /* in */ IDeckLinkTimecode *timecode) = 0; virtual HRESULT SetTimecodeFromComponents (/* in */ BMDTimecodeFormat format, /* in */ uint8_t hours, /* in */ uint8_t minutes, /* in */ uint8_t seconds, /* in */ uint8_t frames, /* in */ BMDTimecodeFlags flags) = 0; virtual HRESULT SetAncillaryData (/* in */ IDeckLinkVideoFrameAncillary *ancillary) = 0; virtual HRESULT SetTimecodeUserBits (/* in */ BMDTimecodeFormat format, /* in */ BMDTimecodeUserBits userBits) = 0; protected: virtual ~IDeckLinkMutableVideoFrame () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrame3DExtensions - Optional interface implemented on IDeckLinkVideoFrame to support 3D frames */ class IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0; virtual HRESULT GetFrameForRightEye (/* out */ IDeckLinkVideoFrame* *rightEyeFrame) = 0; protected: virtual ~IDeckLinkVideoFrame3DExtensions () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ class IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT GetHardwareReferenceTimestamp (/* in */ BMDTimeScale timeScale, /* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration) = 0; protected: virtual ~IDeckLinkVideoInputFrame () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameAncillary - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ class IDeckLinkVideoFrameAncillary : public IUnknown { public: virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; protected: virtual ~IDeckLinkVideoFrameAncillary () {}; // call Release method to drop reference count }; /* Interface IDeckLinkAudioInputPacket - Provided by the IDeckLinkInput callback. */ class IDeckLinkAudioInputPacket : public IUnknown { public: virtual long GetSampleFrameCount (void) = 0; virtual HRESULT GetBytes (/* out */ void **buffer) = 0; virtual HRESULT GetPacketTime (/* out */ BMDTimeValue *packetTime, /* in */ BMDTimeScale timeScale) = 0; protected: virtual ~IDeckLinkAudioInputPacket () {}; // call Release method to drop reference count }; /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ class IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame *theFrame) = 0; protected: virtual ~IDeckLinkScreenPreviewCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkCocoaScreenPreviewCallback - Screen preview callback for Cocoa-based applications */ class IDeckLinkCocoaScreenPreviewCallback : public IDeckLinkScreenPreviewCallback { public: protected: virtual ~IDeckLinkCocoaScreenPreviewCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance(). */ class IDeckLinkGLScreenPreviewHelper : public IUnknown { public: /* Methods must be called with OpenGL context set */ virtual HRESULT InitializeGL (void) = 0; virtual HRESULT PaintGL (void) = 0; virtual HRESULT SetFrame (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT Set3DPreviewFormat (/* in */ BMD3DPreviewFormat previewFormat) = 0; protected: virtual ~IDeckLinkGLScreenPreviewHelper () {}; // call Release method to drop reference count }; /* Interface IDeckLinkConfiguration - DeckLink Configuration interface */ class IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0; virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ CFStringRef value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ CFStringRef *value) = 0; virtual HRESULT WriteConfigurationToPreferences (void) = 0; protected: virtual ~IDeckLinkConfiguration () {}; // call Release method to drop reference count }; /* Interface IDeckLinkAttributes - DeckLink Attribute interface */ class IDeckLinkAttributes : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool *value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ int64_t *value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ double *value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ CFStringRef *value) = 0; protected: virtual ~IDeckLinkAttributes () {}; // call Release method to drop reference count }; /* Interface IDeckLinkKeyer - DeckLink Keyer interface */ class IDeckLinkKeyer : public IUnknown { public: virtual HRESULT Enable (/* in */ bool isExternal) = 0; virtual HRESULT SetLevel (/* in */ uint8_t level) = 0; virtual HRESULT RampUp (/* in */ uint32_t numberOfFrames) = 0; virtual HRESULT RampDown (/* in */ uint32_t numberOfFrames) = 0; virtual HRESULT Disable (void) = 0; protected: virtual ~IDeckLinkKeyer () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance(). */ class IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0; protected: virtual ~IDeckLinkVideoConversion () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */ class IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState newState, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0; protected: virtual ~IDeckLinkDeckControlStatusCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDeckControl - Deck Control main interface */ class IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Close (/* in */ bool standbyOn) = 0; virtual HRESULT GetCurrentState (/* out */ BMDDeckControlMode *mode, /* out */ BMDDeckControlVTRControlState *vtrControlState, /* out */ BMDDeckControlStatusFlags *flags) = 0; virtual HRESULT SetStandby (/* in */ bool standbyOn) = 0; virtual HRESULT SendCommand (/* in */ uint8_t *inBuffer, /* in */ uint32_t inBufferSize, /* out */ uint8_t *outBuffer, /* out */ uint32_t *outDataSize, /* in */ uint32_t outBufferSize, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Play (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT Stop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT TogglePlayStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT Eject (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT GoToTimecode (/* in */ BMDTimecodeBCD timecode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT FastForward (/* in */ bool viewTape, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Rewind (/* in */ bool viewTape, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT StepForward (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT StepBack (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT Jog (/* in */ double rate, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Shuttle (/* in */ double rate, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT GetTimecodeString (/* out */ CFStringRef *currentTimeCode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT GetTimecode (/* out */ IDeckLinkTimecode **currentTimecode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT GetTimecodeBCD (/* out */ BMDTimecodeBCD *currentTimecode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetPreroll (/* in */ uint32_t prerollSeconds) = 0; virtual HRESULT GetPreroll (/* out */ uint32_t *prerollSeconds) = 0; virtual HRESULT SetExportOffset (/* in */ int32_t exportOffsetFields) = 0; virtual HRESULT GetExportOffset (/* out */ int32_t *exportOffsetFields) = 0; virtual HRESULT GetManualExportOffset (/* out */ int32_t *deckManualExportOffsetFields) = 0; virtual HRESULT SetCaptureOffset (/* in */ int32_t captureOffsetFields) = 0; virtual HRESULT GetCaptureOffset (/* out */ int32_t *captureOffsetFields) = 0; virtual HRESULT StartExport (/* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* in */ BMDDeckControlExportModeOpsFlags exportModeOps, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT StartCapture (/* in */ bool useVITC, /* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT GetDeviceID (/* out */ uint16_t *deviceId, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Abort (void) = 0; virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback *callback) = 0; protected: virtual ~IDeckLinkDeckControl () {}; // call Release method to drop reference count }; /* Functions */ extern "C" { IDeckLinkIterator* CreateDeckLinkIteratorInstance (void); IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void); IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void); IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* /* (NSView*) */ parentView); IDeckLinkVideoConversion* CreateVideoConversionInstance (void); }; #endif // defined(__cplusplus) #endif // __DeckLink_API_h__ mlt-6.20.0/src/modules/decklink/darwin/DeckLinkAPIDispatch.cpp000077500000000000000000000116271362234133600241210ustar00rootroot00000000000000/* -LICENSE-START- ** Copyright (c) 2009 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* DeckLinkAPIDispatch.cpp */ #include "DeckLinkAPI.h" #include #if BLACKMAGIC_DECKLINK_API_MAGIC != 1 #error The DeckLink API version of DeckLinkAPIDispatch.cpp is not the same version as DeckLinkAPI.h #endif #define kDeckLinkAPI_BundlePath "/Library/Application Support/Blackmagic Design/Blackmagic DeckLink/DeckLinkAPI.bundle" typedef IDeckLinkIterator* (*CreateIteratorFunc)(void); typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void); typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); typedef IDeckLinkCocoaScreenPreviewCallback* (*CreateCocoaScreenPreviewFunc)(void*); typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; static CFBundleRef gBundleRef = NULL; static CreateIteratorFunc gCreateIteratorFunc = NULL; static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL; static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateCocoaScreenPreviewFunc gCreateCocoaPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; void InitDeckLinkAPI (void) { CFURLRef bundleURL; bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(kDeckLinkAPI_BundlePath), kCFURLPOSIXPathStyle, true); if (bundleURL != NULL) { gBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL); if (gBundleRef != NULL) { gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0001")); gCreateAPIInformationFunc = (CreateAPIInformationFunc)CFBundleGetFunctionPointerForName(gBundleRef, CFSTR("CreateDeckLinkAPIInformationInstance_0001")); gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gBundleRef, CFSTR("CreateOpenGLScreenPreviewHelper_0001")); gCreateCocoaPreviewFunc = (CreateCocoaScreenPreviewFunc)CFBundleGetFunctionPointerForName(gBundleRef, CFSTR("CreateCocoaScreenPreview_0001")); gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)CFBundleGetFunctionPointerForName(gBundleRef, CFSTR("CreateVideoConversionInstance_0001")); } CFRelease(bundleURL); } } bool IsDeckLinkAPIPresent (void) { // If the DeckLink API bundle was successfully loaded, return this knowledge to the caller if (gBundleRef != NULL) return true; return false; } IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); } IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateAPIInformationFunc == NULL) return NULL; return gCreateAPIInformationFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); } IDeckLinkCocoaScreenPreviewCallback* CreateCocoaScreenPreview (void* parentView) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateCocoaPreviewFunc == NULL) return NULL; return gCreateCocoaPreviewFunc(parentView); } IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); } mlt-6.20.0/src/modules/decklink/linux/000077500000000000000000000000001362234133600175605ustar00rootroot00000000000000mlt-6.20.0/src/modules/decklink/linux/DeckLinkAPI.h000066400000000000000000001467501362234133600217640ustar00rootroot00000000000000/* -LICENSE-START- ** Copyright (c) 2009 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ /* DeckLinkAPI.h */ #ifndef __DeckLink_API_h__ #define __DeckLink_API_h__ #include #include "LinuxCOM.h" #define BLACKMAGIC_DECKLINK_API_MAGIC 1 // Type Declarations typedef int64_t BMDTimeValue; typedef int64_t BMDTimeScale; typedef uint32_t BMDTimecodeBCD; typedef uint32_t BMDTimecodeUserBits; // Interface ID Declarations #define IID_IDeckLinkVideoOutputCallback /* 20AA5225-1958-47CB-820B-80A8D521A6EE */ (REFIID){0x20,0xAA,0x52,0x25,0x19,0x58,0x47,0xCB,0x82,0x0B,0x80,0xA8,0xD5,0x21,0xA6,0xEE} #define IID_IDeckLinkInputCallback /* DD04E5EC-7415-42AB-AE4A-E80C4DFC044A */ (REFIID){0xDD,0x04,0xE5,0xEC,0x74,0x15,0x42,0xAB,0xAE,0x4A,0xE8,0x0C,0x4D,0xFC,0x04,0x4A} #define IID_IDeckLinkMemoryAllocator /* B36EB6E7-9D29-4AA8-92EF-843B87A289E8 */ (REFIID){0xB3,0x6E,0xB6,0xE7,0x9D,0x29,0x4A,0xA8,0x92,0xEF,0x84,0x3B,0x87,0xA2,0x89,0xE8} #define IID_IDeckLinkAudioOutputCallback /* 403C681B-7F46-4A12-B993-2BB127084EE6 */ (REFIID){0x40,0x3C,0x68,0x1B,0x7F,0x46,0x4A,0x12,0xB9,0x93,0x2B,0xB1,0x27,0x08,0x4E,0xE6} #define IID_IDeckLinkIterator /* 74E936FC-CC28-4A67-81A0-1E94E52D4E69 */ (REFIID){0x74,0xE9,0x36,0xFC,0xCC,0x28,0x4A,0x67,0x81,0xA0,0x1E,0x94,0xE5,0x2D,0x4E,0x69} #define IID_IDeckLinkAPIInformation /* 7BEA3C68-730D-4322-AF34-8A7152B532A4 */ (REFIID){0x7B,0xEA,0x3C,0x68,0x73,0x0D,0x43,0x22,0xAF,0x34,0x8A,0x71,0x52,0xB5,0x32,0xA4} #define IID_IDeckLinkDisplayModeIterator /* 9C88499F-F601-4021-B80B-032E4EB41C35 */ (REFIID){0x9C,0x88,0x49,0x9F,0xF6,0x01,0x40,0x21,0xB8,0x0B,0x03,0x2E,0x4E,0xB4,0x1C,0x35} #define IID_IDeckLinkDisplayMode /* 3EB2C1AB-0A3D-4523-A3AD-F40D7FB14E78 */ (REFIID){0x3E,0xB2,0xC1,0xAB,0x0A,0x3D,0x45,0x23,0xA3,0xAD,0xF4,0x0D,0x7F,0xB1,0x4E,0x78} #define IID_IDeckLink /* 62BFF75D-6569-4E55-8D4D-66AA03829ABC */ (REFIID){0x62,0xBF,0xF7,0x5D,0x65,0x69,0x4E,0x55,0x8D,0x4D,0x66,0xAA,0x03,0x82,0x9A,0xBC} #define IID_IDeckLinkOutput /* A3EF0963-0862-44ED-92A9-EE89ABF431C7 */ (REFIID){0xA3,0xEF,0x09,0x63,0x08,0x62,0x44,0xED,0x92,0xA9,0xEE,0x89,0xAB,0xF4,0x31,0xC7} #define IID_IDeckLinkInput /* 6D40EF78-28B9-4E21-990D-95BB7750A04F */ (REFIID){0x6D,0x40,0xEF,0x78,0x28,0xB9,0x4E,0x21,0x99,0x0D,0x95,0xBB,0x77,0x50,0xA0,0x4F} #define IID_IDeckLinkTimecode /* BC6CFBD3-8317-4325-AC1C-1216391E9340 */ (REFIID){0xBC,0x6C,0xFB,0xD3,0x83,0x17,0x43,0x25,0xAC,0x1C,0x12,0x16,0x39,0x1E,0x93,0x40} #define IID_IDeckLinkVideoFrame /* 3F716FE0-F023-4111-BE5D-EF4414C05B17 */ (REFIID){0x3F,0x71,0x6F,0xE0,0xF0,0x23,0x41,0x11,0xBE,0x5D,0xEF,0x44,0x14,0xC0,0x5B,0x17} #define IID_IDeckLinkMutableVideoFrame /* 69E2639F-40DA-4E19-B6F2-20ACE815C390 */ (REFIID){0x69,0xE2,0x63,0x9F,0x40,0xDA,0x4E,0x19,0xB6,0xF2,0x20,0xAC,0xE8,0x15,0xC3,0x90} #define IID_IDeckLinkVideoFrame3DExtensions /* DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7 */ (REFIID){0xDA,0x0F,0x7E,0x4A,0xED,0xC7,0x48,0xA8,0x9C,0xDD,0x2D,0xB5,0x1C,0x72,0x9C,0xD7} #define IID_IDeckLinkVideoInputFrame /* 05CFE374-537C-4094-9A57-680525118F44 */ (REFIID){0x05,0xCF,0xE3,0x74,0x53,0x7C,0x40,0x94,0x9A,0x57,0x68,0x05,0x25,0x11,0x8F,0x44} #define IID_IDeckLinkVideoFrameAncillary /* 732E723C-D1A4-4E29-9E8E-4A88797A0004 */ (REFIID){0x73,0x2E,0x72,0x3C,0xD1,0xA4,0x4E,0x29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04} #define IID_IDeckLinkAudioInputPacket /* E43D5870-2894-11DE-8C30-0800200C9A66 */ (REFIID){0xE4,0x3D,0x58,0x70,0x28,0x94,0x11,0xDE,0x8C,0x30,0x08,0x00,0x20,0x0C,0x9A,0x66} #define IID_IDeckLinkScreenPreviewCallback /* B1D3F49A-85FE-4C5D-95C8-0B5D5DCCD438 */ (REFIID){0xB1,0xD3,0xF4,0x9A,0x85,0xFE,0x4C,0x5D,0x95,0xC8,0x0B,0x5D,0x5D,0xCC,0xD4,0x38} #define IID_IDeckLinkGLScreenPreviewHelper /* 504E2209-CAC7-4C1A-9FB4-C5BB6274D22F */ (REFIID){0x50,0x4E,0x22,0x09,0xCA,0xC7,0x4C,0x1A,0x9F,0xB4,0xC5,0xBB,0x62,0x74,0xD2,0x2F} #define IID_IDeckLinkConfiguration /* C679A35B-610C-4D09-B748-1D0478100FC0 */ (REFIID){0xC6,0x79,0xA3,0x5B,0x61,0x0C,0x4D,0x09,0xB7,0x48,0x1D,0x04,0x78,0x10,0x0F,0xC0} #define IID_IDeckLinkAttributes /* ABC11843-D966-44CB-96E2-A1CB5D3135C4 */ (REFIID){0xAB,0xC1,0x18,0x43,0xD9,0x66,0x44,0xCB,0x96,0xE2,0xA1,0xCB,0x5D,0x31,0x35,0xC4} #define IID_IDeckLinkKeyer /* 89AFCAF5-65F8-421E-98F7-96FE5F5BFBA3 */ (REFIID){0x89,0xAF,0xCA,0xF5,0x65,0xF8,0x42,0x1E,0x98,0xF7,0x96,0xFE,0x5F,0x5B,0xFB,0xA3} #define IID_IDeckLinkVideoConversion /* 3BBCB8A2-DA2C-42D9-B5D8-88083644E99A */ (REFIID){0x3B,0xBC,0xB8,0xA2,0xDA,0x2C,0x42,0xD9,0xB5,0xD8,0x88,0x08,0x36,0x44,0xE9,0x9A} #define IID_IDeckLinkDeckControlStatusCallback /* E5F693C1-4283-4716-B18F-C1431521955B */ (REFIID){0xE5,0xF6,0x93,0xC1,0x42,0x83,0x47,0x16,0xB1,0x8F,0xC1,0x43,0x15,0x21,0x95,0x5B} #define IID_IDeckLinkDeckControl /* A4D81043-0619-42B7-8ED6-602D29041DF7 */ (REFIID){0xA4,0xD8,0x10,0x43,0x06,0x19,0x42,0xB7,0x8E,0xD6,0x60,0x2D,0x29,0x04,0x1D,0xF7} /* Enum BMDDisplayMode - Video display modes */ typedef uint32_t BMDDisplayMode; enum _BMDDisplayMode { /* SD Modes */ bmdModeNTSC = /* 'ntsc' */ 0x6E747363, bmdModeNTSC2398 = /* 'nt23' */ 0x6E743233, // 3:2 pulldown bmdModePAL = /* 'pal ' */ 0x70616C20, /* HD 1080 Modes */ bmdModeHD1080p2398 = /* '23ps' */ 0x32337073, bmdModeHD1080p24 = /* '24ps' */ 0x32347073, bmdModeHD1080p25 = /* 'Hp25' */ 0x48703235, bmdModeHD1080p2997 = /* 'Hp29' */ 0x48703239, bmdModeHD1080p30 = /* 'Hp30' */ 0x48703330, bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530, bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539, bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz. bmdModeHD1080p50 = /* 'Hp50' */ 0x48703530, bmdModeHD1080p5994 = /* 'Hp59' */ 0x48703539, bmdModeHD1080p6000 = /* 'Hp60' */ 0x48703630, // N.B. This _really_ is 60.00 Hz. /* HD 720 Modes */ bmdModeHD720p50 = /* 'hp50' */ 0x68703530, bmdModeHD720p5994 = /* 'hp59' */ 0x68703539, bmdModeHD720p60 = /* 'hp60' */ 0x68703630, /* 2k Modes */ bmdMode2k2398 = /* '2k23' */ 0x326B3233, bmdMode2k24 = /* '2k24' */ 0x326B3234, bmdMode2k25 = /* '2k25' */ 0x326B3235, /* DCI Modes (output only) */ bmdMode2kDCI2398 = /* '2d23' */ 0x32643233, bmdMode2kDCI24 = /* '2d24' */ 0x32643234, bmdMode2kDCI25 = /* '2d25' */ 0x32643235, /* 4k Modes */ bmdMode4K2160p2398 = /* '4k23' */ 0x346B3233, bmdMode4K2160p24 = /* '4k24' */ 0x346B3234, bmdMode4K2160p25 = /* '4k25' */ 0x346B3235, bmdMode4K2160p2997 = /* '4k29' */ 0x346B3239, bmdMode4K2160p30 = /* '4k30' */ 0x346B3330, bmdMode4K2160p50 = /* '4k50' */ 0x346B3530, bmdMode4K2160p5994 = /* '4k59' */ 0x346B3539, bmdMode4K2160p60 = /* '4k60' */ 0x346B3630, /* DCI Modes (output only) */ bmdMode4kDCI2398 = /* '4d23' */ 0x34643233, bmdMode4kDCI24 = /* '4d24' */ 0x34643234, bmdMode4kDCI25 = /* '4d25' */ 0x34643235, /* Special Modes */ bmdModeUnknown = /* 'iunk' */ 0x69756E6B }; /* Enum BMDFieldDominance - Video field dominance */ typedef uint32_t BMDFieldDominance; enum _BMDFieldDominance { bmdUnknownFieldDominance = 0, bmdLowerFieldFirst = /* 'lowr' */ 0x6C6F7772, bmdUpperFieldFirst = /* 'uppr' */ 0x75707072, bmdProgressiveFrame = /* 'prog' */ 0x70726F67, bmdProgressiveSegmentedFrame = /* 'psf ' */ 0x70736620 }; /* Enum BMDPixelFormat - Video pixel formats supported for output/input */ typedef uint32_t BMDPixelFormat; enum _BMDPixelFormat { bmdFormat8BitYUV = /* '2vuy' */ 0x32767579, bmdFormat10BitYUV = /* 'v210' */ 0x76323130, bmdFormat8BitARGB = 32, bmdFormat8BitBGRA = /* 'BGRA' */ 0x42475241, bmdFormat10BitRGB = /* 'r210' */ 0x72323130 // Big-endian RGB 10-bit per component with SMPTE video levels (64-960). Packed as 2:10:10:10 }; /* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */ typedef uint32_t BMDDisplayModeFlags; enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = 1 << 0, bmdDisplayModeColorspaceRec601 = 1 << 1, bmdDisplayModeColorspaceRec709 = 1 << 2 }; /* Enum BMDVideoOutputFlags - Flags to control the output of ancillary data along with video. */ typedef uint32_t BMDVideoOutputFlags; enum _BMDVideoOutputFlags { bmdVideoOutputFlagDefault = 0, bmdVideoOutputVANC = 1 << 0, bmdVideoOutputVITC = 1 << 1, bmdVideoOutputRP188 = 1 << 2, bmdVideoOutputDualStream3D = 1 << 4 }; /* Enum BMDFrameFlags - Frame flags */ typedef uint32_t BMDFrameFlags; enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = 1 << 0, /* Flags that are applicable only to instances of IDeckLinkVideoInputFrame */ bmdFrameHasNoInputSource = 1 << 31 }; /* Enum BMDVideoInputFlags - Flags applicable to video input */ typedef uint32_t BMDVideoInputFlags; enum _BMDVideoInputFlags { bmdVideoInputFlagDefault = 0, bmdVideoInputEnableFormatDetection = 1 << 0, bmdVideoInputDualStream3D = 1 << 1 }; /* Enum BMDVideoInputFormatChangedEvents - Bitmask passed to the VideoInputFormatChanged notification to identify the properties of the input signal that have changed */ typedef uint32_t BMDVideoInputFormatChangedEvents; enum _BMDVideoInputFormatChangedEvents { bmdVideoInputDisplayModeChanged = 1 << 0, bmdVideoInputFieldDominanceChanged = 1 << 1, bmdVideoInputColorspaceChanged = 1 << 2 }; /* Enum BMDDetectedVideoInputFormatFlags - Flags passed to the VideoInputFormatChanged notification to describe the detected video input signal */ typedef uint32_t BMDDetectedVideoInputFormatFlags; enum _BMDDetectedVideoInputFormatFlags { bmdDetectedVideoInputYCbCr422 = 1 << 0, bmdDetectedVideoInputRGB444 = 1 << 1 }; /* Enum BMDOutputFrameCompletionResult - Frame Completion Callback */ typedef uint32_t BMDOutputFrameCompletionResult; enum _BMDOutputFrameCompletionResult { bmdOutputFrameCompleted, bmdOutputFrameDisplayedLate, bmdOutputFrameDropped, bmdOutputFrameFlushed }; /* Enum BMDReferenceStatus - GenLock input status */ typedef uint32_t BMDReferenceStatus; enum _BMDReferenceStatus { bmdReferenceNotSupportedByHardware = 1 << 0, bmdReferenceLocked = 1 << 1 }; /* Enum BMDAudioSampleRate - Audio sample rates supported for output/input */ typedef uint32_t BMDAudioSampleRate; enum _BMDAudioSampleRate { bmdAudioSampleRate48kHz = 48000 }; /* Enum BMDAudioSampleType - Audio sample sizes supported for output/input */ typedef uint32_t BMDAudioSampleType; enum _BMDAudioSampleType { bmdAudioSampleType16bitInteger = 16, bmdAudioSampleType32bitInteger = 32 }; /* Enum BMDAudioOutputStreamType - Audio output stream type */ typedef uint32_t BMDAudioOutputStreamType; enum _BMDAudioOutputStreamType { bmdAudioOutputStreamContinuous, bmdAudioOutputStreamContinuousDontResample, bmdAudioOutputStreamTimestamped }; /* Enum BMDDisplayModeSupport - Output mode supported flags */ typedef uint32_t BMDDisplayModeSupport; enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, bmdDisplayModeSupported, bmdDisplayModeSupportedWithConversion }; /* Enum BMDTimecodeFormat - Timecode formats for frame metadata */ typedef uint32_t BMDTimecodeFormat; enum _BMDTimecodeFormat { bmdTimecodeRP188 = /* 'rp18' */ 0x72703138, bmdTimecodeVITC = /* 'vitc' */ 0x76697463, bmdTimecodeSerial = /* 'seri' */ 0x73657269 }; /* Enum BMDTimecodeFlags - Timecode flags */ typedef uint32_t BMDTimecodeFlags; enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = 1 << 0 }; /* Enum BMDVideoConnection - Video connection types */ typedef uint32_t BMDVideoConnection; enum _BMDVideoConnection { bmdVideoConnectionSDI = 1 << 0, bmdVideoConnectionHDMI = 1 << 1, bmdVideoConnectionOpticalSDI = 1 << 2, bmdVideoConnectionComponent = 1 << 3, bmdVideoConnectionComposite = 1 << 4, bmdVideoConnectionSVideo = 1 << 5 }; /* Enum BMDAnalogVideoFlags - Analog video display flags */ typedef uint32_t BMDAnalogVideoFlags; enum _BMDAnalogVideoFlags { bmdAnalogVideoFlagCompositeSetup75 = 1 << 0, bmdAnalogVideoFlagComponentBetacamLevels = 1 << 1 }; /* Enum BMDAudioConnection - Audio connection types */ typedef uint32_t BMDAudioConnection; enum _BMDAudioConnection { bmdAudioConnectionEmbedded = /* 'embd' */ 0x656D6264, bmdAudioConnectionAESEBU = /* 'aes ' */ 0x61657320, bmdAudioConnectionAnalog = /* 'anlg' */ 0x616E6C67 }; /* Enum BMDAudioOutputAnalogAESSwitch - Audio output Analog/AESEBU switch */ typedef uint32_t BMDAudioOutputAnalogAESSwitch; enum _BMDAudioOutputAnalogAESSwitch { bmdAudioOutputSwitchAESEBU = /* 'aes ' */ 0x61657320, bmdAudioOutputSwitchAnalog = /* 'anlg' */ 0x616E6C67 }; /* Enum BMDVideoOutputConversionMode - Video/audio conversion mode */ typedef uint32_t BMDVideoOutputConversionMode; enum _BMDVideoOutputConversionMode { bmdNoVideoOutputConversion = /* 'none' */ 0x6E6F6E65, bmdVideoOutputLetterboxDownconversion = /* 'ltbx' */ 0x6C746278, bmdVideoOutputAnamorphicDownconversion = /* 'amph' */ 0x616D7068, bmdVideoOutputHD720toHD1080Conversion = /* '720c' */ 0x37323063, bmdVideoOutputHardwareLetterboxDownconversion = /* 'HWlb' */ 0x48576C62, bmdVideoOutputHardwareAnamorphicDownconversion = /* 'HWam' */ 0x4857616D, bmdVideoOutputHardwareCenterCutDownconversion = /* 'HWcc' */ 0x48576363, bmdVideoOutputHardware720p1080pCrossconversion = /* 'xcap' */ 0x78636170, bmdVideoOutputHardwareAnamorphic720pUpconversion = /* 'ua7p' */ 0x75613770, bmdVideoOutputHardwareAnamorphic1080iUpconversion = /* 'ua1i' */ 0x75613169, bmdVideoOutputHardwareAnamorphic149To720pUpconversion = /* 'u47p' */ 0x75343770, bmdVideoOutputHardwareAnamorphic149To1080iUpconversion = /* 'u41i' */ 0x75343169, bmdVideoOutputHardwarePillarbox720pUpconversion = /* 'up7p' */ 0x75703770, bmdVideoOutputHardwarePillarbox1080iUpconversion = /* 'up1i' */ 0x75703169 }; /* Enum BMDVideoInputConversionMode - Video input conversion mode */ typedef uint32_t BMDVideoInputConversionMode; enum _BMDVideoInputConversionMode { bmdNoVideoInputConversion = /* 'none' */ 0x6E6F6E65, bmdVideoInputLetterboxDownconversionFromHD1080 = /* '10lb' */ 0x31306C62, bmdVideoInputAnamorphicDownconversionFromHD1080 = /* '10am' */ 0x3130616D, bmdVideoInputLetterboxDownconversionFromHD720 = /* '72lb' */ 0x37326C62, bmdVideoInputAnamorphicDownconversionFromHD720 = /* '72am' */ 0x3732616D, bmdVideoInputLetterboxUpconversion = /* 'lbup' */ 0x6C627570, bmdVideoInputAnamorphicUpconversion = /* 'amup' */ 0x616D7570 }; /* Enum BMDVideo3DPackingFormat - Video 3D packing format */ typedef uint32_t BMDVideo3DPackingFormat; enum _BMDVideo3DPackingFormat { bmdVideo3DPackingSidebySideHalf = /* 'sbsh' */ 0x73627368, bmdVideo3DPackingLinebyLine = /* 'lbyl' */ 0x6C62796C, bmdVideo3DPackingTopAndBottom = /* 'tabo' */ 0x7461626F, bmdVideo3DPackingLeftOnly = /* 'left' */ 0x6C656674, bmdVideo3DPackingRightOnly = /* 'righ' */ 0x72696768 }; /* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */ typedef uint32_t BMDDeckLinkConfigurationID; enum _BMDDeckLinkConfigurationID { /* Video Input/Output Flags */ bmdDeckLinkConfigUse1080pNotPsF = /* 'fpro' */ 0x6670726F, /* Video Input/Output Integers */ bmdDeckLinkConfigHDMI3DPackingFormat = /* '3dpf' */ 0x33647066, /* Audio Input/Output Flags */ bmdDeckLinkConfigAnalogAudioConsumerLevels = /* 'aacl' */ 0x6161636C, /* Video output flags */ bmdDeckLinkConfigFieldFlickerRemoval = /* 'fdfr' */ 0x66646672, bmdDeckLinkConfigHD1080p24ToHD1080i5994Conversion = /* 'to59' */ 0x746F3539, bmdDeckLinkConfig444SDIVideoOutput = /* '444o' */ 0x3434346F, bmdDeckLinkConfig3GBpsVideoOutput = /* '3gbs' */ 0x33676273, bmdDeckLinkConfigBlackVideoOutputDuringCapture = /* 'bvoc' */ 0x62766F63, bmdDeckLinkConfigLowLatencyVideoOutput = /* 'llvo' */ 0x6C6C766F, /* Video Output Integers */ bmdDeckLinkConfigVideoOutputConnection = /* 'vocn' */ 0x766F636E, bmdDeckLinkConfigVideoOutputConversionMode = /* 'vocm' */ 0x766F636D, bmdDeckLinkConfigAnalogVideoOutputFlags = /* 'avof' */ 0x61766F66, bmdDeckLinkConfigReferenceInputTimingOffset = /* 'glot' */ 0x676C6F74, /* Video Input Integers */ bmdDeckLinkConfigVideoInputConnection = /* 'vicn' */ 0x7669636E, bmdDeckLinkConfigAnalogVideoInputFlags = /* 'avif' */ 0x61766966, bmdDeckLinkConfigVideoInputConversionMode = /* 'vicm' */ 0x7669636D, bmdDeckLinkConfig32PulldownSequenceInitialTimecodeFrame = /* 'pdif' */ 0x70646966, bmdDeckLinkConfigVANCSourceLine1Mapping = /* 'vsl1' */ 0x76736C31, bmdDeckLinkConfigVANCSourceLine2Mapping = /* 'vsl2' */ 0x76736C32, bmdDeckLinkConfigVANCSourceLine3Mapping = /* 'vsl3' */ 0x76736C33, /* Audio Input Integers */ bmdDeckLinkConfigAudioInputConnection = /* 'aicn' */ 0x6169636E, /* Audio Input Floats */ bmdDeckLinkConfigAnalogAudioInputScaleChannel1 = /* 'ais1' */ 0x61697331, bmdDeckLinkConfigAnalogAudioInputScaleChannel2 = /* 'ais2' */ 0x61697332, bmdDeckLinkConfigAnalogAudioInputScaleChannel3 = /* 'ais3' */ 0x61697333, bmdDeckLinkConfigAnalogAudioInputScaleChannel4 = /* 'ais4' */ 0x61697334, bmdDeckLinkConfigDigitalAudioInputScale = /* 'dais' */ 0x64616973, /* Audio Output Integers */ bmdDeckLinkConfigAudioOutputAESAnalogSwitch = /* 'aoaa' */ 0x616F6161, /* Audio Output Floats */ bmdDeckLinkConfigAnalogAudioOutputScaleChannel1 = /* 'aos1' */ 0x616F7331, bmdDeckLinkConfigAnalogAudioOutputScaleChannel2 = /* 'aos2' */ 0x616F7332, bmdDeckLinkConfigAnalogAudioOutputScaleChannel3 = /* 'aos3' */ 0x616F7333, bmdDeckLinkConfigAnalogAudioOutputScaleChannel4 = /* 'aos4' */ 0x616F7334, bmdDeckLinkConfigDigitalAudioOutputScale = /* 'daos' */ 0x64616F73 }; /* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */ typedef uint32_t BMDDeckLinkAttributeID; enum _BMDDeckLinkAttributeID { /* Flags */ BMDDeckLinkSupportsInternalKeying = /* 'keyi' */ 0x6B657969, BMDDeckLinkSupportsExternalKeying = /* 'keye' */ 0x6B657965, BMDDeckLinkSupportsHDKeying = /* 'keyh' */ 0x6B657968, BMDDeckLinkSupportsInputFormatDetection = /* 'infd' */ 0x696E6664, BMDDeckLinkHasReferenceInput = /* 'hrin' */ 0x6872696E, BMDDeckLinkHasSerialPort = /* 'hspt' */ 0x68737074, /* Integers */ BMDDeckLinkMaximumAudioChannels = /* 'mach' */ 0x6D616368, BMDDeckLinkNumberOfSubDevices = /* 'nsbd' */ 0x6E736264, BMDDeckLinkSubDeviceIndex = /* 'subi' */ 0x73756269, BMDDeckLinkVideoOutputConnections = /* 'vocn' */ 0x766F636E, BMDDeckLinkVideoInputConnections = /* 'vicn' */ 0x7669636E, /* Strings */ BMDDeckLinkSerialPortDeviceName = /* 'slpn' */ 0x736C706E }; /* Enum BMDDeckLinkAPIInformationID - DeckLinkAPI information ID */ typedef uint32_t BMDDeckLinkAPIInformationID; enum _BMDDeckLinkAPIInformationID { BMDDeckLinkAPIVersion = /* 'vers' */ 0x76657273 }; /* Enum BMDDeckControlMode - DeckControl mode */ typedef uint32_t BMDDeckControlMode; enum _BMDDeckControlMode { bmdDeckControlNotOpened = /* 'ntop' */ 0x6E746F70, bmdDeckControlVTRControlMode = /* 'vtrc' */ 0x76747263, bmdDeckControlExportMode = /* 'expm' */ 0x6578706D, bmdDeckControlCaptureMode = /* 'capm' */ 0x6361706D }; /* Enum BMDDeckControlEvent - DeckControl event */ typedef uint32_t BMDDeckControlEvent; enum _BMDDeckControlEvent { bmdDeckControlAbortedEvent = /* 'abte' */ 0x61627465, // This event is triggered when a capture or edit-to-tape operation is aborted. /* Export-To-Tape events */ bmdDeckControlPrepareForExportEvent = /* 'pfee' */ 0x70666565, // This event is triggered a few frames before reaching the in-point. IDeckLinkInput::StartScheduledPlayback() should be called at this point. bmdDeckControlExportCompleteEvent = /* 'exce' */ 0x65786365, // This event is triggered a few frames after reaching the out-point. At this point, it is safe to stop playback. /* Capture events */ bmdDeckControlPrepareForCaptureEvent = /* 'pfce' */ 0x70666365, // This event is triggered a few frames before reaching the in-point. The serial timecode attached to IDeckLinkVideoInputFrames is now valid. bmdDeckControlCaptureCompleteEvent = /* 'ccev' */ 0x63636576 // This event is triggered a few frames after reaching the out-point. }; /* Enum BMDDeckControlVTRControlState - VTR Control state */ typedef uint32_t BMDDeckControlVTRControlState; enum _BMDDeckControlVTRControlState { bmdDeckControlNotInVTRControlMode = /* 'nvcm' */ 0x6E76636D, bmdDeckControlVTRControlPlaying = /* 'vtrp' */ 0x76747270, bmdDeckControlVTRControlRecording = /* 'vtrr' */ 0x76747272, bmdDeckControlVTRControlStill = /* 'vtra' */ 0x76747261, bmdDeckControlVTRControlSeeking = /* 'vtrs' */ 0x76747273, bmdDeckControlVTRControlStopped = /* 'vtro' */ 0x7674726F }; /* Enum BMDDeckControlStatusFlags - Deck Control status flags */ typedef uint32_t BMDDeckControlStatusFlags; enum _BMDDeckControlStatusFlags { bmdDeckControlStatusDeckConnected = 1 << 0, bmdDeckControlStatusRemoteMode = 1 << 1, bmdDeckControlStatusRecordInhibited = 1 << 2, bmdDeckControlStatusCassetteOut = 1 << 3 }; /* Enum BMDDeckControlExportModeOpsFlags - Export mode flags */ typedef uint32_t BMDDeckControlExportModeOpsFlags; enum _BMDDeckControlExportModeOpsFlags { bmdDeckControlExportModeInsertVideo = 1 << 0, bmdDeckControlExportModeInsertAudio1 = 1 << 1, bmdDeckControlExportModeInsertAudio2 = 1 << 2, bmdDeckControlExportModeInsertAudio3 = 1 << 3, bmdDeckControlExportModeInsertAudio4 = 1 << 4, bmdDeckControlExportModeInsertAudio5 = 1 << 5, bmdDeckControlExportModeInsertAudio6 = 1 << 6, bmdDeckControlExportModeInsertAudio7 = 1 << 7, bmdDeckControlExportModeInsertAudio8 = 1 << 8, bmdDeckControlExportModeInsertAudio9 = 1 << 9, bmdDeckControlExportModeInsertAudio10 = 1 << 10, bmdDeckControlExportModeInsertAudio11 = 1 << 11, bmdDeckControlExportModeInsertAudio12 = 1 << 12, bmdDeckControlExportModeInsertTimeCode = 1 << 13, bmdDeckControlExportModeInsertAssemble = 1 << 14, bmdDeckControlExportModeInsertPreview = 1 << 15, bmdDeckControlUseManualExport = 1 << 16 }; /* Enum BMDDeckControlError - Deck Control error */ typedef uint32_t BMDDeckControlError; enum _BMDDeckControlError { bmdDeckControlNoError = /* 'noer' */ 0x6E6F6572, bmdDeckControlModeError = /* 'moer' */ 0x6D6F6572, bmdDeckControlMissedInPointError = /* 'mier' */ 0x6D696572, bmdDeckControlDeckTimeoutError = /* 'dter' */ 0x64746572, bmdDeckControlCommandFailedError = /* 'cfer' */ 0x63666572, bmdDeckControlDeviceAlreadyOpenedError = /* 'dalo' */ 0x64616C6F, bmdDeckControlFailedToOpenDeviceError = /* 'fder' */ 0x66646572, bmdDeckControlInLocalModeError = /* 'lmer' */ 0x6C6D6572, bmdDeckControlEndOfTapeError = /* 'eter' */ 0x65746572, bmdDeckControlUserAbortError = /* 'uaer' */ 0x75616572, bmdDeckControlNoTapeInDeckError = /* 'nter' */ 0x6E746572, bmdDeckControlNoVideoFromCardError = /* 'nvfc' */ 0x6E766663, bmdDeckControlNoCommunicationError = /* 'ncom' */ 0x6E636F6D, bmdDeckControlUnknownError = /* 'uner' */ 0x756E6572 }; /* Enum BMD3DPreviewFormat - Linked Frame preview format */ typedef uint32_t BMD3DPreviewFormat; enum _BMD3DPreviewFormat { bmd3DPreviewFormatDefault = /* 'defa' */ 0x64656661, bmd3DPreviewFormatLeftOnly = /* 'left' */ 0x6C656674, bmd3DPreviewFormatRightOnly = /* 'righ' */ 0x72696768, bmd3DPreviewFormatSideBySide = /* 'side' */ 0x73696465, bmd3DPreviewFormatTopBottom = /* 'topb' */ 0x746F7062 }; #if defined(__cplusplus) // Forward Declarations class IDeckLinkVideoOutputCallback; class IDeckLinkInputCallback; class IDeckLinkMemoryAllocator; class IDeckLinkAudioOutputCallback; class IDeckLinkIterator; class IDeckLinkAPIInformation; class IDeckLinkDisplayModeIterator; class IDeckLinkDisplayMode; class IDeckLink; class IDeckLinkOutput; class IDeckLinkInput; class IDeckLinkTimecode; class IDeckLinkVideoFrame; class IDeckLinkMutableVideoFrame; class IDeckLinkVideoFrame3DExtensions; class IDeckLinkVideoInputFrame; class IDeckLinkVideoFrameAncillary; class IDeckLinkAudioInputPacket; class IDeckLinkScreenPreviewCallback; class IDeckLinkGLScreenPreviewHelper; class IDeckLinkConfiguration; class IDeckLinkAttributes; class IDeckLinkKeyer; class IDeckLinkVideoConversion; class IDeckLinkDeckControlStatusCallback; class IDeckLinkDeckControl; /* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */ class IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame *completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0; virtual HRESULT ScheduledPlaybackHasStopped (void) = 0; protected: virtual ~IDeckLinkVideoOutputCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkInputCallback - Frame arrival callback. */ class IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode *newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT VideoInputFrameArrived (/* in */ IDeckLinkVideoInputFrame* videoFrame, /* in */ IDeckLinkAudioInputPacket* audioPacket) = 0; protected: virtual ~IDeckLinkInputCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkMemoryAllocator - Memory allocator for video frames. */ class IDeckLinkMemoryAllocator : public IUnknown { public: virtual HRESULT AllocateBuffer (/* in */ uint32_t bufferSize, /* out */ void **allocatedBuffer) = 0; virtual HRESULT ReleaseBuffer (/* in */ void *buffer) = 0; virtual HRESULT Commit (void) = 0; virtual HRESULT Decommit (void) = 0; }; /* Interface IDeckLinkAudioOutputCallback - Optional callback to allow audio samples to be pulled as required. */ class IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT RenderAudioSamples (/* in */ bool preroll) = 0; }; /* Interface IDeckLinkIterator - enumerates installed DeckLink hardware */ class IDeckLinkIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLink **deckLinkInstance) = 0; }; /* Interface IDeckLinkAPIInformation - DeckLinkAPI attribute interface */ class IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ bool *value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ int64_t *value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ double *value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkAPIInformationID cfgID, /* out */ const char **value) = 0; protected: virtual ~IDeckLinkAPIInformation () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDisplayModeIterator - enumerates over supported input/output display modes. */ class IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT Next (/* out */ IDeckLinkDisplayMode **deckLinkDisplayMode) = 0; protected: virtual ~IDeckLinkDisplayModeIterator () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDisplayMode - represents a display mode */ class IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT GetName (/* out */ const char **name) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; virtual long GetWidth (void) = 0; virtual long GetHeight (void) = 0; virtual HRESULT GetFrameRate (/* out */ BMDTimeValue *frameDuration, /* out */ BMDTimeScale *timeScale) = 0; virtual BMDFieldDominance GetFieldDominance (void) = 0; virtual BMDDisplayModeFlags GetFlags (void) = 0; protected: virtual ~IDeckLinkDisplayMode () {}; // call Release method to drop reference count }; /* Interface IDeckLink - represents a DeckLink device */ class IDeckLink : public IUnknown { public: virtual HRESULT GetModelName (/* out */ const char **modelName) = 0; }; /* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */ class IDeckLinkOutput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; /* Video Output */ virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0; virtual HRESULT DisableVideoOutput (void) = 0; virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame **outFrame) = 0; virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback *theCallback) = 0; virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t *bufferedFrameCount) = 0; /* Audio Output */ virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT DisableAudioOutput (void) = 0; virtual HRESULT WriteAudioSamplesSync (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t *sampleFramesWritten) = 0; virtual HRESULT BeginAudioPreroll (void) = 0; virtual HRESULT EndAudioPreroll (void) = 0; virtual HRESULT ScheduleAudioSamples (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t *sampleFramesWritten) = 0; virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t *bufferedSampleFrameCount) = 0; virtual HRESULT FlushBufferedAudioSamples (void) = 0; virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback *theCallback) = 0; /* Output Control */ virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0; virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue *actualStopTime, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool *active) = 0; virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *streamTime, /* out */ double *playbackSpeed) = 0; virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus *referenceStatus) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0; protected: virtual ~IDeckLinkOutput () {}; // call Release method to drop reference count }; /* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */ class IDeckLinkInput : public IUnknown { public: virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; /* Video Input */ virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0; virtual HRESULT DisableVideoInput (void) = 0; virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t *availableFrameCount) = 0; /* Audio Input */ virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0; virtual HRESULT DisableAudioInput (void) = 0; virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t *availableSampleFrameCount) = 0; /* Input Control */ virtual HRESULT StartStreams (void) = 0; virtual HRESULT StopStreams (void) = 0; virtual HRESULT PauseStreams (void) = 0; virtual HRESULT FlushStreams (void) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback *theCallback) = 0; /* Hardware Timing */ virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0; protected: virtual ~IDeckLinkInput () {}; // call Release method to drop reference count }; /* Interface IDeckLinkTimecode - Used for video frame timecode representation. */ class IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD GetBCD (void) = 0; virtual HRESULT GetComponents (/* out */ uint8_t *hours, /* out */ uint8_t *minutes, /* out */ uint8_t *seconds, /* out */ uint8_t *frames) = 0; virtual HRESULT GetString (/* out */ const char **timecode) = 0; virtual BMDTimecodeFlags GetFlags (void) = 0; virtual HRESULT GetTimecodeUserBits (/* out */ BMDTimecodeUserBits *userBits) = 0; protected: virtual ~IDeckLinkTimecode () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */ class IDeckLinkVideoFrame : public IUnknown { public: virtual long GetWidth (void) = 0; virtual long GetHeight (void) = 0; virtual long GetRowBytes (void) = 0; virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDFrameFlags GetFlags (void) = 0; virtual HRESULT GetBytes (/* out */ void **buffer) = 0; virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) = 0; virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) = 0; protected: virtual ~IDeckLinkVideoFrame () {}; // call Release method to drop reference count }; /* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */ class IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0; virtual HRESULT SetTimecode (/* in */ BMDTimecodeFormat format, /* in */ IDeckLinkTimecode *timecode) = 0; virtual HRESULT SetTimecodeFromComponents (/* in */ BMDTimecodeFormat format, /* in */ uint8_t hours, /* in */ uint8_t minutes, /* in */ uint8_t seconds, /* in */ uint8_t frames, /* in */ BMDTimecodeFlags flags) = 0; virtual HRESULT SetAncillaryData (/* in */ IDeckLinkVideoFrameAncillary *ancillary) = 0; virtual HRESULT SetTimecodeUserBits (/* in */ BMDTimecodeFormat format, /* in */ BMDTimecodeUserBits userBits) = 0; protected: virtual ~IDeckLinkMutableVideoFrame () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrame3DExtensions - Optional interface implemented on IDeckLinkVideoFrame to support 3D frames */ class IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0; virtual HRESULT GetFrameForRightEye (/* in */ IDeckLinkVideoFrame* *rightEyeFrame) = 0; protected: virtual ~IDeckLinkVideoFrame3DExtensions () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */ class IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT GetStreamTime (/* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration, /* in */ BMDTimeScale timeScale) = 0; virtual HRESULT GetHardwareReferenceTimestamp (/* in */ BMDTimeScale timeScale, /* out */ BMDTimeValue *frameTime, /* out */ BMDTimeValue *frameDuration) = 0; protected: virtual ~IDeckLinkVideoInputFrame () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoFrameAncillary - Obtained through QueryInterface() on an IDeckLinkVideoFrame object. */ class IDeckLinkVideoFrameAncillary : public IUnknown { public: virtual HRESULT GetBufferForVerticalBlankingLine (/* in */ uint32_t lineNumber, /* out */ void **buffer) = 0; virtual BMDPixelFormat GetPixelFormat (void) = 0; virtual BMDDisplayMode GetDisplayMode (void) = 0; protected: virtual ~IDeckLinkVideoFrameAncillary () {}; // call Release method to drop reference count }; /* Interface IDeckLinkAudioInputPacket - Provided by the IDeckLinkInput callback. */ class IDeckLinkAudioInputPacket : public IUnknown { public: virtual long GetSampleFrameCount (void) = 0; virtual HRESULT GetBytes (/* out */ void **buffer) = 0; virtual HRESULT GetPacketTime (/* out */ BMDTimeValue *packetTime, /* in */ BMDTimeScale timeScale) = 0; protected: virtual ~IDeckLinkAudioInputPacket () {}; // call Release method to drop reference count }; /* Interface IDeckLinkScreenPreviewCallback - Screen preview callback */ class IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame *theFrame) = 0; protected: virtual ~IDeckLinkScreenPreviewCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance(). */ class IDeckLinkGLScreenPreviewHelper : public IUnknown { public: /* Methods must be called with OpenGL context set */ virtual HRESULT InitializeGL (void) = 0; virtual HRESULT PaintGL (void) = 0; virtual HRESULT SetFrame (/* in */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT Set3DPreviewFormat (/* in */ BMD3DPreviewFormat previewFormat) = 0; protected: virtual ~IDeckLinkGLScreenPreviewHelper () {}; // call Release method to drop reference count }; /* Interface IDeckLinkConfiguration - DeckLink Configuration interface */ class IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0; virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0; virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0; virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0; virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char *value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char **value) = 0; virtual HRESULT WriteConfigurationToPreferences (void) = 0; protected: virtual ~IDeckLinkConfiguration () {}; // call Release method to drop reference count }; /* Interface IDeckLinkAttributes - DeckLink Attribute interface */ class IDeckLinkAttributes : public IUnknown { public: virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool *value) = 0; virtual HRESULT GetInt (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ int64_t *value) = 0; virtual HRESULT GetFloat (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ double *value) = 0; virtual HRESULT GetString (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ const char **value) = 0; protected: virtual ~IDeckLinkAttributes () {}; // call Release method to drop reference count }; /* Interface IDeckLinkKeyer - DeckLink Keyer interface */ class IDeckLinkKeyer : public IUnknown { public: virtual HRESULT Enable (/* in */ bool isExternal) = 0; virtual HRESULT SetLevel (/* in */ uint8_t level) = 0; virtual HRESULT RampUp (/* in */ uint32_t numberOfFrames) = 0; virtual HRESULT RampDown (/* in */ uint32_t numberOfFrames) = 0; virtual HRESULT Disable (void) = 0; protected: virtual ~IDeckLinkKeyer () {}; // call Release method to drop reference count }; /* Interface IDeckLinkVideoConversion - Created with CoCreateInstance(). */ class IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0; protected: virtual ~IDeckLinkVideoConversion () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */ class IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0; virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState newState, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0; virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0; protected: virtual ~IDeckLinkDeckControlStatusCallback () {}; // call Release method to drop reference count }; /* Interface IDeckLinkDeckControl - Deck Control main interface */ class IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Close (/* in */ bool standbyOn) = 0; virtual HRESULT GetCurrentState (/* out */ BMDDeckControlMode *mode, /* out */ BMDDeckControlVTRControlState *vtrControlState, /* out */ BMDDeckControlStatusFlags *flags) = 0; virtual HRESULT SetStandby (/* in */ bool standbyOn) = 0; virtual HRESULT Play (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT Stop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT TogglePlayStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT Eject (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT GoToTimecode (/* in */ BMDTimecodeBCD timecode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT FastForward (/* in */ bool viewTape, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Rewind (/* in */ bool viewTape, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT StepForward (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT StepBack (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT Jog (/* in */ double rate, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Shuttle (/* in */ double rate, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT GetTimecodeString (/* out */ const char **currentTimeCode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT GetTimecode (/* out */ IDeckLinkTimecode **currentTimecode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT GetTimecodeBCD (/* out */ BMDTimecodeBCD *currentTimecode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetPreroll (/* in */ uint32_t prerollSeconds) = 0; virtual HRESULT GetPreroll (/* out */ uint32_t *prerollSeconds) = 0; virtual HRESULT SetExportOffset (/* in */ int32_t exportOffsetFields) = 0; virtual HRESULT GetExportOffset (/* out */ int32_t *exportOffsetFields) = 0; virtual HRESULT GetManualExportOffset (/* out */ int32_t *deckManualExportOffsetFields) = 0; virtual HRESULT SetCaptureOffset (/* in */ int32_t captureOffsetFields) = 0; virtual HRESULT GetCaptureOffset (/* out */ int32_t *captureOffsetFields) = 0; virtual HRESULT StartExport (/* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* in */ BMDDeckControlExportModeOpsFlags exportModeOps, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT StartCapture (/* in */ bool useVITC, /* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT GetDeviceID (/* out */ uint16_t *deviceId, /* out */ BMDDeckControlError *error) = 0; virtual HRESULT Abort (void) = 0; virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError *error) = 0; virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback *callback) = 0; protected: virtual ~IDeckLinkDeckControl () {}; // call Release method to drop reference count }; /* Functions */ extern "C" { IDeckLinkIterator* CreateDeckLinkIteratorInstance (void); IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void); IDeckLinkVideoConversion* CreateVideoConversionInstance (void); }; #endif // defined(__cplusplus) #endif // __DeckLink_API_h__ mlt-6.20.0/src/modules/decklink/linux/DeckLinkAPIDispatch.cpp000066400000000000000000000074311362234133600237670ustar00rootroot00000000000000/* -LICENSE-START- ** Copyright (c) 2009 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- **/ #include #include #include #include "DeckLinkAPI.h" #define kDeckLinkAPI_Name "libDeckLinkAPI.so" #define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so" typedef IDeckLinkIterator* (*CreateIteratorFunc)(void); typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void); typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void); static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT; static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT; static CreateIteratorFunc gCreateIteratorFunc = NULL; static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL; static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL; void InitDeckLinkAPI (void) { void *libraryHandle; libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0001"); if (!gCreateIteratorFunc) fprintf(stderr, "%s\n", dlerror()); gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0001"); if (!gCreateVideoConversionFunc) fprintf(stderr, "%s\n", dlerror()); } void InitDeckLinkPreviewAPI (void) { void *libraryHandle; libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL); if (!libraryHandle) { fprintf(stderr, "%s\n", dlerror()); return; } gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0001"); if (!gCreateOpenGLPreviewFunc) fprintf(stderr, "%s\n", dlerror()); } IDeckLinkIterator* CreateDeckLinkIteratorInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateIteratorFunc == NULL) return NULL; return gCreateIteratorFunc(); } IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI); if (gCreateOpenGLPreviewFunc == NULL) return NULL; return gCreateOpenGLPreviewFunc(); } IDeckLinkVideoConversion* CreateVideoConversionInstance (void) { pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI); if (gCreateVideoConversionFunc == NULL) return NULL; return gCreateVideoConversionFunc(); } mlt-6.20.0/src/modules/decklink/linux/LinuxCOM.h000066400000000000000000000065411362234133600213750ustar00rootroot00000000000000/* -LICENSE-START- ** Copyright (c) 2009 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ #ifndef __LINUX_COM_H_ #define __LINUX_COM_H_ struct REFIID { unsigned char byte0; unsigned char byte1; unsigned char byte2; unsigned char byte3; unsigned char byte4; unsigned char byte5; unsigned char byte6; unsigned char byte7; unsigned char byte8; unsigned char byte9; unsigned char byte10; unsigned char byte11; unsigned char byte12; unsigned char byte13; unsigned char byte14; unsigned char byte15; }; typedef REFIID CFUUIDBytes; #define CFUUIDGetUUIDBytes(x) x typedef int HRESULT; typedef unsigned long ULONG; typedef void *LPVOID; #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) #define FAILED(Status) ((HRESULT)(Status)<0) #define IS_ERROR(Status) ((unsigned long)(Status) >> 31 == SEVERITY_ERROR) #define HRESULT_CODE(hr) ((hr) & 0xFFFF) #define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff) #define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1) #define SEVERITY_SUCCESS 0 #define SEVERITY_ERROR 1 #define MAKE_HRESULT(sev,fac,code) ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) #define S_OK ((HRESULT)0x00000000L) #define S_FALSE ((HRESULT)0x00000001L) #define E_UNEXPECTED ((HRESULT)0x8000FFFFL) #define E_NOTIMPL ((HRESULT)0x80000001L) #define E_OUTOFMEMORY ((HRESULT)0x80000002L) #define E_INVALIDARG ((HRESULT)0x80000003L) #define E_NOINTERFACE ((HRESULT)0x80000004L) #define E_POINTER ((HRESULT)0x80000005L) #define E_HANDLE ((HRESULT)0x80000006L) #define E_ABORT ((HRESULT)0x80000007L) #define E_FAIL ((HRESULT)0x80000008L) #define E_ACCESSDENIED ((HRESULT)0x80000009L) #define STDMETHODCALLTYPE #define IID_IUnknown (REFIID){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} #define IUnknownUUID IID_IUnknown #ifdef __cplusplus class IUnknown { public: virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0; virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; virtual ULONG STDMETHODCALLTYPE Release(void) = 0; }; #endif #endif mlt-6.20.0/src/modules/decklink/producer_decklink.cpp000066400000000000000000000676021362234133600226270ustar00rootroot00000000000000/* * producer_decklink.c -- input from Blackmagic Design DeckLink * Copyright (C) 2011-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "common.h" #include struct copy_lines_sliced_desc { BMDPixelFormat in_fmt; mlt_image_format out_fmt; unsigned char *in_buffer, **out_buffers; int in_stride, *out_strides, w, h; }; #define READ_PIXELS( a, b, c ) \ val = *v210++; \ *a++ = ( val & 0x3FF ) << 6; \ *b++ = ( ( val >> 10 ) & 0x3FF ) << 6; \ *c++ = ( ( val >> 20 ) & 0x3FF ) << 6; static int copy_lines_sliced_proc( int id, int idx, int jobs, void* cookie ) { int c, H, Y, i; struct copy_lines_sliced_desc *ctx = (struct copy_lines_sliced_desc*)cookie; H = ( ctx->h + jobs ) / jobs; Y = idx * H; H = MIN( H, ctx->h - Y ); if ( ctx->in_fmt == bmdFormat10BitYUV ) // bmdFormat10BitYUV -> mlt_image_yuv422p16 { for( i = 0; i < H; i++) { uint32_t val, *v210 = (uint32_t*)( ctx->in_buffer + ( Y + i ) * ctx->in_stride ); uint16_t *y = (uint16_t*)( ctx->out_buffers[0] + ( Y + i ) * ctx->out_strides[0] ), *u = (uint16_t*)( ctx->out_buffers[1] + ( Y + i ) * ctx->out_strides[1] ), *v = (uint16_t*)( ctx->out_buffers[2] + ( Y + i ) * ctx->out_strides[2] ); for( c = 0; c < ctx->w / 6; c++ ) { READ_PIXELS( u, y, v ); READ_PIXELS( y, u, y ); READ_PIXELS( v, y, u ); READ_PIXELS( y, v, y ); } } } else // bmdFormat8BitYUV -> mlt_image_yuv422 { if ( ctx->out_strides[0] == ctx->in_stride ) swab2(ctx->in_buffer + Y * ctx->in_stride, ctx->out_buffers[0] + Y * ctx->out_strides[0], H * ctx->in_stride ); else for(i = 0; i < H; i++ ) swab2(ctx->in_buffer + ( Y + i ) * ctx->in_stride, ctx->out_buffers[0] + ( Y + i ) * ctx->out_strides[0], MIN( ctx->in_stride , ctx->out_strides[0] ) ); } return 0; } static void copy_lines( BMDPixelFormat in_fmt, unsigned char* in_buffer, int in_stride, mlt_image_format out_fmt, unsigned char* out_buffers[4], int out_strides[4], int w, int h ) { struct copy_lines_sliced_desc ctx = { in_fmt, out_fmt, in_buffer, out_buffers, in_stride, out_strides, w, h }; if ( h == 1 ) copy_lines_sliced_proc( 0, 0, 1, &ctx ); else mlt_slices_run_normal( mlt_slices_count_normal(), copy_lines_sliced_proc, &ctx ); } static void fill_line( mlt_image_format out_fmt, unsigned char *in[4], int strides[4], int pattern ) { // TODO } class DeckLinkProducer : public IDeckLinkInputCallback { private: mlt_producer m_producer; IDeckLink* m_decklink; IDeckLinkInput* m_decklinkInput; mlt_deque m_queue; pthread_mutex_t m_mutex; pthread_cond_t m_condition; bool m_started; int m_dropped; bool m_isBuffering; int m_topFieldFirst; BMDPixelFormat m_pixel_format; int m_colorspace; int m_vancLines; mlt_cache m_cache; bool m_reprio; BMDDisplayMode getDisplayMode( mlt_profile profile, int vancLines ) { IDeckLinkDisplayModeIterator* iter = NULL; IDeckLinkDisplayMode* mode = NULL; BMDDisplayMode result = (BMDDisplayMode) bmdDisplayModeNotSupported; if ( m_decklinkInput->GetDisplayModeIterator( &iter ) == S_OK ) { while ( !result && iter->Next( &mode ) == S_OK ) { int width = mode->GetWidth(); int height = mode->GetHeight(); BMDTimeValue duration; BMDTimeScale timescale; mode->GetFrameRate( &duration, ×cale ); double fps = (double) timescale / duration; int p = mode->GetFieldDominance() == bmdProgressiveFrame; m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst; m_colorspace = ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601; mlt_log_verbose( getProducer(), "BMD mode %dx%d %.3f fps prog %d tff %d\n", width, height, fps, p, m_topFieldFirst ); if ( width == profile->width && p == profile->progressive && ( height + vancLines == profile->height || ( height == 486 && profile->height == 480 + vancLines ) ) && (int) fps == (int) mlt_profile_fps( profile ) ) result = mode->GetDisplayMode(); SAFE_RELEASE( mode ); } SAFE_RELEASE( iter ); } return result; } public: mlt_profile m_new_input; void setProducer( mlt_producer producer ) { m_producer = producer; } mlt_producer getProducer() const { return m_producer; } DeckLinkProducer() { m_producer = NULL; m_decklink = NULL; m_decklinkInput = NULL; m_new_input = NULL; } virtual ~DeckLinkProducer() { if ( m_queue ) { stop(); mlt_deque_close( m_queue ); pthread_mutex_destroy( &m_mutex ); pthread_cond_destroy( &m_condition ); mlt_cache_close( m_cache ); } SAFE_RELEASE( m_decklinkInput ); SAFE_RELEASE( m_decklink ); } bool open( unsigned card = 0 ) { IDeckLinkIterator* decklinkIterator = NULL; try { #ifdef _WIN32 HRESULT result = CoInitialize( NULL ); if ( FAILED( result ) ) throw "COM initialization failed"; result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ); if ( FAILED( result ) ) throw "The DeckLink drivers are not installed."; #else decklinkIterator = CreateDeckLinkIteratorInstance(); if ( !decklinkIterator ) throw "The DeckLink drivers are not installed."; #endif // Connect to the Nth DeckLink instance for ( unsigned i = 0; decklinkIterator->Next( &m_decklink ) == S_OK ; i++) { if ( i == card ) break; else SAFE_RELEASE( m_decklink ); } SAFE_RELEASE( decklinkIterator ); if ( !m_decklink ) throw "DeckLink card not found."; // Get the input interface if ( m_decklink->QueryInterface( IID_IDeckLinkInput, (void**) &m_decklinkInput ) != S_OK ) throw "No DeckLink cards support input."; // Provide this class as a delegate to the input callback m_decklinkInput->SetCallback( this ); // Initialize other members pthread_mutex_init( &m_mutex, NULL ); pthread_cond_init( &m_condition, NULL ); m_queue = mlt_deque_init(); m_started = false; m_dropped = 0; m_isBuffering = true; m_cache = mlt_cache_init(); // 3 covers YADIF and increasing framerate use cases mlt_cache_set_size( m_cache, 3 ); } catch ( const char *error ) { SAFE_RELEASE( m_decklinkInput ); SAFE_RELEASE( m_decklink ); mlt_log_error( getProducer(), "%s\n", error ); return false; } return true; } bool start( mlt_profile profile = 0 ) { if ( m_started ) return false; try { // Initialize some members m_vancLines = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "vanc" ); if ( m_vancLines == -1 ) m_vancLines = profile->height <= 512 ? 26 : 32; if ( !profile ) profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) ); // Get the display mode BMDDisplayMode displayMode = getDisplayMode( profile, m_vancLines ); if ( displayMode == (BMDDisplayMode) bmdDisplayModeNotSupported ) { mlt_log_info( getProducer(), "profile = %dx%d %f fps %s\n", profile->width, profile->height, mlt_profile_fps( profile ), profile->progressive? "progressive" : "interlace" ); throw "Profile is not compatible with decklink."; } // Determine if supports input format detection #ifdef _WIN32 BOOL doesDetectFormat = FALSE; #else bool doesDetectFormat = false; #endif IDeckLinkAttributes *decklinkAttributes = 0; if ( m_decklink->QueryInterface( IID_IDeckLinkAttributes, (void**) &decklinkAttributes ) == S_OK ) { if ( decklinkAttributes->GetFlag( BMDDeckLinkSupportsInputFormatDetection, &doesDetectFormat ) != S_OK ) doesDetectFormat = false; SAFE_RELEASE( decklinkAttributes ); } mlt_log_verbose( getProducer(), "%s format detection\n", doesDetectFormat ? "supports" : "does not support" ); // Enable video capture m_pixel_format = ( 10 == mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "bitdepth" ) ) ? bmdFormat10BitYUV : bmdFormat8BitYUV; BMDVideoInputFlags flags = doesDetectFormat ? bmdVideoInputEnableFormatDetection : bmdVideoInputFlagDefault; if ( S_OK != m_decklinkInput->EnableVideoInput( displayMode, m_pixel_format, flags ) ) throw "Failed to enable video capture."; // Enable audio capture BMDAudioSampleRate sampleRate = bmdAudioSampleRate48kHz; BMDAudioSampleType sampleType = bmdAudioSampleType16bitInteger; int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ); if ( S_OK != m_decklinkInput->EnableAudioInput( sampleRate, sampleType, channels ) ) throw "Failed to enable audio capture."; // Start capture m_dropped = 0; mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", m_dropped ); m_started = m_decklinkInput->StartStreams() == S_OK; if ( !m_started ) throw "Failed to start capture."; } catch ( const char *error ) { m_decklinkInput->DisableVideoInput(); mlt_log_error( getProducer(), "%s\n", error ); return false; } return true; } void stop() { if ( !m_started ) return; m_started = false; // Release the wait in getFrame pthread_mutex_lock( &m_mutex ); pthread_cond_broadcast( &m_condition ); pthread_mutex_unlock( &m_mutex ); m_decklinkInput->StopStreams(); m_decklinkInput->DisableVideoInput(); m_decklinkInput->DisableAudioInput(); // Cleanup queue pthread_mutex_lock( &m_mutex ); while ( mlt_frame frame = (mlt_frame) mlt_deque_pop_back( m_queue ) ) mlt_frame_close( frame ); pthread_mutex_unlock( &m_mutex ); } mlt_frame getFrame() { struct timeval now; struct timespec tm; double fps = mlt_producer_get_fps( getProducer() ); mlt_position position = mlt_producer_position( getProducer() ); mlt_frame frame = mlt_cache_get_frame( m_cache, position ); // Allow the buffer to fill to the requested initial buffer level. if ( m_isBuffering ) { int prefill = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "prefill" ); int buffer = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" ); m_isBuffering = false; prefill = prefill > buffer ? buffer : prefill; pthread_mutex_lock( &m_mutex ); while ( mlt_deque_count( m_queue ) < prefill ) { // Wait up to buffer/fps seconds gettimeofday( &now, NULL ); long usec = now.tv_sec * 1000000 + now.tv_usec; usec += 1000000 * buffer / fps; tm.tv_sec = usec / 1000000; tm.tv_nsec = (usec % 1000000) * 1000; if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) ) break; } pthread_mutex_unlock( &m_mutex ); } if ( !frame ) { // Wait if queue is empty pthread_mutex_lock( &m_mutex ); while ( mlt_deque_count( m_queue ) < 1 ) { // Wait up to twice frame duration gettimeofday( &now, NULL ); long usec = now.tv_sec * 1000000 + now.tv_usec; usec += 2000000 / fps; tm.tv_sec = usec / 1000000; tm.tv_nsec = (usec % 1000000) * 1000; if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) ) // Stop waiting if error (timed out) break; } frame = ( mlt_frame ) mlt_deque_pop_front( m_queue ); pthread_mutex_unlock( &m_mutex ); // add to cache if ( frame ) { mlt_frame_set_position( frame, position ); mlt_cache_put_frame( m_cache, frame ); } } // Set frame timestamp and properties if ( frame ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) ); mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set_int( properties, "progressive", profile->progressive ); mlt_properties_set_int( properties, "meta.media.progressive", profile->progressive ); mlt_properties_set_int( properties, "top_field_first", m_topFieldFirst ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); mlt_properties_set_int( properties, "meta.media.sample_aspect_num", profile->sample_aspect_num ); mlt_properties_set_int( properties, "meta.media.sample_aspect_den", profile->sample_aspect_den ); mlt_properties_set_int( properties, "meta.media.frame_rate_num", profile->frame_rate_num ); mlt_properties_set_int( properties, "meta.media.frame_rate_den", profile->frame_rate_den ); mlt_properties_set_int( properties, "width", profile->width ); mlt_properties_set_int( properties, "meta.media.width", profile->width ); mlt_properties_set_int( properties, "height", profile->height ); mlt_properties_set_int( properties, "meta.media.height", profile->height ); mlt_properties_set_int( properties, "format", ( m_pixel_format == bmdFormat8BitYUV ) ? mlt_image_yuv422 : mlt_image_yuv422p16 ); mlt_properties_set_int( properties, "colorspace", m_colorspace ); mlt_properties_set_int( properties, "meta.media.colorspace", m_colorspace ); mlt_properties_set_int( properties, "audio_frequency", 48000 ); mlt_properties_set_int( properties, "audio_channels", mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ) ); } else mlt_log_warning( getProducer(), "buffer underrun\n" ); return frame; } // *** DeckLink API implementation of IDeckLinkInputCallback *** // // IUnknown needs only a dummy implementation virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv ) { return E_NOINTERFACE; } virtual ULONG STDMETHODCALLTYPE AddRef() { return 1; } virtual ULONG STDMETHODCALLTYPE Release() { return 1; } /************************* DeckLink API Delegate Methods *****************************/ virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( IDeckLinkVideoInputFrame* video, IDeckLinkAudioInputPacket* audio ) { mlt_frame frame = NULL; struct timeval arrived; gettimeofday(&arrived, NULL); if( !m_reprio ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( getProducer() ); if ( mlt_properties_get( properties, "priority" ) ) { int r; pthread_t thread; pthread_attr_t tattr; struct sched_param param; pthread_attr_init(&tattr); pthread_attr_setschedpolicy(&tattr, SCHED_FIFO); if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) ) param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1; else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) ) param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1; else param.sched_priority = mlt_properties_get_int( properties, "priority" ); pthread_attr_setschedparam(&tattr, ¶m); thread = pthread_self(); r = pthread_setschedparam(thread, SCHED_FIFO, ¶m); if( r ) mlt_log_verbose( getProducer(), "VideoInputFrameArrived: pthread_setschedparam returned %d\n", r); else mlt_log_verbose( getProducer(), "VideoInputFrameArrived: param.sched_priority=%d\n", param.sched_priority); }; m_reprio = true; }; if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "preview" ) && mlt_producer_get_speed( getProducer() ) == 0.0 && !mlt_deque_count( m_queue )) { pthread_cond_broadcast( &m_condition ); return S_OK; } // Copy video if ( video ) { IDeckLinkTimecode* timecode = 0; if ( !( video->GetFlags() & bmdFrameHasNoInputSource ) ) { int vitc_in = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "vitc_in" ); if ( vitc_in && ( S_OK == video->GetTimecode( bmdTimecodeRP188, &timecode ) || S_OK == video->GetTimecode( bmdTimecodeVITC, &timecode )) && timecode ) { int vitc = timecode->GetBCD(); SAFE_RELEASE( timecode ); mlt_log_verbose( getProducer(), "VideoInputFrameArrived: vitc=%.8X vitc_in=%.8X\n", vitc, vitc_in); if ( vitc < vitc_in ) { pthread_cond_broadcast( &m_condition ); return S_OK; } mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "vitc_in", 0 ); } void *buffer; int image_strides[4]; unsigned char* image_buffers[4]; mlt_image_format fmt = ( m_pixel_format == bmdFormat8BitYUV ) ? mlt_image_yuv422 : mlt_image_yuv422p16; int size = mlt_image_format_size( fmt, video->GetWidth(), video->GetHeight() + m_vancLines, NULL ); void* image = mlt_pool_alloc( size ); mlt_image_format_planes( fmt, video->GetWidth(), video->GetHeight() + m_vancLines, image, image_buffers, image_strides ); // Capture VANC if ( m_vancLines > 0 ) { IDeckLinkVideoFrameAncillary* vanc = 0; if ( video->GetAncillaryData( &vanc ) == S_OK && vanc ) { for ( int i = 1; i < m_vancLines + 1; i++ ) { unsigned char* out[4] = { image_buffers[0] + (i - 1) * image_strides[0], image_buffers[1] + (i - 1) * image_strides[1], image_buffers[2] + (i - 1) * image_strides[2], image_buffers[3] + (i - 1) * image_strides[3] }; if ( vanc->GetBufferForVerticalBlankingLine( i, &buffer ) == S_OK ) copy_lines ( m_pixel_format, (unsigned char*)buffer, video->GetRowBytes(), fmt, out, image_strides, video->GetWidth(), 1 ); else { fill_line( fmt, out, image_strides, 0 ); mlt_log_debug( getProducer(), "failed capture vanc line %d\n", i ); } } SAFE_RELEASE(vanc); } } // Capture image video->GetBytes( &buffer ); if ( image && buffer ) { unsigned char* out[4] = { image_buffers[0] + m_vancLines * image_strides[0], image_buffers[1] + m_vancLines * image_strides[1], image_buffers[2] + m_vancLines * image_strides[2], image_buffers[3] + m_vancLines * image_strides[3] }; copy_lines ( m_pixel_format, (unsigned char*)buffer, video->GetRowBytes(), fmt, (unsigned char**)out, image_strides, video->GetWidth(), video->GetHeight() ); frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) ); mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release ); } else if ( image ) { mlt_log_verbose( getProducer(), "no video image\n" ); mlt_pool_release( image ); } } else { mlt_log_verbose( getProducer(), "no signal\n" ); } // Get timecode if ( ( S_OK == video->GetTimecode( bmdTimecodeRP188, &timecode ) || S_OK == video->GetTimecode( bmdTimecodeVITC, &timecode )) && timecode ) { DLString timecodeString = 0; if ( timecode->GetString( &timecodeString ) == S_OK ) { char* s = getCString( timecodeString ); mlt_properties_set( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup", s ); mlt_log_debug( getProducer(), "timecode %s\n", s ); freeCString( s ); } freeDLString( timecodeString ); SAFE_RELEASE( timecode ); } } else { mlt_log_verbose( getProducer(), "no video\n" ); } // Copy audio if ( frame && audio ) { int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ); int size = audio->GetSampleFrameCount() * channels * sizeof(int16_t); mlt_audio_format format = mlt_audio_s16; void* pcm = mlt_pool_alloc( size ); void* buffer = 0; audio->GetBytes( &buffer ); if ( buffer ) { memcpy( pcm, buffer, size ); mlt_frame_set_audio( frame, pcm, format, size, mlt_pool_release ); mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "audio_samples", audio->GetSampleFrameCount() ); } else { mlt_log_verbose( getProducer(), "no audio samples\n" ); mlt_pool_release( pcm ); } } else { mlt_log_verbose( getProducer(), "no audio\n" ); } // Put frame in queue if ( frame ) { mlt_properties_set_int64( MLT_FRAME_PROPERTIES( frame ), "arrived", arrived.tv_sec * 1000000LL + arrived.tv_usec ); int queueMax = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" ); pthread_mutex_lock( &m_mutex ); if ( mlt_deque_count( m_queue ) < queueMax ) { mlt_deque_push_back( m_queue, frame ); pthread_cond_broadcast( &m_condition ); } else { mlt_frame_close( frame ); mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped ); mlt_log_warning( getProducer(), "buffer overrun, frame dropped %d\n", m_dropped ); } pthread_mutex_unlock( &m_mutex ); } return S_OK; } virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged( BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode* mode, BMDDetectedVideoInputFormatFlags flags ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) ); if ( events & bmdVideoInputDisplayModeChanged ) { BMDTimeValue duration; BMDTimeScale timescale; mode->GetFrameRate( &duration, ×cale ); profile->width = mode->GetWidth(); profile->height = mode->GetHeight() + m_vancLines; profile->frame_rate_num = timescale; profile->frame_rate_den = duration; if ( profile->width == 720 ) { if ( profile->height == 576 ) { profile->sample_aspect_num = 16; profile->sample_aspect_den = 15; } else { profile->sample_aspect_num = 8; profile->sample_aspect_den = 9; } profile->display_aspect_num = 4; profile->display_aspect_den = 3; } else { profile->sample_aspect_num = 1; profile->sample_aspect_den = 1; profile->display_aspect_num = 16; profile->display_aspect_den = 9; } free( profile->description ); profile->description = strdup( "decklink" ); mlt_log_verbose( getProducer(), "format changed %dx%d %.3f fps\n", profile->width, profile->height, (double) profile->frame_rate_num / profile->frame_rate_den ); m_new_input = profile; } if ( events & bmdVideoInputFieldDominanceChanged ) { profile->progressive = mode->GetFieldDominance() == bmdProgressiveFrame; m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst; mlt_log_verbose( getProducer(), "field dominance changed prog %d tff %d\n", profile->progressive, m_topFieldFirst ); } if ( events & bmdVideoInputColorspaceChanged ) { profile->colorspace = m_colorspace = ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601; mlt_log_verbose( getProducer(), "colorspace changed %d\n", profile->colorspace ); } return S_OK; } }; static int get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { return mlt_frame_get_audio( frame, (void**) buffer, format, frequency, channels, samples ); } static int get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { return mlt_frame_get_image( frame, buffer, format, width, height, writable ); } static int get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { DeckLinkProducer* decklink = (DeckLinkProducer*) producer->child; mlt_position pos = mlt_producer_position( producer ); mlt_position end = mlt_producer_get_playtime( producer ); end = ( mlt_producer_get_length( producer ) < end ? mlt_producer_get_length( producer ) : end ) - 1; if ( decklink && decklink->m_new_input ) { decklink->m_new_input = NULL; decklink->stop(); decklink->start( decklink->m_new_input ); } // Re-open if needed if ( !decklink && pos < end ) { producer->child = decklink = new DeckLinkProducer(); decklink->setProducer( producer ); decklink->open( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES(producer), "resource" ) ); } // Start if needed if ( decklink ) { decklink->start( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ); // Get the next frame from the decklink object if ( ( *frame = decklink->getFrame() )) { // Add audio and video getters mlt_frame_push_audio( *frame, (void*) get_audio ); mlt_frame_push_get_image( *frame, get_image ); } } if ( !*frame ) *frame = mlt_frame_init( MLT_PRODUCER_SERVICE(producer) ); // Calculate the next timecode mlt_producer_prepare_next( producer ); // Close DeckLink if at end if ( pos >= end && decklink ) { decklink->stop(); delete decklink; producer->child = NULL; } return 0; } static void producer_close( mlt_producer producer ) { delete (DeckLinkProducer*) producer->child; producer->close = NULL; mlt_producer_close( producer ); } extern "C" { // Listen for the list_devices property to be set static void on_property_changed( void*, mlt_properties properties, const char *name ) { IDeckLinkIterator* decklinkIterator = NULL; IDeckLink* decklink = NULL; IDeckLinkInput* decklinkInput = NULL; int i = 0; if ( name && !strcmp( name, "list_devices" ) ) mlt_event_block( (mlt_event) mlt_properties_get_data( properties, "list-devices-event", NULL ) ); else return; #ifdef _WIN32 if ( FAILED( CoInitialize( NULL ) ) ) return; if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) ) return; #else if ( !( decklinkIterator = CreateDeckLinkIteratorInstance() ) ) return; #endif for ( ; decklinkIterator->Next( &decklink ) == S_OK; i++ ) { if ( decklink->QueryInterface( IID_IDeckLinkInput, (void**) &decklinkInput ) == S_OK ) { DLString name = NULL; if ( decklink->GetModelName( &name ) == S_OK ) { char *name_cstr = getCString( name ); const char *format = "device.%d"; char *key = (char*) calloc( 1, strlen( format ) + 1 ); sprintf( key, format, i ); mlt_properties_set( properties, key, name_cstr ); free( key ); freeDLString( name ); freeCString( name_cstr ); } SAFE_RELEASE( decklinkInput ); } SAFE_RELEASE( decklink ); } SAFE_RELEASE( decklinkIterator ); mlt_properties_set_int( properties, "devices", i ); } /** Initialise the producer. */ mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Allocate the producer DeckLinkProducer* decklink = new DeckLinkProducer(); mlt_producer producer = (mlt_producer) calloc( 1, sizeof( *producer ) ); // If allocated and initializes if ( decklink && !mlt_producer_init( producer, decklink ) ) { // Extract resource (card) from arg, removing path prefix, if any. // (modules such as melted may pass arg with root_dir prefix) char *arg_dup = strdup( arg ? arg : "" ); const char *resource = strchr( arg_dup, '/' ) ? strrchr( arg_dup, '/' ) + 1 : arg_dup; // Handle empty string resource (arg supplied as "" or "/some/path/") resource = strlen( resource ) ? resource : "0"; if ( decklink->open( atoi( resource ) ) ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); // Close DeckLink and defer re-open to get_frame delete decklink; producer->child = NULL; // Set callbacks producer->close = (mlt_destructor) producer_close; producer->get_frame = get_frame; // Set properties mlt_properties_set( properties, "resource", resource ); mlt_properties_set_int( properties, "channels", 2 ); mlt_properties_set_int( properties, "buffer", 25 ); mlt_properties_set_int( properties, "prefill", 25 ); // These properties effectively make it infinite. mlt_properties_set_int( properties, "length", INT_MAX ); mlt_properties_set_int( properties, "out", INT_MAX - 1 ); mlt_properties_set( properties, "eof", "loop" ); mlt_event event = mlt_events_listen( properties, properties, "property-changed", (mlt_listener) on_property_changed ); mlt_properties_set_data( properties, "list-devices-event", event, 0, NULL, NULL ); } free( arg_dup ); } return producer; } } mlt-6.20.0/src/modules/decklink/producer_decklink.yml000066400000000000000000000065601362234133600226420ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: decklink title: Blackmagic Design DeckLink Capture version: 1 copyright: Copyright (C) 2011-2018 Meltytech, LLC license: LGPL language: en creator: Dan Dennedy tags: - Audio - Video description: > Capture video and audio using Blackmagic Design DeckLink SDI or Intensity HDMI cards. notes: > Please ensure that you use a MLT profile that is compatible with a broadcast standard which the card you are using supports. If you must use an interlaced profile but wish to deinterlace or scale the input, then you must use the consumer producer, e.g.: melt -profile square_pal consumer:decklink: profile=dv_pal bugs: - It is incompatible with the yadif deinterlacer. - Transport controls such as seeking has no affect. - External deck control is not implemented. - Only 8-bit Y'CbCr is supported at this time. parameters: - identifier: argument title: Card type: integer default: 0 minimum: 0 widget: spinner - identifier: channels title: Audio channels type: integer default: 2 minimum: 2 maximum: 16 widget: spinner - identifier: buffer title: Maximum buffer description: > There is a queue of frames between this plugin and its consumer. If the consumer has a little, intermittent delay then it reduces the risk of dropping a frame. However, this provides a maximum number of frames that can be buffered to prevent consuming memory unbounded in the case of frequent or sustained delays. type: integer default: 25 minimum: 0 unit: frames widget: spinner - identifier: prefill title: Initial buffer description: Initially fill the buffer with a number of frames. type: integer default: 25 minimum: 0 unit: frames widget: spinner - identifier: vanc title: Vertical ancillary capture description: > Captures vertical ancillary data as image data and places it at the top of the visible/active image. You can either set the number of lines to capture or use -1 for automatic (32 lines) mode. type: integer minimum: -1 default: 0 unit: lines widget: spinner - identifier: preview title: Enable preview description: Support preview monitoring when paused (speed = 0). type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: devices title: Number of devices type: integer readonly: yes minimum: 0 - identifier: device.* title: Device model description: The model name of each device that accepts input. type: string readonly: yes - identifier: priority title: Thread priority description: Set the DeckLink thread's scheduling class to realtime and its priority. type: integer minimum: 1 maximum: 99 default: 20 - identifier: vitc_in title: Start timecode type: integer description: > The vertical interval timecode (VITC) in binary-coded decimal (BCD) format. It skips frames that has VITC timecode less then specified. After reaching first frame with timecode greater or equal then specified this property is reset to zero. - identifier: bitdepth title: Bitdepth for capturing description: Enable capturing in 10-bit native SDI signal type: integer values: - 8 # 8-bit data - 10 # 10-bit data mlt-6.20.0/src/modules/decklink/win/000077500000000000000000000000001362234133600172165ustar00rootroot00000000000000mlt-6.20.0/src/modules/decklink/win/DeckLinkAPI_h.h000066400000000000000000011042751362234133600217260ustar00rootroot00000000000000 /* this ALWAYS GENERATED file contains the definitions for the interfaces */ /* File created by MIDL compiler version 7.00.0555 */ /* at Tue Sep 07 12:31:45 2010 */ /* Compiler settings for video\DeckLinkAPI.idl: Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555 protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: __declspec(uuid()), __declspec(selectany), __declspec(novtable) DECLSPEC_UUID(), MIDL_INTERFACE() */ /* @@MIDL_FILE_HEADING( ) */ //#pragma warning( disable: 4049 ) /* more than 64k source lines */ /* verify that the version is high enough to compile this file*/ #ifndef __REQUIRED_RPCNDR_H_VERSION__ #define __REQUIRED_RPCNDR_H_VERSION__ 475 #endif #include "rpc.h" #include "rpcndr.h" #ifndef __RPCNDR_H_VERSION__ #error this stub requires an updated version of #endif // __RPCNDR_H_VERSION__ #ifndef __DeckLinkAPI_h_h__ #define __DeckLinkAPI_h_h__ #if defined(_MSC_VER) && (_MSC_VER >= 1020) #pragma once #endif /* Forward Declarations */ #ifndef __IDeckLinkVideoOutputCallback_FWD_DEFINED__ #define __IDeckLinkVideoOutputCallback_FWD_DEFINED__ typedef interface IDeckLinkVideoOutputCallback IDeckLinkVideoOutputCallback; #endif /* __IDeckLinkVideoOutputCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkInputCallback_FWD_DEFINED__ #define __IDeckLinkInputCallback_FWD_DEFINED__ typedef interface IDeckLinkInputCallback IDeckLinkInputCallback; #endif /* __IDeckLinkInputCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkMemoryAllocator_FWD_DEFINED__ #define __IDeckLinkMemoryAllocator_FWD_DEFINED__ typedef interface IDeckLinkMemoryAllocator IDeckLinkMemoryAllocator; #endif /* __IDeckLinkMemoryAllocator_FWD_DEFINED__ */ #ifndef __IDeckLinkAudioOutputCallback_FWD_DEFINED__ #define __IDeckLinkAudioOutputCallback_FWD_DEFINED__ typedef interface IDeckLinkAudioOutputCallback IDeckLinkAudioOutputCallback; #endif /* __IDeckLinkAudioOutputCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkIterator_FWD_DEFINED__ #define __IDeckLinkIterator_FWD_DEFINED__ typedef interface IDeckLinkIterator IDeckLinkIterator; #endif /* __IDeckLinkIterator_FWD_DEFINED__ */ #ifndef __IDeckLinkAPIInformation_FWD_DEFINED__ #define __IDeckLinkAPIInformation_FWD_DEFINED__ typedef interface IDeckLinkAPIInformation IDeckLinkAPIInformation; #endif /* __IDeckLinkAPIInformation_FWD_DEFINED__ */ #ifndef __IDeckLinkDisplayModeIterator_FWD_DEFINED__ #define __IDeckLinkDisplayModeIterator_FWD_DEFINED__ typedef interface IDeckLinkDisplayModeIterator IDeckLinkDisplayModeIterator; #endif /* __IDeckLinkDisplayModeIterator_FWD_DEFINED__ */ #ifndef __IDeckLinkDisplayMode_FWD_DEFINED__ #define __IDeckLinkDisplayMode_FWD_DEFINED__ typedef interface IDeckLinkDisplayMode IDeckLinkDisplayMode; #endif /* __IDeckLinkDisplayMode_FWD_DEFINED__ */ #ifndef __IDeckLink_FWD_DEFINED__ #define __IDeckLink_FWD_DEFINED__ typedef interface IDeckLink IDeckLink; #endif /* __IDeckLink_FWD_DEFINED__ */ #ifndef __IDeckLinkOutput_FWD_DEFINED__ #define __IDeckLinkOutput_FWD_DEFINED__ typedef interface IDeckLinkOutput IDeckLinkOutput; #endif /* __IDeckLinkOutput_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_FWD_DEFINED__ #define __IDeckLinkInput_FWD_DEFINED__ typedef interface IDeckLinkInput IDeckLinkInput; #endif /* __IDeckLinkInput_FWD_DEFINED__ */ #ifndef __IDeckLinkTimecode_FWD_DEFINED__ #define __IDeckLinkTimecode_FWD_DEFINED__ typedef interface IDeckLinkTimecode IDeckLinkTimecode; #endif /* __IDeckLinkTimecode_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_FWD_DEFINED__ #define __IDeckLinkVideoFrame_FWD_DEFINED__ typedef interface IDeckLinkVideoFrame IDeckLinkVideoFrame; #endif /* __IDeckLinkVideoFrame_FWD_DEFINED__ */ #ifndef __IDeckLinkMutableVideoFrame_FWD_DEFINED__ #define __IDeckLinkMutableVideoFrame_FWD_DEFINED__ typedef interface IDeckLinkMutableVideoFrame IDeckLinkMutableVideoFrame; #endif /* __IDeckLinkMutableVideoFrame_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrame3DExtensions_FWD_DEFINED__ #define __IDeckLinkVideoFrame3DExtensions_FWD_DEFINED__ typedef interface IDeckLinkVideoFrame3DExtensions IDeckLinkVideoFrame3DExtensions; #endif /* __IDeckLinkVideoFrame3DExtensions_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_FWD_DEFINED__ #define __IDeckLinkVideoInputFrame_FWD_DEFINED__ typedef interface IDeckLinkVideoInputFrame IDeckLinkVideoInputFrame; #endif /* __IDeckLinkVideoInputFrame_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrameAncillary_FWD_DEFINED__ #define __IDeckLinkVideoFrameAncillary_FWD_DEFINED__ typedef interface IDeckLinkVideoFrameAncillary IDeckLinkVideoFrameAncillary; #endif /* __IDeckLinkVideoFrameAncillary_FWD_DEFINED__ */ #ifndef __IDeckLinkAudioInputPacket_FWD_DEFINED__ #define __IDeckLinkAudioInputPacket_FWD_DEFINED__ typedef interface IDeckLinkAudioInputPacket IDeckLinkAudioInputPacket; #endif /* __IDeckLinkAudioInputPacket_FWD_DEFINED__ */ #ifndef __IDeckLinkScreenPreviewCallback_FWD_DEFINED__ #define __IDeckLinkScreenPreviewCallback_FWD_DEFINED__ typedef interface IDeckLinkScreenPreviewCallback IDeckLinkScreenPreviewCallback; #endif /* __IDeckLinkScreenPreviewCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ #define __IDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ typedef interface IDeckLinkGLScreenPreviewHelper IDeckLinkGLScreenPreviewHelper; #endif /* __IDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __IDeckLinkConfiguration_FWD_DEFINED__ #define __IDeckLinkConfiguration_FWD_DEFINED__ typedef interface IDeckLinkConfiguration IDeckLinkConfiguration; #endif /* __IDeckLinkConfiguration_FWD_DEFINED__ */ #ifndef __IDeckLinkAttributes_FWD_DEFINED__ #define __IDeckLinkAttributes_FWD_DEFINED__ typedef interface IDeckLinkAttributes IDeckLinkAttributes; #endif /* __IDeckLinkAttributes_FWD_DEFINED__ */ #ifndef __IDeckLinkKeyer_FWD_DEFINED__ #define __IDeckLinkKeyer_FWD_DEFINED__ typedef interface IDeckLinkKeyer IDeckLinkKeyer; #endif /* __IDeckLinkKeyer_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoConversion_FWD_DEFINED__ #define __IDeckLinkVideoConversion_FWD_DEFINED__ typedef interface IDeckLinkVideoConversion IDeckLinkVideoConversion; #endif /* __IDeckLinkVideoConversion_FWD_DEFINED__ */ #ifndef __IDeckLinkDeckControlStatusCallback_FWD_DEFINED__ #define __IDeckLinkDeckControlStatusCallback_FWD_DEFINED__ typedef interface IDeckLinkDeckControlStatusCallback IDeckLinkDeckControlStatusCallback; #endif /* __IDeckLinkDeckControlStatusCallback_FWD_DEFINED__ */ #ifndef __IDeckLinkDeckControl_FWD_DEFINED__ #define __IDeckLinkDeckControl_FWD_DEFINED__ typedef interface IDeckLinkDeckControl IDeckLinkDeckControl; #endif /* __IDeckLinkDeckControl_FWD_DEFINED__ */ #ifndef __CDeckLinkIterator_FWD_DEFINED__ #define __CDeckLinkIterator_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkIterator CDeckLinkIterator; #else typedef struct CDeckLinkIterator CDeckLinkIterator; #endif /* __cplusplus */ #endif /* __CDeckLinkIterator_FWD_DEFINED__ */ #ifndef __CDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ #define __CDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkGLScreenPreviewHelper CDeckLinkGLScreenPreviewHelper; #else typedef struct CDeckLinkGLScreenPreviewHelper CDeckLinkGLScreenPreviewHelper; #endif /* __cplusplus */ #endif /* __CDeckLinkGLScreenPreviewHelper_FWD_DEFINED__ */ #ifndef __CDeckLinkVideoConversion_FWD_DEFINED__ #define __CDeckLinkVideoConversion_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkVideoConversion CDeckLinkVideoConversion; #else typedef struct CDeckLinkVideoConversion CDeckLinkVideoConversion; #endif /* __cplusplus */ #endif /* __CDeckLinkVideoConversion_FWD_DEFINED__ */ #ifndef __IDeckLinkDisplayModeIterator_v7_6_FWD_DEFINED__ #define __IDeckLinkDisplayModeIterator_v7_6_FWD_DEFINED__ typedef interface IDeckLinkDisplayModeIterator_v7_6 IDeckLinkDisplayModeIterator_v7_6; #endif /* __IDeckLinkDisplayModeIterator_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkDisplayMode_v7_6_FWD_DEFINED__ #define __IDeckLinkDisplayMode_v7_6_FWD_DEFINED__ typedef interface IDeckLinkDisplayMode_v7_6 IDeckLinkDisplayMode_v7_6; #endif /* __IDeckLinkDisplayMode_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkOutput_v7_6_FWD_DEFINED__ #define __IDeckLinkOutput_v7_6_FWD_DEFINED__ typedef interface IDeckLinkOutput_v7_6 IDeckLinkOutput_v7_6; #endif /* __IDeckLinkOutput_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_v7_6_FWD_DEFINED__ #define __IDeckLinkInput_v7_6_FWD_DEFINED__ typedef interface IDeckLinkInput_v7_6 IDeckLinkInput_v7_6; #endif /* __IDeckLinkInput_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkTimecode_v7_6_FWD_DEFINED__ #define __IDeckLinkTimecode_v7_6_FWD_DEFINED__ typedef interface IDeckLinkTimecode_v7_6 IDeckLinkTimecode_v7_6; #endif /* __IDeckLinkTimecode_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_v7_6_FWD_DEFINED__ #define __IDeckLinkVideoFrame_v7_6_FWD_DEFINED__ typedef interface IDeckLinkVideoFrame_v7_6 IDeckLinkVideoFrame_v7_6; #endif /* __IDeckLinkVideoFrame_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkMutableVideoFrame_v7_6_FWD_DEFINED__ #define __IDeckLinkMutableVideoFrame_v7_6_FWD_DEFINED__ typedef interface IDeckLinkMutableVideoFrame_v7_6 IDeckLinkMutableVideoFrame_v7_6; #endif /* __IDeckLinkMutableVideoFrame_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_v7_6_FWD_DEFINED__ #define __IDeckLinkVideoInputFrame_v7_6_FWD_DEFINED__ typedef interface IDeckLinkVideoInputFrame_v7_6 IDeckLinkVideoInputFrame_v7_6; #endif /* __IDeckLinkVideoInputFrame_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkScreenPreviewCallback_v7_6_FWD_DEFINED__ #define __IDeckLinkScreenPreviewCallback_v7_6_FWD_DEFINED__ typedef interface IDeckLinkScreenPreviewCallback_v7_6 IDeckLinkScreenPreviewCallback_v7_6; #endif /* __IDeckLinkScreenPreviewCallback_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkGLScreenPreviewHelper_v7_6_FWD_DEFINED__ #define __IDeckLinkGLScreenPreviewHelper_v7_6_FWD_DEFINED__ typedef interface IDeckLinkGLScreenPreviewHelper_v7_6 IDeckLinkGLScreenPreviewHelper_v7_6; #endif /* __IDeckLinkGLScreenPreviewHelper_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoConversion_v7_6_FWD_DEFINED__ #define __IDeckLinkVideoConversion_v7_6_FWD_DEFINED__ typedef interface IDeckLinkVideoConversion_v7_6 IDeckLinkVideoConversion_v7_6; #endif /* __IDeckLinkVideoConversion_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkConfiguration_v7_6_FWD_DEFINED__ #define __IDeckLinkConfiguration_v7_6_FWD_DEFINED__ typedef interface IDeckLinkConfiguration_v7_6 IDeckLinkConfiguration_v7_6; #endif /* __IDeckLinkConfiguration_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoOutputCallback_v7_6_FWD_DEFINED__ #define __IDeckLinkVideoOutputCallback_v7_6_FWD_DEFINED__ typedef interface IDeckLinkVideoOutputCallback_v7_6 IDeckLinkVideoOutputCallback_v7_6; #endif /* __IDeckLinkVideoOutputCallback_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkInputCallback_v7_6_FWD_DEFINED__ #define __IDeckLinkInputCallback_v7_6_FWD_DEFINED__ typedef interface IDeckLinkInputCallback_v7_6 IDeckLinkInputCallback_v7_6; #endif /* __IDeckLinkInputCallback_v7_6_FWD_DEFINED__ */ #ifndef __CDeckLinkGLScreenPreviewHelper_v7_6_FWD_DEFINED__ #define __CDeckLinkGLScreenPreviewHelper_v7_6_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkGLScreenPreviewHelper_v7_6 CDeckLinkGLScreenPreviewHelper_v7_6; #else typedef struct CDeckLinkGLScreenPreviewHelper_v7_6 CDeckLinkGLScreenPreviewHelper_v7_6; #endif /* __cplusplus */ #endif /* __CDeckLinkGLScreenPreviewHelper_v7_6_FWD_DEFINED__ */ #ifndef __CDeckLinkVideoConversion_v7_6_FWD_DEFINED__ #define __CDeckLinkVideoConversion_v7_6_FWD_DEFINED__ #ifdef __cplusplus typedef class CDeckLinkVideoConversion_v7_6 CDeckLinkVideoConversion_v7_6; #else typedef struct CDeckLinkVideoConversion_v7_6 CDeckLinkVideoConversion_v7_6; #endif /* __cplusplus */ #endif /* __CDeckLinkVideoConversion_v7_6_FWD_DEFINED__ */ #ifndef __IDeckLinkInputCallback_v7_3_FWD_DEFINED__ #define __IDeckLinkInputCallback_v7_3_FWD_DEFINED__ typedef interface IDeckLinkInputCallback_v7_3 IDeckLinkInputCallback_v7_3; #endif /* __IDeckLinkInputCallback_v7_3_FWD_DEFINED__ */ #ifndef __IDeckLinkOutput_v7_3_FWD_DEFINED__ #define __IDeckLinkOutput_v7_3_FWD_DEFINED__ typedef interface IDeckLinkOutput_v7_3 IDeckLinkOutput_v7_3; #endif /* __IDeckLinkOutput_v7_3_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_v7_3_FWD_DEFINED__ #define __IDeckLinkInput_v7_3_FWD_DEFINED__ typedef interface IDeckLinkInput_v7_3 IDeckLinkInput_v7_3; #endif /* __IDeckLinkInput_v7_3_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_v7_3_FWD_DEFINED__ #define __IDeckLinkVideoInputFrame_v7_3_FWD_DEFINED__ typedef interface IDeckLinkVideoInputFrame_v7_3 IDeckLinkVideoInputFrame_v7_3; #endif /* __IDeckLinkVideoInputFrame_v7_3_FWD_DEFINED__ */ #ifndef __IDeckLinkDisplayModeIterator_v7_1_FWD_DEFINED__ #define __IDeckLinkDisplayModeIterator_v7_1_FWD_DEFINED__ typedef interface IDeckLinkDisplayModeIterator_v7_1 IDeckLinkDisplayModeIterator_v7_1; #endif /* __IDeckLinkDisplayModeIterator_v7_1_FWD_DEFINED__ */ #ifndef __IDeckLinkDisplayMode_v7_1_FWD_DEFINED__ #define __IDeckLinkDisplayMode_v7_1_FWD_DEFINED__ typedef interface IDeckLinkDisplayMode_v7_1 IDeckLinkDisplayMode_v7_1; #endif /* __IDeckLinkDisplayMode_v7_1_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_v7_1_FWD_DEFINED__ #define __IDeckLinkVideoFrame_v7_1_FWD_DEFINED__ typedef interface IDeckLinkVideoFrame_v7_1 IDeckLinkVideoFrame_v7_1; #endif /* __IDeckLinkVideoFrame_v7_1_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_v7_1_FWD_DEFINED__ #define __IDeckLinkVideoInputFrame_v7_1_FWD_DEFINED__ typedef interface IDeckLinkVideoInputFrame_v7_1 IDeckLinkVideoInputFrame_v7_1; #endif /* __IDeckLinkVideoInputFrame_v7_1_FWD_DEFINED__ */ #ifndef __IDeckLinkAudioInputPacket_v7_1_FWD_DEFINED__ #define __IDeckLinkAudioInputPacket_v7_1_FWD_DEFINED__ typedef interface IDeckLinkAudioInputPacket_v7_1 IDeckLinkAudioInputPacket_v7_1; #endif /* __IDeckLinkAudioInputPacket_v7_1_FWD_DEFINED__ */ #ifndef __IDeckLinkVideoOutputCallback_v7_1_FWD_DEFINED__ #define __IDeckLinkVideoOutputCallback_v7_1_FWD_DEFINED__ typedef interface IDeckLinkVideoOutputCallback_v7_1 IDeckLinkVideoOutputCallback_v7_1; #endif /* __IDeckLinkVideoOutputCallback_v7_1_FWD_DEFINED__ */ #ifndef __IDeckLinkInputCallback_v7_1_FWD_DEFINED__ #define __IDeckLinkInputCallback_v7_1_FWD_DEFINED__ typedef interface IDeckLinkInputCallback_v7_1 IDeckLinkInputCallback_v7_1; #endif /* __IDeckLinkInputCallback_v7_1_FWD_DEFINED__ */ #ifndef __IDeckLinkOutput_v7_1_FWD_DEFINED__ #define __IDeckLinkOutput_v7_1_FWD_DEFINED__ typedef interface IDeckLinkOutput_v7_1 IDeckLinkOutput_v7_1; #endif /* __IDeckLinkOutput_v7_1_FWD_DEFINED__ */ #ifndef __IDeckLinkInput_v7_1_FWD_DEFINED__ #define __IDeckLinkInput_v7_1_FWD_DEFINED__ typedef interface IDeckLinkInput_v7_1 IDeckLinkInput_v7_1; #endif /* __IDeckLinkInput_v7_1_FWD_DEFINED__ */ /* header files for imported files */ #include "unknwn.h" #ifdef __cplusplus extern "C"{ #endif #ifndef __DeckLinkAPI_LIBRARY_DEFINED__ #define __DeckLinkAPI_LIBRARY_DEFINED__ /* library DeckLinkAPI */ /* [helpstring][version][uuid] */ typedef LONGLONG BMDTimeValue; typedef LONGLONG BMDTimeScale; typedef unsigned long BMDTimecodeBCD; typedef unsigned long BMDTimecodeUserBits; typedef unsigned long BMDDisplayModeFlags; typedef unsigned long BMDFrameFlags; typedef unsigned long BMDVideoInputFlags; typedef unsigned long BMDVideoInputFormatChangedEvents; typedef unsigned long BMDDetectedVideoInputFormatFlags; typedef unsigned long BMDTimecodeFlags; typedef unsigned long BMDAnalogVideoFlags; typedef unsigned long BMDDeckControlStatusFlags; typedef unsigned long BMDDeckControlExportModeOpsFlags; #if 0 typedef enum _BMDDisplayModeFlags BMDDisplayModeFlags; typedef enum _BMDFrameFlags BMDFrameFlags; typedef enum _BMDVideoInputFlags BMDVideoInputFlags; typedef enum _BMDVideoInputFormatChangedEvents BMDVideoInputFormatChangedEvents; typedef enum _BMDDetectedVideoInputFormatFlags BMDDetectedVideoInputFormatFlags; typedef enum _BMDTimecodeFlags BMDTimecodeFlags; typedef enum _BMDAnalogVideoFlags BMDAnalogVideoFlags; typedef enum _BMDDeckControlStatusFlags BMDDeckControlStatusFlags; typedef enum _BMDDeckControlExportModeOpsFlags BMDDeckControlExportModeOpsFlags; #endif typedef /* [v1_enum] */ enum _BMDDisplayMode { bmdModeNTSC = 0x6e747363, bmdModeNTSC2398 = 0x6e743233, bmdModePAL = 0x70616c20, bmdModeHD1080p2398 = 0x32337073, bmdModeHD1080p24 = 0x32347073, bmdModeHD1080p25 = 0x48703235, bmdModeHD1080p2997 = 0x48703239, bmdModeHD1080p30 = 0x48703330, bmdModeHD1080i50 = 0x48693530, bmdModeHD1080i5994 = 0x48693539, bmdModeHD1080i6000 = 0x48693630, bmdModeHD1080p50 = 0x48703530, bmdModeHD1080p5994 = 0x48703539, bmdModeHD1080p6000 = 0x48703630, bmdModeHD720p50 = 0x68703530, bmdModeHD720p5994 = 0x68703539, bmdModeHD720p60 = 0x68703630, bmdMode2k2398 = 0x326b3233, bmdMode2k24 = 0x326b3234, bmdMode2k25 = 0x326b3235, bmdMode2kDCI2398 = 0x32643233, bmdMode2kDCI24 = 0x32643234, bmdMode2kDCI25 = 0x32643235, bmdMode4K2160p2398 = 0x346b3233, bmdMode4K2160p24 = 0x346b3234, bmdMode4K2160p25 = 0x346b3235, bmdMode4K2160p2997 = 0x346b3239, bmdMode4K2160p30 = 0x346b3330, bmdMode4K2160p50 = 0x346b3530, bmdMode4K2160p5994 = 0x346b3539, bmdMode4K2160p60 = 0x346b3630, bmdMode4kDCI2398 = 0x34643233, bmdMode4kDCI24 = 0x34643234, bmdMode4kDCI25 = 0x34643235, bmdModeUnknown = 0x69756e6b } BMDDisplayMode; typedef /* [v1_enum] */ enum _BMDFieldDominance { bmdUnknownFieldDominance = 0, bmdLowerFieldFirst = 0x6c6f7772, bmdUpperFieldFirst = 0x75707072, bmdProgressiveFrame = 0x70726f67, bmdProgressiveSegmentedFrame = 0x70736620 } BMDFieldDominance; typedef /* [v1_enum] */ enum _BMDPixelFormat { bmdFormat8BitYUV = 0x32767579, bmdFormat10BitYUV = 0x76323130, bmdFormat8BitARGB = 32, bmdFormat8BitBGRA = 0x42475241, bmdFormat10BitRGB = 0x72323130 } BMDPixelFormat; /* [v1_enum] */ enum _BMDDisplayModeFlags { bmdDisplayModeSupports3D = ( 1 << 0 ) , bmdDisplayModeColorspaceRec601 = ( 1 << 1 ) , bmdDisplayModeColorspaceRec709 = ( 1 << 2 ) } ; typedef /* [v1_enum] */ enum _BMDVideoOutputFlags { bmdVideoOutputFlagDefault = 0, bmdVideoOutputVANC = ( 1 << 0 ) , bmdVideoOutputVITC = ( 1 << 1 ) , bmdVideoOutputRP188 = ( 1 << 2 ) , bmdVideoOutputDualStream3D = ( 1 << 4 ) } BMDVideoOutputFlags; /* [v1_enum] */ enum _BMDFrameFlags { bmdFrameFlagDefault = 0, bmdFrameFlagFlipVertical = ( 1 << 0 ) , bmdFrameHasNoInputSource = ( 1 << 31 ) } ; /* [v1_enum] */ enum _BMDVideoInputFlags { bmdVideoInputFlagDefault = 0, bmdVideoInputEnableFormatDetection = ( 1 << 0 ) , bmdVideoInputDualStream3D = ( 1 << 1 ) } ; /* [v1_enum] */ enum _BMDVideoInputFormatChangedEvents { bmdVideoInputDisplayModeChanged = ( 1 << 0 ) , bmdVideoInputFieldDominanceChanged = ( 1 << 1 ) , bmdVideoInputColorspaceChanged = ( 1 << 2 ) } ; /* [v1_enum] */ enum _BMDDetectedVideoInputFormatFlags { bmdDetectedVideoInputYCbCr422 = ( 1 << 0 ) , bmdDetectedVideoInputRGB444 = ( 1 << 1 ) } ; typedef /* [v1_enum] */ enum _BMDOutputFrameCompletionResult { bmdOutputFrameCompleted = 0, bmdOutputFrameDisplayedLate = ( bmdOutputFrameCompleted + 1 ) , bmdOutputFrameDropped = ( bmdOutputFrameDisplayedLate + 1 ) , bmdOutputFrameFlushed = ( bmdOutputFrameDropped + 1 ) } BMDOutputFrameCompletionResult; typedef /* [v1_enum] */ enum _BMDReferenceStatus { bmdReferenceNotSupportedByHardware = ( 1 << 0 ) , bmdReferenceLocked = ( 1 << 1 ) } BMDReferenceStatus; typedef /* [v1_enum] */ enum _BMDAudioSampleRate { bmdAudioSampleRate48kHz = 48000 } BMDAudioSampleRate; typedef /* [v1_enum] */ enum _BMDAudioSampleType { bmdAudioSampleType16bitInteger = 16, bmdAudioSampleType32bitInteger = 32 } BMDAudioSampleType; typedef /* [v1_enum] */ enum _BMDAudioOutputStreamType { bmdAudioOutputStreamContinuous = 0, bmdAudioOutputStreamContinuousDontResample = ( bmdAudioOutputStreamContinuous + 1 ) , bmdAudioOutputStreamTimestamped = ( bmdAudioOutputStreamContinuousDontResample + 1 ) } BMDAudioOutputStreamType; typedef /* [v1_enum] */ enum _BMDDisplayModeSupport { bmdDisplayModeNotSupported = 0, bmdDisplayModeSupported = ( bmdDisplayModeNotSupported + 1 ) , bmdDisplayModeSupportedWithConversion = ( bmdDisplayModeSupported + 1 ) } BMDDisplayModeSupport; typedef /* [v1_enum] */ enum _BMDTimecodeFormat { bmdTimecodeRP188 = 0x72703138, bmdTimecodeVITC = 0x76697463, bmdTimecodeSerial = 0x73657269 } BMDTimecodeFormat; /* [v1_enum] */ enum _BMDTimecodeFlags { bmdTimecodeFlagDefault = 0, bmdTimecodeIsDropFrame = ( 1 << 0 ) } ; typedef /* [v1_enum] */ enum _BMDVideoConnection { bmdVideoConnectionSDI = ( 1 << 0 ) , bmdVideoConnectionHDMI = ( 1 << 1 ) , bmdVideoConnectionOpticalSDI = ( 1 << 2 ) , bmdVideoConnectionComponent = ( 1 << 3 ) , bmdVideoConnectionComposite = ( 1 << 4 ) , bmdVideoConnectionSVideo = ( 1 << 5 ) } BMDVideoConnection; /* [v1_enum] */ enum _BMDAnalogVideoFlags { bmdAnalogVideoFlagCompositeSetup75 = ( 1 << 0 ) , bmdAnalogVideoFlagComponentBetacamLevels = ( 1 << 1 ) } ; typedef /* [v1_enum] */ enum _BMDAudioConnection { bmdAudioConnectionEmbedded = 0x656d6264, bmdAudioConnectionAESEBU = 0x61657320, bmdAudioConnectionAnalog = 0x616e6c67 } BMDAudioConnection; typedef /* [v1_enum] */ enum _BMDAudioOutputAnalogAESSwitch { bmdAudioOutputSwitchAESEBU = 0x61657320, bmdAudioOutputSwitchAnalog = 0x616e6c67 } BMDAudioOutputAnalogAESSwitch; typedef /* [v1_enum] */ enum _BMDVideoOutputConversionMode { bmdNoVideoOutputConversion = 0x6e6f6e65, bmdVideoOutputLetterboxDownconversion = 0x6c746278, bmdVideoOutputAnamorphicDownconversion = 0x616d7068, bmdVideoOutputHD720toHD1080Conversion = 0x37323063, bmdVideoOutputHardwareLetterboxDownconversion = 0x48576c62, bmdVideoOutputHardwareAnamorphicDownconversion = 0x4857616d, bmdVideoOutputHardwareCenterCutDownconversion = 0x48576363, bmdVideoOutputHardware720p1080pCrossconversion = 0x78636170, bmdVideoOutputHardwareAnamorphic720pUpconversion = 0x75613770, bmdVideoOutputHardwareAnamorphic1080iUpconversion = 0x75613169, bmdVideoOutputHardwareAnamorphic149To720pUpconversion = 0x75343770, bmdVideoOutputHardwareAnamorphic149To1080iUpconversion = 0x75343169, bmdVideoOutputHardwarePillarbox720pUpconversion = 0x75703770, bmdVideoOutputHardwarePillarbox1080iUpconversion = 0x75703169 } BMDVideoOutputConversionMode; typedef /* [v1_enum] */ enum _BMDVideoInputConversionMode { bmdNoVideoInputConversion = 0x6e6f6e65, bmdVideoInputLetterboxDownconversionFromHD1080 = 0x31306c62, bmdVideoInputAnamorphicDownconversionFromHD1080 = 0x3130616d, bmdVideoInputLetterboxDownconversionFromHD720 = 0x37326c62, bmdVideoInputAnamorphicDownconversionFromHD720 = 0x3732616d, bmdVideoInputLetterboxUpconversion = 0x6c627570, bmdVideoInputAnamorphicUpconversion = 0x616d7570 } BMDVideoInputConversionMode; typedef /* [v1_enum] */ enum _BMDVideo3DPackingFormat { bmdVideo3DPackingSidebySideHalf = 0x73627368, bmdVideo3DPackingLinebyLine = 0x6c62796c, bmdVideo3DPackingTopAndBottom = 0x7461626f, bmdVideo3DPackingLeftOnly = 0x6c656674, bmdVideo3DPackingRightOnly = 0x72696768 } BMDVideo3DPackingFormat; typedef /* [v1_enum] */ enum _BMDDeckLinkConfigurationID { bmdDeckLinkConfigUse1080pNotPsF = 0x6670726f, bmdDeckLinkConfigHDMI3DPackingFormat = 0x33647066, bmdDeckLinkConfigAnalogAudioConsumerLevels = 0x6161636c, bmdDeckLinkConfigFieldFlickerRemoval = 0x66646672, bmdDeckLinkConfigHD1080p24ToHD1080i5994Conversion = 0x746f3539, bmdDeckLinkConfig444SDIVideoOutput = 0x3434346f, bmdDeckLinkConfig3GBpsVideoOutput = 0x33676273, bmdDeckLinkConfigBlackVideoOutputDuringCapture = 0x62766f63, bmdDeckLinkConfigLowLatencyVideoOutput = 0x6c6c766f, bmdDeckLinkConfigVideoOutputConnection = 0x766f636e, bmdDeckLinkConfigVideoOutputConversionMode = 0x766f636d, bmdDeckLinkConfigAnalogVideoOutputFlags = 0x61766f66, bmdDeckLinkConfigReferenceInputTimingOffset = 0x676c6f74, bmdDeckLinkConfigVideoInputConnection = 0x7669636e, bmdDeckLinkConfigAnalogVideoInputFlags = 0x61766966, bmdDeckLinkConfigVideoInputConversionMode = 0x7669636d, bmdDeckLinkConfig32PulldownSequenceInitialTimecodeFrame = 0x70646966, bmdDeckLinkConfigVANCSourceLine1Mapping = 0x76736c31, bmdDeckLinkConfigVANCSourceLine2Mapping = 0x76736c32, bmdDeckLinkConfigVANCSourceLine3Mapping = 0x76736c33, bmdDeckLinkConfigAudioInputConnection = 0x6169636e, bmdDeckLinkConfigAnalogAudioInputScaleChannel1 = 0x61697331, bmdDeckLinkConfigAnalogAudioInputScaleChannel2 = 0x61697332, bmdDeckLinkConfigAnalogAudioInputScaleChannel3 = 0x61697333, bmdDeckLinkConfigAnalogAudioInputScaleChannel4 = 0x61697334, bmdDeckLinkConfigDigitalAudioInputScale = 0x64616973, bmdDeckLinkConfigAudioOutputAESAnalogSwitch = 0x616f6161, bmdDeckLinkConfigAnalogAudioOutputScaleChannel1 = 0x616f7331, bmdDeckLinkConfigAnalogAudioOutputScaleChannel2 = 0x616f7332, bmdDeckLinkConfigAnalogAudioOutputScaleChannel3 = 0x616f7333, bmdDeckLinkConfigAnalogAudioOutputScaleChannel4 = 0x616f7334, bmdDeckLinkConfigDigitalAudioOutputScale = 0x64616f73 } BMDDeckLinkConfigurationID; typedef /* [v1_enum] */ enum _BMDDeckLinkAttributeID { BMDDeckLinkSupportsInternalKeying = 0x6b657969, BMDDeckLinkSupportsExternalKeying = 0x6b657965, BMDDeckLinkSupportsHDKeying = 0x6b657968, BMDDeckLinkSupportsInputFormatDetection = 0x696e6664, BMDDeckLinkHasReferenceInput = 0x6872696e, BMDDeckLinkHasSerialPort = 0x68737074, BMDDeckLinkMaximumAudioChannels = 0x6d616368, BMDDeckLinkNumberOfSubDevices = 0x6e736264, BMDDeckLinkSubDeviceIndex = 0x73756269, BMDDeckLinkVideoOutputConnections = 0x766f636e, BMDDeckLinkVideoInputConnections = 0x7669636e, BMDDeckLinkSerialPortDeviceName = 0x736c706e } BMDDeckLinkAttributeID; typedef /* [v1_enum] */ enum _BMDDeckLinkAPIInformationID { BMDDeckLinkAPIVersion = 0x76657273 } BMDDeckLinkAPIInformationID; typedef /* [v1_enum] */ enum _BMDDeckControlMode { bmdDeckControlNotOpened = 0x6e746f70, bmdDeckControlVTRControlMode = 0x76747263, bmdDeckControlExportMode = 0x6578706d, bmdDeckControlCaptureMode = 0x6361706d } BMDDeckControlMode; typedef /* [v1_enum] */ enum _BMDDeckControlEvent { bmdDeckControlAbortedEvent = 0x61627465, bmdDeckControlPrepareForExportEvent = 0x70666565, bmdDeckControlExportCompleteEvent = 0x65786365, bmdDeckControlPrepareForCaptureEvent = 0x70666365, bmdDeckControlCaptureCompleteEvent = 0x63636576 } BMDDeckControlEvent; typedef /* [v1_enum] */ enum _BMDDeckControlVTRControlState { bmdDeckControlNotInVTRControlMode = 0x6e76636d, bmdDeckControlVTRControlPlaying = 0x76747270, bmdDeckControlVTRControlRecording = 0x76747272, bmdDeckControlVTRControlStill = 0x76747261, bmdDeckControlVTRControlSeeking = 0x76747273, bmdDeckControlVTRControlStopped = 0x7674726f } BMDDeckControlVTRControlState; /* [v1_enum] */ enum _BMDDeckControlStatusFlags { bmdDeckControlStatusDeckConnected = ( 1 << 0 ) , bmdDeckControlStatusRemoteMode = ( 1 << 1 ) , bmdDeckControlStatusRecordInhibited = ( 1 << 2 ) , bmdDeckControlStatusCassetteOut = ( 1 << 3 ) } ; /* [v1_enum] */ enum _BMDDeckControlExportModeOpsFlags { bmdDeckControlExportModeInsertVideo = ( 1 << 0 ) , bmdDeckControlExportModeInsertAudio1 = ( 1 << 1 ) , bmdDeckControlExportModeInsertAudio2 = ( 1 << 2 ) , bmdDeckControlExportModeInsertAudio3 = ( 1 << 3 ) , bmdDeckControlExportModeInsertAudio4 = ( 1 << 4 ) , bmdDeckControlExportModeInsertAudio5 = ( 1 << 5 ) , bmdDeckControlExportModeInsertAudio6 = ( 1 << 6 ) , bmdDeckControlExportModeInsertAudio7 = ( 1 << 7 ) , bmdDeckControlExportModeInsertAudio8 = ( 1 << 8 ) , bmdDeckControlExportModeInsertAudio9 = ( 1 << 9 ) , bmdDeckControlExportModeInsertAudio10 = ( 1 << 10 ) , bmdDeckControlExportModeInsertAudio11 = ( 1 << 11 ) , bmdDeckControlExportModeInsertAudio12 = ( 1 << 12 ) , bmdDeckControlExportModeInsertTimeCode = ( 1 << 13 ) , bmdDeckControlExportModeInsertAssemble = ( 1 << 14 ) , bmdDeckControlExportModeInsertPreview = ( 1 << 15 ) , bmdDeckControlUseManualExport = ( 1 << 16 ) } ; typedef /* [v1_enum] */ enum _BMDDeckControlError { bmdDeckControlNoError = 0x6e6f6572, bmdDeckControlModeError = 0x6d6f6572, bmdDeckControlMissedInPointError = 0x6d696572, bmdDeckControlDeckTimeoutError = 0x64746572, bmdDeckControlCommandFailedError = 0x63666572, bmdDeckControlDeviceAlreadyOpenedError = 0x64616c6f, bmdDeckControlFailedToOpenDeviceError = 0x66646572, bmdDeckControlInLocalModeError = 0x6c6d6572, bmdDeckControlEndOfTapeError = 0x65746572, bmdDeckControlUserAbortError = 0x75616572, bmdDeckControlNoTapeInDeckError = 0x6e746572, bmdDeckControlNoVideoFromCardError = 0x6e766663, bmdDeckControlNoCommunicationError = 0x6e636f6d, bmdDeckControlUnknownError = 0x756e6572 } BMDDeckControlError; typedef /* [v1_enum] */ enum _BMD3DPreviewFormat { bmd3DPreviewFormatDefault = 0x64656661, bmd3DPreviewFormatLeftOnly = 0x6c656674, bmd3DPreviewFormatRightOnly = 0x72696768, bmd3DPreviewFormatSideBySide = 0x73696465, bmd3DPreviewFormatTopBottom = 0x746f7062 } BMD3DPreviewFormat; typedef /* [v1_enum] */ enum _BMDVideoConnection_v7_6 { bmdVideoConnectionSDI_v7_6 = 0x73646920, bmdVideoConnectionHDMI_v7_6 = 0x68646d69, bmdVideoConnectionOpticalSDI_v7_6 = 0x6f707469, bmdVideoConnectionComponent_v7_6 = 0x63706e74, bmdVideoConnectionComposite_v7_6 = 0x636d7374, bmdVideoConnectionSVideo_v7_6 = 0x73766964 } BMDVideoConnection_v7_6; EXTERN_C const IID LIBID_DeckLinkAPI; #ifndef __IDeckLinkVideoOutputCallback_INTERFACE_DEFINED__ #define __IDeckLinkVideoOutputCallback_INTERFACE_DEFINED__ /* interface IDeckLinkVideoOutputCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoOutputCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("20AA5225-1958-47CB-820B-80A8D521A6EE") IDeckLinkVideoOutputCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( /* [in] */ IDeckLinkVideoFrame *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoOutputCallbackVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoOutputCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoOutputCallback * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoOutputCallback * This); HRESULT ( STDMETHODCALLTYPE *ScheduledFrameCompleted )( IDeckLinkVideoOutputCallback * This, /* [in] */ IDeckLinkVideoFrame *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result); HRESULT ( STDMETHODCALLTYPE *ScheduledPlaybackHasStopped )( IDeckLinkVideoOutputCallback * This); END_INTERFACE } IDeckLinkVideoOutputCallbackVtbl; interface IDeckLinkVideoOutputCallback { CONST_VTBL struct IDeckLinkVideoOutputCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoOutputCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoOutputCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoOutputCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoOutputCallback_ScheduledFrameCompleted(This,completedFrame,result) \ ( (This)->lpVtbl -> ScheduledFrameCompleted(This,completedFrame,result) ) #define IDeckLinkVideoOutputCallback_ScheduledPlaybackHasStopped(This) \ ( (This)->lpVtbl -> ScheduledPlaybackHasStopped(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoOutputCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInputCallback_INTERFACE_DEFINED__ #define __IDeckLinkInputCallback_INTERFACE_DEFINED__ /* interface IDeckLinkInputCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInputCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("DD04E5EC-7415-42AB-AE4A-E80C4DFC044A") IDeckLinkInputCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged( /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( /* [in] */ IDeckLinkVideoInputFrame *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputCallbackVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInputCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInputCallback * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInputCallback * This); HRESULT ( STDMETHODCALLTYPE *VideoInputFormatChanged )( IDeckLinkInputCallback * This, /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags); HRESULT ( STDMETHODCALLTYPE *VideoInputFrameArrived )( IDeckLinkInputCallback * This, /* [in] */ IDeckLinkVideoInputFrame *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket); END_INTERFACE } IDeckLinkInputCallbackVtbl; interface IDeckLinkInputCallback { CONST_VTBL struct IDeckLinkInputCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInputCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInputCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInputCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInputCallback_VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) \ ( (This)->lpVtbl -> VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) ) #define IDeckLinkInputCallback_VideoInputFrameArrived(This,videoFrame,audioPacket) \ ( (This)->lpVtbl -> VideoInputFrameArrived(This,videoFrame,audioPacket) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInputCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkMemoryAllocator_INTERFACE_DEFINED__ #define __IDeckLinkMemoryAllocator_INTERFACE_DEFINED__ /* interface IDeckLinkMemoryAllocator */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkMemoryAllocator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("B36EB6E7-9D29-4AA8-92EF-843B87A289E8") IDeckLinkMemoryAllocator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE AllocateBuffer( /* [in] */ unsigned long bufferSize, /* [out] */ void **allocatedBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer( /* [in] */ void *buffer) = 0; virtual HRESULT STDMETHODCALLTYPE Commit( void) = 0; virtual HRESULT STDMETHODCALLTYPE Decommit( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkMemoryAllocatorVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkMemoryAllocator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkMemoryAllocator * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkMemoryAllocator * This); HRESULT ( STDMETHODCALLTYPE *AllocateBuffer )( IDeckLinkMemoryAllocator * This, /* [in] */ unsigned long bufferSize, /* [out] */ void **allocatedBuffer); HRESULT ( STDMETHODCALLTYPE *ReleaseBuffer )( IDeckLinkMemoryAllocator * This, /* [in] */ void *buffer); HRESULT ( STDMETHODCALLTYPE *Commit )( IDeckLinkMemoryAllocator * This); HRESULT ( STDMETHODCALLTYPE *Decommit )( IDeckLinkMemoryAllocator * This); END_INTERFACE } IDeckLinkMemoryAllocatorVtbl; interface IDeckLinkMemoryAllocator { CONST_VTBL struct IDeckLinkMemoryAllocatorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkMemoryAllocator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkMemoryAllocator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkMemoryAllocator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkMemoryAllocator_AllocateBuffer(This,bufferSize,allocatedBuffer) \ ( (This)->lpVtbl -> AllocateBuffer(This,bufferSize,allocatedBuffer) ) #define IDeckLinkMemoryAllocator_ReleaseBuffer(This,buffer) \ ( (This)->lpVtbl -> ReleaseBuffer(This,buffer) ) #define IDeckLinkMemoryAllocator_Commit(This) \ ( (This)->lpVtbl -> Commit(This) ) #define IDeckLinkMemoryAllocator_Decommit(This) \ ( (This)->lpVtbl -> Decommit(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkMemoryAllocator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAudioOutputCallback_INTERFACE_DEFINED__ #define __IDeckLinkAudioOutputCallback_INTERFACE_DEFINED__ /* interface IDeckLinkAudioOutputCallback */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAudioOutputCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("403C681B-7F46-4A12-B993-2BB127084EE6") IDeckLinkAudioOutputCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples( /* [in] */ BOOL preroll) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAudioOutputCallbackVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAudioOutputCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAudioOutputCallback * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAudioOutputCallback * This); HRESULT ( STDMETHODCALLTYPE *RenderAudioSamples )( IDeckLinkAudioOutputCallback * This, /* [in] */ BOOL preroll); END_INTERFACE } IDeckLinkAudioOutputCallbackVtbl; interface IDeckLinkAudioOutputCallback { CONST_VTBL struct IDeckLinkAudioOutputCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAudioOutputCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAudioOutputCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAudioOutputCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAudioOutputCallback_RenderAudioSamples(This,preroll) \ ( (This)->lpVtbl -> RenderAudioSamples(This,preroll) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAudioOutputCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkIterator_INTERFACE_DEFINED__ #define __IDeckLinkIterator_INTERFACE_DEFINED__ /* interface IDeckLinkIterator */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkIterator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("74E936FC-CC28-4A67-81A0-1E94E52D4E69") IDeckLinkIterator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLink **deckLinkInstance) = 0; }; #else /* C style interface */ typedef struct IDeckLinkIteratorVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkIterator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkIterator * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkIterator * This); HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkIterator * This, /* [out] */ IDeckLink **deckLinkInstance); END_INTERFACE } IDeckLinkIteratorVtbl; interface IDeckLinkIterator { CONST_VTBL struct IDeckLinkIteratorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkIterator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkIterator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkIterator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkIterator_Next(This,deckLinkInstance) \ ( (This)->lpVtbl -> Next(This,deckLinkInstance) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkIterator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAPIInformation_INTERFACE_DEFINED__ #define __IDeckLinkAPIInformation_INTERFACE_DEFINED__ /* interface IDeckLinkAPIInformation */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAPIInformation; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("7BEA3C68-730D-4322-AF34-8A7152B532A4") IDeckLinkAPIInformation : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ BSTR *value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAPIInformationVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAPIInformation * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAPIInformation * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAPIInformation * This); HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkAPIInformation * This, /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ BOOL *value); HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkAPIInformation * This, /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ LONGLONG *value); HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkAPIInformation * This, /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ double *value); HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkAPIInformation * This, /* [in] */ BMDDeckLinkAPIInformationID cfgID, /* [out] */ BSTR *value); END_INTERFACE } IDeckLinkAPIInformationVtbl; interface IDeckLinkAPIInformation { CONST_VTBL struct IDeckLinkAPIInformationVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAPIInformation_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAPIInformation_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAPIInformation_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAPIInformation_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkAPIInformation_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkAPIInformation_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkAPIInformation_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAPIInformation_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDisplayModeIterator_INTERFACE_DEFINED__ #define __IDeckLinkDisplayModeIterator_INTERFACE_DEFINED__ /* interface IDeckLinkDisplayModeIterator */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDisplayModeIterator; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("9C88499F-F601-4021-B80B-032E4EB41C35") IDeckLinkDisplayModeIterator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLinkDisplayMode **deckLinkDisplayMode) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDisplayModeIteratorVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDisplayModeIterator * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDisplayModeIterator * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDisplayModeIterator * This); HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkDisplayModeIterator * This, /* [out] */ IDeckLinkDisplayMode **deckLinkDisplayMode); END_INTERFACE } IDeckLinkDisplayModeIteratorVtbl; interface IDeckLinkDisplayModeIterator { CONST_VTBL struct IDeckLinkDisplayModeIteratorVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDisplayModeIterator_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDisplayModeIterator_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDisplayModeIterator_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDisplayModeIterator_Next(This,deckLinkDisplayMode) \ ( (This)->lpVtbl -> Next(This,deckLinkDisplayMode) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDisplayModeIterator_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDisplayMode_INTERFACE_DEFINED__ #define __IDeckLinkDisplayMode_INTERFACE_DEFINED__ /* interface IDeckLinkDisplayMode */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDisplayMode; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3EB2C1AB-0A3D-4523-A3AD-F40D7FB14E78") IDeckLinkDisplayMode : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetName( /* [out] */ BSTR *name) = 0; virtual BMDDisplayMode STDMETHODCALLTYPE GetDisplayMode( void) = 0; virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameRate( /* [out] */ BMDTimeValue *frameDuration, /* [out] */ BMDTimeScale *timeScale) = 0; virtual BMDFieldDominance STDMETHODCALLTYPE GetFieldDominance( void) = 0; virtual BMDDisplayModeFlags STDMETHODCALLTYPE GetFlags( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDisplayModeVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDisplayMode * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDisplayMode * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDisplayMode * This); HRESULT ( STDMETHODCALLTYPE *GetName )( IDeckLinkDisplayMode * This, /* [out] */ BSTR *name); BMDDisplayMode ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkDisplayMode * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkDisplayMode * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkDisplayMode * This); HRESULT ( STDMETHODCALLTYPE *GetFrameRate )( IDeckLinkDisplayMode * This, /* [out] */ BMDTimeValue *frameDuration, /* [out] */ BMDTimeScale *timeScale); BMDFieldDominance ( STDMETHODCALLTYPE *GetFieldDominance )( IDeckLinkDisplayMode * This); BMDDisplayModeFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkDisplayMode * This); END_INTERFACE } IDeckLinkDisplayModeVtbl; interface IDeckLinkDisplayMode { CONST_VTBL struct IDeckLinkDisplayModeVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDisplayMode_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDisplayMode_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDisplayMode_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDisplayMode_GetName(This,name) \ ( (This)->lpVtbl -> GetName(This,name) ) #define IDeckLinkDisplayMode_GetDisplayMode(This) \ ( (This)->lpVtbl -> GetDisplayMode(This) ) #define IDeckLinkDisplayMode_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkDisplayMode_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkDisplayMode_GetFrameRate(This,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetFrameRate(This,frameDuration,timeScale) ) #define IDeckLinkDisplayMode_GetFieldDominance(This) \ ( (This)->lpVtbl -> GetFieldDominance(This) ) #define IDeckLinkDisplayMode_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDisplayMode_INTERFACE_DEFINED__ */ #ifndef __IDeckLink_INTERFACE_DEFINED__ #define __IDeckLink_INTERFACE_DEFINED__ /* interface IDeckLink */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLink; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("62BFF75D-6569-4E55-8D4D-66AA03829ABC") IDeckLink : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetModelName( /* [out] */ BSTR *modelName) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLink * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLink * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLink * This); HRESULT ( STDMETHODCALLTYPE *GetModelName )( IDeckLink * This, /* [out] */ BSTR *modelName); END_INTERFACE } IDeckLinkVtbl; interface IDeckLink { CONST_VTBL struct IDeckLinkVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLink_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLink_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLink_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLink_GetModelName(This,modelName) \ ( (This)->lpVtbl -> GetModelName(This,modelName) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLink_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkOutput_INTERFACE_DEFINED__ #define __IDeckLinkOutput_INTERFACE_DEFINED__ /* interface IDeckLinkOutput */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkOutput; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("A3EF0963-0862-44ED-92A9-EE89ABF431C7") IDeckLinkOutput : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoOutputFlags flags, /* [out] */ BMDDisplayModeSupport *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame( /* [in] */ long width, /* [in] */ long height, /* [in] */ long rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateAncillaryData( /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync( /* [in] */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame( /* [in] */ IDeckLinkVideoFrame *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback( /* [in] */ IDeckLinkVideoOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedVideoFrameCount( /* [out] */ unsigned long *bufferedFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned long channelCount, /* [in] */ BMDAudioOutputStreamType streamType) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync( /* [in] */ void *buffer, /* [in] */ unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples( /* [in] */ void *buffer, /* [in] */ unsigned long sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned long *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned long *bufferedSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioCallback( /* [in] */ IDeckLinkAudioOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback( /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback( /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE IsScheduledPlaybackRunning( /* [out] */ BOOL *active) = 0; virtual HRESULT STDMETHODCALLTYPE GetScheduledStreamTime( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE GetReferenceStatus( /* [out] */ BMDReferenceStatus *referenceStatus) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkOutputVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkOutput * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkOutput * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkOutput * This); HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkOutput * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoOutputFlags flags, /* [out] */ BMDDisplayModeSupport *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkOutput * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkOutput * This, /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback); HRESULT ( STDMETHODCALLTYPE *EnableVideoOutput )( IDeckLinkOutput * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDVideoOutputFlags flags); HRESULT ( STDMETHODCALLTYPE *DisableVideoOutput )( IDeckLinkOutput * This); HRESULT ( STDMETHODCALLTYPE *SetVideoOutputFrameMemoryAllocator )( IDeckLinkOutput * This, /* [in] */ IDeckLinkMemoryAllocator *theAllocator); HRESULT ( STDMETHODCALLTYPE *CreateVideoFrame )( IDeckLinkOutput * This, /* [in] */ long width, /* [in] */ long height, /* [in] */ long rowBytes, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame **outFrame); HRESULT ( STDMETHODCALLTYPE *CreateAncillaryData )( IDeckLinkOutput * This, /* [in] */ BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer); HRESULT ( STDMETHODCALLTYPE *DisplayVideoFrameSync )( IDeckLinkOutput * This, /* [in] */ IDeckLinkVideoFrame *theFrame); HRESULT ( STDMETHODCALLTYPE *ScheduleVideoFrame )( IDeckLinkOutput * This, /* [in] */ IDeckLinkVideoFrame *theFrame, /* [in] */ BMDTimeValue displayTime, /* [in] */ BMDTimeValue displayDuration, /* [in] */ BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *SetScheduledFrameCompletionCallback )( IDeckLinkOutput * This, /* [in] */ IDeckLinkVideoOutputCallback *theCallback); HRESULT ( STDMETHODCALLTYPE *GetBufferedVideoFrameCount )( IDeckLinkOutput * This, /* [out] */ unsigned long *bufferedFrameCount); HRESULT ( STDMETHODCALLTYPE *EnableAudioOutput )( IDeckLinkOutput * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned long channelCount, /* [in] */ BMDAudioOutputStreamType streamType); HRESULT ( STDMETHODCALLTYPE *DisableAudioOutput )( IDeckLinkOutput * This); HRESULT ( STDMETHODCALLTYPE *WriteAudioSamplesSync )( IDeckLinkOutput * This, /* [in] */ void *buffer, /* [in] */ unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesWritten); HRESULT ( STDMETHODCALLTYPE *BeginAudioPreroll )( IDeckLinkOutput * This); HRESULT ( STDMETHODCALLTYPE *EndAudioPreroll )( IDeckLinkOutput * This); HRESULT ( STDMETHODCALLTYPE *ScheduleAudioSamples )( IDeckLinkOutput * This, /* [in] */ void *buffer, /* [in] */ unsigned long sampleFrameCount, /* [in] */ BMDTimeValue streamTime, /* [in] */ BMDTimeScale timeScale, /* [out] */ unsigned long *sampleFramesWritten); HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkOutput * This, /* [out] */ unsigned long *bufferedSampleFrameCount); HRESULT ( STDMETHODCALLTYPE *FlushBufferedAudioSamples )( IDeckLinkOutput * This); HRESULT ( STDMETHODCALLTYPE *SetAudioCallback )( IDeckLinkOutput * This, /* [in] */ IDeckLinkAudioOutputCallback *theCallback); HRESULT ( STDMETHODCALLTYPE *StartScheduledPlayback )( IDeckLinkOutput * This, /* [in] */ BMDTimeValue playbackStartTime, /* [in] */ BMDTimeScale timeScale, /* [in] */ double playbackSpeed); HRESULT ( STDMETHODCALLTYPE *StopScheduledPlayback )( IDeckLinkOutput * This, /* [in] */ BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, /* [in] */ BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *IsScheduledPlaybackRunning )( IDeckLinkOutput * This, /* [out] */ BOOL *active); HRESULT ( STDMETHODCALLTYPE *GetScheduledStreamTime )( IDeckLinkOutput * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed); HRESULT ( STDMETHODCALLTYPE *GetReferenceStatus )( IDeckLinkOutput * This, /* [out] */ BMDReferenceStatus *referenceStatus); HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkOutput * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkOutputVtbl; interface IDeckLinkOutput { CONST_VTBL struct IDeckLinkOutputVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkOutput_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkOutput_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkOutput_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkOutput_DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) ) #define IDeckLinkOutput_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkOutput_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkOutput_EnableVideoOutput(This,displayMode,flags) \ ( (This)->lpVtbl -> EnableVideoOutput(This,displayMode,flags) ) #define IDeckLinkOutput_DisableVideoOutput(This) \ ( (This)->lpVtbl -> DisableVideoOutput(This) ) #define IDeckLinkOutput_SetVideoOutputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoOutputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkOutput_CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_CreateAncillaryData(This,pixelFormat,outBuffer) \ ( (This)->lpVtbl -> CreateAncillaryData(This,pixelFormat,outBuffer) ) #define IDeckLinkOutput_DisplayVideoFrameSync(This,theFrame) \ ( (This)->lpVtbl -> DisplayVideoFrameSync(This,theFrame) ) #define IDeckLinkOutput_ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) \ ( (This)->lpVtbl -> ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) ) #define IDeckLinkOutput_SetScheduledFrameCompletionCallback(This,theCallback) \ ( (This)->lpVtbl -> SetScheduledFrameCompletionCallback(This,theCallback) ) #define IDeckLinkOutput_GetBufferedVideoFrameCount(This,bufferedFrameCount) \ ( (This)->lpVtbl -> GetBufferedVideoFrameCount(This,bufferedFrameCount) ) #define IDeckLinkOutput_EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) \ ( (This)->lpVtbl -> EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) ) #define IDeckLinkOutput_DisableAudioOutput(This) \ ( (This)->lpVtbl -> DisableAudioOutput(This) ) #define IDeckLinkOutput_WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) \ ( (This)->lpVtbl -> WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) ) #define IDeckLinkOutput_BeginAudioPreroll(This) \ ( (This)->lpVtbl -> BeginAudioPreroll(This) ) #define IDeckLinkOutput_EndAudioPreroll(This) \ ( (This)->lpVtbl -> EndAudioPreroll(This) ) #define IDeckLinkOutput_ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) \ ( (This)->lpVtbl -> ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) ) #define IDeckLinkOutput_GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) ) #define IDeckLinkOutput_FlushBufferedAudioSamples(This) \ ( (This)->lpVtbl -> FlushBufferedAudioSamples(This) ) #define IDeckLinkOutput_SetAudioCallback(This,theCallback) \ ( (This)->lpVtbl -> SetAudioCallback(This,theCallback) ) #define IDeckLinkOutput_StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) \ ( (This)->lpVtbl -> StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) ) #define IDeckLinkOutput_StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) \ ( (This)->lpVtbl -> StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) ) #define IDeckLinkOutput_IsScheduledPlaybackRunning(This,active) \ ( (This)->lpVtbl -> IsScheduledPlaybackRunning(This,active) ) #define IDeckLinkOutput_GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) \ ( (This)->lpVtbl -> GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) ) #define IDeckLinkOutput_GetReferenceStatus(This,referenceStatus) \ ( (This)->lpVtbl -> GetReferenceStatus(This,referenceStatus) ) #define IDeckLinkOutput_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkOutput_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_INTERFACE_DEFINED__ #define __IDeckLinkInput_INTERFACE_DEFINED__ /* interface IDeckLinkInput */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("6D40EF78-28B9-4E21-990D-95BB7750A04F") IDeckLinkInput : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags, /* [out] */ BMDDisplayModeSupport *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoFrameCount( /* [out] */ unsigned long *availableFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned long channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned long *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput * This); HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags, /* [out] */ BMDDisplayModeSupport *result, /* [out] */ IDeckLinkDisplayMode **resultDisplayMode); HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput * This, /* [out] */ IDeckLinkDisplayModeIterator **iterator); HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkInput * This, /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback); HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput * This, /* [in] */ BMDDisplayMode displayMode, /* [in] */ BMDPixelFormat pixelFormat, /* [in] */ BMDVideoInputFlags flags); HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput * This); HRESULT ( STDMETHODCALLTYPE *GetAvailableVideoFrameCount )( IDeckLinkInput * This, /* [out] */ unsigned long *availableFrameCount); HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput * This, /* [in] */ BMDAudioSampleRate sampleRate, /* [in] */ BMDAudioSampleType sampleType, /* [in] */ unsigned long channelCount); HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput * This); HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkInput * This, /* [out] */ unsigned long *availableSampleFrameCount); HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput * This); HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput * This); HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput * This); HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkInput * This); HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput * This, /* [in] */ IDeckLinkInputCallback *theCallback); HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkInput * This, /* [in] */ BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkInputVtbl; interface IDeckLinkInput { CONST_VTBL struct IDeckLinkInputVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,flags,result,resultDisplayMode) ) #define IDeckLinkInput_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkInput_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_GetAvailableVideoFrameCount(This,availableFrameCount) \ ( (This)->lpVtbl -> GetAvailableVideoFrameCount(This,availableFrameCount) ) #define IDeckLinkInput_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkInput_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkInput_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkInput_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkTimecode_INTERFACE_DEFINED__ #define __IDeckLinkTimecode_INTERFACE_DEFINED__ /* interface IDeckLinkTimecode */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkTimecode; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("BC6CFBD3-8317-4325-AC1C-1216391E9340") IDeckLinkTimecode : public IUnknown { public: virtual BMDTimecodeBCD STDMETHODCALLTYPE GetBCD( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetComponents( /* [out] */ unsigned char *hours, /* [out] */ unsigned char *minutes, /* [out] */ unsigned char *seconds, /* [out] */ unsigned char *frames) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [out] */ BSTR *timecode) = 0; virtual BMDTimecodeFlags STDMETHODCALLTYPE GetFlags( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecodeUserBits( /* [out] */ BMDTimecodeUserBits *userBits) = 0; }; #else /* C style interface */ typedef struct IDeckLinkTimecodeVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkTimecode * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkTimecode * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkTimecode * This); BMDTimecodeBCD ( STDMETHODCALLTYPE *GetBCD )( IDeckLinkTimecode * This); HRESULT ( STDMETHODCALLTYPE *GetComponents )( IDeckLinkTimecode * This, /* [out] */ unsigned char *hours, /* [out] */ unsigned char *minutes, /* [out] */ unsigned char *seconds, /* [out] */ unsigned char *frames); HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkTimecode * This, /* [out] */ BSTR *timecode); BMDTimecodeFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkTimecode * This); HRESULT ( STDMETHODCALLTYPE *GetTimecodeUserBits )( IDeckLinkTimecode * This, /* [out] */ BMDTimecodeUserBits *userBits); END_INTERFACE } IDeckLinkTimecodeVtbl; interface IDeckLinkTimecode { CONST_VTBL struct IDeckLinkTimecodeVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkTimecode_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkTimecode_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkTimecode_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkTimecode_GetBCD(This) \ ( (This)->lpVtbl -> GetBCD(This) ) #define IDeckLinkTimecode_GetComponents(This,hours,minutes,seconds,frames) \ ( (This)->lpVtbl -> GetComponents(This,hours,minutes,seconds,frames) ) #define IDeckLinkTimecode_GetString(This,timecode) \ ( (This)->lpVtbl -> GetString(This,timecode) ) #define IDeckLinkTimecode_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkTimecode_GetTimecodeUserBits(This,userBits) \ ( (This)->lpVtbl -> GetTimecodeUserBits(This,userBits) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkTimecode_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrame_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrame */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrame; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3F716FE0-F023-4111-BE5D-EF4414C05B17") IDeckLinkVideoFrame : public IUnknown { public: virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual long STDMETHODCALLTYPE GetRowBytes( void) = 0; virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat( void) = 0; virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecode( /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode) = 0; virtual HRESULT STDMETHODCALLTYPE GetAncillaryData( /* [out] */ IDeckLinkVideoFrameAncillary **ancillary) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrameVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrame * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrame * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrame * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoFrame * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoFrame * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoFrame * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoFrame * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoFrame * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoFrame * This, /* [out] */ void **buffer); HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoFrame * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); END_INTERFACE } IDeckLinkVideoFrameVtbl; interface IDeckLinkVideoFrame { CONST_VTBL struct IDeckLinkVideoFrameVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrame_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrame_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrame_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrame_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoFrame_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoFrame_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoFrame_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoFrame_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoFrame_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoFrame_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoFrame_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrame_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkMutableVideoFrame_INTERFACE_DEFINED__ #define __IDeckLinkMutableVideoFrame_INTERFACE_DEFINED__ /* interface IDeckLinkMutableVideoFrame */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkMutableVideoFrame; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("69E2639F-40DA-4E19-B6F2-20ACE815C390") IDeckLinkMutableVideoFrame : public IDeckLinkVideoFrame { public: virtual HRESULT STDMETHODCALLTYPE SetFlags( /* [in] */ BMDFrameFlags newFlags) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecode( /* [in] */ BMDTimecodeFormat format, /* [in] */ IDeckLinkTimecode *timecode) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecodeFromComponents( /* [in] */ BMDTimecodeFormat format, /* [in] */ unsigned char hours, /* [in] */ unsigned char minutes, /* [in] */ unsigned char seconds, /* [in] */ unsigned char frames, /* [in] */ BMDTimecodeFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE SetAncillaryData( /* [in] */ IDeckLinkVideoFrameAncillary *ancillary) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecodeUserBits( /* [in] */ BMDTimecodeFormat format, /* [in] */ BMDTimecodeUserBits userBits) = 0; }; #else /* C style interface */ typedef struct IDeckLinkMutableVideoFrameVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkMutableVideoFrame * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkMutableVideoFrame * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkMutableVideoFrame * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkMutableVideoFrame * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkMutableVideoFrame * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkMutableVideoFrame * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkMutableVideoFrame * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkMutableVideoFrame * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkMutableVideoFrame * This, /* [out] */ void **buffer); HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkMutableVideoFrame * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); HRESULT ( STDMETHODCALLTYPE *SetFlags )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDFrameFlags newFlags); HRESULT ( STDMETHODCALLTYPE *SetTimecode )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ IDeckLinkTimecode *timecode); HRESULT ( STDMETHODCALLTYPE *SetTimecodeFromComponents )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ unsigned char hours, /* [in] */ unsigned char minutes, /* [in] */ unsigned char seconds, /* [in] */ unsigned char frames, /* [in] */ BMDTimecodeFlags flags); HRESULT ( STDMETHODCALLTYPE *SetAncillaryData )( IDeckLinkMutableVideoFrame * This, /* [in] */ IDeckLinkVideoFrameAncillary *ancillary); HRESULT ( STDMETHODCALLTYPE *SetTimecodeUserBits )( IDeckLinkMutableVideoFrame * This, /* [in] */ BMDTimecodeFormat format, /* [in] */ BMDTimecodeUserBits userBits); END_INTERFACE } IDeckLinkMutableVideoFrameVtbl; interface IDeckLinkMutableVideoFrame { CONST_VTBL struct IDeckLinkMutableVideoFrameVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkMutableVideoFrame_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkMutableVideoFrame_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkMutableVideoFrame_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkMutableVideoFrame_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkMutableVideoFrame_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkMutableVideoFrame_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkMutableVideoFrame_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkMutableVideoFrame_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkMutableVideoFrame_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkMutableVideoFrame_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkMutableVideoFrame_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkMutableVideoFrame_SetFlags(This,newFlags) \ ( (This)->lpVtbl -> SetFlags(This,newFlags) ) #define IDeckLinkMutableVideoFrame_SetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> SetTimecode(This,format,timecode) ) #define IDeckLinkMutableVideoFrame_SetTimecodeFromComponents(This,format,hours,minutes,seconds,frames,flags) \ ( (This)->lpVtbl -> SetTimecodeFromComponents(This,format,hours,minutes,seconds,frames,flags) ) #define IDeckLinkMutableVideoFrame_SetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> SetAncillaryData(This,ancillary) ) #define IDeckLinkMutableVideoFrame_SetTimecodeUserBits(This,format,userBits) \ ( (This)->lpVtbl -> SetTimecodeUserBits(This,format,userBits) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkMutableVideoFrame_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrame3DExtensions_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrame3DExtensions_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrame3DExtensions */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrame3DExtensions; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7") IDeckLinkVideoFrame3DExtensions : public IUnknown { public: virtual BMDVideo3DPackingFormat STDMETHODCALLTYPE Get3DPackingFormat( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameForRightEye( /* [in] */ IDeckLinkVideoFrame **rightEyeFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrame3DExtensionsVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrame3DExtensions * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrame3DExtensions * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrame3DExtensions * This); BMDVideo3DPackingFormat ( STDMETHODCALLTYPE *Get3DPackingFormat )( IDeckLinkVideoFrame3DExtensions * This); HRESULT ( STDMETHODCALLTYPE *GetFrameForRightEye )( IDeckLinkVideoFrame3DExtensions * This, /* [in] */ IDeckLinkVideoFrame **rightEyeFrame); END_INTERFACE } IDeckLinkVideoFrame3DExtensionsVtbl; interface IDeckLinkVideoFrame3DExtensions { CONST_VTBL struct IDeckLinkVideoFrame3DExtensionsVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrame3DExtensions_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrame3DExtensions_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrame3DExtensions_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrame3DExtensions_Get3DPackingFormat(This) \ ( (This)->lpVtbl -> Get3DPackingFormat(This) ) #define IDeckLinkVideoFrame3DExtensions_GetFrameForRightEye(This,rightEyeFrame) \ ( (This)->lpVtbl -> GetFrameForRightEye(This,rightEyeFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrame3DExtensions_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_INTERFACE_DEFINED__ #define __IDeckLinkVideoInputFrame_INTERFACE_DEFINED__ /* interface IDeckLinkVideoInputFrame */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoInputFrame; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("05CFE374-537C-4094-9A57-680525118F44") IDeckLinkVideoInputFrame : public IDeckLinkVideoFrame { public: virtual HRESULT STDMETHODCALLTYPE GetStreamTime( /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, /* [in] */ BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceTimestamp( /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoInputFrameVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoInputFrame * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoInputFrame * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoInputFrame * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoInputFrame * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoInputFrame * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoInputFrame * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoInputFrame * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoInputFrame * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoInputFrame * This, /* [out] */ void **buffer); HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoInputFrame * This, /* [in] */ BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode **timecode); HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoInputFrame * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkVideoInputFrame * This, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, /* [in] */ BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceTimestamp )( IDeckLinkVideoInputFrame * This, /* [in] */ BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration); END_INTERFACE } IDeckLinkVideoInputFrameVtbl; interface IDeckLinkVideoInputFrame { CONST_VTBL struct IDeckLinkVideoInputFrameVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoInputFrame_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoInputFrame_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoInputFrame_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoInputFrame_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoInputFrame_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoInputFrame_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoInputFrame_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoInputFrame_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoInputFrame_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoInputFrame_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoInputFrame_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkVideoInputFrame_GetStreamTime(This,frameTime,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,frameDuration,timeScale) ) #define IDeckLinkVideoInputFrame_GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) \ ( (This)->lpVtbl -> GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoInputFrame_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrameAncillary_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrameAncillary_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrameAncillary */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrameAncillary; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("732E723C-D1A4-4E29-9E8E-4A88797A0004") IDeckLinkVideoFrameAncillary : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetBufferForVerticalBlankingLine( /* [in] */ unsigned long lineNumber, /* [out] */ void **buffer) = 0; virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat( void) = 0; virtual BMDDisplayMode STDMETHODCALLTYPE GetDisplayMode( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrameAncillaryVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrameAncillary * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrameAncillary * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrameAncillary * This); HRESULT ( STDMETHODCALLTYPE *GetBufferForVerticalBlankingLine )( IDeckLinkVideoFrameAncillary * This, /* [in] */ unsigned long lineNumber, /* [out] */ void **buffer); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoFrameAncillary * This); BMDDisplayMode ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkVideoFrameAncillary * This); END_INTERFACE } IDeckLinkVideoFrameAncillaryVtbl; interface IDeckLinkVideoFrameAncillary { CONST_VTBL struct IDeckLinkVideoFrameAncillaryVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrameAncillary_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrameAncillary_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrameAncillary_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrameAncillary_GetBufferForVerticalBlankingLine(This,lineNumber,buffer) \ ( (This)->lpVtbl -> GetBufferForVerticalBlankingLine(This,lineNumber,buffer) ) #define IDeckLinkVideoFrameAncillary_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoFrameAncillary_GetDisplayMode(This) \ ( (This)->lpVtbl -> GetDisplayMode(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrameAncillary_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAudioInputPacket_INTERFACE_DEFINED__ #define __IDeckLinkAudioInputPacket_INTERFACE_DEFINED__ /* interface IDeckLinkAudioInputPacket */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAudioInputPacket; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("E43D5870-2894-11DE-8C30-0800200C9A66") IDeckLinkAudioInputPacket : public IUnknown { public: virtual long STDMETHODCALLTYPE GetSampleFrameCount( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetPacketTime( /* [out] */ BMDTimeValue *packetTime, /* [in] */ BMDTimeScale timeScale) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAudioInputPacketVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAudioInputPacket * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAudioInputPacket * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAudioInputPacket * This); long ( STDMETHODCALLTYPE *GetSampleFrameCount )( IDeckLinkAudioInputPacket * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkAudioInputPacket * This, /* [out] */ void **buffer); HRESULT ( STDMETHODCALLTYPE *GetPacketTime )( IDeckLinkAudioInputPacket * This, /* [out] */ BMDTimeValue *packetTime, /* [in] */ BMDTimeScale timeScale); END_INTERFACE } IDeckLinkAudioInputPacketVtbl; interface IDeckLinkAudioInputPacket { CONST_VTBL struct IDeckLinkAudioInputPacketVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAudioInputPacket_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAudioInputPacket_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAudioInputPacket_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAudioInputPacket_GetSampleFrameCount(This) \ ( (This)->lpVtbl -> GetSampleFrameCount(This) ) #define IDeckLinkAudioInputPacket_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkAudioInputPacket_GetPacketTime(This,packetTime,timeScale) \ ( (This)->lpVtbl -> GetPacketTime(This,packetTime,timeScale) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAudioInputPacket_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkScreenPreviewCallback_INTERFACE_DEFINED__ #define __IDeckLinkScreenPreviewCallback_INTERFACE_DEFINED__ /* interface IDeckLinkScreenPreviewCallback */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkScreenPreviewCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("B1D3F49A-85FE-4C5D-95C8-0B5D5DCCD438") IDeckLinkScreenPreviewCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DrawFrame( /* [in] */ IDeckLinkVideoFrame *theFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkScreenPreviewCallbackVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkScreenPreviewCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkScreenPreviewCallback * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkScreenPreviewCallback * This); HRESULT ( STDMETHODCALLTYPE *DrawFrame )( IDeckLinkScreenPreviewCallback * This, /* [in] */ IDeckLinkVideoFrame *theFrame); END_INTERFACE } IDeckLinkScreenPreviewCallbackVtbl; interface IDeckLinkScreenPreviewCallback { CONST_VTBL struct IDeckLinkScreenPreviewCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkScreenPreviewCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkScreenPreviewCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkScreenPreviewCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkScreenPreviewCallback_DrawFrame(This,theFrame) \ ( (This)->lpVtbl -> DrawFrame(This,theFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkScreenPreviewCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkGLScreenPreviewHelper_INTERFACE_DEFINED__ #define __IDeckLinkGLScreenPreviewHelper_INTERFACE_DEFINED__ /* interface IDeckLinkGLScreenPreviewHelper */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkGLScreenPreviewHelper; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("504E2209-CAC7-4C1A-9FB4-C5BB6274D22F") IDeckLinkGLScreenPreviewHelper : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE InitializeGL( void) = 0; virtual HRESULT STDMETHODCALLTYPE PaintGL( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetFrame( /* [in] */ IDeckLinkVideoFrame *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE Set3DPreviewFormat( /* [in] */ BMD3DPreviewFormat previewFormat) = 0; }; #else /* C style interface */ typedef struct IDeckLinkGLScreenPreviewHelperVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkGLScreenPreviewHelper * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkGLScreenPreviewHelper * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkGLScreenPreviewHelper * This); HRESULT ( STDMETHODCALLTYPE *InitializeGL )( IDeckLinkGLScreenPreviewHelper * This); HRESULT ( STDMETHODCALLTYPE *PaintGL )( IDeckLinkGLScreenPreviewHelper * This); HRESULT ( STDMETHODCALLTYPE *SetFrame )( IDeckLinkGLScreenPreviewHelper * This, /* [in] */ IDeckLinkVideoFrame *theFrame); HRESULT ( STDMETHODCALLTYPE *Set3DPreviewFormat )( IDeckLinkGLScreenPreviewHelper * This, /* [in] */ BMD3DPreviewFormat previewFormat); END_INTERFACE } IDeckLinkGLScreenPreviewHelperVtbl; interface IDeckLinkGLScreenPreviewHelper { CONST_VTBL struct IDeckLinkGLScreenPreviewHelperVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkGLScreenPreviewHelper_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkGLScreenPreviewHelper_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkGLScreenPreviewHelper_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkGLScreenPreviewHelper_InitializeGL(This) \ ( (This)->lpVtbl -> InitializeGL(This) ) #define IDeckLinkGLScreenPreviewHelper_PaintGL(This) \ ( (This)->lpVtbl -> PaintGL(This) ) #define IDeckLinkGLScreenPreviewHelper_SetFrame(This,theFrame) \ ( (This)->lpVtbl -> SetFrame(This,theFrame) ) #define IDeckLinkGLScreenPreviewHelper_Set3DPreviewFormat(This,previewFormat) \ ( (This)->lpVtbl -> Set3DPreviewFormat(This,previewFormat) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkGLScreenPreviewHelper_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkConfiguration_INTERFACE_DEFINED__ #define __IDeckLinkConfiguration_INTERFACE_DEFINED__ /* interface IDeckLinkConfiguration */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkConfiguration; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C679A35B-610C-4D09-B748-1D0478100FC0") IDeckLinkConfiguration : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE SetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value) = 0; virtual HRESULT STDMETHODCALLTYPE WriteConfigurationToPreferences( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkConfigurationVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkConfiguration * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkConfiguration * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkConfiguration * This); HRESULT ( STDMETHODCALLTYPE *SetFlag )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BOOL value); HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BOOL *value); HRESULT ( STDMETHODCALLTYPE *SetInt )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ LONGLONG value); HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ LONGLONG *value); HRESULT ( STDMETHODCALLTYPE *SetFloat )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ double value); HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ double *value); HRESULT ( STDMETHODCALLTYPE *SetString )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [in] */ BSTR value); HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkConfiguration * This, /* [in] */ BMDDeckLinkConfigurationID cfgID, /* [out] */ BSTR *value); HRESULT ( STDMETHODCALLTYPE *WriteConfigurationToPreferences )( IDeckLinkConfiguration * This); END_INTERFACE } IDeckLinkConfigurationVtbl; interface IDeckLinkConfiguration { CONST_VTBL struct IDeckLinkConfigurationVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkConfiguration_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkConfiguration_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkConfiguration_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkConfiguration_SetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> SetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkConfiguration_SetInt(This,cfgID,value) \ ( (This)->lpVtbl -> SetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkConfiguration_SetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> SetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkConfiguration_SetString(This,cfgID,value) \ ( (This)->lpVtbl -> SetString(This,cfgID,value) ) #define IDeckLinkConfiguration_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #define IDeckLinkConfiguration_WriteConfigurationToPreferences(This) \ ( (This)->lpVtbl -> WriteConfigurationToPreferences(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkConfiguration_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAttributes_INTERFACE_DEFINED__ #define __IDeckLinkAttributes_INTERFACE_DEFINED__ /* interface IDeckLinkAttributes */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAttributes; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("ABC11843-D966-44CB-96E2-A1CB5D3135C4") IDeckLinkAttributes : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetFlag( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BOOL *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetInt( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ LONGLONG *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetFloat( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ double *value) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BSTR *value) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAttributesVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAttributes * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAttributes * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAttributes * This); HRESULT ( STDMETHODCALLTYPE *GetFlag )( IDeckLinkAttributes * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BOOL *value); HRESULT ( STDMETHODCALLTYPE *GetInt )( IDeckLinkAttributes * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ LONGLONG *value); HRESULT ( STDMETHODCALLTYPE *GetFloat )( IDeckLinkAttributes * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ double *value); HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkAttributes * This, /* [in] */ BMDDeckLinkAttributeID cfgID, /* [out] */ BSTR *value); END_INTERFACE } IDeckLinkAttributesVtbl; interface IDeckLinkAttributes { CONST_VTBL struct IDeckLinkAttributesVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAttributes_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAttributes_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAttributes_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAttributes_GetFlag(This,cfgID,value) \ ( (This)->lpVtbl -> GetFlag(This,cfgID,value) ) #define IDeckLinkAttributes_GetInt(This,cfgID,value) \ ( (This)->lpVtbl -> GetInt(This,cfgID,value) ) #define IDeckLinkAttributes_GetFloat(This,cfgID,value) \ ( (This)->lpVtbl -> GetFloat(This,cfgID,value) ) #define IDeckLinkAttributes_GetString(This,cfgID,value) \ ( (This)->lpVtbl -> GetString(This,cfgID,value) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAttributes_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkKeyer_INTERFACE_DEFINED__ #define __IDeckLinkKeyer_INTERFACE_DEFINED__ /* interface IDeckLinkKeyer */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkKeyer; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("89AFCAF5-65F8-421E-98F7-96FE5F5BFBA3") IDeckLinkKeyer : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Enable( /* [in] */ BOOL isExternal) = 0; virtual HRESULT STDMETHODCALLTYPE SetLevel( /* [in] */ unsigned char level) = 0; virtual HRESULT STDMETHODCALLTYPE RampUp( /* [in] */ unsigned long numberOfFrames) = 0; virtual HRESULT STDMETHODCALLTYPE RampDown( /* [in] */ unsigned long numberOfFrames) = 0; virtual HRESULT STDMETHODCALLTYPE Disable( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkKeyerVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkKeyer * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkKeyer * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkKeyer * This); HRESULT ( STDMETHODCALLTYPE *Enable )( IDeckLinkKeyer * This, /* [in] */ BOOL isExternal); HRESULT ( STDMETHODCALLTYPE *SetLevel )( IDeckLinkKeyer * This, /* [in] */ unsigned char level); HRESULT ( STDMETHODCALLTYPE *RampUp )( IDeckLinkKeyer * This, /* [in] */ unsigned long numberOfFrames); HRESULT ( STDMETHODCALLTYPE *RampDown )( IDeckLinkKeyer * This, /* [in] */ unsigned long numberOfFrames); HRESULT ( STDMETHODCALLTYPE *Disable )( IDeckLinkKeyer * This); END_INTERFACE } IDeckLinkKeyerVtbl; interface IDeckLinkKeyer { CONST_VTBL struct IDeckLinkKeyerVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkKeyer_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkKeyer_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkKeyer_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkKeyer_Enable(This,isExternal) \ ( (This)->lpVtbl -> Enable(This,isExternal) ) #define IDeckLinkKeyer_SetLevel(This,level) \ ( (This)->lpVtbl -> SetLevel(This,level) ) #define IDeckLinkKeyer_RampUp(This,numberOfFrames) \ ( (This)->lpVtbl -> RampUp(This,numberOfFrames) ) #define IDeckLinkKeyer_RampDown(This,numberOfFrames) \ ( (This)->lpVtbl -> RampDown(This,numberOfFrames) ) #define IDeckLinkKeyer_Disable(This) \ ( (This)->lpVtbl -> Disable(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkKeyer_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoConversion_INTERFACE_DEFINED__ #define __IDeckLinkVideoConversion_INTERFACE_DEFINED__ /* interface IDeckLinkVideoConversion */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoConversion; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3BBCB8A2-DA2C-42D9-B5D8-88083644E99A") IDeckLinkVideoConversion : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ConvertFrame( /* [in] */ IDeckLinkVideoFrame *srcFrame, /* [in] */ IDeckLinkVideoFrame *dstFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoConversionVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoConversion * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoConversion * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoConversion * This); HRESULT ( STDMETHODCALLTYPE *ConvertFrame )( IDeckLinkVideoConversion * This, /* [in] */ IDeckLinkVideoFrame *srcFrame, /* [in] */ IDeckLinkVideoFrame *dstFrame); END_INTERFACE } IDeckLinkVideoConversionVtbl; interface IDeckLinkVideoConversion { CONST_VTBL struct IDeckLinkVideoConversionVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoConversion_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoConversion_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoConversion_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoConversion_ConvertFrame(This,srcFrame,dstFrame) \ ( (This)->lpVtbl -> ConvertFrame(This,srcFrame,dstFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoConversion_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDeckControlStatusCallback_INTERFACE_DEFINED__ #define __IDeckLinkDeckControlStatusCallback_INTERFACE_DEFINED__ /* interface IDeckLinkDeckControlStatusCallback */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDeckControlStatusCallback; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("E5F693C1-4283-4716-B18F-C1431521955B") IDeckLinkDeckControlStatusCallback : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE TimecodeUpdate( /* [in] */ BMDTimecodeBCD currentTimecode) = 0; virtual HRESULT STDMETHODCALLTYPE VTRControlStateChanged( /* [in] */ BMDDeckControlVTRControlState newState, /* [in] */ BMDDeckControlError error) = 0; virtual HRESULT STDMETHODCALLTYPE DeckControlEventReceived( /* [in] */ BMDDeckControlEvent event, /* [in] */ BMDDeckControlError error) = 0; virtual HRESULT STDMETHODCALLTYPE DeckControlStatusChanged( /* [in] */ BMDDeckControlStatusFlags flags, /* [in] */ unsigned long mask) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDeckControlStatusCallbackVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDeckControlStatusCallback * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDeckControlStatusCallback * This); HRESULT ( STDMETHODCALLTYPE *TimecodeUpdate )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ BMDTimecodeBCD currentTimecode); HRESULT ( STDMETHODCALLTYPE *VTRControlStateChanged )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ BMDDeckControlVTRControlState newState, /* [in] */ BMDDeckControlError error); HRESULT ( STDMETHODCALLTYPE *DeckControlEventReceived )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ BMDDeckControlEvent event, /* [in] */ BMDDeckControlError error); HRESULT ( STDMETHODCALLTYPE *DeckControlStatusChanged )( IDeckLinkDeckControlStatusCallback * This, /* [in] */ BMDDeckControlStatusFlags flags, /* [in] */ unsigned long mask); END_INTERFACE } IDeckLinkDeckControlStatusCallbackVtbl; interface IDeckLinkDeckControlStatusCallback { CONST_VTBL struct IDeckLinkDeckControlStatusCallbackVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDeckControlStatusCallback_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDeckControlStatusCallback_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDeckControlStatusCallback_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDeckControlStatusCallback_TimecodeUpdate(This,currentTimecode) \ ( (This)->lpVtbl -> TimecodeUpdate(This,currentTimecode) ) #define IDeckLinkDeckControlStatusCallback_VTRControlStateChanged(This,newState,error) \ ( (This)->lpVtbl -> VTRControlStateChanged(This,newState,error) ) #define IDeckLinkDeckControlStatusCallback_DeckControlEventReceived(This,event,error) \ ( (This)->lpVtbl -> DeckControlEventReceived(This,event,error) ) #define IDeckLinkDeckControlStatusCallback_DeckControlStatusChanged(This,flags,mask) \ ( (This)->lpVtbl -> DeckControlStatusChanged(This,flags,mask) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDeckControlStatusCallback_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDeckControl_INTERFACE_DEFINED__ #define __IDeckLinkDeckControl_INTERFACE_DEFINED__ /* interface IDeckLinkDeckControl */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDeckControl; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("A4D81043-0619-42B7-8ED6-602D29041DF7") IDeckLinkDeckControl : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Open( /* [in] */ BMDTimeScale timeScale, /* [in] */ BMDTimeValue timeValue, /* [in] */ BOOL timecodeIsDropFrame, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Close( /* [in] */ BOOL standbyOn) = 0; virtual HRESULT STDMETHODCALLTYPE GetCurrentState( /* [out] */ BMDDeckControlMode *mode, /* [out] */ BMDDeckControlVTRControlState *vtrControlState, /* [out] */ BMDDeckControlStatusFlags *flags) = 0; virtual HRESULT STDMETHODCALLTYPE SetStandby( /* [in] */ BOOL standbyOn) = 0; virtual HRESULT STDMETHODCALLTYPE Play( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Stop( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE TogglePlayStop( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Eject( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GoToTimecode( /* [in] */ BMDTimecodeBCD timecode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE FastForward( /* [in] */ BOOL viewTape, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Rewind( /* [in] */ BOOL viewTape, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE StepForward( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE StepBack( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Jog( /* [in] */ double rate, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Shuttle( /* [in] */ double rate, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecodeString( /* [out] */ BSTR *currentTimeCode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecode( /* [out] */ IDeckLinkTimecode **currentTimecode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecodeBCD( /* [out] */ BMDTimecodeBCD *currentTimecode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE SetPreroll( /* [in] */ unsigned long prerollSeconds) = 0; virtual HRESULT STDMETHODCALLTYPE GetPreroll( /* [out] */ unsigned long *prerollSeconds) = 0; virtual HRESULT STDMETHODCALLTYPE SetExportOffset( /* [in] */ long exportOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE GetExportOffset( /* [out] */ long *exportOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE GetManualExportOffset( /* [out] */ long *deckManualExportOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE SetCaptureOffset( /* [in] */ long captureOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE GetCaptureOffset( /* [out] */ long *captureOffsetFields) = 0; virtual HRESULT STDMETHODCALLTYPE StartExport( /* [in] */ BMDTimecodeBCD inTimecode, /* [in] */ BMDTimecodeBCD outTimecode, /* [in] */ BMDDeckControlExportModeOpsFlags exportModeOps, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE StartCapture( /* [in] */ BOOL useVITC, /* [in] */ BMDTimecodeBCD inTimecode, /* [in] */ BMDTimecodeBCD outTimecode, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE GetDeviceID( /* [out] */ unsigned short *deviceId, /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE Abort( void) = 0; virtual HRESULT STDMETHODCALLTYPE CrashRecordStart( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE CrashRecordStop( /* [out] */ BMDDeckControlError *error) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkDeckControlStatusCallback *callback) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDeckControlVtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDeckControl * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDeckControl * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDeckControl * This); HRESULT ( STDMETHODCALLTYPE *Open )( IDeckLinkDeckControl * This, /* [in] */ BMDTimeScale timeScale, /* [in] */ BMDTimeValue timeValue, /* [in] */ BOOL timecodeIsDropFrame, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *Close )( IDeckLinkDeckControl * This, /* [in] */ BOOL standbyOn); HRESULT ( STDMETHODCALLTYPE *GetCurrentState )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlMode *mode, /* [out] */ BMDDeckControlVTRControlState *vtrControlState, /* [out] */ BMDDeckControlStatusFlags *flags); HRESULT ( STDMETHODCALLTYPE *SetStandby )( IDeckLinkDeckControl * This, /* [in] */ BOOL standbyOn); HRESULT ( STDMETHODCALLTYPE *Play )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *Stop )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *TogglePlayStop )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *Eject )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *GoToTimecode )( IDeckLinkDeckControl * This, /* [in] */ BMDTimecodeBCD timecode, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *FastForward )( IDeckLinkDeckControl * This, /* [in] */ BOOL viewTape, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *Rewind )( IDeckLinkDeckControl * This, /* [in] */ BOOL viewTape, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *StepForward )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *StepBack )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *Jog )( IDeckLinkDeckControl * This, /* [in] */ double rate, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *Shuttle )( IDeckLinkDeckControl * This, /* [in] */ double rate, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *GetTimecodeString )( IDeckLinkDeckControl * This, /* [out] */ BSTR *currentTimeCode, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkDeckControl * This, /* [out] */ IDeckLinkTimecode **currentTimecode, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *GetTimecodeBCD )( IDeckLinkDeckControl * This, /* [out] */ BMDTimecodeBCD *currentTimecode, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *SetPreroll )( IDeckLinkDeckControl * This, /* [in] */ unsigned long prerollSeconds); HRESULT ( STDMETHODCALLTYPE *GetPreroll )( IDeckLinkDeckControl * This, /* [out] */ unsigned long *prerollSeconds); HRESULT ( STDMETHODCALLTYPE *SetExportOffset )( IDeckLinkDeckControl * This, /* [in] */ long exportOffsetFields); HRESULT ( STDMETHODCALLTYPE *GetExportOffset )( IDeckLinkDeckControl * This, /* [out] */ long *exportOffsetFields); HRESULT ( STDMETHODCALLTYPE *GetManualExportOffset )( IDeckLinkDeckControl * This, /* [out] */ long *deckManualExportOffsetFields); HRESULT ( STDMETHODCALLTYPE *SetCaptureOffset )( IDeckLinkDeckControl * This, /* [in] */ long captureOffsetFields); HRESULT ( STDMETHODCALLTYPE *GetCaptureOffset )( IDeckLinkDeckControl * This, /* [out] */ long *captureOffsetFields); HRESULT ( STDMETHODCALLTYPE *StartExport )( IDeckLinkDeckControl * This, /* [in] */ BMDTimecodeBCD inTimecode, /* [in] */ BMDTimecodeBCD outTimecode, /* [in] */ BMDDeckControlExportModeOpsFlags exportModeOps, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *StartCapture )( IDeckLinkDeckControl * This, /* [in] */ BOOL useVITC, /* [in] */ BMDTimecodeBCD inTimecode, /* [in] */ BMDTimecodeBCD outTimecode, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *GetDeviceID )( IDeckLinkDeckControl * This, /* [out] */ unsigned short *deviceId, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *Abort )( IDeckLinkDeckControl * This); HRESULT ( STDMETHODCALLTYPE *CrashRecordStart )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *CrashRecordStop )( IDeckLinkDeckControl * This, /* [out] */ BMDDeckControlError *error); HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkDeckControl * This, /* [in] */ IDeckLinkDeckControlStatusCallback *callback); END_INTERFACE } IDeckLinkDeckControlVtbl; interface IDeckLinkDeckControl { CONST_VTBL struct IDeckLinkDeckControlVtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDeckControl_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDeckControl_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDeckControl_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDeckControl_Open(This,timeScale,timeValue,timecodeIsDropFrame,error) \ ( (This)->lpVtbl -> Open(This,timeScale,timeValue,timecodeIsDropFrame,error) ) #define IDeckLinkDeckControl_Close(This,standbyOn) \ ( (This)->lpVtbl -> Close(This,standbyOn) ) #define IDeckLinkDeckControl_GetCurrentState(This,mode,vtrControlState,flags) \ ( (This)->lpVtbl -> GetCurrentState(This,mode,vtrControlState,flags) ) #define IDeckLinkDeckControl_SetStandby(This,standbyOn) \ ( (This)->lpVtbl -> SetStandby(This,standbyOn) ) #define IDeckLinkDeckControl_Play(This,error) \ ( (This)->lpVtbl -> Play(This,error) ) #define IDeckLinkDeckControl_Stop(This,error) \ ( (This)->lpVtbl -> Stop(This,error) ) #define IDeckLinkDeckControl_TogglePlayStop(This,error) \ ( (This)->lpVtbl -> TogglePlayStop(This,error) ) #define IDeckLinkDeckControl_Eject(This,error) \ ( (This)->lpVtbl -> Eject(This,error) ) #define IDeckLinkDeckControl_GoToTimecode(This,timecode,error) \ ( (This)->lpVtbl -> GoToTimecode(This,timecode,error) ) #define IDeckLinkDeckControl_FastForward(This,viewTape,error) \ ( (This)->lpVtbl -> FastForward(This,viewTape,error) ) #define IDeckLinkDeckControl_Rewind(This,viewTape,error) \ ( (This)->lpVtbl -> Rewind(This,viewTape,error) ) #define IDeckLinkDeckControl_StepForward(This,error) \ ( (This)->lpVtbl -> StepForward(This,error) ) #define IDeckLinkDeckControl_StepBack(This,error) \ ( (This)->lpVtbl -> StepBack(This,error) ) #define IDeckLinkDeckControl_Jog(This,rate,error) \ ( (This)->lpVtbl -> Jog(This,rate,error) ) #define IDeckLinkDeckControl_Shuttle(This,rate,error) \ ( (This)->lpVtbl -> Shuttle(This,rate,error) ) #define IDeckLinkDeckControl_GetTimecodeString(This,currentTimeCode,error) \ ( (This)->lpVtbl -> GetTimecodeString(This,currentTimeCode,error) ) #define IDeckLinkDeckControl_GetTimecode(This,currentTimecode,error) \ ( (This)->lpVtbl -> GetTimecode(This,currentTimecode,error) ) #define IDeckLinkDeckControl_GetTimecodeBCD(This,currentTimecode,error) \ ( (This)->lpVtbl -> GetTimecodeBCD(This,currentTimecode,error) ) #define IDeckLinkDeckControl_SetPreroll(This,prerollSeconds) \ ( (This)->lpVtbl -> SetPreroll(This,prerollSeconds) ) #define IDeckLinkDeckControl_GetPreroll(This,prerollSeconds) \ ( (This)->lpVtbl -> GetPreroll(This,prerollSeconds) ) #define IDeckLinkDeckControl_SetExportOffset(This,exportOffsetFields) \ ( (This)->lpVtbl -> SetExportOffset(This,exportOffsetFields) ) #define IDeckLinkDeckControl_GetExportOffset(This,exportOffsetFields) \ ( (This)->lpVtbl -> GetExportOffset(This,exportOffsetFields) ) #define IDeckLinkDeckControl_GetManualExportOffset(This,deckManualExportOffsetFields) \ ( (This)->lpVtbl -> GetManualExportOffset(This,deckManualExportOffsetFields) ) #define IDeckLinkDeckControl_SetCaptureOffset(This,captureOffsetFields) \ ( (This)->lpVtbl -> SetCaptureOffset(This,captureOffsetFields) ) #define IDeckLinkDeckControl_GetCaptureOffset(This,captureOffsetFields) \ ( (This)->lpVtbl -> GetCaptureOffset(This,captureOffsetFields) ) #define IDeckLinkDeckControl_StartExport(This,inTimecode,outTimecode,exportModeOps,error) \ ( (This)->lpVtbl -> StartExport(This,inTimecode,outTimecode,exportModeOps,error) ) #define IDeckLinkDeckControl_StartCapture(This,useVITC,inTimecode,outTimecode,error) \ ( (This)->lpVtbl -> StartCapture(This,useVITC,inTimecode,outTimecode,error) ) #define IDeckLinkDeckControl_GetDeviceID(This,deviceId,error) \ ( (This)->lpVtbl -> GetDeviceID(This,deviceId,error) ) #define IDeckLinkDeckControl_Abort(This) \ ( (This)->lpVtbl -> Abort(This) ) #define IDeckLinkDeckControl_CrashRecordStart(This,error) \ ( (This)->lpVtbl -> CrashRecordStart(This,error) ) #define IDeckLinkDeckControl_CrashRecordStop(This,error) \ ( (This)->lpVtbl -> CrashRecordStop(This,error) ) #define IDeckLinkDeckControl_SetCallback(This,callback) \ ( (This)->lpVtbl -> SetCallback(This,callback) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDeckControl_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_CDeckLinkIterator; #ifdef __cplusplus class DECLSPEC_UUID("D9EDA3B3-2887-41FA-B724-017CF1EB1D37") CDeckLinkIterator; #endif EXTERN_C const CLSID CLSID_CDeckLinkGLScreenPreviewHelper; #ifdef __cplusplus class DECLSPEC_UUID("F63E77C7-B655-4A4A-9AD0-3CA85D394343") CDeckLinkGLScreenPreviewHelper; #endif EXTERN_C const CLSID CLSID_CDeckLinkVideoConversion; #ifdef __cplusplus class DECLSPEC_UUID("7DBBBB11-5B7B-467D-AEA4-CEA468FD368C") CDeckLinkVideoConversion; #endif #ifndef __IDeckLinkDisplayModeIterator_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkDisplayModeIterator_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkDisplayModeIterator_v7_6 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDisplayModeIterator_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("455D741F-1779-4800-86F5-0B5D13D79751") IDeckLinkDisplayModeIterator_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLinkDisplayMode_v7_6 **deckLinkDisplayMode) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDisplayModeIterator_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDisplayModeIterator_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDisplayModeIterator_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDisplayModeIterator_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkDisplayModeIterator_v7_6 * This, /* [out] */ IDeckLinkDisplayMode_v7_6 **deckLinkDisplayMode); END_INTERFACE } IDeckLinkDisplayModeIterator_v7_6Vtbl; interface IDeckLinkDisplayModeIterator_v7_6 { CONST_VTBL struct IDeckLinkDisplayModeIterator_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDisplayModeIterator_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDisplayModeIterator_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDisplayModeIterator_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDisplayModeIterator_v7_6_Next(This,deckLinkDisplayMode) \ ( (This)->lpVtbl -> Next(This,deckLinkDisplayMode) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDisplayModeIterator_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDisplayMode_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkDisplayMode_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkDisplayMode_v7_6 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDisplayMode_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("87451E84-2B7E-439E-A629-4393EA4A8550") IDeckLinkDisplayMode_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetName( /* [out] */ BSTR *name) = 0; virtual BMDDisplayMode STDMETHODCALLTYPE GetDisplayMode( void) = 0; virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameRate( /* [out] */ BMDTimeValue *frameDuration, /* [out] */ BMDTimeScale *timeScale) = 0; virtual BMDFieldDominance STDMETHODCALLTYPE GetFieldDominance( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDisplayMode_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDisplayMode_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDisplayMode_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDisplayMode_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetName )( IDeckLinkDisplayMode_v7_6 * This, /* [out] */ BSTR *name); BMDDisplayMode ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkDisplayMode_v7_6 * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkDisplayMode_v7_6 * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkDisplayMode_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetFrameRate )( IDeckLinkDisplayMode_v7_6 * This, /* [out] */ BMDTimeValue *frameDuration, /* [out] */ BMDTimeScale *timeScale); BMDFieldDominance ( STDMETHODCALLTYPE *GetFieldDominance )( IDeckLinkDisplayMode_v7_6 * This); END_INTERFACE } IDeckLinkDisplayMode_v7_6Vtbl; interface IDeckLinkDisplayMode_v7_6 { CONST_VTBL struct IDeckLinkDisplayMode_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDisplayMode_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDisplayMode_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDisplayMode_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDisplayMode_v7_6_GetName(This,name) \ ( (This)->lpVtbl -> GetName(This,name) ) #define IDeckLinkDisplayMode_v7_6_GetDisplayMode(This) \ ( (This)->lpVtbl -> GetDisplayMode(This) ) #define IDeckLinkDisplayMode_v7_6_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkDisplayMode_v7_6_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkDisplayMode_v7_6_GetFrameRate(This,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetFrameRate(This,frameDuration,timeScale) ) #define IDeckLinkDisplayMode_v7_6_GetFieldDominance(This) \ ( (This)->lpVtbl -> GetFieldDominance(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDisplayMode_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkOutput_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkOutput_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkOutput_v7_6 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkOutput_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("29228142-EB8C-4141-A621-F74026450955") IDeckLinkOutput_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator_v7_6 **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v7_6 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput( BMDDisplayMode displayMode, BMDVideoOutputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame( long width, long height, long rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v7_6 **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateAncillaryData( BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync( /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame( /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback( /* [in] */ IDeckLinkVideoOutputCallback_v7_6 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedVideoFrameCount( /* [out] */ unsigned long *bufferedFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput( BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount, BMDAudioOutputStreamType streamType) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync( /* [in] */ void *buffer, unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples( /* [in] */ void *buffer, unsigned long sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, /* [out] */ unsigned long *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned long *bufferedSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioCallback( /* [in] */ IDeckLinkAudioOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback( BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback( BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE IsScheduledPlaybackRunning( /* [out] */ BOOL *active) = 0; virtual HRESULT STDMETHODCALLTYPE GetScheduledStreamTime( BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkOutput_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkOutput_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkOutput_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkOutput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkOutput_v7_6 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result); HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkOutput_v7_6 * This, /* [out] */ IDeckLinkDisplayModeIterator_v7_6 **iterator); HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkOutput_v7_6 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v7_6 *previewCallback); HRESULT ( STDMETHODCALLTYPE *EnableVideoOutput )( IDeckLinkOutput_v7_6 * This, BMDDisplayMode displayMode, BMDVideoOutputFlags flags); HRESULT ( STDMETHODCALLTYPE *DisableVideoOutput )( IDeckLinkOutput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *SetVideoOutputFrameMemoryAllocator )( IDeckLinkOutput_v7_6 * This, /* [in] */ IDeckLinkMemoryAllocator *theAllocator); HRESULT ( STDMETHODCALLTYPE *CreateVideoFrame )( IDeckLinkOutput_v7_6 * This, long width, long height, long rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v7_6 **outFrame); HRESULT ( STDMETHODCALLTYPE *CreateAncillaryData )( IDeckLinkOutput_v7_6 * This, BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer); HRESULT ( STDMETHODCALLTYPE *DisplayVideoFrameSync )( IDeckLinkOutput_v7_6 * This, /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame); HRESULT ( STDMETHODCALLTYPE *ScheduleVideoFrame )( IDeckLinkOutput_v7_6 * This, /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *SetScheduledFrameCompletionCallback )( IDeckLinkOutput_v7_6 * This, /* [in] */ IDeckLinkVideoOutputCallback_v7_6 *theCallback); HRESULT ( STDMETHODCALLTYPE *GetBufferedVideoFrameCount )( IDeckLinkOutput_v7_6 * This, /* [out] */ unsigned long *bufferedFrameCount); HRESULT ( STDMETHODCALLTYPE *EnableAudioOutput )( IDeckLinkOutput_v7_6 * This, BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount, BMDAudioOutputStreamType streamType); HRESULT ( STDMETHODCALLTYPE *DisableAudioOutput )( IDeckLinkOutput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *WriteAudioSamplesSync )( IDeckLinkOutput_v7_6 * This, /* [in] */ void *buffer, unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesWritten); HRESULT ( STDMETHODCALLTYPE *BeginAudioPreroll )( IDeckLinkOutput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *EndAudioPreroll )( IDeckLinkOutput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *ScheduleAudioSamples )( IDeckLinkOutput_v7_6 * This, /* [in] */ void *buffer, unsigned long sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, /* [out] */ unsigned long *sampleFramesWritten); HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkOutput_v7_6 * This, /* [out] */ unsigned long *bufferedSampleFrameCount); HRESULT ( STDMETHODCALLTYPE *FlushBufferedAudioSamples )( IDeckLinkOutput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *SetAudioCallback )( IDeckLinkOutput_v7_6 * This, /* [in] */ IDeckLinkAudioOutputCallback *theCallback); HRESULT ( STDMETHODCALLTYPE *StartScheduledPlayback )( IDeckLinkOutput_v7_6 * This, BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed); HRESULT ( STDMETHODCALLTYPE *StopScheduledPlayback )( IDeckLinkOutput_v7_6 * This, BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *IsScheduledPlaybackRunning )( IDeckLinkOutput_v7_6 * This, /* [out] */ BOOL *active); HRESULT ( STDMETHODCALLTYPE *GetScheduledStreamTime )( IDeckLinkOutput_v7_6 * This, BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *streamTime, /* [out] */ double *playbackSpeed); HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkOutput_v7_6 * This, BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkOutput_v7_6Vtbl; interface IDeckLinkOutput_v7_6 { CONST_VTBL struct IDeckLinkOutput_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkOutput_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkOutput_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkOutput_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkOutput_v7_6_DoesSupportVideoMode(This,displayMode,pixelFormat,result) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,result) ) #define IDeckLinkOutput_v7_6_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkOutput_v7_6_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkOutput_v7_6_EnableVideoOutput(This,displayMode,flags) \ ( (This)->lpVtbl -> EnableVideoOutput(This,displayMode,flags) ) #define IDeckLinkOutput_v7_6_DisableVideoOutput(This) \ ( (This)->lpVtbl -> DisableVideoOutput(This) ) #define IDeckLinkOutput_v7_6_SetVideoOutputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoOutputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkOutput_v7_6_CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_v7_6_CreateAncillaryData(This,pixelFormat,outBuffer) \ ( (This)->lpVtbl -> CreateAncillaryData(This,pixelFormat,outBuffer) ) #define IDeckLinkOutput_v7_6_DisplayVideoFrameSync(This,theFrame) \ ( (This)->lpVtbl -> DisplayVideoFrameSync(This,theFrame) ) #define IDeckLinkOutput_v7_6_ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) \ ( (This)->lpVtbl -> ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) ) #define IDeckLinkOutput_v7_6_SetScheduledFrameCompletionCallback(This,theCallback) \ ( (This)->lpVtbl -> SetScheduledFrameCompletionCallback(This,theCallback) ) #define IDeckLinkOutput_v7_6_GetBufferedVideoFrameCount(This,bufferedFrameCount) \ ( (This)->lpVtbl -> GetBufferedVideoFrameCount(This,bufferedFrameCount) ) #define IDeckLinkOutput_v7_6_EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) \ ( (This)->lpVtbl -> EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) ) #define IDeckLinkOutput_v7_6_DisableAudioOutput(This) \ ( (This)->lpVtbl -> DisableAudioOutput(This) ) #define IDeckLinkOutput_v7_6_WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) \ ( (This)->lpVtbl -> WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) ) #define IDeckLinkOutput_v7_6_BeginAudioPreroll(This) \ ( (This)->lpVtbl -> BeginAudioPreroll(This) ) #define IDeckLinkOutput_v7_6_EndAudioPreroll(This) \ ( (This)->lpVtbl -> EndAudioPreroll(This) ) #define IDeckLinkOutput_v7_6_ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) \ ( (This)->lpVtbl -> ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) ) #define IDeckLinkOutput_v7_6_GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) ) #define IDeckLinkOutput_v7_6_FlushBufferedAudioSamples(This) \ ( (This)->lpVtbl -> FlushBufferedAudioSamples(This) ) #define IDeckLinkOutput_v7_6_SetAudioCallback(This,theCallback) \ ( (This)->lpVtbl -> SetAudioCallback(This,theCallback) ) #define IDeckLinkOutput_v7_6_StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) \ ( (This)->lpVtbl -> StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) ) #define IDeckLinkOutput_v7_6_StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) \ ( (This)->lpVtbl -> StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) ) #define IDeckLinkOutput_v7_6_IsScheduledPlaybackRunning(This,active) \ ( (This)->lpVtbl -> IsScheduledPlaybackRunning(This,active) ) #define IDeckLinkOutput_v7_6_GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) \ ( (This)->lpVtbl -> GetScheduledStreamTime(This,desiredTimeScale,streamTime,playbackSpeed) ) #define IDeckLinkOutput_v7_6_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkOutput_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkInput_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkInput_v7_6 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("300C135A-9F43-48E2-9906-6D7911D93CF1") IDeckLinkInput_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator_v7_6 **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback_v7_6 *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoFrameCount( /* [out] */ unsigned long *availableFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned long *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback_v7_6 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInput_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput_v7_6 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result); HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput_v7_6 * This, /* [out] */ IDeckLinkDisplayModeIterator_v7_6 **iterator); HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkInput_v7_6 * This, /* [in] */ IDeckLinkScreenPreviewCallback_v7_6 *previewCallback); HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput_v7_6 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags); HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetAvailableVideoFrameCount )( IDeckLinkInput_v7_6 * This, /* [out] */ unsigned long *availableFrameCount); HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput_v7_6 * This, BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount); HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkInput_v7_6 * This, /* [out] */ unsigned long *availableSampleFrameCount); HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkInput_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput_v7_6 * This, /* [in] */ IDeckLinkInputCallback_v7_6 *theCallback); HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkInput_v7_6 * This, BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *hardwareTime, /* [out] */ BMDTimeValue *timeInFrame, /* [out] */ BMDTimeValue *ticksPerFrame); END_INTERFACE } IDeckLinkInput_v7_6Vtbl; interface IDeckLinkInput_v7_6 { CONST_VTBL struct IDeckLinkInput_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_v7_6_DoesSupportVideoMode(This,displayMode,pixelFormat,result) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,result) ) #define IDeckLinkInput_v7_6_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_v7_6_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkInput_v7_6_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_v7_6_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_v7_6_GetAvailableVideoFrameCount(This,availableFrameCount) \ ( (This)->lpVtbl -> GetAvailableVideoFrameCount(This,availableFrameCount) ) #define IDeckLinkInput_v7_6_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_v7_6_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_v7_6_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkInput_v7_6_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_v7_6_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_v7_6_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_v7_6_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkInput_v7_6_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #define IDeckLinkInput_v7_6_GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,hardwareTime,timeInFrame,ticksPerFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkTimecode_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkTimecode_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkTimecode_v7_6 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkTimecode_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("EFB9BCA6-A521-44F7-BD69-2332F24D9EE6") IDeckLinkTimecode_v7_6 : public IUnknown { public: virtual BMDTimecodeBCD STDMETHODCALLTYPE GetBCD( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetComponents( /* [out] */ unsigned char *hours, /* [out] */ unsigned char *minutes, /* [out] */ unsigned char *seconds, /* [out] */ unsigned char *frames) = 0; virtual HRESULT STDMETHODCALLTYPE GetString( /* [out] */ BSTR *timecode) = 0; virtual BMDTimecodeFlags STDMETHODCALLTYPE GetFlags( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkTimecode_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkTimecode_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkTimecode_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkTimecode_v7_6 * This); BMDTimecodeBCD ( STDMETHODCALLTYPE *GetBCD )( IDeckLinkTimecode_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetComponents )( IDeckLinkTimecode_v7_6 * This, /* [out] */ unsigned char *hours, /* [out] */ unsigned char *minutes, /* [out] */ unsigned char *seconds, /* [out] */ unsigned char *frames); HRESULT ( STDMETHODCALLTYPE *GetString )( IDeckLinkTimecode_v7_6 * This, /* [out] */ BSTR *timecode); BMDTimecodeFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkTimecode_v7_6 * This); END_INTERFACE } IDeckLinkTimecode_v7_6Vtbl; interface IDeckLinkTimecode_v7_6 { CONST_VTBL struct IDeckLinkTimecode_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkTimecode_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkTimecode_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkTimecode_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkTimecode_v7_6_GetBCD(This) \ ( (This)->lpVtbl -> GetBCD(This) ) #define IDeckLinkTimecode_v7_6_GetComponents(This,hours,minutes,seconds,frames) \ ( (This)->lpVtbl -> GetComponents(This,hours,minutes,seconds,frames) ) #define IDeckLinkTimecode_v7_6_GetString(This,timecode) \ ( (This)->lpVtbl -> GetString(This,timecode) ) #define IDeckLinkTimecode_v7_6_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkTimecode_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrame_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrame_v7_6 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrame_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("A8D8238E-6B18-4196-99E1-5AF717B83D32") IDeckLinkVideoFrame_v7_6 : public IUnknown { public: virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual long STDMETHODCALLTYPE GetRowBytes( void) = 0; virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat( void) = 0; virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( /* [out] */ void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetTimecode( BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode_v7_6 **timecode) = 0; virtual HRESULT STDMETHODCALLTYPE GetAncillaryData( /* [out] */ IDeckLinkVideoFrameAncillary **ancillary) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrame_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrame_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrame_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoFrame_v7_6 * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoFrame_v7_6 * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoFrame_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoFrame_v7_6 * This, /* [out] */ void **buffer); HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoFrame_v7_6 * This, BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode_v7_6 **timecode); HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoFrame_v7_6 * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); END_INTERFACE } IDeckLinkVideoFrame_v7_6Vtbl; interface IDeckLinkVideoFrame_v7_6 { CONST_VTBL struct IDeckLinkVideoFrame_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrame_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrame_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrame_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrame_v7_6_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoFrame_v7_6_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoFrame_v7_6_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoFrame_v7_6_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoFrame_v7_6_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoFrame_v7_6_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoFrame_v7_6_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoFrame_v7_6_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrame_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkMutableVideoFrame_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkMutableVideoFrame_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkMutableVideoFrame_v7_6 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkMutableVideoFrame_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("46FCEE00-B4E6-43D0-91C0-023A7FCEB34F") IDeckLinkMutableVideoFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT STDMETHODCALLTYPE SetFlags( BMDFrameFlags newFlags) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecode( BMDTimecodeFormat format, /* [in] */ IDeckLinkTimecode_v7_6 *timecode) = 0; virtual HRESULT STDMETHODCALLTYPE SetTimecodeFromComponents( BMDTimecodeFormat format, unsigned char hours, unsigned char minutes, unsigned char seconds, unsigned char frames, BMDTimecodeFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE SetAncillaryData( /* [in] */ IDeckLinkVideoFrameAncillary *ancillary) = 0; }; #else /* C style interface */ typedef struct IDeckLinkMutableVideoFrame_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkMutableVideoFrame_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkMutableVideoFrame_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkMutableVideoFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkMutableVideoFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkMutableVideoFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkMutableVideoFrame_v7_6 * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkMutableVideoFrame_v7_6 * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkMutableVideoFrame_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkMutableVideoFrame_v7_6 * This, /* [out] */ void **buffer); HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkMutableVideoFrame_v7_6 * This, BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode_v7_6 **timecode); HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkMutableVideoFrame_v7_6 * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); HRESULT ( STDMETHODCALLTYPE *SetFlags )( IDeckLinkMutableVideoFrame_v7_6 * This, BMDFrameFlags newFlags); HRESULT ( STDMETHODCALLTYPE *SetTimecode )( IDeckLinkMutableVideoFrame_v7_6 * This, BMDTimecodeFormat format, /* [in] */ IDeckLinkTimecode_v7_6 *timecode); HRESULT ( STDMETHODCALLTYPE *SetTimecodeFromComponents )( IDeckLinkMutableVideoFrame_v7_6 * This, BMDTimecodeFormat format, unsigned char hours, unsigned char minutes, unsigned char seconds, unsigned char frames, BMDTimecodeFlags flags); HRESULT ( STDMETHODCALLTYPE *SetAncillaryData )( IDeckLinkMutableVideoFrame_v7_6 * This, /* [in] */ IDeckLinkVideoFrameAncillary *ancillary); END_INTERFACE } IDeckLinkMutableVideoFrame_v7_6Vtbl; interface IDeckLinkMutableVideoFrame_v7_6 { CONST_VTBL struct IDeckLinkMutableVideoFrame_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkMutableVideoFrame_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkMutableVideoFrame_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkMutableVideoFrame_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkMutableVideoFrame_v7_6_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkMutableVideoFrame_v7_6_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkMutableVideoFrame_v7_6_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkMutableVideoFrame_v7_6_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkMutableVideoFrame_v7_6_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkMutableVideoFrame_v7_6_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkMutableVideoFrame_v7_6_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkMutableVideoFrame_v7_6_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkMutableVideoFrame_v7_6_SetFlags(This,newFlags) \ ( (This)->lpVtbl -> SetFlags(This,newFlags) ) #define IDeckLinkMutableVideoFrame_v7_6_SetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> SetTimecode(This,format,timecode) ) #define IDeckLinkMutableVideoFrame_v7_6_SetTimecodeFromComponents(This,format,hours,minutes,seconds,frames,flags) \ ( (This)->lpVtbl -> SetTimecodeFromComponents(This,format,hours,minutes,seconds,frames,flags) ) #define IDeckLinkMutableVideoFrame_v7_6_SetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> SetAncillaryData(This,ancillary) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkMutableVideoFrame_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkVideoInputFrame_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkVideoInputFrame_v7_6 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoInputFrame_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("9A74FA41-AE9F-47AC-8CF4-01F42DD59965") IDeckLinkVideoInputFrame_v7_6 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT STDMETHODCALLTYPE GetStreamTime( /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceTimestamp( BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoInputFrame_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoInputFrame_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoInputFrame_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoInputFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoInputFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoInputFrame_v7_6 * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoInputFrame_v7_6 * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoInputFrame_v7_6 * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoInputFrame_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoInputFrame_v7_6 * This, /* [out] */ void **buffer); HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoInputFrame_v7_6 * This, BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode_v7_6 **timecode); HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoInputFrame_v7_6 * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkVideoInputFrame_v7_6 * This, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceTimestamp )( IDeckLinkVideoInputFrame_v7_6 * This, BMDTimeScale timeScale, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration); END_INTERFACE } IDeckLinkVideoInputFrame_v7_6Vtbl; interface IDeckLinkVideoInputFrame_v7_6 { CONST_VTBL struct IDeckLinkVideoInputFrame_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoInputFrame_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoInputFrame_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoInputFrame_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoInputFrame_v7_6_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoInputFrame_v7_6_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoInputFrame_v7_6_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoInputFrame_v7_6_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoInputFrame_v7_6_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoInputFrame_v7_6_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoInputFrame_v7_6_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoInputFrame_v7_6_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkVideoInputFrame_v7_6_GetStreamTime(This,frameTime,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,frameDuration,timeScale) ) #define IDeckLinkVideoInputFrame_v7_6_GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) \ ( (This)->lpVtbl -> GetHardwareReferenceTimestamp(This,timeScale,frameTime,frameDuration) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoInputFrame_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkScreenPreviewCallback_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkScreenPreviewCallback_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkScreenPreviewCallback_v7_6 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkScreenPreviewCallback_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("373F499D-4B4D-4518-AD22-6354E5A5825E") IDeckLinkScreenPreviewCallback_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DrawFrame( /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkScreenPreviewCallback_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkScreenPreviewCallback_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkScreenPreviewCallback_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkScreenPreviewCallback_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *DrawFrame )( IDeckLinkScreenPreviewCallback_v7_6 * This, /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame); END_INTERFACE } IDeckLinkScreenPreviewCallback_v7_6Vtbl; interface IDeckLinkScreenPreviewCallback_v7_6 { CONST_VTBL struct IDeckLinkScreenPreviewCallback_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkScreenPreviewCallback_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkScreenPreviewCallback_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkScreenPreviewCallback_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkScreenPreviewCallback_v7_6_DrawFrame(This,theFrame) \ ( (This)->lpVtbl -> DrawFrame(This,theFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkScreenPreviewCallback_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkGLScreenPreviewHelper_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkGLScreenPreviewHelper_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkGLScreenPreviewHelper_v7_6 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkGLScreenPreviewHelper_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("BA575CD9-A15E-497B-B2C2-F9AFE7BE4EBA") IDeckLinkGLScreenPreviewHelper_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE InitializeGL( void) = 0; virtual HRESULT STDMETHODCALLTYPE PaintGL( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetFrame( /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkGLScreenPreviewHelper_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkGLScreenPreviewHelper_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkGLScreenPreviewHelper_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkGLScreenPreviewHelper_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *InitializeGL )( IDeckLinkGLScreenPreviewHelper_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *PaintGL )( IDeckLinkGLScreenPreviewHelper_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *SetFrame )( IDeckLinkGLScreenPreviewHelper_v7_6 * This, /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame); END_INTERFACE } IDeckLinkGLScreenPreviewHelper_v7_6Vtbl; interface IDeckLinkGLScreenPreviewHelper_v7_6 { CONST_VTBL struct IDeckLinkGLScreenPreviewHelper_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkGLScreenPreviewHelper_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkGLScreenPreviewHelper_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkGLScreenPreviewHelper_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkGLScreenPreviewHelper_v7_6_InitializeGL(This) \ ( (This)->lpVtbl -> InitializeGL(This) ) #define IDeckLinkGLScreenPreviewHelper_v7_6_PaintGL(This) \ ( (This)->lpVtbl -> PaintGL(This) ) #define IDeckLinkGLScreenPreviewHelper_v7_6_SetFrame(This,theFrame) \ ( (This)->lpVtbl -> SetFrame(This,theFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkGLScreenPreviewHelper_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoConversion_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkVideoConversion_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkVideoConversion_v7_6 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoConversion_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("3EB504C9-F97D-40FE-A158-D407D48CB53B") IDeckLinkVideoConversion_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ConvertFrame( /* [in] */ IDeckLinkVideoFrame_v7_6 *srcFrame, /* [in] */ IDeckLinkVideoFrame_v7_6 *dstFrame) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoConversion_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoConversion_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoConversion_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoConversion_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *ConvertFrame )( IDeckLinkVideoConversion_v7_6 * This, /* [in] */ IDeckLinkVideoFrame_v7_6 *srcFrame, /* [in] */ IDeckLinkVideoFrame_v7_6 *dstFrame); END_INTERFACE } IDeckLinkVideoConversion_v7_6Vtbl; interface IDeckLinkVideoConversion_v7_6 { CONST_VTBL struct IDeckLinkVideoConversion_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoConversion_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoConversion_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoConversion_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoConversion_v7_6_ConvertFrame(This,srcFrame,dstFrame) \ ( (This)->lpVtbl -> ConvertFrame(This,srcFrame,dstFrame) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoConversion_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkConfiguration_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkConfiguration_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkConfiguration_v7_6 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkConfiguration_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("B8EAD569-B764-47F0-A73F-AE40DF6CBF10") IDeckLinkConfiguration_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetConfigurationValidator( /* [out] */ IDeckLinkConfiguration_v7_6 **configObject) = 0; virtual HRESULT STDMETHODCALLTYPE WriteConfigurationToPreferences( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFormat( /* [in] */ BMDVideoConnection_v7_6 videoOutputConnection) = 0; virtual HRESULT STDMETHODCALLTYPE IsVideoOutputActive( /* [in] */ BMDVideoConnection_v7_6 videoOutputConnection, /* [out] */ BOOL *active) = 0; virtual HRESULT STDMETHODCALLTYPE SetAnalogVideoOutputFlags( /* [in] */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT STDMETHODCALLTYPE GetAnalogVideoOutputFlags( /* [out] */ BMDAnalogVideoFlags *analogVideoFlags) = 0; virtual HRESULT STDMETHODCALLTYPE EnableFieldFlickerRemovalWhenPaused( /* [in] */ BOOL enable) = 0; virtual HRESULT STDMETHODCALLTYPE IsEnabledFieldFlickerRemovalWhenPaused( /* [out] */ BOOL *enabled) = 0; virtual HRESULT STDMETHODCALLTYPE Set444And3GBpsVideoOutput( /* [in] */ BOOL enable444VideoOutput, /* [in] */ BOOL enable3GbsOutput) = 0; virtual HRESULT STDMETHODCALLTYPE Get444And3GBpsVideoOutput( /* [out] */ BOOL *is444VideoOutputEnabled, /* [out] */ BOOL *threeGbsOutputEnabled) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputConversionMode( /* [in] */ BMDVideoOutputConversionMode conversionMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetVideoOutputConversionMode( /* [out] */ BMDVideoOutputConversionMode *conversionMode) = 0; virtual HRESULT STDMETHODCALLTYPE Set_HD1080p24_to_HD1080i5994_Conversion( /* [in] */ BOOL enable) = 0; virtual HRESULT STDMETHODCALLTYPE Get_HD1080p24_to_HD1080i5994_Conversion( /* [out] */ BOOL *enabled) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoInputFormat( /* [in] */ BMDVideoConnection_v7_6 videoInputFormat) = 0; virtual HRESULT STDMETHODCALLTYPE GetVideoInputFormat( /* [out] */ BMDVideoConnection_v7_6 *videoInputFormat) = 0; virtual HRESULT STDMETHODCALLTYPE SetAnalogVideoInputFlags( /* [in] */ BMDAnalogVideoFlags analogVideoFlags) = 0; virtual HRESULT STDMETHODCALLTYPE GetAnalogVideoInputFlags( /* [out] */ BMDAnalogVideoFlags *analogVideoFlags) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoInputConversionMode( /* [in] */ BMDVideoInputConversionMode conversionMode) = 0; virtual HRESULT STDMETHODCALLTYPE GetVideoInputConversionMode( /* [out] */ BMDVideoInputConversionMode *conversionMode) = 0; virtual HRESULT STDMETHODCALLTYPE SetBlackVideoOutputDuringCapture( /* [in] */ BOOL blackOutInCapture) = 0; virtual HRESULT STDMETHODCALLTYPE GetBlackVideoOutputDuringCapture( /* [out] */ BOOL *blackOutInCapture) = 0; virtual HRESULT STDMETHODCALLTYPE Set32PulldownSequenceInitialTimecodeFrame( /* [in] */ unsigned long aFrameTimecode) = 0; virtual HRESULT STDMETHODCALLTYPE Get32PulldownSequenceInitialTimecodeFrame( /* [out] */ unsigned long *aFrameTimecode) = 0; virtual HRESULT STDMETHODCALLTYPE SetVancSourceLineMapping( /* [in] */ unsigned long activeLine1VANCsource, /* [in] */ unsigned long activeLine2VANCsource, /* [in] */ unsigned long activeLine3VANCsource) = 0; virtual HRESULT STDMETHODCALLTYPE GetVancSourceLineMapping( /* [out] */ unsigned long *activeLine1VANCsource, /* [out] */ unsigned long *activeLine2VANCsource, /* [out] */ unsigned long *activeLine3VANCsource) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioInputFormat( /* [in] */ BMDAudioConnection audioInputFormat) = 0; virtual HRESULT STDMETHODCALLTYPE GetAudioInputFormat( /* [out] */ BMDAudioConnection *audioInputFormat) = 0; }; #else /* C style interface */ typedef struct IDeckLinkConfiguration_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkConfiguration_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkConfiguration_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *GetConfigurationValidator )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ IDeckLinkConfiguration_v7_6 **configObject); HRESULT ( STDMETHODCALLTYPE *WriteConfigurationToPreferences )( IDeckLinkConfiguration_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *SetVideoOutputFormat )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BMDVideoConnection_v7_6 videoOutputConnection); HRESULT ( STDMETHODCALLTYPE *IsVideoOutputActive )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BMDVideoConnection_v7_6 videoOutputConnection, /* [out] */ BOOL *active); HRESULT ( STDMETHODCALLTYPE *SetAnalogVideoOutputFlags )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BMDAnalogVideoFlags analogVideoFlags); HRESULT ( STDMETHODCALLTYPE *GetAnalogVideoOutputFlags )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BMDAnalogVideoFlags *analogVideoFlags); HRESULT ( STDMETHODCALLTYPE *EnableFieldFlickerRemovalWhenPaused )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BOOL enable); HRESULT ( STDMETHODCALLTYPE *IsEnabledFieldFlickerRemovalWhenPaused )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BOOL *enabled); HRESULT ( STDMETHODCALLTYPE *Set444And3GBpsVideoOutput )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BOOL enable444VideoOutput, /* [in] */ BOOL enable3GbsOutput); HRESULT ( STDMETHODCALLTYPE *Get444And3GBpsVideoOutput )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BOOL *is444VideoOutputEnabled, /* [out] */ BOOL *threeGbsOutputEnabled); HRESULT ( STDMETHODCALLTYPE *SetVideoOutputConversionMode )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BMDVideoOutputConversionMode conversionMode); HRESULT ( STDMETHODCALLTYPE *GetVideoOutputConversionMode )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BMDVideoOutputConversionMode *conversionMode); HRESULT ( STDMETHODCALLTYPE *Set_HD1080p24_to_HD1080i5994_Conversion )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BOOL enable); HRESULT ( STDMETHODCALLTYPE *Get_HD1080p24_to_HD1080i5994_Conversion )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BOOL *enabled); HRESULT ( STDMETHODCALLTYPE *SetVideoInputFormat )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BMDVideoConnection_v7_6 videoInputFormat); HRESULT ( STDMETHODCALLTYPE *GetVideoInputFormat )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BMDVideoConnection_v7_6 *videoInputFormat); HRESULT ( STDMETHODCALLTYPE *SetAnalogVideoInputFlags )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BMDAnalogVideoFlags analogVideoFlags); HRESULT ( STDMETHODCALLTYPE *GetAnalogVideoInputFlags )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BMDAnalogVideoFlags *analogVideoFlags); HRESULT ( STDMETHODCALLTYPE *SetVideoInputConversionMode )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BMDVideoInputConversionMode conversionMode); HRESULT ( STDMETHODCALLTYPE *GetVideoInputConversionMode )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BMDVideoInputConversionMode *conversionMode); HRESULT ( STDMETHODCALLTYPE *SetBlackVideoOutputDuringCapture )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BOOL blackOutInCapture); HRESULT ( STDMETHODCALLTYPE *GetBlackVideoOutputDuringCapture )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BOOL *blackOutInCapture); HRESULT ( STDMETHODCALLTYPE *Set32PulldownSequenceInitialTimecodeFrame )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ unsigned long aFrameTimecode); HRESULT ( STDMETHODCALLTYPE *Get32PulldownSequenceInitialTimecodeFrame )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ unsigned long *aFrameTimecode); HRESULT ( STDMETHODCALLTYPE *SetVancSourceLineMapping )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ unsigned long activeLine1VANCsource, /* [in] */ unsigned long activeLine2VANCsource, /* [in] */ unsigned long activeLine3VANCsource); HRESULT ( STDMETHODCALLTYPE *GetVancSourceLineMapping )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ unsigned long *activeLine1VANCsource, /* [out] */ unsigned long *activeLine2VANCsource, /* [out] */ unsigned long *activeLine3VANCsource); HRESULT ( STDMETHODCALLTYPE *SetAudioInputFormat )( IDeckLinkConfiguration_v7_6 * This, /* [in] */ BMDAudioConnection audioInputFormat); HRESULT ( STDMETHODCALLTYPE *GetAudioInputFormat )( IDeckLinkConfiguration_v7_6 * This, /* [out] */ BMDAudioConnection *audioInputFormat); END_INTERFACE } IDeckLinkConfiguration_v7_6Vtbl; interface IDeckLinkConfiguration_v7_6 { CONST_VTBL struct IDeckLinkConfiguration_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkConfiguration_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkConfiguration_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkConfiguration_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkConfiguration_v7_6_GetConfigurationValidator(This,configObject) \ ( (This)->lpVtbl -> GetConfigurationValidator(This,configObject) ) #define IDeckLinkConfiguration_v7_6_WriteConfigurationToPreferences(This) \ ( (This)->lpVtbl -> WriteConfigurationToPreferences(This) ) #define IDeckLinkConfiguration_v7_6_SetVideoOutputFormat(This,videoOutputConnection) \ ( (This)->lpVtbl -> SetVideoOutputFormat(This,videoOutputConnection) ) #define IDeckLinkConfiguration_v7_6_IsVideoOutputActive(This,videoOutputConnection,active) \ ( (This)->lpVtbl -> IsVideoOutputActive(This,videoOutputConnection,active) ) #define IDeckLinkConfiguration_v7_6_SetAnalogVideoOutputFlags(This,analogVideoFlags) \ ( (This)->lpVtbl -> SetAnalogVideoOutputFlags(This,analogVideoFlags) ) #define IDeckLinkConfiguration_v7_6_GetAnalogVideoOutputFlags(This,analogVideoFlags) \ ( (This)->lpVtbl -> GetAnalogVideoOutputFlags(This,analogVideoFlags) ) #define IDeckLinkConfiguration_v7_6_EnableFieldFlickerRemovalWhenPaused(This,enable) \ ( (This)->lpVtbl -> EnableFieldFlickerRemovalWhenPaused(This,enable) ) #define IDeckLinkConfiguration_v7_6_IsEnabledFieldFlickerRemovalWhenPaused(This,enabled) \ ( (This)->lpVtbl -> IsEnabledFieldFlickerRemovalWhenPaused(This,enabled) ) #define IDeckLinkConfiguration_v7_6_Set444And3GBpsVideoOutput(This,enable444VideoOutput,enable3GbsOutput) \ ( (This)->lpVtbl -> Set444And3GBpsVideoOutput(This,enable444VideoOutput,enable3GbsOutput) ) #define IDeckLinkConfiguration_v7_6_Get444And3GBpsVideoOutput(This,is444VideoOutputEnabled,threeGbsOutputEnabled) \ ( (This)->lpVtbl -> Get444And3GBpsVideoOutput(This,is444VideoOutputEnabled,threeGbsOutputEnabled) ) #define IDeckLinkConfiguration_v7_6_SetVideoOutputConversionMode(This,conversionMode) \ ( (This)->lpVtbl -> SetVideoOutputConversionMode(This,conversionMode) ) #define IDeckLinkConfiguration_v7_6_GetVideoOutputConversionMode(This,conversionMode) \ ( (This)->lpVtbl -> GetVideoOutputConversionMode(This,conversionMode) ) #define IDeckLinkConfiguration_v7_6_Set_HD1080p24_to_HD1080i5994_Conversion(This,enable) \ ( (This)->lpVtbl -> Set_HD1080p24_to_HD1080i5994_Conversion(This,enable) ) #define IDeckLinkConfiguration_v7_6_Get_HD1080p24_to_HD1080i5994_Conversion(This,enabled) \ ( (This)->lpVtbl -> Get_HD1080p24_to_HD1080i5994_Conversion(This,enabled) ) #define IDeckLinkConfiguration_v7_6_SetVideoInputFormat(This,videoInputFormat) \ ( (This)->lpVtbl -> SetVideoInputFormat(This,videoInputFormat) ) #define IDeckLinkConfiguration_v7_6_GetVideoInputFormat(This,videoInputFormat) \ ( (This)->lpVtbl -> GetVideoInputFormat(This,videoInputFormat) ) #define IDeckLinkConfiguration_v7_6_SetAnalogVideoInputFlags(This,analogVideoFlags) \ ( (This)->lpVtbl -> SetAnalogVideoInputFlags(This,analogVideoFlags) ) #define IDeckLinkConfiguration_v7_6_GetAnalogVideoInputFlags(This,analogVideoFlags) \ ( (This)->lpVtbl -> GetAnalogVideoInputFlags(This,analogVideoFlags) ) #define IDeckLinkConfiguration_v7_6_SetVideoInputConversionMode(This,conversionMode) \ ( (This)->lpVtbl -> SetVideoInputConversionMode(This,conversionMode) ) #define IDeckLinkConfiguration_v7_6_GetVideoInputConversionMode(This,conversionMode) \ ( (This)->lpVtbl -> GetVideoInputConversionMode(This,conversionMode) ) #define IDeckLinkConfiguration_v7_6_SetBlackVideoOutputDuringCapture(This,blackOutInCapture) \ ( (This)->lpVtbl -> SetBlackVideoOutputDuringCapture(This,blackOutInCapture) ) #define IDeckLinkConfiguration_v7_6_GetBlackVideoOutputDuringCapture(This,blackOutInCapture) \ ( (This)->lpVtbl -> GetBlackVideoOutputDuringCapture(This,blackOutInCapture) ) #define IDeckLinkConfiguration_v7_6_Set32PulldownSequenceInitialTimecodeFrame(This,aFrameTimecode) \ ( (This)->lpVtbl -> Set32PulldownSequenceInitialTimecodeFrame(This,aFrameTimecode) ) #define IDeckLinkConfiguration_v7_6_Get32PulldownSequenceInitialTimecodeFrame(This,aFrameTimecode) \ ( (This)->lpVtbl -> Get32PulldownSequenceInitialTimecodeFrame(This,aFrameTimecode) ) #define IDeckLinkConfiguration_v7_6_SetVancSourceLineMapping(This,activeLine1VANCsource,activeLine2VANCsource,activeLine3VANCsource) \ ( (This)->lpVtbl -> SetVancSourceLineMapping(This,activeLine1VANCsource,activeLine2VANCsource,activeLine3VANCsource) ) #define IDeckLinkConfiguration_v7_6_GetVancSourceLineMapping(This,activeLine1VANCsource,activeLine2VANCsource,activeLine3VANCsource) \ ( (This)->lpVtbl -> GetVancSourceLineMapping(This,activeLine1VANCsource,activeLine2VANCsource,activeLine3VANCsource) ) #define IDeckLinkConfiguration_v7_6_SetAudioInputFormat(This,audioInputFormat) \ ( (This)->lpVtbl -> SetAudioInputFormat(This,audioInputFormat) ) #define IDeckLinkConfiguration_v7_6_GetAudioInputFormat(This,audioInputFormat) \ ( (This)->lpVtbl -> GetAudioInputFormat(This,audioInputFormat) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkConfiguration_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoOutputCallback_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkVideoOutputCallback_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkVideoOutputCallback_v7_6 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoOutputCallback_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("E763A626-4A3C-49D1-BF13-E7AD3692AE52") IDeckLinkVideoOutputCallback_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( /* [in] */ IDeckLinkVideoFrame_v7_6 *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped( void) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoOutputCallback_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoOutputCallback_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoOutputCallback_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoOutputCallback_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *ScheduledFrameCompleted )( IDeckLinkVideoOutputCallback_v7_6 * This, /* [in] */ IDeckLinkVideoFrame_v7_6 *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result); HRESULT ( STDMETHODCALLTYPE *ScheduledPlaybackHasStopped )( IDeckLinkVideoOutputCallback_v7_6 * This); END_INTERFACE } IDeckLinkVideoOutputCallback_v7_6Vtbl; interface IDeckLinkVideoOutputCallback_v7_6 { CONST_VTBL struct IDeckLinkVideoOutputCallback_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoOutputCallback_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoOutputCallback_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoOutputCallback_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoOutputCallback_v7_6_ScheduledFrameCompleted(This,completedFrame,result) \ ( (This)->lpVtbl -> ScheduledFrameCompleted(This,completedFrame,result) ) #define IDeckLinkVideoOutputCallback_v7_6_ScheduledPlaybackHasStopped(This) \ ( (This)->lpVtbl -> ScheduledPlaybackHasStopped(This) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoOutputCallback_v7_6_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInputCallback_v7_6_INTERFACE_DEFINED__ #define __IDeckLinkInputCallback_v7_6_INTERFACE_DEFINED__ /* interface IDeckLinkInputCallback_v7_6 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInputCallback_v7_6; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("31D28EE7-88B6-4CB1-897A-CDBF79A26414") IDeckLinkInputCallback_v7_6 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged( /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode_v7_6 *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( /* [in] */ IDeckLinkVideoInputFrame_v7_6 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputCallback_v7_6Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInputCallback_v7_6 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInputCallback_v7_6 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInputCallback_v7_6 * This); HRESULT ( STDMETHODCALLTYPE *VideoInputFormatChanged )( IDeckLinkInputCallback_v7_6 * This, /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode_v7_6 *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags); HRESULT ( STDMETHODCALLTYPE *VideoInputFrameArrived )( IDeckLinkInputCallback_v7_6 * This, /* [in] */ IDeckLinkVideoInputFrame_v7_6 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket); END_INTERFACE } IDeckLinkInputCallback_v7_6Vtbl; interface IDeckLinkInputCallback_v7_6 { CONST_VTBL struct IDeckLinkInputCallback_v7_6Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInputCallback_v7_6_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInputCallback_v7_6_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInputCallback_v7_6_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInputCallback_v7_6_VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) \ ( (This)->lpVtbl -> VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) ) #define IDeckLinkInputCallback_v7_6_VideoInputFrameArrived(This,videoFrame,audioPacket) \ ( (This)->lpVtbl -> VideoInputFrameArrived(This,videoFrame,audioPacket) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInputCallback_v7_6_INTERFACE_DEFINED__ */ EXTERN_C const CLSID CLSID_CDeckLinkGLScreenPreviewHelper_v7_6; #ifdef __cplusplus class DECLSPEC_UUID("D398CEE7-4434-4CA3-9BA6-5AE34556B905") CDeckLinkGLScreenPreviewHelper_v7_6; #endif EXTERN_C const CLSID CLSID_CDeckLinkVideoConversion_v7_6; #ifdef __cplusplus class DECLSPEC_UUID("FFA84F77-73BE-4FB7-B03E-B5E44B9F759B") CDeckLinkVideoConversion_v7_6; #endif #ifndef __IDeckLinkInputCallback_v7_3_INTERFACE_DEFINED__ #define __IDeckLinkInputCallback_v7_3_INTERFACE_DEFINED__ /* interface IDeckLinkInputCallback_v7_3 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInputCallback_v7_3; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("FD6F311D-4D00-444B-9ED4-1F25B5730AD0") IDeckLinkInputCallback_v7_3 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged( /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode_v7_6 *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0; virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( /* [in] */ IDeckLinkVideoInputFrame_v7_3 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputCallback_v7_3Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInputCallback_v7_3 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInputCallback_v7_3 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInputCallback_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *VideoInputFormatChanged )( IDeckLinkInputCallback_v7_3 * This, /* [in] */ BMDVideoInputFormatChangedEvents notificationEvents, /* [in] */ IDeckLinkDisplayMode_v7_6 *newDisplayMode, /* [in] */ BMDDetectedVideoInputFormatFlags detectedSignalFlags); HRESULT ( STDMETHODCALLTYPE *VideoInputFrameArrived )( IDeckLinkInputCallback_v7_3 * This, /* [in] */ IDeckLinkVideoInputFrame_v7_3 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket *audioPacket); END_INTERFACE } IDeckLinkInputCallback_v7_3Vtbl; interface IDeckLinkInputCallback_v7_3 { CONST_VTBL struct IDeckLinkInputCallback_v7_3Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInputCallback_v7_3_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInputCallback_v7_3_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInputCallback_v7_3_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInputCallback_v7_3_VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) \ ( (This)->lpVtbl -> VideoInputFormatChanged(This,notificationEvents,newDisplayMode,detectedSignalFlags) ) #define IDeckLinkInputCallback_v7_3_VideoInputFrameArrived(This,videoFrame,audioPacket) \ ( (This)->lpVtbl -> VideoInputFrameArrived(This,videoFrame,audioPacket) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInputCallback_v7_3_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkOutput_v7_3_INTERFACE_DEFINED__ #define __IDeckLinkOutput_v7_3_INTERFACE_DEFINED__ /* interface IDeckLinkOutput_v7_3 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkOutput_v7_3; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("271C65E3-C323-4344-A30F-D908BCB20AA3") IDeckLinkOutput_v7_3 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator_v7_6 **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput( BMDDisplayMode displayMode, BMDVideoOutputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame( long width, long height, long rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v7_6 **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateAncillaryData( BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer) = 0; virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync( /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame( /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback( /* [in] */ IDeckLinkVideoOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedVideoFrameCount( /* [out] */ unsigned long *bufferedFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput( BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount, BMDAudioOutputStreamType streamType) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync( /* [in] */ void *buffer, unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples( /* [in] */ void *buffer, unsigned long sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, /* [out] */ unsigned long *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned long *bufferedSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioCallback( /* [in] */ IDeckLinkAudioOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback( BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback( BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE IsScheduledPlaybackRunning( /* [out] */ BOOL *active) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *elapsedTimeSinceSchedulerBegan) = 0; }; #else /* C style interface */ typedef struct IDeckLinkOutput_v7_3Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkOutput_v7_3 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkOutput_v7_3 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkOutput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkOutput_v7_3 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result); HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkOutput_v7_3 * This, /* [out] */ IDeckLinkDisplayModeIterator_v7_6 **iterator); HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkOutput_v7_3 * This, /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback); HRESULT ( STDMETHODCALLTYPE *EnableVideoOutput )( IDeckLinkOutput_v7_3 * This, BMDDisplayMode displayMode, BMDVideoOutputFlags flags); HRESULT ( STDMETHODCALLTYPE *DisableVideoOutput )( IDeckLinkOutput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *SetVideoOutputFrameMemoryAllocator )( IDeckLinkOutput_v7_3 * This, /* [in] */ IDeckLinkMemoryAllocator *theAllocator); HRESULT ( STDMETHODCALLTYPE *CreateVideoFrame )( IDeckLinkOutput_v7_3 * This, long width, long height, long rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, /* [out] */ IDeckLinkMutableVideoFrame_v7_6 **outFrame); HRESULT ( STDMETHODCALLTYPE *CreateAncillaryData )( IDeckLinkOutput_v7_3 * This, BMDPixelFormat pixelFormat, /* [out] */ IDeckLinkVideoFrameAncillary **outBuffer); HRESULT ( STDMETHODCALLTYPE *DisplayVideoFrameSync )( IDeckLinkOutput_v7_3 * This, /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame); HRESULT ( STDMETHODCALLTYPE *ScheduleVideoFrame )( IDeckLinkOutput_v7_3 * This, /* [in] */ IDeckLinkVideoFrame_v7_6 *theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *SetScheduledFrameCompletionCallback )( IDeckLinkOutput_v7_3 * This, /* [in] */ IDeckLinkVideoOutputCallback *theCallback); HRESULT ( STDMETHODCALLTYPE *GetBufferedVideoFrameCount )( IDeckLinkOutput_v7_3 * This, /* [out] */ unsigned long *bufferedFrameCount); HRESULT ( STDMETHODCALLTYPE *EnableAudioOutput )( IDeckLinkOutput_v7_3 * This, BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount, BMDAudioOutputStreamType streamType); HRESULT ( STDMETHODCALLTYPE *DisableAudioOutput )( IDeckLinkOutput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *WriteAudioSamplesSync )( IDeckLinkOutput_v7_3 * This, /* [in] */ void *buffer, unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesWritten); HRESULT ( STDMETHODCALLTYPE *BeginAudioPreroll )( IDeckLinkOutput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *EndAudioPreroll )( IDeckLinkOutput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *ScheduleAudioSamples )( IDeckLinkOutput_v7_3 * This, /* [in] */ void *buffer, unsigned long sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, /* [out] */ unsigned long *sampleFramesWritten); HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkOutput_v7_3 * This, /* [out] */ unsigned long *bufferedSampleFrameCount); HRESULT ( STDMETHODCALLTYPE *FlushBufferedAudioSamples )( IDeckLinkOutput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *SetAudioCallback )( IDeckLinkOutput_v7_3 * This, /* [in] */ IDeckLinkAudioOutputCallback *theCallback); HRESULT ( STDMETHODCALLTYPE *StartScheduledPlayback )( IDeckLinkOutput_v7_3 * This, BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed); HRESULT ( STDMETHODCALLTYPE *StopScheduledPlayback )( IDeckLinkOutput_v7_3 * This, BMDTimeValue stopPlaybackAtTime, /* [out] */ BMDTimeValue *actualStopTime, BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *IsScheduledPlaybackRunning )( IDeckLinkOutput_v7_3 * This, /* [out] */ BOOL *active); HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkOutput_v7_3 * This, BMDTimeScale desiredTimeScale, /* [out] */ BMDTimeValue *elapsedTimeSinceSchedulerBegan); END_INTERFACE } IDeckLinkOutput_v7_3Vtbl; interface IDeckLinkOutput_v7_3 { CONST_VTBL struct IDeckLinkOutput_v7_3Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkOutput_v7_3_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkOutput_v7_3_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkOutput_v7_3_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkOutput_v7_3_DoesSupportVideoMode(This,displayMode,pixelFormat,result) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,result) ) #define IDeckLinkOutput_v7_3_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkOutput_v7_3_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkOutput_v7_3_EnableVideoOutput(This,displayMode,flags) \ ( (This)->lpVtbl -> EnableVideoOutput(This,displayMode,flags) ) #define IDeckLinkOutput_v7_3_DisableVideoOutput(This) \ ( (This)->lpVtbl -> DisableVideoOutput(This) ) #define IDeckLinkOutput_v7_3_SetVideoOutputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoOutputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkOutput_v7_3_CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_v7_3_CreateAncillaryData(This,pixelFormat,outBuffer) \ ( (This)->lpVtbl -> CreateAncillaryData(This,pixelFormat,outBuffer) ) #define IDeckLinkOutput_v7_3_DisplayVideoFrameSync(This,theFrame) \ ( (This)->lpVtbl -> DisplayVideoFrameSync(This,theFrame) ) #define IDeckLinkOutput_v7_3_ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) \ ( (This)->lpVtbl -> ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) ) #define IDeckLinkOutput_v7_3_SetScheduledFrameCompletionCallback(This,theCallback) \ ( (This)->lpVtbl -> SetScheduledFrameCompletionCallback(This,theCallback) ) #define IDeckLinkOutput_v7_3_GetBufferedVideoFrameCount(This,bufferedFrameCount) \ ( (This)->lpVtbl -> GetBufferedVideoFrameCount(This,bufferedFrameCount) ) #define IDeckLinkOutput_v7_3_EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) \ ( (This)->lpVtbl -> EnableAudioOutput(This,sampleRate,sampleType,channelCount,streamType) ) #define IDeckLinkOutput_v7_3_DisableAudioOutput(This) \ ( (This)->lpVtbl -> DisableAudioOutput(This) ) #define IDeckLinkOutput_v7_3_WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) \ ( (This)->lpVtbl -> WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) ) #define IDeckLinkOutput_v7_3_BeginAudioPreroll(This) \ ( (This)->lpVtbl -> BeginAudioPreroll(This) ) #define IDeckLinkOutput_v7_3_EndAudioPreroll(This) \ ( (This)->lpVtbl -> EndAudioPreroll(This) ) #define IDeckLinkOutput_v7_3_ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) \ ( (This)->lpVtbl -> ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) ) #define IDeckLinkOutput_v7_3_GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleFrameCount) ) #define IDeckLinkOutput_v7_3_FlushBufferedAudioSamples(This) \ ( (This)->lpVtbl -> FlushBufferedAudioSamples(This) ) #define IDeckLinkOutput_v7_3_SetAudioCallback(This,theCallback) \ ( (This)->lpVtbl -> SetAudioCallback(This,theCallback) ) #define IDeckLinkOutput_v7_3_StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) \ ( (This)->lpVtbl -> StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) ) #define IDeckLinkOutput_v7_3_StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) \ ( (This)->lpVtbl -> StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) ) #define IDeckLinkOutput_v7_3_IsScheduledPlaybackRunning(This,active) \ ( (This)->lpVtbl -> IsScheduledPlaybackRunning(This,active) ) #define IDeckLinkOutput_v7_3_GetHardwareReferenceClock(This,desiredTimeScale,elapsedTimeSinceSchedulerBegan) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,elapsedTimeSinceSchedulerBegan) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkOutput_v7_3_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_v7_3_INTERFACE_DEFINED__ #define __IDeckLinkInput_v7_3_INTERFACE_DEFINED__ /* interface IDeckLinkInput_v7_3 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput_v7_3; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("4973F012-9925-458C-871C-18774CDBBECB") IDeckLinkInput_v7_3 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator_v7_6 **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE SetScreenPreviewCallback( /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoFrameCount( /* [out] */ unsigned long *availableFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetAvailableAudioSampleFrameCount( /* [out] */ unsigned long *availableSampleFrameCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE FlushStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback_v7_3 *theCallback) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInput_v7_3Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput_v7_3 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput_v7_3 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput_v7_3 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result); HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput_v7_3 * This, /* [out] */ IDeckLinkDisplayModeIterator_v7_6 **iterator); HRESULT ( STDMETHODCALLTYPE *SetScreenPreviewCallback )( IDeckLinkInput_v7_3 * This, /* [in] */ IDeckLinkScreenPreviewCallback *previewCallback); HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput_v7_3 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags); HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *GetAvailableVideoFrameCount )( IDeckLinkInput_v7_3 * This, /* [out] */ unsigned long *availableFrameCount); HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput_v7_3 * This, BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount); HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *GetAvailableAudioSampleFrameCount )( IDeckLinkInput_v7_3 * This, /* [out] */ unsigned long *availableSampleFrameCount); HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *FlushStreams )( IDeckLinkInput_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput_v7_3 * This, /* [in] */ IDeckLinkInputCallback_v7_3 *theCallback); END_INTERFACE } IDeckLinkInput_v7_3Vtbl; interface IDeckLinkInput_v7_3 { CONST_VTBL struct IDeckLinkInput_v7_3Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_v7_3_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_v7_3_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_v7_3_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_v7_3_DoesSupportVideoMode(This,displayMode,pixelFormat,result) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,result) ) #define IDeckLinkInput_v7_3_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_v7_3_SetScreenPreviewCallback(This,previewCallback) \ ( (This)->lpVtbl -> SetScreenPreviewCallback(This,previewCallback) ) #define IDeckLinkInput_v7_3_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_v7_3_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_v7_3_GetAvailableVideoFrameCount(This,availableFrameCount) \ ( (This)->lpVtbl -> GetAvailableVideoFrameCount(This,availableFrameCount) ) #define IDeckLinkInput_v7_3_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_v7_3_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_v7_3_GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) \ ( (This)->lpVtbl -> GetAvailableAudioSampleFrameCount(This,availableSampleFrameCount) ) #define IDeckLinkInput_v7_3_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_v7_3_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_v7_3_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_v7_3_FlushStreams(This) \ ( (This)->lpVtbl -> FlushStreams(This) ) #define IDeckLinkInput_v7_3_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_v7_3_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_v7_3_INTERFACE_DEFINED__ #define __IDeckLinkVideoInputFrame_v7_3_INTERFACE_DEFINED__ /* interface IDeckLinkVideoInputFrame_v7_3 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoInputFrame_v7_3; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("CF317790-2894-11DE-8C30-0800200C9A66") IDeckLinkVideoInputFrame_v7_3 : public IDeckLinkVideoFrame_v7_6 { public: virtual HRESULT STDMETHODCALLTYPE GetStreamTime( /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoInputFrame_v7_3Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoInputFrame_v7_3 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoInputFrame_v7_3 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoInputFrame_v7_3 * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoInputFrame_v7_3 * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoInputFrame_v7_3 * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoInputFrame_v7_3 * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoInputFrame_v7_3 * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoInputFrame_v7_3 * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoInputFrame_v7_3 * This, /* [out] */ void **buffer); HRESULT ( STDMETHODCALLTYPE *GetTimecode )( IDeckLinkVideoInputFrame_v7_3 * This, BMDTimecodeFormat format, /* [out] */ IDeckLinkTimecode_v7_6 **timecode); HRESULT ( STDMETHODCALLTYPE *GetAncillaryData )( IDeckLinkVideoInputFrame_v7_3 * This, /* [out] */ IDeckLinkVideoFrameAncillary **ancillary); HRESULT ( STDMETHODCALLTYPE *GetStreamTime )( IDeckLinkVideoInputFrame_v7_3 * This, /* [out] */ BMDTimeValue *frameTime, /* [out] */ BMDTimeValue *frameDuration, BMDTimeScale timeScale); END_INTERFACE } IDeckLinkVideoInputFrame_v7_3Vtbl; interface IDeckLinkVideoInputFrame_v7_3 { CONST_VTBL struct IDeckLinkVideoInputFrame_v7_3Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoInputFrame_v7_3_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoInputFrame_v7_3_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoInputFrame_v7_3_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoInputFrame_v7_3_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoInputFrame_v7_3_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoInputFrame_v7_3_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoInputFrame_v7_3_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoInputFrame_v7_3_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoInputFrame_v7_3_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoInputFrame_v7_3_GetTimecode(This,format,timecode) \ ( (This)->lpVtbl -> GetTimecode(This,format,timecode) ) #define IDeckLinkVideoInputFrame_v7_3_GetAncillaryData(This,ancillary) \ ( (This)->lpVtbl -> GetAncillaryData(This,ancillary) ) #define IDeckLinkVideoInputFrame_v7_3_GetStreamTime(This,frameTime,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetStreamTime(This,frameTime,frameDuration,timeScale) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoInputFrame_v7_3_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDisplayModeIterator_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkDisplayModeIterator_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkDisplayModeIterator_v7_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDisplayModeIterator_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("B28131B6-59AC-4857-B5AC-CD75D5883E2F") IDeckLinkDisplayModeIterator_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Next( /* [out] */ IDeckLinkDisplayMode_v7_1 **deckLinkDisplayMode) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDisplayModeIterator_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDisplayModeIterator_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDisplayModeIterator_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDisplayModeIterator_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *Next )( IDeckLinkDisplayModeIterator_v7_1 * This, /* [out] */ IDeckLinkDisplayMode_v7_1 **deckLinkDisplayMode); END_INTERFACE } IDeckLinkDisplayModeIterator_v7_1Vtbl; interface IDeckLinkDisplayModeIterator_v7_1 { CONST_VTBL struct IDeckLinkDisplayModeIterator_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDisplayModeIterator_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDisplayModeIterator_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDisplayModeIterator_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDisplayModeIterator_v7_1_Next(This,deckLinkDisplayMode) \ ( (This)->lpVtbl -> Next(This,deckLinkDisplayMode) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDisplayModeIterator_v7_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkDisplayMode_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkDisplayMode_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkDisplayMode_v7_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkDisplayMode_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("AF0CD6D5-8376-435E-8433-54F9DD530AC3") IDeckLinkDisplayMode_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE GetName( /* [out] */ BSTR *name) = 0; virtual BMDDisplayMode STDMETHODCALLTYPE GetDisplayMode( void) = 0; virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetFrameRate( /* [out] */ BMDTimeValue *frameDuration, /* [out] */ BMDTimeScale *timeScale) = 0; }; #else /* C style interface */ typedef struct IDeckLinkDisplayMode_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkDisplayMode_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkDisplayMode_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkDisplayMode_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *GetName )( IDeckLinkDisplayMode_v7_1 * This, /* [out] */ BSTR *name); BMDDisplayMode ( STDMETHODCALLTYPE *GetDisplayMode )( IDeckLinkDisplayMode_v7_1 * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkDisplayMode_v7_1 * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkDisplayMode_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *GetFrameRate )( IDeckLinkDisplayMode_v7_1 * This, /* [out] */ BMDTimeValue *frameDuration, /* [out] */ BMDTimeScale *timeScale); END_INTERFACE } IDeckLinkDisplayMode_v7_1Vtbl; interface IDeckLinkDisplayMode_v7_1 { CONST_VTBL struct IDeckLinkDisplayMode_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkDisplayMode_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkDisplayMode_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkDisplayMode_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkDisplayMode_v7_1_GetName(This,name) \ ( (This)->lpVtbl -> GetName(This,name) ) #define IDeckLinkDisplayMode_v7_1_GetDisplayMode(This) \ ( (This)->lpVtbl -> GetDisplayMode(This) ) #define IDeckLinkDisplayMode_v7_1_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkDisplayMode_v7_1_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkDisplayMode_v7_1_GetFrameRate(This,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetFrameRate(This,frameDuration,timeScale) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkDisplayMode_v7_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoFrame_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkVideoFrame_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkVideoFrame_v7_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoFrame_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("333F3A10-8C2D-43CF-B79D-46560FEEA1CE") IDeckLinkVideoFrame_v7_1 : public IUnknown { public: virtual long STDMETHODCALLTYPE GetWidth( void) = 0; virtual long STDMETHODCALLTYPE GetHeight( void) = 0; virtual long STDMETHODCALLTYPE GetRowBytes( void) = 0; virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat( void) = 0; virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( void **buffer) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoFrame_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoFrame_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoFrame_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoFrame_v7_1 * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoFrame_v7_1 * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoFrame_v7_1 * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoFrame_v7_1 * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoFrame_v7_1 * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoFrame_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoFrame_v7_1 * This, void **buffer); END_INTERFACE } IDeckLinkVideoFrame_v7_1Vtbl; interface IDeckLinkVideoFrame_v7_1 { CONST_VTBL struct IDeckLinkVideoFrame_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoFrame_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoFrame_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoFrame_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoFrame_v7_1_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoFrame_v7_1_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoFrame_v7_1_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoFrame_v7_1_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoFrame_v7_1_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoFrame_v7_1_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoFrame_v7_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoInputFrame_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkVideoInputFrame_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkVideoInputFrame_v7_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoInputFrame_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C8B41D95-8848-40EE-9B37-6E3417FB114B") IDeckLinkVideoInputFrame_v7_1 : public IDeckLinkVideoFrame_v7_1 { public: virtual HRESULT STDMETHODCALLTYPE GetFrameTime( BMDTimeValue *frameTime, BMDTimeValue *frameDuration, BMDTimeScale timeScale) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoInputFrame_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoInputFrame_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoInputFrame_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoInputFrame_v7_1 * This); long ( STDMETHODCALLTYPE *GetWidth )( IDeckLinkVideoInputFrame_v7_1 * This); long ( STDMETHODCALLTYPE *GetHeight )( IDeckLinkVideoInputFrame_v7_1 * This); long ( STDMETHODCALLTYPE *GetRowBytes )( IDeckLinkVideoInputFrame_v7_1 * This); BMDPixelFormat ( STDMETHODCALLTYPE *GetPixelFormat )( IDeckLinkVideoInputFrame_v7_1 * This); BMDFrameFlags ( STDMETHODCALLTYPE *GetFlags )( IDeckLinkVideoInputFrame_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkVideoInputFrame_v7_1 * This, void **buffer); HRESULT ( STDMETHODCALLTYPE *GetFrameTime )( IDeckLinkVideoInputFrame_v7_1 * This, BMDTimeValue *frameTime, BMDTimeValue *frameDuration, BMDTimeScale timeScale); END_INTERFACE } IDeckLinkVideoInputFrame_v7_1Vtbl; interface IDeckLinkVideoInputFrame_v7_1 { CONST_VTBL struct IDeckLinkVideoInputFrame_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoInputFrame_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoInputFrame_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoInputFrame_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoInputFrame_v7_1_GetWidth(This) \ ( (This)->lpVtbl -> GetWidth(This) ) #define IDeckLinkVideoInputFrame_v7_1_GetHeight(This) \ ( (This)->lpVtbl -> GetHeight(This) ) #define IDeckLinkVideoInputFrame_v7_1_GetRowBytes(This) \ ( (This)->lpVtbl -> GetRowBytes(This) ) #define IDeckLinkVideoInputFrame_v7_1_GetPixelFormat(This) \ ( (This)->lpVtbl -> GetPixelFormat(This) ) #define IDeckLinkVideoInputFrame_v7_1_GetFlags(This) \ ( (This)->lpVtbl -> GetFlags(This) ) #define IDeckLinkVideoInputFrame_v7_1_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkVideoInputFrame_v7_1_GetFrameTime(This,frameTime,frameDuration,timeScale) \ ( (This)->lpVtbl -> GetFrameTime(This,frameTime,frameDuration,timeScale) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoInputFrame_v7_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkAudioInputPacket_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkAudioInputPacket_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkAudioInputPacket_v7_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkAudioInputPacket_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("C86DE4F6-A29F-42E3-AB3A-1363E29F0788") IDeckLinkAudioInputPacket_v7_1 : public IUnknown { public: virtual long STDMETHODCALLTYPE GetSampleCount( void) = 0; virtual HRESULT STDMETHODCALLTYPE GetBytes( void **buffer) = 0; virtual HRESULT STDMETHODCALLTYPE GetAudioPacketTime( BMDTimeValue *packetTime, BMDTimeScale timeScale) = 0; }; #else /* C style interface */ typedef struct IDeckLinkAudioInputPacket_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkAudioInputPacket_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkAudioInputPacket_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkAudioInputPacket_v7_1 * This); long ( STDMETHODCALLTYPE *GetSampleCount )( IDeckLinkAudioInputPacket_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *GetBytes )( IDeckLinkAudioInputPacket_v7_1 * This, void **buffer); HRESULT ( STDMETHODCALLTYPE *GetAudioPacketTime )( IDeckLinkAudioInputPacket_v7_1 * This, BMDTimeValue *packetTime, BMDTimeScale timeScale); END_INTERFACE } IDeckLinkAudioInputPacket_v7_1Vtbl; interface IDeckLinkAudioInputPacket_v7_1 { CONST_VTBL struct IDeckLinkAudioInputPacket_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkAudioInputPacket_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkAudioInputPacket_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkAudioInputPacket_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkAudioInputPacket_v7_1_GetSampleCount(This) \ ( (This)->lpVtbl -> GetSampleCount(This) ) #define IDeckLinkAudioInputPacket_v7_1_GetBytes(This,buffer) \ ( (This)->lpVtbl -> GetBytes(This,buffer) ) #define IDeckLinkAudioInputPacket_v7_1_GetAudioPacketTime(This,packetTime,timeScale) \ ( (This)->lpVtbl -> GetAudioPacketTime(This,packetTime,timeScale) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkAudioInputPacket_v7_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkVideoOutputCallback_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkVideoOutputCallback_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkVideoOutputCallback_v7_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkVideoOutputCallback_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("EBD01AFA-E4B0-49C6-A01D-EDB9D1B55FD9") IDeckLinkVideoOutputCallback_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( /* [in] */ IDeckLinkVideoFrame_v7_1 *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result) = 0; }; #else /* C style interface */ typedef struct IDeckLinkVideoOutputCallback_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkVideoOutputCallback_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkVideoOutputCallback_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkVideoOutputCallback_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *ScheduledFrameCompleted )( IDeckLinkVideoOutputCallback_v7_1 * This, /* [in] */ IDeckLinkVideoFrame_v7_1 *completedFrame, /* [in] */ BMDOutputFrameCompletionResult result); END_INTERFACE } IDeckLinkVideoOutputCallback_v7_1Vtbl; interface IDeckLinkVideoOutputCallback_v7_1 { CONST_VTBL struct IDeckLinkVideoOutputCallback_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkVideoOutputCallback_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkVideoOutputCallback_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkVideoOutputCallback_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkVideoOutputCallback_v7_1_ScheduledFrameCompleted(This,completedFrame,result) \ ( (This)->lpVtbl -> ScheduledFrameCompleted(This,completedFrame,result) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkVideoOutputCallback_v7_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInputCallback_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkInputCallback_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkInputCallback_v7_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInputCallback_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("7F94F328-5ED4-4E9F-9729-76A86BDC99CC") IDeckLinkInputCallback_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( /* [in] */ IDeckLinkVideoInputFrame_v7_1 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket_v7_1 *audioPacket) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInputCallback_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInputCallback_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInputCallback_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInputCallback_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *VideoInputFrameArrived )( IDeckLinkInputCallback_v7_1 * This, /* [in] */ IDeckLinkVideoInputFrame_v7_1 *videoFrame, /* [in] */ IDeckLinkAudioInputPacket_v7_1 *audioPacket); END_INTERFACE } IDeckLinkInputCallback_v7_1Vtbl; interface IDeckLinkInputCallback_v7_1 { CONST_VTBL struct IDeckLinkInputCallback_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInputCallback_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInputCallback_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInputCallback_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInputCallback_v7_1_VideoInputFrameArrived(This,videoFrame,audioPacket) \ ( (This)->lpVtbl -> VideoInputFrameArrived(This,videoFrame,audioPacket) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInputCallback_v7_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkOutput_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkOutput_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkOutput_v7_1 */ /* [helpstring][local][uuid][object] */ EXTERN_C const IID IID_IDeckLinkOutput_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("AE5B3E9B-4E1E-4535-B6E8-480FF52F6CE5") IDeckLinkOutput_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator_v7_1 **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoOutput( BMDDisplayMode displayMode) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetVideoOutputFrameMemoryAllocator( /* [in] */ IDeckLinkMemoryAllocator *theAllocator) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrame( long width, long height, long rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1 **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE CreateVideoFrameFromBuffer( void *buffer, long width, long height, long rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1 **outFrame) = 0; virtual HRESULT STDMETHODCALLTYPE DisplayVideoFrameSync( IDeckLinkVideoFrame_v7_1 *theFrame) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleVideoFrame( IDeckLinkVideoFrame_v7_1 *theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE SetScheduledFrameCompletionCallback( /* [in] */ IDeckLinkVideoOutputCallback_v7_1 *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioOutput( BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioOutput( void) = 0; virtual HRESULT STDMETHODCALLTYPE WriteAudioSamplesSync( void *buffer, unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE BeginAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE EndAudioPreroll( void) = 0; virtual HRESULT STDMETHODCALLTYPE ScheduleAudioSamples( void *buffer, unsigned long sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, /* [out] */ unsigned long *sampleFramesWritten) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned long *bufferedSampleCount) = 0; virtual HRESULT STDMETHODCALLTYPE FlushBufferedAudioSamples( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetAudioCallback( /* [in] */ IDeckLinkAudioOutputCallback *theCallback) = 0; virtual HRESULT STDMETHODCALLTYPE StartScheduledPlayback( BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed) = 0; virtual HRESULT STDMETHODCALLTYPE StopScheduledPlayback( BMDTimeValue stopPlaybackAtTime, BMDTimeValue *actualStopTime, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetHardwareReferenceClock( BMDTimeScale desiredTimeScale, BMDTimeValue *elapsedTimeSinceSchedulerBegan) = 0; }; #else /* C style interface */ typedef struct IDeckLinkOutput_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkOutput_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkOutput_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkOutput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkOutput_v7_1 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result); HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkOutput_v7_1 * This, /* [out] */ IDeckLinkDisplayModeIterator_v7_1 **iterator); HRESULT ( STDMETHODCALLTYPE *EnableVideoOutput )( IDeckLinkOutput_v7_1 * This, BMDDisplayMode displayMode); HRESULT ( STDMETHODCALLTYPE *DisableVideoOutput )( IDeckLinkOutput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *SetVideoOutputFrameMemoryAllocator )( IDeckLinkOutput_v7_1 * This, /* [in] */ IDeckLinkMemoryAllocator *theAllocator); HRESULT ( STDMETHODCALLTYPE *CreateVideoFrame )( IDeckLinkOutput_v7_1 * This, long width, long height, long rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1 **outFrame); HRESULT ( STDMETHODCALLTYPE *CreateVideoFrameFromBuffer )( IDeckLinkOutput_v7_1 * This, void *buffer, long width, long height, long rowBytes, BMDPixelFormat pixelFormat, BMDFrameFlags flags, IDeckLinkVideoFrame_v7_1 **outFrame); HRESULT ( STDMETHODCALLTYPE *DisplayVideoFrameSync )( IDeckLinkOutput_v7_1 * This, IDeckLinkVideoFrame_v7_1 *theFrame); HRESULT ( STDMETHODCALLTYPE *ScheduleVideoFrame )( IDeckLinkOutput_v7_1 * This, IDeckLinkVideoFrame_v7_1 *theFrame, BMDTimeValue displayTime, BMDTimeValue displayDuration, BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *SetScheduledFrameCompletionCallback )( IDeckLinkOutput_v7_1 * This, /* [in] */ IDeckLinkVideoOutputCallback_v7_1 *theCallback); HRESULT ( STDMETHODCALLTYPE *EnableAudioOutput )( IDeckLinkOutput_v7_1 * This, BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount); HRESULT ( STDMETHODCALLTYPE *DisableAudioOutput )( IDeckLinkOutput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *WriteAudioSamplesSync )( IDeckLinkOutput_v7_1 * This, void *buffer, unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesWritten); HRESULT ( STDMETHODCALLTYPE *BeginAudioPreroll )( IDeckLinkOutput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *EndAudioPreroll )( IDeckLinkOutput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *ScheduleAudioSamples )( IDeckLinkOutput_v7_1 * This, void *buffer, unsigned long sampleFrameCount, BMDTimeValue streamTime, BMDTimeScale timeScale, /* [out] */ unsigned long *sampleFramesWritten); HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkOutput_v7_1 * This, /* [out] */ unsigned long *bufferedSampleCount); HRESULT ( STDMETHODCALLTYPE *FlushBufferedAudioSamples )( IDeckLinkOutput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *SetAudioCallback )( IDeckLinkOutput_v7_1 * This, /* [in] */ IDeckLinkAudioOutputCallback *theCallback); HRESULT ( STDMETHODCALLTYPE *StartScheduledPlayback )( IDeckLinkOutput_v7_1 * This, BMDTimeValue playbackStartTime, BMDTimeScale timeScale, double playbackSpeed); HRESULT ( STDMETHODCALLTYPE *StopScheduledPlayback )( IDeckLinkOutput_v7_1 * This, BMDTimeValue stopPlaybackAtTime, BMDTimeValue *actualStopTime, BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *GetHardwareReferenceClock )( IDeckLinkOutput_v7_1 * This, BMDTimeScale desiredTimeScale, BMDTimeValue *elapsedTimeSinceSchedulerBegan); END_INTERFACE } IDeckLinkOutput_v7_1Vtbl; interface IDeckLinkOutput_v7_1 { CONST_VTBL struct IDeckLinkOutput_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkOutput_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkOutput_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkOutput_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkOutput_v7_1_DoesSupportVideoMode(This,displayMode,pixelFormat,result) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,result) ) #define IDeckLinkOutput_v7_1_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkOutput_v7_1_EnableVideoOutput(This,displayMode) \ ( (This)->lpVtbl -> EnableVideoOutput(This,displayMode) ) #define IDeckLinkOutput_v7_1_DisableVideoOutput(This) \ ( (This)->lpVtbl -> DisableVideoOutput(This) ) #define IDeckLinkOutput_v7_1_SetVideoOutputFrameMemoryAllocator(This,theAllocator) \ ( (This)->lpVtbl -> SetVideoOutputFrameMemoryAllocator(This,theAllocator) ) #define IDeckLinkOutput_v7_1_CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrame(This,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_v7_1_CreateVideoFrameFromBuffer(This,buffer,width,height,rowBytes,pixelFormat,flags,outFrame) \ ( (This)->lpVtbl -> CreateVideoFrameFromBuffer(This,buffer,width,height,rowBytes,pixelFormat,flags,outFrame) ) #define IDeckLinkOutput_v7_1_DisplayVideoFrameSync(This,theFrame) \ ( (This)->lpVtbl -> DisplayVideoFrameSync(This,theFrame) ) #define IDeckLinkOutput_v7_1_ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) \ ( (This)->lpVtbl -> ScheduleVideoFrame(This,theFrame,displayTime,displayDuration,timeScale) ) #define IDeckLinkOutput_v7_1_SetScheduledFrameCompletionCallback(This,theCallback) \ ( (This)->lpVtbl -> SetScheduledFrameCompletionCallback(This,theCallback) ) #define IDeckLinkOutput_v7_1_EnableAudioOutput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioOutput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkOutput_v7_1_DisableAudioOutput(This) \ ( (This)->lpVtbl -> DisableAudioOutput(This) ) #define IDeckLinkOutput_v7_1_WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) \ ( (This)->lpVtbl -> WriteAudioSamplesSync(This,buffer,sampleFrameCount,sampleFramesWritten) ) #define IDeckLinkOutput_v7_1_BeginAudioPreroll(This) \ ( (This)->lpVtbl -> BeginAudioPreroll(This) ) #define IDeckLinkOutput_v7_1_EndAudioPreroll(This) \ ( (This)->lpVtbl -> EndAudioPreroll(This) ) #define IDeckLinkOutput_v7_1_ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) \ ( (This)->lpVtbl -> ScheduleAudioSamples(This,buffer,sampleFrameCount,streamTime,timeScale,sampleFramesWritten) ) #define IDeckLinkOutput_v7_1_GetBufferedAudioSampleFrameCount(This,bufferedSampleCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleCount) ) #define IDeckLinkOutput_v7_1_FlushBufferedAudioSamples(This) \ ( (This)->lpVtbl -> FlushBufferedAudioSamples(This) ) #define IDeckLinkOutput_v7_1_SetAudioCallback(This,theCallback) \ ( (This)->lpVtbl -> SetAudioCallback(This,theCallback) ) #define IDeckLinkOutput_v7_1_StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) \ ( (This)->lpVtbl -> StartScheduledPlayback(This,playbackStartTime,timeScale,playbackSpeed) ) #define IDeckLinkOutput_v7_1_StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) \ ( (This)->lpVtbl -> StopScheduledPlayback(This,stopPlaybackAtTime,actualStopTime,timeScale) ) #define IDeckLinkOutput_v7_1_GetHardwareReferenceClock(This,desiredTimeScale,elapsedTimeSinceSchedulerBegan) \ ( (This)->lpVtbl -> GetHardwareReferenceClock(This,desiredTimeScale,elapsedTimeSinceSchedulerBegan) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkOutput_v7_1_INTERFACE_DEFINED__ */ #ifndef __IDeckLinkInput_v7_1_INTERFACE_DEFINED__ #define __IDeckLinkInput_v7_1_INTERFACE_DEFINED__ /* interface IDeckLinkInput_v7_1 */ /* [helpstring][uuid][object] */ EXTERN_C const IID IID_IDeckLinkInput_v7_1; #if defined(__cplusplus) && !defined(CINTERFACE) MIDL_INTERFACE("2B54EDEF-5B32-429F-BA11-BB990596EACD") IDeckLinkInput_v7_1 : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE DoesSupportVideoMode( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result) = 0; virtual HRESULT STDMETHODCALLTYPE GetDisplayModeIterator( /* [out] */ IDeckLinkDisplayModeIterator_v7_1 **iterator) = 0; virtual HRESULT STDMETHODCALLTYPE EnableVideoInput( BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags) = 0; virtual HRESULT STDMETHODCALLTYPE DisableVideoInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE EnableAudioInput( BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount) = 0; virtual HRESULT STDMETHODCALLTYPE DisableAudioInput( void) = 0; virtual HRESULT STDMETHODCALLTYPE ReadAudioSamples( void *buffer, unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesRead, /* [out] */ BMDTimeValue *audioPacketTime, BMDTimeScale timeScale) = 0; virtual HRESULT STDMETHODCALLTYPE GetBufferedAudioSampleFrameCount( /* [out] */ unsigned long *bufferedSampleCount) = 0; virtual HRESULT STDMETHODCALLTYPE StartStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE StopStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE PauseStreams( void) = 0; virtual HRESULT STDMETHODCALLTYPE SetCallback( /* [in] */ IDeckLinkInputCallback_v7_1 *theCallback) = 0; }; #else /* C style interface */ typedef struct IDeckLinkInput_v7_1Vtbl { BEGIN_INTERFACE HRESULT ( STDMETHODCALLTYPE *QueryInterface )( IDeckLinkInput_v7_1 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ __RPC__deref_out void **ppvObject); ULONG ( STDMETHODCALLTYPE *AddRef )( IDeckLinkInput_v7_1 * This); ULONG ( STDMETHODCALLTYPE *Release )( IDeckLinkInput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *DoesSupportVideoMode )( IDeckLinkInput_v7_1 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, /* [out] */ BMDDisplayModeSupport *result); HRESULT ( STDMETHODCALLTYPE *GetDisplayModeIterator )( IDeckLinkInput_v7_1 * This, /* [out] */ IDeckLinkDisplayModeIterator_v7_1 **iterator); HRESULT ( STDMETHODCALLTYPE *EnableVideoInput )( IDeckLinkInput_v7_1 * This, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat, BMDVideoInputFlags flags); HRESULT ( STDMETHODCALLTYPE *DisableVideoInput )( IDeckLinkInput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *EnableAudioInput )( IDeckLinkInput_v7_1 * This, BMDAudioSampleRate sampleRate, BMDAudioSampleType sampleType, unsigned long channelCount); HRESULT ( STDMETHODCALLTYPE *DisableAudioInput )( IDeckLinkInput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *ReadAudioSamples )( IDeckLinkInput_v7_1 * This, void *buffer, unsigned long sampleFrameCount, /* [out] */ unsigned long *sampleFramesRead, /* [out] */ BMDTimeValue *audioPacketTime, BMDTimeScale timeScale); HRESULT ( STDMETHODCALLTYPE *GetBufferedAudioSampleFrameCount )( IDeckLinkInput_v7_1 * This, /* [out] */ unsigned long *bufferedSampleCount); HRESULT ( STDMETHODCALLTYPE *StartStreams )( IDeckLinkInput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *StopStreams )( IDeckLinkInput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *PauseStreams )( IDeckLinkInput_v7_1 * This); HRESULT ( STDMETHODCALLTYPE *SetCallback )( IDeckLinkInput_v7_1 * This, /* [in] */ IDeckLinkInputCallback_v7_1 *theCallback); END_INTERFACE } IDeckLinkInput_v7_1Vtbl; interface IDeckLinkInput_v7_1 { CONST_VTBL struct IDeckLinkInput_v7_1Vtbl *lpVtbl; }; #ifdef COBJMACROS #define IDeckLinkInput_v7_1_QueryInterface(This,riid,ppvObject) \ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) #define IDeckLinkInput_v7_1_AddRef(This) \ ( (This)->lpVtbl -> AddRef(This) ) #define IDeckLinkInput_v7_1_Release(This) \ ( (This)->lpVtbl -> Release(This) ) #define IDeckLinkInput_v7_1_DoesSupportVideoMode(This,displayMode,pixelFormat,result) \ ( (This)->lpVtbl -> DoesSupportVideoMode(This,displayMode,pixelFormat,result) ) #define IDeckLinkInput_v7_1_GetDisplayModeIterator(This,iterator) \ ( (This)->lpVtbl -> GetDisplayModeIterator(This,iterator) ) #define IDeckLinkInput_v7_1_EnableVideoInput(This,displayMode,pixelFormat,flags) \ ( (This)->lpVtbl -> EnableVideoInput(This,displayMode,pixelFormat,flags) ) #define IDeckLinkInput_v7_1_DisableVideoInput(This) \ ( (This)->lpVtbl -> DisableVideoInput(This) ) #define IDeckLinkInput_v7_1_EnableAudioInput(This,sampleRate,sampleType,channelCount) \ ( (This)->lpVtbl -> EnableAudioInput(This,sampleRate,sampleType,channelCount) ) #define IDeckLinkInput_v7_1_DisableAudioInput(This) \ ( (This)->lpVtbl -> DisableAudioInput(This) ) #define IDeckLinkInput_v7_1_ReadAudioSamples(This,buffer,sampleFrameCount,sampleFramesRead,audioPacketTime,timeScale) \ ( (This)->lpVtbl -> ReadAudioSamples(This,buffer,sampleFrameCount,sampleFramesRead,audioPacketTime,timeScale) ) #define IDeckLinkInput_v7_1_GetBufferedAudioSampleFrameCount(This,bufferedSampleCount) \ ( (This)->lpVtbl -> GetBufferedAudioSampleFrameCount(This,bufferedSampleCount) ) #define IDeckLinkInput_v7_1_StartStreams(This) \ ( (This)->lpVtbl -> StartStreams(This) ) #define IDeckLinkInput_v7_1_StopStreams(This) \ ( (This)->lpVtbl -> StopStreams(This) ) #define IDeckLinkInput_v7_1_PauseStreams(This) \ ( (This)->lpVtbl -> PauseStreams(This) ) #define IDeckLinkInput_v7_1_SetCallback(This,theCallback) \ ( (This)->lpVtbl -> SetCallback(This,theCallback) ) #endif /* COBJMACROS */ #endif /* C style interface */ #endif /* __IDeckLinkInput_v7_1_INTERFACE_DEFINED__ */ #endif /* __DeckLinkAPI_LIBRARY_DEFINED__ */ /* Additional Prototypes for ALL interfaces */ /* end of Additional Prototypes */ #ifdef __cplusplus } #endif #endif mlt-6.20.0/src/modules/decklink/win/DeckLinkAPI_i.cpp000066400000000000000000000210461362234133600222530ustar00rootroot00000000000000 /* this ALWAYS GENERATED file contains the IIDs and CLSIDs */ /* link this file in with the server and any clients */ /* File created by MIDL compiler version 7.00.0555 */ /* at Tue Sep 07 12:00:00 2010 */ /* Compiler settings for video\DeckLinkAPI.idl: Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555 protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: __declspec(uuid()), __declspec(selectany), __declspec(novtable) DECLSPEC_UUID(), MIDL_INTERFACE() */ /* @@MIDL_FILE_HEADING( ) */ //#pragma warning( disable: 4049 ) /* more than 64k source lines */ #ifdef __cplusplus extern "C"{ #endif #include "DeckLinkAPI_h.h" #include #include #ifdef _MIDL_USE_GUIDDEF_ #ifndef INITGUID #define INITGUID #include #undef INITGUID #else #include #endif #define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) #else // !_MIDL_USE_GUIDDEF_ #ifndef __IID_DEFINED__ #define __IID_DEFINED__ typedef struct _IID { unsigned long x; unsigned short s1; unsigned short s2; unsigned char c[8]; } IID; #endif // __IID_DEFINED__ #ifndef CLSID_DEFINED #define CLSID_DEFINED typedef IID CLSID; #endif // CLSID_DEFINED #define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} #endif // !_MIDL_USE_GUIDDEF_ MIDL_DEFINE_GUID(IID, LIBID_DeckLinkAPI,0xD864517A,0xEDD5,0x466D,0x86,0x7D,0xC8,0x19,0xF1,0xC0,0x52,0xBB); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoOutputCallback,0x20AA5225,0x1958,0x47CB,0x82,0x0B,0x80,0xA8,0xD5,0x21,0xA6,0xEE); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInputCallback,0xDD04E5EC,0x7415,0x42AB,0xAE,0x4A,0xE8,0x0C,0x4D,0xFC,0x04,0x4A); MIDL_DEFINE_GUID(IID, IID_IDeckLinkMemoryAllocator,0xB36EB6E7,0x9D29,0x4AA8,0x92,0xEF,0x84,0x3B,0x87,0xA2,0x89,0xE8); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAudioOutputCallback,0x403C681B,0x7F46,0x4A12,0xB9,0x93,0x2B,0xB1,0x27,0x08,0x4E,0xE6); MIDL_DEFINE_GUID(IID, IID_IDeckLinkIterator,0x74E936FC,0xCC28,0x4A67,0x81,0xA0,0x1E,0x94,0xE5,0x2D,0x4E,0x69); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAPIInformation,0x7BEA3C68,0x730D,0x4322,0xAF,0x34,0x8A,0x71,0x52,0xB5,0x32,0xA4); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDisplayModeIterator,0x9C88499F,0xF601,0x4021,0xB8,0x0B,0x03,0x2E,0x4E,0xB4,0x1C,0x35); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDisplayMode,0x3EB2C1AB,0x0A3D,0x4523,0xA3,0xAD,0xF4,0x0D,0x7F,0xB1,0x4E,0x78); MIDL_DEFINE_GUID(IID, IID_IDeckLink,0x62BFF75D,0x6569,0x4E55,0x8D,0x4D,0x66,0xAA,0x03,0x82,0x9A,0xBC); MIDL_DEFINE_GUID(IID, IID_IDeckLinkOutput,0xA3EF0963,0x0862,0x44ED,0x92,0xA9,0xEE,0x89,0xAB,0xF4,0x31,0xC7); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput,0x6D40EF78,0x28B9,0x4E21,0x99,0x0D,0x95,0xBB,0x77,0x50,0xA0,0x4F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkTimecode,0xBC6CFBD3,0x8317,0x4325,0xAC,0x1C,0x12,0x16,0x39,0x1E,0x93,0x40); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrame,0x3F716FE0,0xF023,0x4111,0xBE,0x5D,0xEF,0x44,0x14,0xC0,0x5B,0x17); MIDL_DEFINE_GUID(IID, IID_IDeckLinkMutableVideoFrame,0x69E2639F,0x40DA,0x4E19,0xB6,0xF2,0x20,0xAC,0xE8,0x15,0xC3,0x90); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrame3DExtensions,0xDA0F7E4A,0xEDC7,0x48A8,0x9C,0xDD,0x2D,0xB5,0x1C,0x72,0x9C,0xD7); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoInputFrame,0x05CFE374,0x537C,0x4094,0x9A,0x57,0x68,0x05,0x25,0x11,0x8F,0x44); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrameAncillary,0x732E723C,0xD1A4,0x4E29,0x9E,0x8E,0x4A,0x88,0x79,0x7A,0x00,0x04); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAudioInputPacket,0xE43D5870,0x2894,0x11DE,0x8C,0x30,0x08,0x00,0x20,0x0C,0x9A,0x66); MIDL_DEFINE_GUID(IID, IID_IDeckLinkScreenPreviewCallback,0xB1D3F49A,0x85FE,0x4C5D,0x95,0xC8,0x0B,0x5D,0x5D,0xCC,0xD4,0x38); MIDL_DEFINE_GUID(IID, IID_IDeckLinkGLScreenPreviewHelper,0x504E2209,0xCAC7,0x4C1A,0x9F,0xB4,0xC5,0xBB,0x62,0x74,0xD2,0x2F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkConfiguration,0xC679A35B,0x610C,0x4D09,0xB7,0x48,0x1D,0x04,0x78,0x10,0x0F,0xC0); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAttributes,0xABC11843,0xD966,0x44CB,0x96,0xE2,0xA1,0xCB,0x5D,0x31,0x35,0xC4); MIDL_DEFINE_GUID(IID, IID_IDeckLinkKeyer,0x89AFCAF5,0x65F8,0x421E,0x98,0xF7,0x96,0xFE,0x5F,0x5B,0xFB,0xA3); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoConversion,0x3BBCB8A2,0xDA2C,0x42D9,0xB5,0xD8,0x88,0x08,0x36,0x44,0xE9,0x9A); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDeckControlStatusCallback,0xE5F693C1,0x4283,0x4716,0xB1,0x8F,0xC1,0x43,0x15,0x21,0x95,0x5B); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDeckControl,0xA4D81043,0x0619,0x42B7,0x8E,0xD6,0x60,0x2D,0x29,0x04,0x1D,0xF7); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkIterator,0xD9EDA3B3,0x2887,0x41FA,0xB7,0x24,0x01,0x7C,0xF1,0xEB,0x1D,0x37); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkGLScreenPreviewHelper,0xF63E77C7,0xB655,0x4A4A,0x9A,0xD0,0x3C,0xA8,0x5D,0x39,0x43,0x43); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkVideoConversion,0x7DBBBB11,0x5B7B,0x467D,0xAE,0xA4,0xCE,0xA4,0x68,0xFD,0x36,0x8C); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDisplayModeIterator_v7_6,0x455D741F,0x1779,0x4800,0x86,0xF5,0x0B,0x5D,0x13,0xD7,0x97,0x51); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDisplayMode_v7_6,0x87451E84,0x2B7E,0x439E,0xA6,0x29,0x43,0x93,0xEA,0x4A,0x85,0x50); MIDL_DEFINE_GUID(IID, IID_IDeckLinkOutput_v7_6,0x29228142,0xEB8C,0x4141,0xA6,0x21,0xF7,0x40,0x26,0x45,0x09,0x55); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput_v7_6,0x300C135A,0x9F43,0x48E2,0x99,0x06,0x6D,0x79,0x11,0xD9,0x3C,0xF1); MIDL_DEFINE_GUID(IID, IID_IDeckLinkTimecode_v7_6,0xEFB9BCA6,0xA521,0x44F7,0xBD,0x69,0x23,0x32,0xF2,0x4D,0x9E,0xE6); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrame_v7_6,0xA8D8238E,0x6B18,0x4196,0x99,0xE1,0x5A,0xF7,0x17,0xB8,0x3D,0x32); MIDL_DEFINE_GUID(IID, IID_IDeckLinkMutableVideoFrame_v7_6,0x46FCEE00,0xB4E6,0x43D0,0x91,0xC0,0x02,0x3A,0x7F,0xCE,0xB3,0x4F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoInputFrame_v7_6,0x9A74FA41,0xAE9F,0x47AC,0x8C,0xF4,0x01,0xF4,0x2D,0xD5,0x99,0x65); MIDL_DEFINE_GUID(IID, IID_IDeckLinkScreenPreviewCallback_v7_6,0x373F499D,0x4B4D,0x4518,0xAD,0x22,0x63,0x54,0xE5,0xA5,0x82,0x5E); MIDL_DEFINE_GUID(IID, IID_IDeckLinkGLScreenPreviewHelper_v7_6,0xBA575CD9,0xA15E,0x497B,0xB2,0xC2,0xF9,0xAF,0xE7,0xBE,0x4E,0xBA); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoConversion_v7_6,0x3EB504C9,0xF97D,0x40FE,0xA1,0x58,0xD4,0x07,0xD4,0x8C,0xB5,0x3B); MIDL_DEFINE_GUID(IID, IID_IDeckLinkConfiguration_v7_6,0xB8EAD569,0xB764,0x47F0,0xA7,0x3F,0xAE,0x40,0xDF,0x6C,0xBF,0x10); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoOutputCallback_v7_6,0xE763A626,0x4A3C,0x49D1,0xBF,0x13,0xE7,0xAD,0x36,0x92,0xAE,0x52); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInputCallback_v7_6,0x31D28EE7,0x88B6,0x4CB1,0x89,0x7A,0xCD,0xBF,0x79,0xA2,0x64,0x14); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkGLScreenPreviewHelper_v7_6,0xD398CEE7,0x4434,0x4CA3,0x9B,0xA6,0x5A,0xE3,0x45,0x56,0xB9,0x05); MIDL_DEFINE_GUID(CLSID, CLSID_CDeckLinkVideoConversion_v7_6,0xFFA84F77,0x73BE,0x4FB7,0xB0,0x3E,0xB5,0xE4,0x4B,0x9F,0x75,0x9B); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInputCallback_v7_3,0xFD6F311D,0x4D00,0x444B,0x9E,0xD4,0x1F,0x25,0xB5,0x73,0x0A,0xD0); MIDL_DEFINE_GUID(IID, IID_IDeckLinkOutput_v7_3,0x271C65E3,0xC323,0x4344,0xA3,0x0F,0xD9,0x08,0xBC,0xB2,0x0A,0xA3); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput_v7_3,0x4973F012,0x9925,0x458C,0x87,0x1C,0x18,0x77,0x4C,0xDB,0xBE,0xCB); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoInputFrame_v7_3,0xCF317790,0x2894,0x11DE,0x8C,0x30,0x08,0x00,0x20,0x0C,0x9A,0x66); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDisplayModeIterator_v7_1,0xB28131B6,0x59AC,0x4857,0xB5,0xAC,0xCD,0x75,0xD5,0x88,0x3E,0x2F); MIDL_DEFINE_GUID(IID, IID_IDeckLinkDisplayMode_v7_1,0xAF0CD6D5,0x8376,0x435E,0x84,0x33,0x54,0xF9,0xDD,0x53,0x0A,0xC3); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoFrame_v7_1,0x333F3A10,0x8C2D,0x43CF,0xB7,0x9D,0x46,0x56,0x0F,0xEE,0xA1,0xCE); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoInputFrame_v7_1,0xC8B41D95,0x8848,0x40EE,0x9B,0x37,0x6E,0x34,0x17,0xFB,0x11,0x4B); MIDL_DEFINE_GUID(IID, IID_IDeckLinkAudioInputPacket_v7_1,0xC86DE4F6,0xA29F,0x42E3,0xAB,0x3A,0x13,0x63,0xE2,0x9F,0x07,0x88); MIDL_DEFINE_GUID(IID, IID_IDeckLinkVideoOutputCallback_v7_1,0xEBD01AFA,0xE4B0,0x49C6,0xA0,0x1D,0xED,0xB9,0xD1,0xB5,0x5F,0xD9); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInputCallback_v7_1,0x7F94F328,0x5ED4,0x4E9F,0x97,0x29,0x76,0xA8,0x6B,0xDC,0x99,0xCC); MIDL_DEFINE_GUID(IID, IID_IDeckLinkOutput_v7_1,0xAE5B3E9B,0x4E1E,0x4535,0xB6,0xE8,0x48,0x0F,0xF5,0x2F,0x6C,0xE5); MIDL_DEFINE_GUID(IID, IID_IDeckLinkInput_v7_1,0x2B54EDEF,0x5B32,0x429F,0xBA,0x11,0xBB,0x99,0x05,0x96,0xEA,0xCD); #undef MIDL_DEFINE_GUID #ifdef __cplusplus } #endif mlt-6.20.0/src/modules/dv/000077500000000000000000000000001362234133600152465ustar00rootroot00000000000000mlt-6.20.0/src/modules/dv/Makefile000066400000000000000000000012731362234133600167110ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread include ../../../config.mak TARGET = ../libmltdv$(LIBSUF) OBJS = factory.o \ producer_libdv.o \ consumer_libdv.o CFLAGS += `pkg-config --cflags libdv` 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/dv" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/dv" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/dv/configure000077500000000000000000000003151362234133600171540ustar00rootroot00000000000000#!/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 mlt-6.20.0/src/modules/dv/consumer_libdv.c000066400000000000000000000303711362234133600204310ustar00rootroot00000000000000/* * consumer_libdv.c -- a DV encoder based on libdv * Copyright (C) 2003-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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_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 ); } mlt_events_fire( this_properties, "consumer-frame-show", frame, NULL ); } 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_s16; 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, (void**) &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 = mlt_fopen( target, "wb" ); 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 ); } mlt-6.20.0/src/modules/dv/consumer_libdv.yml000066400000000000000000000006331362234133600210060ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: libdv title: libdv (*deprecated*) version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video description: > DV consumer using libdv. parameters: - identifier: argument title: File type: string description: The filename to write to, e.g. /dev/dv1394. required: yes widget: filesave mlt-6.20.0/src/modules/dv/deprecated000066400000000000000000000000001362234133600172570ustar00rootroot00000000000000mlt-6.20.0/src/modules/dv/factory.c000066400000000000000000000032271362234133600170650ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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 ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/dv/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "libdv", consumer_libdv_init ); MLT_REGISTER( producer_type, "libdv", producer_libdv_init ); MLT_REGISTER_METADATA( consumer_type, "libdv", metadata, "consumer_libdv.yml" ); MLT_REGISTER_METADATA( producer_type, "libdv", metadata, "producer_libdv.yml" ); } mlt-6.20.0/src/modules/dv/producer_libdv.c000066400000000000000000000401101362234133600204110ustar00rootroot00000000000000/* * producer_libdv.c -- simple libdv test case * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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( 1, sizeof( struct producer_libdv_s ) ); 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 ) { double aspect_ratio; // 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 ); if ( this->is_pal ) { if ( dv_format_wide( dv_decoder ) ) aspect_ratio = 64.0 / 45.0; else aspect_ratio = 16.0 / 15.0; } else { if ( dv_format_wide( dv_decoder ) ) aspect_ratio = 32.0 / 27.0; else aspect_ratio = 8 / 9; } mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio); 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" ); mlt_properties_set_int( properties, "meta.media.width", 720 ); mlt_properties_set_int( properties, "meta.media.height", this->is_pal ? 576 : 480 ); mlt_properties_set_int( properties, "meta.media.frame_rate_num", this->is_pal? 25 : 30000 ); mlt_properties_set_int( properties, "meta.media.frame_rate_den", this->is_pal? 1 : 1001 ); // 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_rgb24 ) { // Allocate an image uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 2 ); // Pass to properties for clean up mlt_frame_set_image( this, image, *width * ( *height + 1 ) * 2, mlt_pool_release ); // 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_frame_set_image( this, image, *width * ( *height + 1 ) * 3, mlt_pool_release ); // 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, void **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 ) { int size = *channels * DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ); // Obtain required values *frequency = decoder->audio->frequency; *samples = decoder->audio->samples_this_frame; *channels = decoder->audio->num_channels; *format = mlt_audio_s16; // 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( size ); // Pass the allocated audio buffer as a property mlt_frame_set_audio( this, *buffer, *format, size, mlt_pool_release ); // 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, "top_field_first", !this->is_pal ? 0 : ( data[ 5 ] & 0x07 ) == 0 ? 0 : 1 ); mlt_properties_set_int( properties, "colorspace", 601 ); // 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, "audio_frequency", dv_decoder->audio->frequency ); mlt_properties_set_int( properties, "audio_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 if ( *frame != NULL ) 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 ); } mlt-6.20.0/src/modules/dv/producer_libdv.yml000066400000000000000000000011131362234133600207700ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: libdv title: libdv (*deprecated*) version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video description: A libdv based decoder for video and audio. parameters: - identifier: argument title: File type: string required: yes readonly: no widget: fileopen - identifier: quality title: Quality type: string description: One of "best," "fast" or anything else chooses medium. readonly: no mutable: yes widget: combo default: best mlt-6.20.0/src/modules/feeds/000077500000000000000000000000001362234133600157235ustar00rootroot00000000000000mlt-6.20.0/src/modules/feeds/CMakeLists.txt000066400000000000000000000003041362234133600204600ustar00rootroot00000000000000file(GLOB PAL PAL/*.*) install(FILES ${PAL} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/feeds/PAL) file(GLOB NTSC NTSC/*.*) install(FILES ${NTSC} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/feeds/NTSC) mlt-6.20.0/src/modules/feeds/Makefile000066400000000000000000000004511362234133600173630ustar00rootroot00000000000000include ../../../config.mak all: depend: distclean: clean: install: all install -d "$(DESTDIR)$(mltdatadir)/feeds/PAL" install -d "$(DESTDIR)$(mltdatadir)/feeds/NTSC" install -m 644 PAL/*.* "$(DESTDIR)$(mltdatadir)/feeds/PAL" install -m 644 NTSC/*.* "$(DESTDIR)$(mltdatadir)/feeds/NTSC" mlt-6.20.0/src/modules/feeds/NTSC/000077500000000000000000000000001362234133600164725ustar00rootroot00000000000000mlt-6.20.0/src/modules/feeds/NTSC/data_fx.properties000066400000000000000000000043211362234133600222160ustar00rootroot00000000000000# 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:0x000000ff .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:0x000000ff .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:0x000000ff .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.family=San .filter[1].producer.size=32 .filter[1].composite.titles=1 mlt-6.20.0/src/modules/feeds/NTSC/etv.properties000066400000000000000000000127631362234133600214170ustar00rootroot00000000000000# 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.family=filter[1].producer.family .properties.size=filter[1].producer.size .period=2 .properties.length[0]=composite.out .composite.geometry=0/14%:32%x5%:0;15=/: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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .period=2 .properties.length[0]=composite.out .composite.geometry=0/20%:32%x5%:0;15=/: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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .period=2 .properties.length[0]=composite.out .composite.geometry=-32%/20%:32%x5%;15=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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .properties.length[0]=composite.out .composite.geometry=82%/28%:11%x4%:0;15=/: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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .properties.length[0]=filter[0].composite.out .properties.length[1]=filter[1].composite.out .composite.geometry=65%/65%:35%x6% .filter[0]=watermark .filter[0].resource=colour:0x6c0101ff .filter[0].composite.geometry=100%/0%:100%x100%:0;15=0%/0%:x:100 .filter[0].composite.distort=1 .filter[1]=watermark .filter[1].resource=pango: .filter[1].producer.text=Special .filter[1].producer.family=Sans .filter[1].producer.size=24 .filter[1].producer.weight=700 .filter[1].composite.geometry=100%/0%:100%x100%:0;15=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .properties.length[0]=filter[1].composite.out .composite.geometry=0/87%:101%x13% .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.family=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.family .properties.s0=filter[1].producer.size .properties.f1=filter[2].producer.family .properties.s1=filter[2].producer.size .properties.length[0]=composite.out .period=2 .composite.geometry=0/71%:100%x16%:0;30=/: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.family=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.family=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 mlt-6.20.0/src/modules/feeds/NTSC/obscure.properties000066400000000000000000000011751362234133600222560ustar00rootroot00000000000000# 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% mlt-6.20.0/src/modules/feeds/PAL/000077500000000000000000000000001362234133600163375ustar00rootroot00000000000000mlt-6.20.0/src/modules/feeds/PAL/border.properties000066400000000000000000000007471362234133600217420ustar00rootroot00000000000000border_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 mlt-6.20.0/src/modules/feeds/PAL/data_fx.properties000066400000000000000000000041741362234133600220710ustar00rootroot00000000000000# 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:0x000000ff .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:0x000000ff .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 # # 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:0x000000ff .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.family=San .filter[1].producer.size=32 .filter[1].composite.titles=1 mlt-6.20.0/src/modules/feeds/PAL/etv.properties000066400000000000000000000127631362234133600212640ustar00rootroot00000000000000# 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.family=filter[1].producer.family .properties.size=filter[1].producer.size .period=2 .properties.length[0]=composite.out .composite.geometry=0/14%:32%x5%: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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .period=2 .properties.length[0]=composite.out .composite.geometry=0/20%:32%x5%: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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .period=2 .properties.length[0]=composite.out .composite.geometry=-32%/20%:32%x5%;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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .properties.length[0]=composite.out .composite.geometry=82%/28%:11%x4%: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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .properties.length[0]=filter[0].composite.out .properties.length[1]=filter[1].composite.out .composite.geometry=65%/65%:35%x6% .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.family=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.family=filter[1].producer.family .properties.size=filter[1].producer.size .properties.length[0]=filter[1].composite.out .composite.geometry=0/87%:101%x13% .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.family=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.family .properties.s0=filter[1].producer.size .properties.f1=filter[2].producer.family .properties.s1=filter[2].producer.size .properties.length[0]=composite.out .period=2 .composite.geometry=0/71%:100%x16%: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.family=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.family=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 mlt-6.20.0/src/modules/feeds/PAL/example.properties000066400000000000000000000002301362234133600221030ustar00rootroot00000000000000greyscale=greyscale .description=Greyscale sepia=sepia .description=Sepia charcoal=charcoal .description=Charcoal invert=invert .description=Invert mlt-6.20.0/src/modules/feeds/PAL/obscure.properties000066400000000000000000000015221362234133600221170ustar00rootroot00000000000000# 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 mlt-6.20.0/src/modules/frei0r/000077500000000000000000000000001362234133600160245ustar00rootroot00000000000000mlt-6.20.0/src/modules/frei0r/CMakeLists.txt000066400000000000000000000007271362234133600205720ustar00rootroot00000000000000pkg_check_modules(frei0r IMPORTED_TARGET frei0r) if(TARGET PkgConfig::frei0r) file(GLOB mltfrei0r_src *.c) add_library(mltfrei0r MODULE ${mltfrei0r_src}) target_link_libraries(mltfrei0r mlt m PkgConfig::frei0r) install(TARGETS mltfrei0r LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES blacklist.txt not_thread_safe.txt param_name_map.yaml ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/frei0r) endif() mlt-6.20.0/src/modules/frei0r/Makefile000066400000000000000000000020271362234133600174650ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt include ../../../config.mak TARGET = ../libmltfrei0r$(LIBSUF) OBJS = factory.o \ producer_frei0r.o \ filter_frei0r.o \ transition_frei0r.o \ frei0r_helper.o \ filter_cairoblend_mode.o CFLAGS += $(shell pkg-config --cflags frei0r 2> /dev/null) LDFLAGS += -lm $(LIBDL) LDFLAGS += $(shell pkg-config --libs frei0r 2> /dev/null) 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/frei0r" install -m 644 blacklist.txt "$(DESTDIR)$(mltdatadir)/frei0r" install -m 644 not_thread_safe.txt "$(DESTDIR)$(mltdatadir)/frei0r" install -m 644 param_name_map.yaml "$(DESTDIR)$(mltdatadir)/frei0r" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/frei0r" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/frei0r/blacklist.txt000066400000000000000000000000141362234133600205300ustar00rootroot00000000000000perspective mlt-6.20.0/src/modules/frei0r/configure000077500000000000000000000005371362234133600177400ustar00rootroot00000000000000#! /bin/sh if [ "$help" != "1" ] then echo "#include int main(){ f0r_plugin_info_t test; test.name;return 0;}"| $CC $(pkg-config --cflags frei0r) $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 mlt-6.20.0/src/modules/frei0r/factory.c000066400000000000000000000447221362234133600176500ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) #define LIBSUF ".dll" #define FREI0R_PLUGIN_PATH "\\lib\\frei0r-1" #elif defined(__APPLE__) && defined(RELOCATABLE) #define LIBSUF ".so" #define FREI0R_PLUGIN_PATH "/PlugIns/frei0r-1" #else #define LIBSUF ".so" #define FREI0R_PLUGIN_PATH "/usr/lib/frei0r-1:/usr/lib64/frei0r-1:/opt/local/lib/frei0r-1:/usr/local/lib/frei0r-1:$HOME/.frei0r-1/lib" #endif #define GET_FREI0R_PATH (getenv("FREI0R_PATH") ? getenv("FREI0R_PATH") : getenv("MLT_FREI0R_PLUGIN_PATH") ? getenv("MLT_FREI0R_PLUGIN_PATH") : FREI0R_PLUGIN_PATH) extern mlt_filter filter_frei0r_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_cairoblend_mode_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_frame filter_process( mlt_filter filter, mlt_frame frame ); extern void filter_close( mlt_filter filter ); extern int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); extern void producer_close( mlt_producer producer ); extern void transition_close( mlt_transition transition ); extern mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ); static char* get_frei0r_path() { #ifdef _WIN32 char *dirname = malloc( strlen( mlt_environment( "MLT_APPDIR" ) ) + strlen( FREI0R_PLUGIN_PATH ) + 1 ); strcpy( dirname, mlt_environment( "MLT_APPDIR" ) ); strcat( dirname, FREI0R_PLUGIN_PATH ); return dirname; #elif defined(__APPLE__) && defined(RELOCATABLE) char *dirname = malloc( strlen( mlt_environment( "MLT_APPDIR" ) ) + strlen( FREI0R_PLUGIN_PATH ) + 1 ); strcpy( dirname, mlt_environment( "MLT_APPDIR" ) ); strcat( dirname, FREI0R_PLUGIN_PATH ); return dirname; #else return strdup( GET_FREI0R_PATH ); #endif } static void check_thread_safe(mlt_properties properties, const char *name) { char dirname[PATH_MAX]; snprintf(dirname, PATH_MAX, "%s/frei0r/not_thread_safe.txt", mlt_environment("MLT_DATA")); mlt_properties not_thread_safe = mlt_properties_load(dirname); double version = mlt_properties_get_double( properties, "version" ); int i; for (i = 0; i < mlt_properties_count(not_thread_safe); i++) { if (!strcmp(name, mlt_properties_get_name(not_thread_safe, i))) { double thread_safe_version = mlt_properties_get_double(not_thread_safe, name); if (thread_safe_version == 0.0 || version < thread_safe_version) mlt_properties_set_int(properties, "_not_thread_safe", 1); break; } } mlt_properties_close(not_thread_safe); } 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); memset(&stat_buff, 0, sizeof(stat_buff)); 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"); void (*f0r_init) (void) = dlsym(handle, "f0r_init"); void (*f0r_deinit) (void) = dlsym(handle, "f0r_deinit"); f0r_instance_t (*f0r_construct) (unsigned int, unsigned int) = dlsym(handle, "f0r_construct"); void (*f0r_destruct) (f0r_instance_t) = dlsym(handle, "f0r_destruct"); void (*f0r_get_param_value) (f0r_instance_t instance, f0r_param_t param, int param_index) = dlsym(handle, "f0r_get_param_value"); 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; f0r_init(); f0r_instance_t instance = f0r_construct(720, 576); if (!instance) { f0r_deinit(); dlclose(handle); mlt_properties_close(metadata); return NULL; } plginfo(&info); snprintf(string, sizeof(string) , "%d" , info.minor_version); mlt_properties_set_double(metadata, "schema_version", 0.3); mlt_properties_set(metadata, "title" , info.name); mlt_properties_set_double(metadata, "version", info.major_version + info.minor_version / pow(10, strlen(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 tags = mlt_properties_new(); mlt_properties_set_data(metadata, "tags", tags, 0, (mlt_destructor) mlt_properties_close, NULL); mlt_properties_set(tags, "0", "Video"); mlt_properties parameter = mlt_properties_new(); mlt_properties_set_data(metadata, "parameters", parameter, 0, (mlt_destructor) mlt_properties_close, NULL); for (j = 0; j < info.num_params; j++) { snprintf(string, sizeof(string), "%d", j); mlt_properties pnum = mlt_properties_new(); mlt_properties_set_data(parameter, string, pnum, 0, (mlt_destructor) mlt_properties_close, NULL); f0r_param_info_t paraminfo; param_info(¶minfo, j); mlt_properties_set(pnum, "identifier", string); mlt_properties_set(pnum, "title", paraminfo.name); mlt_properties_set(pnum, "description", paraminfo.explanation); if (paraminfo.type == F0R_PARAM_DOUBLE) { double deflt = 0; mlt_properties_set(pnum, "type", "float"); mlt_properties_set(pnum, "minimum", "0"); mlt_properties_set(pnum, "maximum", "1"); f0r_get_param_value(instance, &deflt, j); mlt_properties_set_double(pnum, "default", CLAMP(deflt, 0.0, 1.0)); mlt_properties_set(pnum, "mutable", "yes" ); mlt_properties_set(pnum, "widget", "spinner"); } else if (paraminfo.type == F0R_PARAM_BOOL) { double deflt = 0; mlt_properties_set(pnum, "type", "boolean"); mlt_properties_set(pnum, "minimum", "0"); mlt_properties_set(pnum, "maximum", "1"); f0r_get_param_value(instance, &deflt, j); mlt_properties_set_int(pnum, "default", deflt != 0.0); mlt_properties_set(pnum, "mutable", "yes"); mlt_properties_set(pnum, "widget", "checkbox"); } else if (paraminfo.type == F0R_PARAM_COLOR) { char colorstr[8]; f0r_param_color_t deflt = {0, 0, 0}; mlt_properties_set(pnum, "type", "color"); f0r_get_param_value( instance, &deflt, j); sprintf(colorstr, "#%02x%02x%02x", (unsigned) CLAMP(deflt.r * 255, 0 , 255), (unsigned) CLAMP(deflt.g * 255, 0 , 255), (unsigned) CLAMP(deflt.b * 255, 0 , 255)); colorstr[7] = 0; mlt_properties_set(pnum, "default", colorstr); mlt_properties_set(pnum, "mutable", "yes"); mlt_properties_set(pnum, "widget", "color"); } else if (paraminfo.type == F0R_PARAM_STRING) { char *deflt = NULL; mlt_properties_set(pnum, "type", "string"); f0r_get_param_value(instance, &deflt, j); mlt_properties_set(pnum, "default", deflt); mlt_properties_set(pnum, "mutable", "yes"); mlt_properties_set(pnum, "widget", "text"); } } f0r_destruct(instance); f0r_deinit(); dlclose(handle); return metadata; } static void* load_lib(mlt_profile profile, mlt_service_type type , void* handle, const char *name) { int i = 0; void (*f0r_get_plugin_info) (f0r_plugin_info_t*), *f0r_construct, *f0r_update, *f0r_destruct, (*f0r_get_param_info) (f0r_param_info_t* info, int param_index), (*f0r_init) (void), *f0r_deinit, (*f0r_set_param_value) (f0r_instance_t instance, f0r_param_t param, int param_index), (*f0r_get_param_value) (f0r_instance_t instance, f0r_param_t param, int param_index), (*f0r_update2) (f0r_instance_t instance, double time, const uint32_t* inframe1, const uint32_t* inframe2,const uint32_t* inframe3, uint32_t* outframe); if ((f0r_construct = dlsym(handle, "f0r_construct")) && (f0r_destruct = dlsym(handle, "f0r_destruct")) && (f0r_get_plugin_info = dlsym(handle, "f0r_get_plugin_info")) && (f0r_get_param_info = dlsym(handle, "f0r_get_param_info")) && (f0r_set_param_value = dlsym(handle, "f0r_set_param_value")) && (f0r_get_param_value = dlsym(handle, "f0r_get_param_value")) && (f0r_init = dlsym(handle, "f0r_init")) && (f0r_deinit = dlsym(handle, "f0r_deinit")) ) { f0r_update = dlsym(handle, "f0r_update"); f0r_update2 = dlsym(handle, "f0r_update2"); f0r_plugin_info_t info; f0r_get_plugin_info(&info); void* ret = NULL; mlt_properties properties = NULL; char minor[12]; if (type == producer_type && info.plugin_type == F0R_PLUGIN_TYPE_SOURCE) { mlt_producer producer = mlt_producer_new(profile); if (producer) { producer->get_frame = producer_get_frame; producer->close = (mlt_destructor) producer_close; f0r_init(); properties = MLT_PRODUCER_PROPERTIES(producer); for (i = 0; i < info.num_params; i++) { f0r_param_info_t pinfo; f0r_get_param_info(&pinfo, i); } ret = producer; } } else if (type == filter_type && info.plugin_type == F0R_PLUGIN_TYPE_FILTER) { mlt_filter filter = mlt_filter_new(); if (filter) { filter->process = filter_process; filter->close = filter_close; f0r_init(); properties=MLT_FILTER_PROPERTIES(filter); for (i = 0; i < info.num_params; i++) { f0r_param_info_t pinfo; f0r_get_param_info(&pinfo, i); } ret = filter; } } else if (type == transition_type && info.plugin_type == F0R_PLUGIN_TYPE_MIXER2) { mlt_transition transition = mlt_transition_new(); if (transition) { transition->process = transition_process; transition->close = transition_close; f0r_init(); properties = MLT_TRANSITION_PROPERTIES( transition ); mlt_properties_set_int(properties, "_transition_type", 1 ); ret = transition; } } mlt_properties_set_data(properties, "_dlclose_handle", handle, sizeof(handle), NULL, NULL); mlt_properties_set_data(properties, "_dlclose", dlclose, sizeof(void*), NULL, NULL); mlt_properties_set_data(properties, "f0r_construct", f0r_construct, sizeof(f0r_construct), NULL, NULL); mlt_properties_set_data(properties, "f0r_update", f0r_update, sizeof(f0r_update), NULL, NULL); if (f0r_update2) mlt_properties_set_data(properties, "f0r_update2", f0r_update2, sizeof(f0r_update2), NULL, NULL); mlt_properties_set_data(properties, "f0r_destruct", f0r_destruct, sizeof(f0r_destruct), 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); // Let frei0r plugin version be serialized using same format as metadata snprintf(minor, sizeof(minor), "%d", info.minor_version); mlt_properties_set_double(properties, "version", info.major_version + info.minor_version / pow(10, strlen(minor))); check_thread_safe(properties, name); // Use the global param name map for backwards compatibility when // param names change and setting frei0r params by name instead of index. mlt_properties param_name_map = mlt_properties_get_data(mlt_global_properties(), "frei0r.param_name_map", NULL); if (param_name_map) { // Lookup my plugin in the map param_name_map = mlt_properties_get_data(param_name_map, name, NULL); mlt_properties_set_data(properties, "_param_name_map", param_name_map, 0, NULL, NULL); } param_name_map = mlt_properties_get_data(mlt_global_properties(), "frei0r.resolution_scale", NULL); if (param_name_map) { // Lookup my plugin in the map param_name_map = mlt_properties_get_data(param_name_map, name, NULL); mlt_properties_set_data(properties, "_resolution_scale", param_name_map, 0, NULL, NULL); } return ret; } else { mlt_log_error(NULL, "frei0r plugin \"%s\" is missing a function\n", name); 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(); char *frei0r_path = get_frei0r_path(); int dircount = mlt_tokeniser_parse_new ( tokeniser, frei0r_path, MLT_DIRLIST_DELIMITER ); void* ret = NULL; while (dircount-- && !ret) { char soname[PATH_MAX]; char *myid = strdup(id); #ifdef _WIN32 char *firstname = strtok(myid, "."); #else char *save_firstptr = NULL; char *firstname = strtok_r(myid, ".", &save_firstptr); #endif char* directory = mlt_tokeniser_get_string(tokeniser, dircount); #ifdef _WIN32 firstname = strtok(NULL, "."); #else firstname = strtok_r(NULL, ".", &save_firstptr); #endif if (strncmp(directory, "$HOME", 5)) snprintf(soname, PATH_MAX, "%s/%s" LIBSUF, directory, firstname); else snprintf(soname, PATH_MAX, "%s%s/%s" LIBSUF, getenv("HOME"), strchr(directory, '/'), firstname); if (firstname) { void* handle = dlopen(soname, RTLD_LAZY); if (handle) { ret = load_lib(profile, type, handle, firstname); } else { dlerror(); } } free(myid); } mlt_tokeniser_close(tokeniser); free(frei0r_path); return ret; } static mlt_properties metadata(mlt_service_type type, const char *id, void *data) { char file[ PATH_MAX ]; snprintf(file, PATH_MAX, "%s/frei0r/%s", mlt_environment("MLT_DATA"), (char*) data); return mlt_properties_parse_yaml(file); } MLT_REPOSITORY { int i = 0; mlt_tokeniser tokeniser = mlt_tokeniser_init(); char *frei0r_path = get_frei0r_path(); int dircount = mlt_tokeniser_parse_new( tokeniser , frei0r_path, MLT_DIRLIST_DELIMITER ); char dirname[PATH_MAX]; snprintf(dirname, PATH_MAX, "%s/frei0r/blacklist.txt", mlt_environment("MLT_DATA")); mlt_properties blacklist = mlt_properties_load(dirname); // Load a param name map into global properties for backwards compatibility when // param names change and setting frei0r params by name instead of index. snprintf(dirname, PATH_MAX, "%s/frei0r/param_name_map.yaml", mlt_environment("MLT_DATA")); mlt_properties_set_data(mlt_global_properties(), "frei0r.param_name_map", mlt_properties_parse_yaml(dirname), 0, (mlt_destructor) mlt_properties_close, NULL); // Load a list of parameters impacted by consumer scale into global properties. snprintf(dirname, PATH_MAX, "%s/frei0r/resolution_scale.yml", mlt_environment("MLT_DATA")); mlt_properties_set_data(mlt_global_properties(), "frei0r.resolution_scale", mlt_properties_parse_yaml(dirname), 0, (mlt_destructor) mlt_properties_close, NULL); while (dircount--) { mlt_properties direntries = mlt_properties_new(); char* directory = mlt_tokeniser_get_string(tokeniser, dircount); if (strncmp(directory, "$HOME", 5)) snprintf(dirname, PATH_MAX, "%s", directory); else snprintf(dirname, PATH_MAX, "%s%s", getenv("HOME"), strchr(directory, '/')); mlt_properties_dir_list(direntries, dirname ,"*" LIBSUF, 1); for (i = 0; i < mlt_properties_count(direntries); i++) { char* name = mlt_properties_get_value(direntries, i); char* shortname = name + strlen(dirname) + 1; #ifdef _WIN32 char* firstname = strtok( shortname, "." ); #else char *save_firstptr = NULL; char* firstname = strtok_r(shortname, ".", &save_firstptr); #endif char pluginname[1024] = "frei0r."; if (firstname) strncat(pluginname, firstname, sizeof(pluginname) - strlen(pluginname) - 1); if (firstname && mlt_properties_get(blacklist, firstname)) continue; void* handle = dlopen(strcat(name, LIBSUF), RTLD_LAZY); if (handle) { void (*plginfo) (f0r_plugin_info_t*) = dlsym(handle, "f0r_get_plugin_info"); if (plginfo) { f0r_plugin_info_t info; plginfo(&info); if (firstname && info.plugin_type==F0R_PLUGIN_TYPE_SOURCE) { if (mlt_properties_get(mlt_repository_producers(repository), pluginname)) { dlclose(handle); continue; } MLT_REGISTER(producer_type, pluginname, create_frei0r_item); MLT_REGISTER_METADATA(producer_type, pluginname, fill_param_info, name); } else if (firstname && info.plugin_type == F0R_PLUGIN_TYPE_FILTER) { if (mlt_properties_get(mlt_repository_filters(repository), pluginname)) { dlclose(handle); continue; } MLT_REGISTER(filter_type, pluginname, create_frei0r_item); MLT_REGISTER_METADATA(filter_type, pluginname, fill_param_info, name); } else if (firstname && info.plugin_type==F0R_PLUGIN_TYPE_MIXER2 ) { if (mlt_properties_get(mlt_repository_transitions(repository), pluginname)) { dlclose(handle); continue; } MLT_REGISTER(transition_type, pluginname, create_frei0r_item); MLT_REGISTER_METADATA(transition_type, pluginname, fill_param_info, name); } } dlclose(handle); } } mlt_factory_register_for_clean_up(direntries, (mlt_destructor) mlt_properties_close); } mlt_tokeniser_close(tokeniser); mlt_properties_close(blacklist); free(frei0r_path); MLT_REGISTER(filter_type, "cairoblend_mode", filter_cairoblend_mode_init); MLT_REGISTER_METADATA(filter_type, "cairoblend_mode", metadata, "filter_cairoblend_mode.yml"); } mlt-6.20.0/src/modules/frei0r/filter_cairoblend_mode.c000066400000000000000000000027211362234133600226450ustar00rootroot00000000000000/* * filter_cairoblend_mode.c * Copyright (C) 2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "frei0r_helper.h" #include #include static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties_set(MLT_FRAME_PROPERTIES(frame), CAIROBLEND_MODE_PROPERTY, mlt_properties_get(MLT_FILTER_PROPERTIES(filter), "mode")); return frame; } mlt_filter filter_cairoblend_mode_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if (filter) { filter->process = filter_process; mlt_properties_set(MLT_FILTER_PROPERTIES( filter ), "mode", arg? arg : "normal"); } return filter; } mlt-6.20.0/src/modules/frei0r/filter_cairoblend_mode.yml000066400000000000000000000010451362234133600232220ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: cairoblend_mode title: Set Cairographics Blend Mode version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: Change the blend mode used by the frei0r.cairoblend transition. parameters: - identifier: mode argument: yes title: Blend mode type: string description: > This filter can be used to change the blend mode for a single clip when using the cairoblend transition to composite tracks. default: normal mlt-6.20.0/src/modules/frei0r/filter_frei0r.c000066400000000000000000000041141362234133600207240ustar00rootroot00000000000000/* * filter_frei0r.c -- frei0r filter * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "frei0r_helper.h" #include static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = mlt_frame_pop_service( frame ); *format = mlt_image_rgb24a; mlt_log_debug( MLT_FILTER_SERVICE( filter ), "frei0r %dx%d\n", *width, *height ); int error = mlt_frame_get_image( frame, image, format, width, height, 0 ); if ( error == 0 && *image ) { mlt_position position = mlt_filter_get_position( filter, frame ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); double time = (double) position / mlt_profile_fps( profile ); int length = mlt_filter_get_length2( filter, frame ); process_frei0r_item( MLT_FILTER_SERVICE(filter), position, time, length, frame, image, width, height ); } return error; } mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } void filter_close( mlt_filter filter ) { destruct( MLT_FILTER_PROPERTIES ( filter ) ); filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } mlt-6.20.0/src/modules/frei0r/frei0r_helper.c000066400000000000000000000250301362234133600207160ustar00rootroot00000000000000/* * frei0r_helper.c -- frei0r helper * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frei0r_helper.h" #include #include #include #ifdef _WIN32 #include #endif const char *CAIROBLEND_MODE_PROPERTY = "frei0r.cairoblend.mode"; static void rgba_bgra( uint8_t *src, uint8_t* dst, int width, int height ) { int n = width * height + 1; while ( --n ) { *dst++ = src[2]; *dst++ = src[1]; *dst++ = src[0]; *dst++ = src[3]; src += 4; } } struct update_context { f0r_instance_t frei0r; int width; int height; double time; uint32_t* inputs[2]; uint32_t* output; void (*f0r_update) (f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe); void (*f0r_update2) (f0r_instance_t instance, double time, const uint32_t* inframe1, const uint32_t* inframe2,const uint32_t* inframe3, uint32_t* outframe); }; static int f0r_update_slice( int id, int index, int count, void *context ) { struct update_context *ctx = context; int slice_height = ctx->height / count; int slice_bytes = ctx->width * slice_height; uint32_t *input = ctx->inputs[0] + index * slice_bytes; uint32_t *output = ctx->output + index * slice_bytes; ctx->f0r_update(ctx->frei0r, ctx->time, input, output); return 0; } static int f0r_update2_slice( int id, int index, int count, void *context ) { struct update_context *ctx = context; int slice_height = ctx->height / count; int slice_bytes = ctx->width * slice_height; uint32_t *inputs[2] = { ctx->inputs[0] + index * slice_bytes, ctx->inputs[1] + index * slice_bytes }; uint32_t *output = ctx->output + index * slice_bytes; ctx->f0r_update2(ctx->frei0r, ctx->time, inputs[0], inputs[1], NULL, output); return 0; } int process_frei0r_item( mlt_service service, mlt_position position, double time, int length, mlt_frame frame, uint8_t **image, int *width, int *height ) { int i=0; mlt_properties prop = MLT_SERVICE_PROPERTIES(service); f0r_instance_t (*f0r_construct) (unsigned int, unsigned int) = mlt_properties_get_data(prop, "f0r_construct", NULL); if (!f0r_construct) { return -1; } 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_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); mlt_service_type type = mlt_service_identify(service); int not_thread_safe = mlt_properties_get_int(prop, "_not_thread_safe"); int slice_count = mlt_properties_get(prop, "threads") ? mlt_properties_get_int(prop, "threads") : -1; const char *service_name = mlt_properties_get(prop, "mlt_service"); int is_cairoblend = service_name && !strcmp("frei0r.cairoblend", service_name); double scale = mlt_profile_scale_width(mlt_service_profile(service), *width); mlt_properties scale_map = mlt_properties_get_data(prop, "_resolution_scale", NULL); if (slice_count >= 0) slice_count = CLAMP(slice_count, 0, mlt_slices_count_normal()); //use as name the width and height int slice_height = *height / (slice_count > 0? slice_count : 1); char ctorname[1024] = ""; if (not_thread_safe) sprintf(ctorname, "ctor-%dx%d", *width, slice_height); else #ifdef _WIN32 sprintf(ctorname, "ctor-%dx%d-%lu", *width, slice_height, GetCurrentThreadId()); #else sprintf(ctorname, "ctor-%dx%d-%p", *width, slice_height, (void*) pthread_self()); #endif mlt_service_lock(service); f0r_instance_t inst = mlt_properties_get_data(prop, ctorname, NULL); if (!inst) { inst = f0r_construct(*width, slice_height); mlt_properties_set_data(prop, ctorname, inst, 0, NULL, NULL); } if (!not_thread_safe) mlt_service_unlock(service); f0r_plugin_info_t info; memset(&info, 0, sizeof(info)); if (f0r_get_plugin_info) { f0r_get_plugin_info(&info); for (i = 0; i < info.num_params; i++) { prop = MLT_SERVICE_PROPERTIES(service); f0r_param_info_t pinfo; f0r_get_param_info(&pinfo,i); char index[20]; snprintf( index, sizeof(index), "%d", i ); const char *name = index; char *val = mlt_properties_get(prop, name); // Special cairoblend handling for an override from the cairoblend_mode filter. if (is_cairoblend && i == 1) { if (mlt_properties_get(MLT_FRAME_PROPERTIES(frame), CAIROBLEND_MODE_PROPERTY)) { name = CAIROBLEND_MODE_PROPERTY; prop = MLT_FRAME_PROPERTIES(frame); val = mlt_properties_get(prop, name); } else if (!val && !mlt_properties_get(MLT_FRAME_PROPERTIES(frame), name)) { // Reset plugin back to its default value. char *default_val = "normal"; char *plugin_val = NULL; f0r_get_param_value(inst, &plugin_val, i); if (plugin_val && strcmp(default_val, plugin_val)) { f0r_set_param_value(inst, &default_val, i); continue; } } } if (!val) { name = pinfo.name; val = mlt_properties_get(prop, name); } if (!val) { // Use the backwards-compatibility param name map. mlt_properties map = mlt_properties_get_data(prop, "_param_name_map", NULL); if (map) { int j; for (j = 0; !val && j < mlt_properties_count(map); j++) { if (!strcmp(mlt_properties_get_value(map, j), index)) { name = mlt_properties_get_name(map, j); val = mlt_properties_get(prop, name); } } } } if (val) { switch (pinfo.type) { case F0R_PARAM_DOUBLE: case F0R_PARAM_BOOL: { double t = mlt_properties_anim_get_double(prop, name, position, length); if (scale != 1.0) { double scale2 = mlt_properties_get_double(scale_map, name); if (scale2 != 0.0) t *= scale * scale2; } f0r_set_param_value(inst,&t,i); break; } case F0R_PARAM_COLOR: { f0r_param_color_t f_color; mlt_color m_color = mlt_properties_get(prop, index) ? mlt_properties_get_color(prop, index) : mlt_properties_get_color(prop, pinfo.name); f_color.r = (float) m_color.r / 255.0f; f_color.g = (float) m_color.g / 255.0f; f_color.b = (float) m_color.b / 255.0f; f0r_set_param_value(inst, &f_color, i); break; } case F0R_PARAM_STRING: { val = mlt_properties_anim_get(prop, name, position, length); f0r_set_param_value(inst, &val, i); break; } } } } } int video_area = *width * *height; uint32_t *result = mlt_pool_alloc(video_area * sizeof(uint32_t)); uint32_t *extra = NULL; uint32_t *source[2] = { (uint32_t*) image[0], (uint32_t*) image[1] }; uint32_t *dest = result; if (info.color_model == F0R_COLOR_MODEL_BGRA8888) { if (type == producer_type) { dest = source[0]; } else { rgba_bgra(image[0], (uint8_t*) result, *width, *height); source[0] = result; dest = (uint32_t*) image[0]; if (type == transition_type && f0r_update2) { extra = mlt_pool_alloc(video_area * sizeof(uint32_t)); rgba_bgra(image[1], (uint8_t*) extra, *width, *height); source[1] = extra; } } } if (type == producer_type) { if (slice_count > 0) { struct update_context ctx = { .frei0r = inst, .width = *width, .height = *height, .time = time, .inputs = { NULL, NULL }, .output = dest, .f0r_update = f0r_update }; mlt_slices_run_normal(slice_count, f0r_update_slice, &ctx); } else { f0r_update(inst, time, NULL, dest); } } else if (type == filter_type) { if (slice_count > 0) { struct update_context ctx = { .frei0r = inst, .width = *width, .height = *height, .time = time, .inputs = { source[0], NULL }, .output = dest, .f0r_update = f0r_update }; mlt_slices_run_normal(slice_count, f0r_update_slice, &ctx); } else { f0r_update(inst, time, source[0], dest); } } else if (type == transition_type && f0r_update2) { if (slice_count > 0) { struct update_context ctx = { .frei0r = inst, .width = *width, .height = *height, .time = time, .inputs = { source[0], source[1] }, .output = dest, .f0r_update2 = f0r_update2 }; mlt_slices_run_normal(slice_count, f0r_update2_slice, &ctx); } else { f0r_update2(inst, time, source[0], source[1], NULL, dest); } } if (not_thread_safe) mlt_service_unlock(service); if (info.color_model == F0R_COLOR_MODEL_BGRA8888) { rgba_bgra((uint8_t*) dest, (uint8_t*) result, *width, *height); } *image = (uint8_t*) result; mlt_frame_set_image(frame, (uint8_t*) result, video_area * sizeof(uint32_t), mlt_pool_release); if (extra) mlt_pool_release(extra); return 0; } void destruct (mlt_properties prop ) { void (*f0r_destruct) (f0r_instance_t instance) = mlt_properties_get_data(prop, "f0r_destruct", NULL); void (*f0r_deinit) (void) = mlt_properties_get_data(prop, "f0r_deinit", NULL); int i = 0; if (f0r_deinit) f0r_deinit(); for (i=0; i < mlt_properties_count(prop); i++) { if (strstr(mlt_properties_get_name(prop, i), "ctor-")) { void * inst = mlt_properties_get_data(prop, mlt_properties_get_name(prop, i), NULL); if (inst) { f0r_destruct((f0r_instance_t) inst); } } } void (*dlclose) (void*) = mlt_properties_get_data(prop, "_dlclose", NULL); void *handle = mlt_properties_get_data(prop, "_dlclose_handle", NULL); if (handle && dlclose) dlclose(handle); } mlt-6.20.0/src/modules/frei0r/frei0r_helper.h000066400000000000000000000021631362234133600207250ustar00rootroot00000000000000/* * frei0r_helper.h -- frei0r helper * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include int process_frei0r_item( mlt_service, mlt_position position, double time, int length, mlt_frame, uint8_t **image, int *width, int *height ); void destruct (mlt_properties prop ); extern const char *CAIROBLEND_MODE_PROPERTY; mlt-6.20.0/src/modules/frei0r/not_thread_safe.txt000066400000000000000000000002541362234133600217130ustar00rootroot00000000000000# plugin name = lowest version that is thread safe or empty if not yet thread safe baltan delay0r delaygrab ising0r nervous partik0l plasma tehRoxx0r vertigo colorhalftone mlt-6.20.0/src/modules/frei0r/param_name_map.yaml000066400000000000000000000003701362234133600216450ustar00rootroot00000000000000# MLT frei0r param name mapping from old name to current index # plugin: # param_name: index lenscorrection: xcenter: 0 ycenter: 1 correctionnearcenter: 2 correctionnearedges: 3 brightness: 4 pixeliz0r: BlockSizeX: 0 BlockSizeY: 1 mlt-6.20.0/src/modules/frei0r/producer_frei0r.c000066400000000000000000000063641362234133600212730ustar00rootroot00000000000000/* * producer_frei0r.c -- frei0r producer * Copyright (c) 2009 Jean-Baptiste Mardelle * Copyright (C) 2009-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 ) { mlt_producer producer = mlt_frame_pop_service( frame ); // Choose suitable out values if nothing specific requested if ( *width <= 0 ) *width = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->width; if ( *height <= 0 ) *height = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->height; // Allocate the image *format = mlt_image_rgb24a; int size = mlt_image_format_size( *format, *width, *height, NULL ); // Allocate the image *buffer = mlt_pool_alloc( size ); // Update the frame mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); if ( *buffer != NULL ) { mlt_position position = mlt_frame_get_position( frame ); mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); double time = (double) position / mlt_profile_fps( profile ); int length = mlt_producer_get_playtime( producer ); process_frei0r_item( MLT_PRODUCER_SERVICE(producer), position, time, length, frame, buffer, width, height ); } 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 ); // 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_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); mlt_properties_set_int( properties, "meta.media.width", profile->width ); mlt_properties_set_int( properties, "meta.media.height", profile->height ); // Push the get_image method mlt_frame_push_service( *frame, producer ); 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 ); } mlt-6.20.0/src/modules/frei0r/resolution_scale.yml000066400000000000000000000004261362234133600221230ustar00rootroot00000000000000# This files contains a list of plugin parameters that are sensitive to the # image resolution. This only works for parameters with type F0R_PARAM_DOUBLE. # plugin: # parameter#: scale factor in addition to mlt_frame_resolution_scale colorhalftone: 0: 1.0 IIRblur: 0: 1.7 mlt-6.20.0/src/modules/frei0r/transition_frei0r.c000066400000000000000000000100531362234133600216300ustar00rootroot00000000000000/* * transition_frei0r.c -- frei0r transition * Copyright (c) 2008 Marco Gittler * Copyright (C) 2009-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "frei0r_helper.h" #include static int is_opaque( uint8_t *image, int width, int height ) { int pixels = width * height + 1; while ( --pixels ) { if ( image[3] != 0xff ) return 0; image += 4; } return 1; } static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ){ 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" ); uint8_t *images[] = {NULL, NULL, NULL}; int error = 0; // Get the B-frame. *format = mlt_image_rgb24a; error = mlt_frame_get_image( b_frame, &images[1], format, width, height, 0 ); if ( error ) return error; const char *service_name = mlt_properties_get(properties, "mlt_service"); int is_cairoblend = service_name && !strcmp("frei0r.cairoblend", service_name); const char *blend_mode = mlt_properties_get(b_props, CAIROBLEND_MODE_PROPERTY); // An optimization for cairoblend in normal (over) mode and opaque B frame. if (is_cairoblend && ( !mlt_properties_get( properties, "0" ) || mlt_properties_get_double( properties, "0" ) == 1.0 ) && ( !mlt_properties_get( properties, "1" ) || !strcmp( "normal", mlt_properties_get( properties, "1" ) ) ) && ( !blend_mode || !strcmp("normal", blend_mode) ) // Check if the alpha channel is entirely opaque. && is_opaque( images[1], *width, *height ) ) { if (invert) error = mlt_frame_get_image( a_frame, image, format, width, height, 0 ); else *image = images[1]; } else { error = mlt_frame_get_image( a_frame, &images[0], format, width, height, 0 ); if ( error ) return error; mlt_position position = mlt_transition_get_position( transition, a_frame ); mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) ); double time = (double) position / mlt_profile_fps( profile ); int length = mlt_transition_get_length( transition ); // Special cairoblend handling for an override from the cairoblend_mode filter. if (is_cairoblend) { mlt_properties_set(a_props, CAIROBLEND_MODE_PROPERTY, blend_mode); } process_frei0r_item( MLT_TRANSITION_SERVICE(transition), position, time, length, !invert ? a_frame : b_frame, images, width, height ); *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 error; } mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_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 transition ) { destruct ( MLT_TRANSITION_PROPERTIES ( transition ) ); transition->close = NULL; mlt_transition_close( transition ); } mlt-6.20.0/src/modules/gtk2/000077500000000000000000000000001362234133600155045ustar00rootroot00000000000000mlt-6.20.0/src/modules/gtk2/CMakeLists.txt000066400000000000000000000025251362234133600202500ustar00rootroot00000000000000set(mltgtk2_src factory.c) set(mltgtk2_lib mlt m Threads::Threads) set(mltgtk2_def "") find_package(GTK2 COMPONENTS gtk) if(TARGET GTK2::gtk) list(APPEND mltgtk2_src consumer_gtk2.c) list(APPEND mltgtk2_lib GTK2::gtk) list(APPEND mltgtk2_def USE_GTK2) endif() if(TARGET GTK2::gdk_pixbuf) list(APPEND mltgtk2_src producer_pixbuf.c pixops.c filter_rescale.c) list(APPEND mltgtk2_lib GTK2::gdk_pixbuf) list(APPEND mltgtk2_def USE_PIXBUF) endif() if(TARGET GTK2::pango) pkg_check_modules(fontconfig IMPORTED_TARGET fontconfig) if(TARGET PkgConfig::fontconfig) list(APPEND mltgtk2_src producer_pango.c) list(APPEND mltgtk2_lib GTK2::pango PkgConfig::fontconfig) list(APPEND mltgtk2_def USE_PANGO) endif() endif() pkg_check_modules(libexif IMPORTED_TARGET libexif) if(TARGET PkgConfig::libexif) list(APPEND mltgtk2_lib PkgConfig::libexif) list(APPEND mltgtk2_def USE_EXIF) endif() # Only for MMX but not x86_64: deprecated # list(APPEND mltgtk2_src have_mmx.S scale_line_22_yuv_mmx.S) add_library(mltgtk2 MODULE ${mltgtk2_src}) target_link_libraries(mltgtk2 ${mltgtk2_lib}) target_compile_definitions(mltgtk2 PRIVATE ${mltgtk2_def}) install(TARGETS mltgtk2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/gtk2) mlt-6.20.0/src/modules/gtk2/Makefile000066400000000000000000000034661362234133600171550ustar00rootroot00000000000000include ../../../config.mak include config.mak CFLAGS := -I../.. $(CFLAGS) LDFLAGS := -L../../framework -lmlt -lpthread -lm $(LDFLAGS) TARGET = ../libmltgtk2$(LIBSUF) OBJS = factory.o ifdef USE_GTK2 OBJS += consumer_gtk2.o CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags gtk+-2.0) LDFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --libs gtk+-2.0) endif ifdef USE_PIXBUF OBJS += producer_pixbuf.o pixops.o filter_rescale.o CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags gdk-pixbuf-2.0) LDFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --libs gdk-pixbuf-2.0) endif ifdef USE_EXIF CFLAGS += $(EXIFCXXFLAGS) LDFLAGS += $(EXIFLIBS) 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 += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags pangoft2) CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags-only-I freetype2 | awk '{for (i=1; i<=NF; i++) $$i=sprintf("%s/freetype", $$i); print}') LDFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --libs pangoft2) ifeq ($(targetos),Darwin) LDFLAGS += -liconv endif ifeq ($(targetos),FreeBSD) LDFLAGS += -liconv endif ifeq ($(targetos), MinGW) LDFLAGS += -liconv endif 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/gtk2" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/gtk2" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/gtk2/configure000077500000000000000000000043741362234133600174230ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF GTK+ options: --gtk2-prefix=path - Override the gtk+-2.0 prefix for pkg-config EOF else pkgconfig_prefix= for i in "$@" do case $i in --gtk2-prefix=* ) pkgconfig_prefix="${i#--gtk2-prefix=}" ;; esac done [ "$pkgconfig_prefix" != "" ] && pkgconfig_prefix="--define-variable=prefix=\"$pkgconfig_prefix\"" pkg-config $pkgconfig_prefix gtk+-2.0 2> /dev/null disable_gtk2=$? pkg-config $pkgconfig_prefix gdk-pixbuf-2.0 2> /dev/null disable_pixbuf=$? pkg-config $pkgconfig_prefix gdk-pixbuf-2.0 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 echo > config.mak if [ "$disable_gtk2" = "0" ] then echo "CFLAGS += -DUSE_GTK2" >> config.mak echo "USE_GTK2=1" >> config.mak else echo "- gtk2 not found: gtk2 preview disabled" fi if [ "$disable_pixbuf" = "0" ] then echo "CFLAGS += -DUSE_PIXBUF" >> config.mak echo "USE_PIXBUF=1" >> config.mak else echo "- pixbuf not found: pixbuf loader and rescaler disabled" fi if [ "$disable_pango" = "0" ] then echo "CFLAGS += -DUSE_PANGO" >> config.mak echo "USE_PANGO=1" >> config.mak else echo "- pango not found: pango titler disabled" fi [ "$pkgconfig_prefix" != "" ] && echo "PKGCONFIG_PREFIX=$pkgconfig_prefix" >> config.mak pkg-config --exists 'libexif' if [ $? -eq 0 ] then echo "- Libexif found, enabling auto rotate" echo "USE_EXIF=1" >> config.mak echo EXIFCXXFLAGS=$(pkg-config --cflags libexif ) >> config.mak echo EXIFCXXFLAGS += -DUSE_EXIF >> config.mak echo EXIFLIBS=$(pkg-config --libs libexif) >> config.mak elif [ -d "$exif_libdir" -a -d "$exif_includedir" ] then # test if we have a libexif if [ -f "$exif_libdir/exif-data.h" ] then echo "- Libexif found, enabling auto rotate" echo "USE_EXIF=1" >> config.mak echo EXIFCXXFLAGS=-I$exif_includedir >> config.mak echo EXIFCXXFLAGS += -DUSE_EXIF >> config.mak echo EXIFLIBS=-L$exif_libdir lexif >> config.mak else echo "- Libexif not found, disabling exif features (auto rotate)" fi else echo "- Libexif not found, disabling exif features (auto rotate)" fi exit 0 fi mlt-6.20.0/src/modules/gtk2/consumer_gtk2.c000066400000000000000000000037741362234133600204450ustar00rootroot00000000000000/* * consumer_gtk2.c -- A consumer for GTK2 apps * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #ifdef _WIN32 #include #else #include #endif #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 ) { #ifdef _WIN32 HWND xwin = GDK_WINDOW_HWND( widget->window ); #else Window xwin = GDK_WINDOW_XWINDOW( widget->window ); #endif char windowhack[ 32 ]; sprintf( windowhack, "%ld", (long) 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; } mlt-6.20.0/src/modules/gtk2/consumer_gtk2_preview.yml000066400000000000000000000004251362234133600225530ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: gtk_preview title: GTK+ description: A wrapper for sdl_preview that makes it easy to embed in GTK+ applications. version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video mlt-6.20.0/src/modules/gtk2/factory.c000066400000000000000000000062461362234133600173270ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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( ); if ( getenv("MLT_PIXBUF_PRODUCER_CACHE") ) { int n = atoi( getenv("MLT_PIXBUF_PRODUCER_CACHE" ) ); mlt_service_cache_set_size( NULL, "pixbuf.image", n ); mlt_service_cache_set_size( NULL, "pixbuf.alpha", n ); mlt_service_cache_set_size( NULL, "pixbuf.pixbuf", n ); } if ( getenv("MLT_PANGO_PRODUCER_CACHE") ) { int n = atoi( getenv("MLT_PANGO_PRODUCER_CACHE" ) ); mlt_service_cache_set_size( NULL, "pango.image", n ); } } } 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; } static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/gtk2/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } 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 ); MLT_REGISTER_METADATA( consumer_type, "gtk2_preview", metadata, "consumer_gtk2_preview.yml" ); MLT_REGISTER_METADATA( filter_type, "gtkrescale", metadata, "filter_rescale.yml" ); MLT_REGISTER_METADATA( producer_type, "pango", metadata, "producer_pango.yml" ); MLT_REGISTER_METADATA( producer_type, "pixbuf", metadata, "producer_pixbuf.yml" ); } mlt-6.20.0/src/modules/gtk2/filter_rescale.c000066400000000000000000000100151362234133600206300ustar00rootroot00000000000000/* * filter_rescale.c -- scale the producer video frame size to match the consumer * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pixops.h" #include #include #include #include #include #include #include static int filter_scale( mlt_frame this, uint8_t **image, mlt_image_format *format, 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 || strcmp( interps, "bicubic" ) == 0 ) interp = PIXOPS_INTERP_HYPER; int bpp; int size = mlt_image_format_size( *format, owidth, oheight, &bpp ); // Carry out the rescaling switch ( *format ) { case mlt_image_yuv422: { // Create the output image uint8_t *output = mlt_pool_alloc( size ); // 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_frame_set_image( this, output, size, mlt_pool_release ); // Return the output *image = output; break; } case mlt_image_rgb24: case mlt_image_rgb24a: case mlt_image_opengl: { if ( strcmp( interps, "none" ) && ( iwidth != owidth || iheight != oheight ) ) { // Create the output image uint8_t *output = mlt_pool_alloc( size ); GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( *image, GDK_COLORSPACE_RGB, ( *format == mlt_image_rgb24a || *format == mlt_image_opengl ), 8, iwidth, iheight, iwidth * bpp, NULL, NULL ); GdkPixbuf *scaled = gdk_pixbuf_scale_simple( pixbuf, owidth, oheight, interp ); g_object_unref( pixbuf ); int src_stride = gdk_pixbuf_get_rowstride( scaled ); int dst_stride = owidth * bpp; if ( src_stride != dst_stride ) { int y = oheight; uint8_t *src = gdk_pixbuf_get_pixels( scaled ); uint8_t *dst = output; while ( y-- ) { memcpy( dst, src, dst_stride ); dst += dst_stride; src += src_stride; } } else { memcpy( output, gdk_pixbuf_get_pixels( scaled ), owidth * oheight * bpp ); } g_object_unref( scaled ); // Now update the frame mlt_frame_set_image( this, output, size, mlt_pool_release ); // Return the output *image = output; } break; } default: break; } 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; } mlt-6.20.0/src/modules/gtk2/filter_rescale.yml000066400000000000000000000022031362234133600212070ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: gtkrescale title: Gtk Rescale version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video - Hidden description: > Scale the producer video frame size to match the consumer. This filter is designed for use as a normaliser for the loader producer. notes: > 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. parameters: - identifier: argument title: Interpolation type: string description: The rescaling method. values: - nearest (lowest quality, fastest) - tiles - bilinear (good quality, moderate speed) - hyper (best quality, slowest) required: no readonly: no default: bilinear widget: combo mlt-6.20.0/src/modules/gtk2/have_mmx.S000066400000000000000000000030451362234133600174360ustar00rootroot00000000000000/* * Copyright (C) 2000 Red Hat, Inc * * This library is free software; you can redistribute it 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ .file "have_mmx.S" .version "01.01" gcc2_compiled.: .text .align 16 #if !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(__INTERIX) /* Magic indicating no need for an executable stack */ #if !defined __powerpc64__ && !defined __ia64__ .section .note.GNU-stack; .previous #endif .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 mlt-6.20.0/src/modules/gtk2/pixops.c000066400000000000000000000472021362234133600171770ustar00rootroot00000000000000/* GdkPixbuf library - Scaling and compositing functions * * Original: * Copyright (C) 2000 Red Hat, Inc * Author: Owen Taylor * * Modification for MLT: * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, 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 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; guchar *outbuf = dest_buf + dest_rowstride * i; guchar *outbuf_end = outbuf + dest_channels * ( render_x1 - render_x0 ); 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 ); } mlt-6.20.0/src/modules/gtk2/pixops.h000066400000000000000000000047351362234133600172100ustar00rootroot00000000000000/* GdkPixbuf library - Scaling and compositing functions * * Original: * Copyright (C) 2000 Red Hat, Inc * Author: Owen Taylor * * Modification for MLT: * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, 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 mlt-6.20.0/src/modules/gtk2/producer_pango.c000066400000000000000000001046231362234133600206650ustar00rootroot00000000000000/* * producer_pango.c -- a pango-based titler * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #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 pango_cached_image_s { uint8_t *image, *alpha; mlt_image_format format; int width, height; }; static void pango_cached_image_destroy( void* p ) { struct pango_cached_image_s* i = p; if ( !i ) return; if ( i->image ) mlt_pool_release( i->image ); if ( i->alpha ) mlt_pool_release( i->alpha ); mlt_pool_release( i ); }; struct producer_pango_s { struct mlt_producer_s parent; int width; int height; GdkPixbuf *pixbuf; char *fgcolor; char *bgcolor; char *olcolor; int align; int pad; int outline; char *markup; char *text; char *font; char *family; int size; int style; int weight; int stretch; int rotate; int width_crop; int width_fit; int wrap_type; int wrap_width; int line_spacing; double aspect_ratio; }; static void clean_cached( producer_pango self ) { mlt_service_cache_put( MLT_PRODUCER_SERVICE( &self->parent ), "pango.image", NULL, 0, NULL ); } // 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, rgba_color ol, int pad, int align, char* family, int style, int weight, int stretch, int size, int outline, int rotate, int width_crop, int width_fit, int wrap_type, int wrap_width, int line_spacing, double aspect_ratio ); static void fill_pixbuf( GdkPixbuf* pixbuf, FT_Bitmap* bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg ); static void fill_pixbuf_with_outline( GdkPixbuf* pixbuf, FT_Bitmap* bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg, rgba_color ol, int outline ); /** 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 parse_alignment( 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; } /** Parse the style property. */ static int parse_style( char* style ) { int ret = PANGO_STYLE_NORMAL; if( !strncmp(style, "italic", 6) ) ret = PANGO_STYLE_ITALIC; if( !strncmp(style, "oblique", 7) ) ret = PANGO_STYLE_OBLIQUE; return ret; } static PangoFT2FontMap *fontmap = NULL; static void on_fontmap_reload( ); mlt_producer producer_pango_init( const char *filename ) { producer_pango self = calloc( 1, sizeof( struct producer_pango_s ) ); if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) { mlt_producer producer = &self->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( &self->parent ); mlt_events_register( properties, "fontmap-reload", NULL ); mlt_events_listen( properties, producer, "fontmap-reload", (mlt_listener) on_fontmap_reload ); // Set the default properties mlt_properties_set( properties, "fgcolour", "0xffffffff" ); mlt_properties_set( properties, "bgcolour", "0x00000000" ); mlt_properties_set( properties, "olcolour", "0x00000000" ); mlt_properties_set_int( properties, "align", pango_align_left ); mlt_properties_set_int( properties, "pad", 0 ); mlt_properties_set_int( properties, "outline", 0 ); mlt_properties_set( properties, "text", "" ); mlt_properties_set( properties, "font", NULL ); mlt_properties_set( properties, "family", "Sans" ); mlt_properties_set_int( properties, "size", 48 ); mlt_properties_set( properties, "style", "normal" ); mlt_properties_set( properties, "encoding", "UTF-8" ); mlt_properties_set_int( properties, "weight", PANGO_WEIGHT_NORMAL ); mlt_properties_set_int( properties, "stretch", PANGO_STRETCH_NORMAL + 1 ); mlt_properties_set_int( properties, "rotate", 0 ); mlt_properties_set_int( properties, "seekable", 1 ); if ( filename == NULL || ( filename && ( !strcmp( filename, "" ) || strstr( filename, "" ) // workaround for old kdenlive countdown generator || strstr( filename, "<producer>" ) ) ) ) { 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; if ( strrchr( markup, '.' ) ) ( *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_position out_point = 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 ); out_point = MAX( out_point, item.frame ); } mlt_geometry_interpolate( key_frames ); mlt_properties_set_position( properties, "length", out_point + 1 ); mlt_properties_set_position( properties, "out", out_point ); } else { mlt_properties_set( properties, "resource", filename ); FILE *f = mlt_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 ); if ( markup ) strcat( markup, line ); } else { markup = strdup( line ); } } fclose( f ); if ( markup && markup[ strlen( markup ) - 1 ] == '\n' ) markup[ strlen( markup ) - 1 ] = '\0'; if ( markup ) mlt_properties_set( properties, "markup", markup ); else mlt_properties_set( properties, "markup", "" ); free( markup ); } else { producer->close = NULL; mlt_producer_close( producer ); producer = NULL; free( self ); } } return producer; } free( self ); 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, 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; } /** 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 ( text && ( 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 ); result = 0; } iconv_close( cd ); return result; } static void refresh_image( producer_pango self, 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 mlt_producer producer = &self->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" ); char *ol = mlt_properties_get( producer_props, "olcolour" ); int align = parse_alignment( mlt_properties_get( producer_props, "align" ) ); int pad = mlt_properties_get_int( producer_props, "pad" ); int outline = mlt_properties_get_int( producer_props, "outline" ); 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 *family = mlt_properties_get( producer_props, "family" ); int style = parse_style( mlt_properties_get( producer_props, "style" ) ); char *encoding = mlt_properties_get( producer_props, "encoding" ); int weight = mlt_properties_get_int( producer_props, "weight" ); int stretch = mlt_properties_get_int( producer_props, "stretch" ); int rotate = mlt_properties_get_int( producer_props, "rotate" ); int size = mlt_properties_get_int( producer_props, "size" ); int width_crop = mlt_properties_get_int( producer_props, "width_crop" ); int width_fit = mlt_properties_get_int( producer_props, "width_fit" ); int wrap_type = mlt_properties_get_int( producer_props, "wrap_type" ); int wrap_width = mlt_properties_get_int( producer_props, "wrap_width" ); int line_spacing = mlt_properties_get_int( properties, "line_spacing" ); double aspect_ratio = mlt_properties_get_double( properties, "aspect_ratio" ); int property_changed = 0; if ( pixbuf == NULL ) { // Check for file support 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, mlt_frame_original_position( frame ) ); sprintf( temp, "%d", item.frame ); markup = mlt_properties_get( contents, temp ); } // See if any properties changed property_changed = ( align != self->align ); property_changed = property_changed || ( self->fgcolor == NULL || ( fg && strcmp( fg, self->fgcolor ) ) ); property_changed = property_changed || ( self->bgcolor == NULL || ( bg && strcmp( bg, self->bgcolor ) ) ); property_changed = property_changed || ( self->olcolor == NULL || ( ol && strcmp( ol, self->olcolor ) ) ); property_changed = property_changed || ( pad != self->pad ); property_changed = property_changed || ( outline != self->outline ); property_changed = property_changed || ( markup && self->markup && strcmp( markup, self->markup ) ); property_changed = property_changed || ( text && self->text && strcmp( text, self->text ) ); property_changed = property_changed || ( font && self->font && strcmp( font, self->font ) ); property_changed = property_changed || ( family && self->family && strcmp( family, self->family ) ); property_changed = property_changed || ( weight != self->weight ); property_changed = property_changed || ( stretch != self->stretch ); property_changed = property_changed || ( rotate != self->rotate ); property_changed = property_changed || ( style != self->style ); property_changed = property_changed || ( size != self->size ); property_changed = property_changed || ( width_crop != self->width_crop ); property_changed = property_changed || ( width_fit != self->width_fit ); property_changed = property_changed || ( wrap_type != self->wrap_type ); property_changed = property_changed || ( wrap_width != self->wrap_width ); property_changed = property_changed || ( line_spacing != self->line_spacing ); property_changed = property_changed || ( aspect_ratio != self->aspect_ratio ); // Save the properties for next comparison self->align = align; self->pad = pad; self->outline = outline; set_string( &self->fgcolor, fg, "0xffffffff" ); set_string( &self->bgcolor, bg, "0x00000000" ); set_string( &self->olcolor, ol, "0x00000000" ); set_string( &self->markup, markup, NULL ); set_string( &self->text, text, NULL ); set_string( &self->font, font, NULL ); set_string( &self->family, family, "Sans" ); self->weight = weight; self->stretch = stretch; self->rotate = rotate; self->style = style; self->size = size; self->width_crop = width_crop; self->width_fit = width_fit; self->wrap_type = wrap_type; self->wrap_width = wrap_width; self->line_spacing = line_spacing; self->aspect_ratio = aspect_ratio; } if ( pixbuf == NULL && property_changed ) { rgba_color fgcolor = parse_color( self->fgcolor, mlt_properties_get_int( producer_props, "fgcolour" ) ); rgba_color bgcolor = parse_color( self->bgcolor, mlt_properties_get_int( producer_props, "bgcolour" ) ); rgba_color olcolor = parse_color( self->olcolor, mlt_properties_get_int( producer_props, "olcolour" ) ); if ( self->pixbuf ) g_object_unref( self->pixbuf ); self->pixbuf = NULL; clean_cached( self ); // 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( &self->markup, markup, NULL ); } if ( text != NULL && iconv_utf8( producer_props, "text", encoding ) != -1 ) { text = mlt_properties_get( producer_props, "text" ); set_string( &self->text, text, NULL ); } } // Render the title pixbuf = pango_get_pixbuf( markup, text, font, fgcolor, bgcolor, olcolor, pad, align, family, style, weight, stretch, size, outline, rotate, width_crop, width_fit, wrap_type, wrap_width, line_spacing, aspect_ratio ); if ( pixbuf != NULL ) { // Register self 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, "meta.media.width", gdk_pixbuf_get_width( pixbuf ) ); mlt_properties_set_int( producer_props, "meta.media.height", gdk_pixbuf_get_height( pixbuf ) ); // Store the width/height of the pixbuf temporarily self->width = gdk_pixbuf_get_width( pixbuf ); self->height = gdk_pixbuf_get_height( pixbuf ); } } else if ( pixbuf == NULL && width > 0 && ( self->pixbuf == NULL || width != self->width || height != self->height ) ) { if ( self->pixbuf ) g_object_unref( self->pixbuf ); self->pixbuf = NULL; clean_cached( self ); 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 || strcmp( interps, "bicubic" ) == 0 ) interp = GDK_INTERP_HYPER; // fprintf(stderr,"%s: scaling from %dx%d to %dx%d\n", __FILE__, self->width, self->height, width, height); // Note - the original pixbuf is already safe and ready for destruction self->pixbuf = gdk_pixbuf_scale_simple( pixbuf, width, height, interp ); clean_cached( self ); // Store width and height self->width = width; self->height = height; } // Set width/height mlt_properties_set_int( properties, "width", self->width ); mlt_properties_set_int( properties, "height", self->height ); } static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; producer_pango self = ( producer_pango ) mlt_frame_pop_service( frame ); // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); *width = mlt_properties_get_int( properties, "rescale_width" ); *height = mlt_properties_get_int( properties, "rescale_height" ); mlt_service_lock( MLT_PRODUCER_SERVICE( &self->parent ) ); // Refresh the image pthread_mutex_lock( &pango_mutex ); refresh_image( self, frame, *width, *height ); // Get width and height *width = self->width; *height = self->height; // Always clone here to allow 'animated' text if ( self->pixbuf ) { int size, bpp; uint8_t *buf; mlt_cache_item cached_item = mlt_service_cache_get( MLT_PRODUCER_SERVICE( &self->parent ), "pango.image" ); struct pango_cached_image_s* cached = mlt_cache_item_data( cached_item, NULL ); // destroy cached data if request is differ if ( !cached || ( cached && (cached->format != *format || cached->width != *width || cached->height != *height ))) { mlt_cache_item_close( cached_item ); cached_item = NULL; cached = NULL; clean_cached( self ); } // create cached image if ( !cached ) { int dst_stride, src_stride; cached = mlt_pool_alloc( sizeof( struct pango_cached_image_s )); cached->width = self->width; cached->height = self->height; cached->format = gdk_pixbuf_get_has_alpha( self->pixbuf ) ? mlt_image_rgb24a : mlt_image_rgb24; cached->alpha = NULL; cached->image = NULL; src_stride = gdk_pixbuf_get_rowstride( self->pixbuf ); dst_stride = self->width * ( mlt_image_rgb24a == cached->format ? 4 : 3 ); size = mlt_image_format_size( cached->format, cached->width, cached->height, &bpp ); buf = mlt_pool_alloc( size ); uint8_t *buf_save = buf; if ( src_stride != dst_stride ) { int y = self->height; uint8_t *src = gdk_pixbuf_get_pixels( self->pixbuf ); uint8_t *dst = buf; while ( y-- ) { memcpy( dst, src, dst_stride ); dst += dst_stride; src += src_stride; } } else { memcpy( buf, gdk_pixbuf_get_pixels( self->pixbuf ), src_stride * self->height ); } // convert image if(frame->convert_image && cached->format != *format) { frame->convert_image( frame, &buf, &cached->format, *format ); *format = cached->format; if ( buf != buf_save ) mlt_pool_release( buf_save ); } size = mlt_image_format_size(cached->format, cached->width, cached->height, &bpp ); cached->image = mlt_pool_alloc( size ); memcpy( cached->image, buf, size ); if ( ( buf = mlt_frame_get_alpha( frame ) ) ) { size = cached->width * cached->height; cached->alpha = mlt_pool_alloc( size ); memcpy( cached->alpha, buf, size ); } } if ( cached ) { // clone image surface size = mlt_image_format_size(cached->format, cached->width, cached->height, &bpp ); buf = mlt_pool_alloc( size ); memcpy( buf, cached->image, size ); // set image surface mlt_frame_set_image( frame, buf, size, mlt_pool_release ); *buffer = buf; // set alpha if ( cached->alpha ) { size = cached->width * cached->height; buf = mlt_pool_alloc( size ); memcpy( buf, cached->alpha, size ); mlt_frame_set_alpha( frame, buf, size, mlt_pool_release ); } } if ( cached_item ) mlt_cache_item_close( cached_item ); else mlt_service_cache_put( MLT_PRODUCER_SERVICE( &self->parent ), "pango.image", cached, sizeof( struct pango_cached_image_s ), pango_cached_image_destroy ); } else { error = 1; } pthread_mutex_unlock( &pango_mutex ); mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) ); return error; } static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { producer_pango self = producer->child; // Fetch the producers properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); // Generate a frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); // 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 ); double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" ); if ( force_ratio > 0.0 ) mlt_properties_set_double( properties, "aspect_ratio", force_ratio ); else { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); } // Refresh the pango image pthread_mutex_lock( &pango_mutex ); refresh_image( self, *frame, 0, 0 ); pthread_mutex_unlock( &pango_mutex ); // Stack the get image callback mlt_frame_push_service( *frame, self ); 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 self = parent->child; if ( self->pixbuf ) g_object_unref( self->pixbuf ); mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) ); free( self->fgcolor ); free( self->bgcolor ); free( self->olcolor ); free( self->markup ); free( self->text ); free( self->font ); free( self->family ); parent->close = NULL; mlt_producer_close( parent ); free( self ); } 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, rgba_color ol, int pad, int align, char* family, int style, int weight, int stretch, int size, int outline, int rotate, int width_crop, int width_fit, int wrap_type, int wrap_width, int line_spacing, double aspect_ratio ) { PangoContext *context = pango_ft2_font_map_create_context( fontmap ); PangoLayout *layout = pango_layout_new( context ); int w, h; int x = 0, y = 0; GdkPixbuf *pixbuf = NULL; FT_Bitmap bitmap; PangoFontDescription *desc = NULL; if( font ) { // Support for deprecated "font" property. desc = pango_font_description_from_string( font ); pango_ft2_font_map_set_resolution( fontmap, 72, 72 ); } else { desc = pango_font_description_from_string( family ); pango_font_description_set_size( desc, PANGO_SCALE * size ); pango_font_description_set_style( desc, (PangoStyle) style ); } pango_font_description_set_weight( desc, ( PangoWeight ) weight ); if ( stretch ) pango_font_description_set_stretch( desc, ( PangoStretch ) ( stretch - 1 ) ); // set line_spacing if ( line_spacing ) pango_layout_set_spacing( layout, PANGO_SCALE * line_spacing ); // set wrapping constraints if ( wrap_width <= 0 ) pango_layout_set_width( layout, -1 ); else { pango_layout_set_width( layout, PANGO_SCALE * wrap_width ); pango_layout_set_wrap( layout, ( PangoWrapMode ) wrap_type ); } pango_layout_set_font_description( layout, desc ); 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 ) { pango_layout_set_text( layout, text, strlen( text ) ); } else { // Pango doesn't like empty strings pango_layout_set_text( layout, " ", 2 ); } if ( rotate ) { double n_x, n_y; PangoRectangle rect; PangoMatrix m_layout = PANGO_MATRIX_INIT, m_offset = PANGO_MATRIX_INIT; pango_matrix_rotate( &m_layout, rotate ); pango_matrix_rotate( &m_offset, -rotate ); pango_context_set_base_gravity( context, PANGO_GRAVITY_AUTO ); pango_context_set_matrix( context, &m_layout ); pango_layout_context_changed( layout ); pango_layout_get_extents( layout, NULL, &rect ); pango_matrix_transform_rectangle( pango_context_get_matrix( context ), &rect); n_x = -rect.x; n_y = -rect.y; pango_matrix_transform_point( &m_offset, &n_x, &n_y ); rect.x = n_x; rect.y = n_y; pango_extents_to_pixels( &rect, NULL ); w = rect.width; h = rect.height; x = rect.x; y = rect.y; } else pango_layout_get_pixel_size( layout, &w, &h ); // respect aspect ratio if ( 0.0 < aspect_ratio && aspect_ratio != 1.0) { double n_x, n_y; PangoRectangle rect; PangoMatrix m_layout = PANGO_MATRIX_INIT, m_offset = PANGO_MATRIX_INIT; #if 1 pango_matrix_scale( &m_layout, 1.0 / aspect_ratio, 1.0 ); pango_matrix_scale( &m_offset, 1.0 / aspect_ratio, 1.0 ); #else pango_matrix_scale( &m_layout, 1.0, aspect_ratio ); pango_matrix_scale( &m_offset, 1.0, aspect_ratio ); #endif pango_context_set_base_gravity( context, PANGO_GRAVITY_AUTO ); pango_context_set_matrix( context, &m_layout ); pango_layout_context_changed( layout ); pango_layout_get_extents( layout, NULL, &rect ); pango_matrix_transform_rectangle( pango_context_get_matrix( context ), &rect); n_x = -rect.x; n_y = -rect.y; pango_matrix_transform_point( &m_offset, &n_x, &n_y ); rect.x = n_x; rect.y = n_y; pango_extents_to_pixels( &rect, NULL ); w = rect.width; h = rect.height; x = rect.x; y = rect.y; } // limit width if ( width_crop && w > width_crop) w = width_crop; else if (width_fit && w > width_fit) { double n_x, n_y; PangoRectangle rect; PangoMatrix m_layout = PANGO_MATRIX_INIT, m_offset = PANGO_MATRIX_INIT; pango_matrix_scale( &m_layout, width_fit / (double)w, 1.0 ); pango_matrix_scale( &m_offset, width_fit / (double)w, 1.0 ); pango_context_set_base_gravity( context, PANGO_GRAVITY_AUTO ); pango_context_set_matrix( context, &m_layout ); pango_layout_context_changed( layout ); pango_layout_get_extents( layout, NULL, &rect ); pango_matrix_transform_rectangle( pango_context_get_matrix( context ), &rect); n_x = -rect.x; n_y = -rect.y; pango_matrix_transform_point( &m_offset, &n_x, &n_y ); rect.x = n_x; rect.y = n_y; pango_extents_to_pixels( &rect, NULL ); w = rect.width; h = rect.height; x = rect.x; y = rect.y; } if ( pad == 0 ) pad = 1; pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE /* has alpha */, 8, w + 2 * pad, h + 2 * pad ); pango_draw_background( pixbuf, bg ); 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, x, y ); if ( outline ) { fill_pixbuf_with_outline( pixbuf, &bitmap, w, h, pad, align, fg, bg, ol, outline ); } else { fill_pixbuf( pixbuf, &bitmap, w, h, pad, align, fg, bg ); } mlt_pool_release( bitmap.buffer ); pango_font_description_free( desc ); g_object_unref( layout ); g_object_unref( context ); return pixbuf; } static void fill_pixbuf( GdkPixbuf* pixbuf, FT_Bitmap* bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg ) { int stride = gdk_pixbuf_get_rowstride( pixbuf ); uint8_t* src = bitmap->buffer; int x = ( gdk_pixbuf_get_width( pixbuf ) - w - 2 * pad ) * align / 2 + pad; uint8_t* dest = gdk_pixbuf_get_pixels( pixbuf ) + 4 * x + pad * stride; int j = h; int i = 0; uint8_t *d, *s, a; 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; } } static void fill_pixbuf_with_outline( GdkPixbuf* pixbuf, FT_Bitmap* bitmap, int w, int h, int pad, int align, rgba_color fg, rgba_color bg, rgba_color ol, int outline ) { int stride = gdk_pixbuf_get_rowstride( pixbuf ); int x = ( gdk_pixbuf_get_width( pixbuf ) - w - 2 * pad ) * align / 2 + pad; uint8_t* dest = gdk_pixbuf_get_pixels( pixbuf ) + 4 * x + pad * stride; int j ,i; uint8_t *d = NULL; float a_ol = 0; float a_fg = 0; for ( j = 0; j < h; j++ ) { d = dest; for ( i = 0; i < w; i++ ) { #define geta(x, y) (float) bitmap->buffer[ (y) * bitmap->pitch + (x) ] / 255.0 a_ol = geta(i, j); // One pixel fake circle if ( i > 0 ) a_ol = MAX( a_ol, geta(i - 1, j) ); if ( i < w - 1 ) a_ol = MAX( a_ol, geta(i + 1, j) ); if ( j > 0 ) a_ol = MAX( a_ol, geta(i, j - 1) ); if ( j < h - 1 ) a_ol = MAX( a_ol, geta(i, j + 1) ); if ( outline >= 2 ) { // Two pixels fake circle if ( i > 1 ) { a_ol = MAX( a_ol, geta(i - 2, j) ); if ( j > 0 ) a_ol = MAX( a_ol, geta(i - 2, j - 1) ); if ( j < h - 1 ) a_ol = MAX( a_ol, geta(i - 2, j + 1) ); } if ( i > 0 ) { if ( j > 0 ) a_ol = MAX( a_ol, geta(i - 1, j - 1) ); if ( j > 1 ) a_ol = MAX( a_ol, geta(i - 1, j - 2) ); if ( j < h - 1 ) a_ol = MAX( a_ol, geta(i - 1, j + 1) ); if ( j < h - 2 ) a_ol = MAX( a_ol, geta(i - 1, j + 2) ); } if ( j > 1 ) a_ol = MAX( a_ol, geta(i, j - 2) ); if ( j < h - 2 ) a_ol = MAX( a_ol, geta(i, j + 2) ); if ( i < w - 1 ) { if ( j > 0 ) a_ol = MAX( a_ol, geta(i + 1, j - 1) ); if ( j > 1 ) a_ol = MAX( a_ol, geta(i + 1, j - 2) ); if ( j < h - 1 ) a_ol = MAX( a_ol, geta(i + 1, j + 1) ); if ( j < h - 2 ) a_ol = MAX( a_ol, geta(i + 1, j + 2) ); } if ( i < w - 2 ) { a_ol = MAX( a_ol, geta(i + 2, j) ); if ( j > 0 ) a_ol = MAX( a_ol, geta(i + 2, j - 1) ); if ( j < h - 1 ) a_ol = MAX( a_ol, geta(i + 2, j + 1) ); } } if ( outline >= 3 ) { // Three pixels fake circle if ( i > 2 ) { a_ol = MAX( a_ol, geta(i - 3, j) ); if ( j > 0 ) a_ol = MAX( a_ol, geta(i - 3, j - 1) ); if ( j < h - 1 ) a_ol = MAX( a_ol, geta(i - 3, j + 1) ); } if ( i > 1 ) { if ( j > 1 ) a_ol = MAX( a_ol, geta(i - 2, j - 2) ); if ( j < h - 2 ) a_ol = MAX( a_ol, geta(i - 2, j + 2) ); } if ( i > 0 ) { if ( j > 2 ) a_ol = MAX( a_ol, geta(i - 1, j - 3) ); if ( j < h - 3 ) a_ol = MAX( a_ol, geta(i - 1, j + 3) ); } if ( j > 2 ) a_ol = MAX( a_ol, geta(i, j - 3) ); if ( j < h - 3 ) a_ol = MAX( a_ol, geta(i, j + 3) ); if ( i < w - 1 ) { if ( j > 2 ) a_ol = MAX( a_ol, geta(i + 1, j - 3) ); if ( j < h - 3 ) a_ol = MAX( a_ol, geta(i + 1, j + 3) ); } if ( i < w - 2 ) { if ( j > 1 ) a_ol = MAX( a_ol, geta(i + 2, j - 2) ); if ( j < h - 2 ) a_ol = MAX( a_ol, geta(i + 2, j + 2) ); } if ( i < w - 3 ) { a_ol = MAX( a_ol, geta(i + 3, j) ); if ( j > 0 ) a_ol = MAX( a_ol, geta(i + 3, j - 1) ); if ( j < h - 1 ) a_ol = MAX( a_ol, geta(i + 3, j + 1) ); } } a_fg = ( float ) bitmap->buffer[ j * bitmap->pitch + i ] / 255.0; *d++ = ( int ) ( a_fg * fg.r + ( 1 - a_fg ) * ( a_ol * ol.r + ( 1 - a_ol ) * bg.r ) ); *d++ = ( int ) ( a_fg * fg.g + ( 1 - a_fg ) * ( a_ol * ol.g + ( 1 - a_ol ) * bg.g ) ); *d++ = ( int ) ( a_fg * fg.b + ( 1 - a_fg ) * ( a_ol * ol.b + ( 1 - a_ol ) * bg.b ) ); *d++ = ( int ) ( a_fg * fg.a + ( 1 - a_fg ) * ( a_ol * ol.a + ( 1 - a_ol ) * bg.a ) ); } dest += stride; } } static void on_fontmap_reload() { PangoFT2FontMap *new_fontmap = NULL, *old_fontmap = NULL; FcInitReinitialize(); new_fontmap = (PangoFT2FontMap*) pango_ft2_font_map_new(); pthread_mutex_lock( &pango_mutex ); old_fontmap = fontmap; fontmap = new_fontmap; pthread_mutex_unlock( &pango_mutex ); if ( old_fontmap ) g_object_unref( old_fontmap ); } mlt-6.20.0/src/modules/gtk2/producer_pango.yml000066400000000000000000000213211362234133600212350ustar00rootroot00000000000000schema_version: 0.3 type: producer identifier: pango title: Pango version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > A title generator that uses the Pango international text layout and Freetype2 font renderer. notes: > Supplying a filename with extension ".txt" causes the loader 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 melt commands. For MLT XML, 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. Environment variable MLT_PANGO_PRODUCER_CACHE could be used to override and increase the size of cached converted images of simultaneous use. Fontset used by pango producer loaded once. That behavior prevents using new fonts till process used pango producer been restarted. To force fontmap reload you need to send signal "fontmap-reload" to pango producer: { mlt_profile profile = mlt_profile_init("dv_pal"); mlt_producer producer = mlt_factory_producer(profile, "pango", NULL); mlt_events_fire(mlt_producer_properties(producer), "fontmap-reload", NULL, NULL ); mlt_producer_close(producer); mlt_profile_close(profile); }; parameters: - identifier: resource title: File type: string description: | A text file containing Pango markup, see: https://developer.gnome.org/pango/stable/PangoMarkupFormat.html requires xml-like encoding special chars from: <, >, & -to- <, >, & readonly: no argument: yes mutable: no widget: fileopen - identifier: markup title: Markup type: string description: | 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- <, >, & readonly: no argument: yes mutable: yes widget: textbox - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner unit: pixels - identifier: align title: Paragraph alignment type: string description: > left, centre, right (also, numbers 0, 1 and 2 can be used respectively) readonly: no default: left mutable: yes widget: combo - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner unit: pixels - identifier: text title: Text type: string description: | A non-markup string in UTF-8 encoding (can contain markup chars un-encoded) readonly: no mutable: yes widget: textbox - identifier: font title: Font type: string description: > The default typeface to use when not using markup. FreeType2 renders at 72 dpi. This property is deprecated. Use family, size and style instead. readonly: no mutable: yes widget: combo - identifier: family title: Font family type: string description: > The default typeface to use when not using markup. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font to use when not using markup. default: 48 readonly: no mutable: yes widget: spinner unit: pixels - identifier: style title: Font style type: string description: > The style of the font to use when not using markup. values: - normal - italic - oblique default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: encoding title: Encoding type: string description: > The text encoding type of the input if not UTF-8. See 'iconv --list' for a list of possible inputs. default: UTF-8 readonly: no mutable: yes widget: combo - identifier: real_width title: Real width type: integer description: The original, unscaled width of the rendered title. readonly: yes unit: pixels - identifier: real_height title: Real height type: integer description: The original, unscaled height of the rendered title. readonly: yes unit: pixels - identifier: width title: Width type: integer description: The last requested scaled image width. readonly: yes unit: pixels - identifier: height title: Height type: integer description: The last requested scaled image height. readonly: yes unit: pixels - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio mutable: yes - identifier: rotate title: Rotation angle type: integer description: > The angle of text rotation in degrees. Positive value is clockwise. default: 0 readonly: no mutable: yes widget: spinner unit: degrees - identifier: width_crop title: Width to crop type: integer description: > Limit width of rendered image. default: 0 readonly: no mutable: yes widget: spinner unit: pixels - identifier: width_fit title: Fit width type: integer description: > Scale pango layout to fit specified width. default: 0 readonly: no mutable: yes widget: spinner unit: pixels - identifier: line_spacing title: Sets lines spacing type: integer description: > Sets the amount of spacing between the lines of the layout. default: 0 readonly: no mutable: yes widget: spinner - identifier: stretch title: Font stretch type: integer description: > The stretch feature of pango's font description. Possible values: 1 - ULTRA_CONDENSED 2 - EXTRA_CONDENSED 3 - CONDENSED 4 - SEMI_CONDENSED 5 - NORMAL 6 - SEMI_EXPANDED 7 - EXPANDED 8 - EXTRA_EXPANDED 9 - ULTRA_EXPANDED minimum: 0 maximum: 9 default: 4 readonly: no mutable: yes widget: spinner - identifier: wrap_width title: Sets the width to wrap to type: integer description: > Sets the width to which the lines of the PangoLayout should wrap. default: 0 readonly: no mutable: yes widget: spinner unit: pixels - identifier: wrap_type title: Sets the wrap mode type: integer description: > Sets the wrap mode; the wrap mode only has effect if a 'wrap_width' is set. Possible values: 0 - wrap lines at word boundaries 1 - wrap lines at character boundaries 2 - wrap lines at word boundaries, but fall back to character boundaries if there is not enough space for a full word default: 0 readonly: no mutable: yes widget: spinner mlt-6.20.0/src/modules/gtk2/producer_pixbuf.c000066400000000000000000000622761362234133600210650ustar00rootroot00000000000000/* * producer_pixbuf.c -- raster image loader based upon gdk-pixbuf * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #ifdef USE_EXIF #include #endif #include #include #include #include #include #include #include #include #include #include // this protects concurrent access to gdk_pixbuf 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; mlt_position *outs; int count; int image_idx; int pixbuf_idx; int width; int height; uint8_t *alpha; uint8_t *image; mlt_cache_item image_cache; mlt_cache_item alpha_cache; mlt_cache_item pixbuf_cache; GdkPixbuf *pixbuf; mlt_image_format format; }; static void load_filenames( producer_pixbuf self, mlt_properties producer_properties ); static int refresh_pixbuf( producer_pixbuf self, mlt_frame frame ); static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index ); static void producer_close( mlt_producer parent ); static void refresh_length( mlt_properties properties, producer_pixbuf self ) { if ( self->count > mlt_properties_get_int( properties, "length" ) || mlt_properties_get_int( properties, "autolength" ) ) { int ttl = mlt_properties_get_int( properties, "ttl" ); mlt_position length = self->count * ttl; mlt_properties_set_position( properties, "length", length ); mlt_properties_set_position( properties, "out", length - 1 ); } } static void on_property_changed( mlt_service owner, mlt_producer producer, char *name ) { if ( !strcmp( name, "ttl" ) ) refresh_length( MLT_PRODUCER_PROPERTIES(producer), producer->child ); } mlt_producer producer_pixbuf_init( char *filename ) { producer_pixbuf self = calloc( 1, sizeof( struct producer_pixbuf_s ) ); if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) { mlt_producer producer = &self->parent; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent ); // Reject if animation. GError *error = NULL; pthread_mutex_lock( &g_mutex ); GdkPixbufAnimation *anim = gdk_pixbuf_animation_new_from_file( filename, &error ); if ( anim ) { gboolean is_anim = !gdk_pixbuf_animation_is_static_image( anim ); g_object_unref( anim ); if ( is_anim ) { pthread_mutex_unlock( &g_mutex ); mlt_producer_close( &self->parent ); free( self ); return NULL; } } pthread_mutex_unlock( &g_mutex ); // 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 ); mlt_properties_set_int( properties, "seekable", 1 ); mlt_properties_set_int( properties, "loop", 1 ); // Validate the resource if ( filename ) load_filenames( self, properties ); if ( self->count ) { mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); if ( frame ) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set_data( frame_properties, "producer_pixbuf", self, 0, NULL, NULL ); mlt_frame_set_position( frame, mlt_producer_position( producer ) ); refresh_pixbuf( self, frame ); mlt_cache_item_close( self->pixbuf_cache ); mlt_frame_close( frame ); } } if ( self->width == 0 ) { producer_close( producer ); producer = NULL; } else { mlt_events_listen( properties, self, "property-changed", (mlt_listener) on_property_changed ); } return producer; } free( self ); return NULL; } static int load_svg( producer_pixbuf self, mlt_properties properties, const char *filename ) { int result = 0; // Read xml string if ( strstr( filename, " -1 ) { // Write the svg into the temp file ssize_t remaining_bytes; const 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( self->filenames, "0", fullname ); // Teehe - when the producer closes, delete the temp file and the space allo mlt_properties_set_data( properties, "__temporary_file__", fullname, 0, ( mlt_destructor )unlink, NULL ); result = 1; } } return result; } static int load_sequence_sprintf( producer_pixbuf self, mlt_properties properties, const char *filename ) { int result = 0; // Obtain filenames with pattern if ( strchr( filename, '%' ) != NULL ) { // handle picture sequences int i = mlt_properties_get_int( 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( self->filenames, key, full ); gap = 0; } else { gap ++; } } if ( mlt_properties_count( self->filenames ) > 0 ) { mlt_properties_set_int( properties, "ttl", 1 ); result = 1; } } return result; } static int load_sequence_deprecated( producer_pixbuf self, mlt_properties properties, const char *filename ) { int result = 0; const char *start; // This approach is deprecated in favor of the begin querystring parameter. // Obtain filenames with pattern containing a begin value, e.g. foo%1234d.png if ( ( start = strchr( filename, '%' ) ) ) { const char *end = ++start; while ( isdigit( *end ) ) end++; if ( end > start && ( end[0] == 'd' || end[0] == 'i' || end[0] == 'u' ) ) { int n = end - start; char *s = calloc( 1, n + 1 ); strncpy( s, start, n ); mlt_properties_set( properties, "begin", s ); free( s ); s = calloc( 1, strlen( filename ) + 2 ); strncpy( s, filename, start - filename ); sprintf( s + ( start - filename ), ".%d%s", n, end ); result = load_sequence_sprintf( self, properties, s ); free( s ); } } return result; } static int load_sequence_querystring( producer_pixbuf self, mlt_properties properties, const char *filename ) { int result = 0; // Obtain filenames with pattern and begin value in query string if ( strchr( filename, '%' ) && strchr( filename, '?' ) ) { // Split filename into pattern and query string char *s = strdup( filename ); char *querystring = strrchr( s, '?' ); *querystring++ = '\0'; if ( strstr( filename, "begin=" ) ) mlt_properties_set( properties, "begin", strstr( querystring, "begin=" ) + 6 ); else if ( strstr( filename, "begin:" ) ) mlt_properties_set( properties, "begin", strstr( querystring, "begin:" ) + 6 ); // Coerce to an int value so serialization does not have any extra query string cruft mlt_properties_set_int( properties, "begin", mlt_properties_get_int( properties, "begin" ) ); result = load_sequence_sprintf( self, properties, s ); free( s ); } return result; } static int load_folder( producer_pixbuf self, mlt_properties properties, const char *filename ) { int result = 0; // Obtain filenames with folder 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( self->filenames, dir_name, wildcard, 1 ); free( dir_name ); result = 1; } return result; } static int load_sequence_csv( producer_pixbuf self, mlt_properties properties, const char *filename ) { int result = 0; const char *csv_extension = strstr( filename, ".csv" ); if ( csv_extension != NULL && csv_extension[4] == '\0' ) { FILE *csv = fopen( filename, "r" ); if ( csv != NULL ) { int keyvalue = 0; int out = 0; int nlines = 0; while ( !feof( csv ) ) { char line[ 1024 ]; if ( fgets( line, 1024, csv ) != NULL ) { nlines++; } } self->outs = (mlt_position*)malloc(nlines * sizeof(mlt_position)); fseek(csv, 0, SEEK_SET); int index = 0; while ( !feof( csv ) ) { char line[ 1024 ]; if ( fgets( line, 1024, csv ) != NULL ) { char *sep = strchr(line, ';'); if ( sep != NULL ) { int ttl = 0; int n = 0; char key[ 50 ]; struct stat buf; *sep++ = '\0'; n = sscanf( sep, "%d", &ttl); if ( n == 0 ) { break; } if ( stat( line, &buf ) != 0 ) { break; } out += ttl; mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "file:'%s' ttl=%d out=%d\n", line, ttl, out ); sprintf( key, "%d", keyvalue++ ); mlt_properties_set( self->filenames, key, line ); self->outs[index++] = out; } } } fclose( csv ); result = 1; } } return result; } static void load_filenames( producer_pixbuf self, mlt_properties properties ) { char *filename = mlt_properties_get( properties, "resource" ); self->filenames = mlt_properties_new( ); self->outs = NULL; if (!load_svg( self, properties, filename ) && !load_sequence_querystring( self, properties, filename ) && !load_sequence_sprintf( self, properties, filename ) && !load_sequence_deprecated( self, properties, filename ) && !load_sequence_csv( self, properties, filename ) && !load_folder( self, properties, filename ) ) { mlt_properties_set( self->filenames, "0", filename ); } self->count = mlt_properties_count( self->filenames ); refresh_length( properties, self ); } static GdkPixbuf* reorient_with_exif( producer_pixbuf self, int image_idx, GdkPixbuf *pixbuf ) { #ifdef USE_EXIF mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &self->parent ); ExifData *d = exif_data_new_from_file( mlt_properties_get_value( self->filenames, image_idx ) ); ExifEntry *entry; int exif_orientation = 0; /* get orientation and rotate image accordingly if necessary */ if (d) { if ( ( entry = exif_content_get_entry ( d->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION ) ) ) exif_orientation = exif_get_short (entry->data, exif_data_get_byte_order (d)); /* Free the EXIF data */ exif_data_unref(d); } // Remember EXIF value, might be useful for someone mlt_properties_set_int( producer_props, "_exif_orientation" , exif_orientation ); if ( exif_orientation > 1 ) { GdkPixbuf *processed = NULL; GdkPixbufRotation matrix = GDK_PIXBUF_ROTATE_NONE; // Rotate image according to exif data switch ( exif_orientation ) { case 2: processed = gdk_pixbuf_flip ( pixbuf, TRUE ); break; case 3: matrix = GDK_PIXBUF_ROTATE_UPSIDEDOWN; processed = pixbuf; break; case 4: processed = gdk_pixbuf_flip ( pixbuf, FALSE ); break; case 5: matrix = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE; processed = gdk_pixbuf_flip ( pixbuf, TRUE ); break; case 6: matrix = GDK_PIXBUF_ROTATE_CLOCKWISE; processed = pixbuf; break; case 7: matrix = GDK_PIXBUF_ROTATE_CLOCKWISE; processed = gdk_pixbuf_flip ( pixbuf, TRUE ); break; case 8: matrix = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE; processed = pixbuf; break; } if ( processed ) { pixbuf = gdk_pixbuf_rotate_simple( processed, matrix ); g_object_unref( processed ); } } #endif return pixbuf; } static int refresh_pixbuf( producer_pixbuf self, mlt_frame frame ) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); // Check if user wants us to reload the image if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { self->pixbuf = NULL; self->image = NULL; mlt_properties_set_int( producer_props, "force_reload", 0 ); } // Get the original position of this frame mlt_position position = mlt_frame_original_position( frame ); position += mlt_producer_get_in( producer ); int loop = mlt_properties_get_int( producer_props, "loop" ); // Image index int current_idx = 0; if ( !self->outs ) { // Get the time to live for each frame double ttl = mlt_properties_get_int( producer_props, "ttl" ); if (loop) { current_idx = ( int )floor( ( double )position / ttl ) % self->count; } else { current_idx = MIN(( double )position / ttl, self->count - 1); } } else { int total_ttl = ( int )floor( self->outs[self->count - 1]); mlt_position looped_pos = loop ? ( int )floor( position ) % total_ttl : position; while ( current_idx < self->count ) { if (self->outs[ current_idx ] > looped_pos) { break; } current_idx++; } if ( current_idx >= self->count ) { current_idx = self->count - 1; } mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "position=%d current_idx=%d\n", position, current_idx ); } int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" ); if ( current_idx != self->pixbuf_idx ) self->pixbuf = NULL; if ( !self->pixbuf || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif ) { GError *error = NULL; self->image = NULL; pthread_mutex_lock( &g_mutex ); self->pixbuf = gdk_pixbuf_new_from_file( mlt_properties_get_value( self->filenames, current_idx ), &error ); if ( self->pixbuf ) { // Read the exif value for this file if ( !disable_exif ) self->pixbuf = reorient_with_exif( self, current_idx, self->pixbuf ); // Register this pixbuf for destruction and reuse mlt_cache_item_close( self->pixbuf_cache ); mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf", self->pixbuf, 0, ( mlt_destructor )g_object_unref ); self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" ); self->pixbuf_idx = current_idx; // Store the width/height of the pixbuf temporarily self->width = gdk_pixbuf_get_width( self->pixbuf ); self->height = gdk_pixbuf_get_height( self->pixbuf ); mlt_events_block( producer_props, NULL ); mlt_properties_set_int( producer_props, "meta.media.width", self->width ); mlt_properties_set_int( producer_props, "meta.media.height", self->height ); mlt_properties_set_int( producer_props, "_disable_exif", disable_exif ); mlt_events_unblock( producer_props, NULL ); } pthread_mutex_unlock( &g_mutex ); } // Set width/height of frame mlt_properties_set_int( properties, "width", self->width ); mlt_properties_set_int( properties, "height", self->height ); return current_idx; } static void refresh_image( producer_pixbuf self, mlt_frame frame, mlt_image_format format, int width, int height ) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = &self->parent; // Get index and pixbuf int current_idx = refresh_pixbuf( self, frame ); // optimization for subsequent iterations on single picture if ( current_idx != self->image_idx || width != self->width || height != self->height ) self->image = NULL; mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "image %p pixbuf %p idx %d current_idx %d pixbuf_idx %d width %d\n", self->image, self->pixbuf, current_idx, self->image_idx, self->pixbuf_idx, width ); // If we have a pixbuf and we need an image if ( self->pixbuf && ( !self->image || ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) ) ) { char *interps = mlt_properties_get( properties, "rescale.interp" ); if ( interps ) interps = strdup( interps ); int interp = GDK_INTERP_BILINEAR; if ( !interps ) { // Keep bilinear by default } else 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 || strcmp( interps, "bicubic" ) == 0 ) interp = GDK_INTERP_HYPER; free( interps ); // Note - the original pixbuf is already safe and ready for destruction pthread_mutex_lock( &g_mutex ); GdkPixbuf* pixbuf = gdk_pixbuf_scale_simple( self->pixbuf, width, height, interp ); // Store width and height self->width = width; self->height = height; // Allocate/define image int has_alpha = gdk_pixbuf_get_has_alpha( pixbuf ); int src_stride = gdk_pixbuf_get_rowstride( pixbuf ); int dst_stride = self->width * ( has_alpha ? 4 : 3 ); self->format = has_alpha ? mlt_image_rgb24a : mlt_image_rgb24; int image_size = mlt_image_format_size( self->format, width, height, NULL ); self->image = mlt_pool_alloc( image_size ); self->alpha = NULL; if ( src_stride != dst_stride ) { int y = self->height; uint8_t *src = gdk_pixbuf_get_pixels( pixbuf ); uint8_t *dst = self->image; while ( y-- ) { memcpy( dst, src, dst_stride ); dst += dst_stride; src += src_stride; } } else { memcpy( self->image, gdk_pixbuf_get_pixels( pixbuf ), src_stride * height ); } pthread_mutex_unlock( &g_mutex ); // Convert image to requested format if ( format != mlt_image_none && format != mlt_image_glsl && format != self->format && frame->convert_image ) { // cache copies of the image and alpha buffers uint8_t *buffer = self->image; if ( buffer ) { mlt_frame_set_image( frame, self->image, image_size, mlt_pool_release ); mlt_properties_set_int( properties, "width", self->width ); mlt_properties_set_int( properties, "height", self->height ); mlt_properties_set_int( properties, "format", self->format ); if ( !frame->convert_image( frame, &self->image, &self->format, format ) ) { buffer = self->image; image_size = mlt_image_format_size( self->format, self->width, self->height, NULL ); self->image = mlt_pool_alloc( image_size ); // We use height-1 because mlt_image_format_size() uses height + 1. // XXX Remove -1 when mlt_image_format_size() is changed. memcpy( self->image, buffer, mlt_image_format_size( self->format, self->width, self->height - 1, NULL ) ); } } if ( ( buffer = mlt_frame_get_alpha( frame ) ) ) { self->alpha = mlt_pool_alloc( width * height ); memcpy( self->alpha, buffer, width * height ); } } // Update the cache mlt_cache_item_close( self->image_cache ); mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image", self->image, image_size, mlt_pool_release ); self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" ); self->image_idx = current_idx; mlt_cache_item_close( self->alpha_cache ); self->alpha_cache = NULL; if ( self->alpha ) { mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha", self->alpha, width * height, mlt_pool_release ); self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" ); } // Finished with pixbuf now g_object_unref( pixbuf ); } // Set width/height of frame mlt_properties_set_int( properties, "width", self->width ); mlt_properties_set_int( properties, "height", self->height ); } static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); producer_pixbuf self = mlt_properties_get_data( properties, "producer_pixbuf", NULL ); mlt_producer producer = &self->parent; // Use the width and height suggested by the rescale filter because we can do our own scaling. if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 ) *width = mlt_properties_get_int( properties, "rescale_width" ); if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 ) *height = mlt_properties_get_int( properties, "rescale_height" ); // Restore pixbuf and image mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" ); self->pixbuf = mlt_cache_item_data( self->pixbuf_cache, NULL ); self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.image" ); self->image = mlt_cache_item_data( self->image_cache, NULL ); self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.alpha" ); self->alpha = mlt_cache_item_data( self->alpha_cache, NULL ); // Refresh the image refresh_image( self, frame, *format, *width, *height ); // Get width and height (may have changed during the refresh) *width = self->width; *height = self->height; *format = self->format; // 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 ( self->image ) { // Clone the image int image_size = mlt_image_format_size( self->format, self->width, self->height, NULL ); uint8_t *image_copy = mlt_pool_alloc( image_size ); // We use height-1 because mlt_image_format_size() uses height + 1. // XXX Remove -1 when mlt_image_format_size() is changed. memcpy( image_copy, self->image, mlt_image_format_size( self->format, self->width, self->height - 1, NULL ) ); // Now update properties so we free the copy after mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); // We're going to pass the copy on *buffer = image_copy; mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n", self->width, self->height, mlt_image_format_name( *format ) ); // Clone the alpha channel if ( self->alpha ) { image_copy = mlt_pool_alloc( self->width * self->height ); memcpy( image_copy, self->alpha, self->width * self->height ); mlt_frame_set_alpha( frame, image_copy, self->width * self->height, mlt_pool_release ); } } else { error = 1; } // Release references and locks mlt_cache_item_close( self->pixbuf_cache ); mlt_cache_item_close( self->image_cache ); mlt_cache_item_close( self->alpha_cache ); mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) ); return error; } static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { // Get the real structure for this producer producer_pixbuf self = producer->child; // Fetch the producers properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL ) load_filenames( self, producer_properties ); // Generate a frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); if ( *frame != NULL && self->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", self, 0, NULL, NULL ); // Update timecode on the frame we're creating mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Refresh the pixbuf self->pixbuf_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "pixbuf.pixbuf" ); self->pixbuf = mlt_cache_item_data( self->pixbuf_cache, NULL ); refresh_pixbuf( self, *frame ); mlt_cache_item_close( self->pixbuf_cache ); // Set producer-specific frame properties mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) ); double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" ); if ( force_ratio > 0.0 ) mlt_properties_set_double( properties, "aspect_ratio", force_ratio ); else mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "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; } static void producer_close( mlt_producer parent ) { producer_pixbuf self = parent->child; parent->close = NULL; mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) ); mlt_producer_close( parent ); free( self->outs ); self->outs = NULL; mlt_properties_close( self->filenames ); free( self ); } mlt-6.20.0/src/modules/gtk2/producer_pixbuf.yml000066400000000000000000000077401362234133600214370ustar00rootroot00000000000000schema_version: 0.3 type: producer identifier: pixbuf title: GDK-PixBuf version: 2 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video description: > A still graphics to video generator using gdk-pixbuf notes: > Pixbuf has builtin scaling. It will rescale the originally rendered title to whatever the consumer requests. Therefore, it will lose its aspect ratio if so requested, and it is up to the consumer to request a proper width and height that maintains the image aspect. Environment variable MLT_PIXBUF_PRODUCER_CACHE could be used to to override /increase the number of cached converted images for simultaneous use. parameters: - identifier: resource title: File type: string description: > The name of a graphics file loadable by a gdk-pixbuf loader. See the output of gdk-pixbuf-query-loaders. Definitely png, jpeg, tiff, pnm, and xpm will work. If "%" in filename, the filename is used with sprintf to generate a filename from a counter for multi-file/flipbook animation. The file sequence ends when numeric discontinuity >100. If the file sequence does not begin within the count of 100 you can pass the begin property like a query string parameter, for example: anim-%04d.png?begin=1000. If filename contains "/.all.", suffix with an extension to load all pictures with matching extension from a directory. If filename contains the string " Reload the file instead of using its cached image. This property automatically resets itself once it has been set 1 and processed. minimum: 0 maximum: 1 mutable: yes - identifier: disable_exif title: Disable auto-rotation type: boolean default: 0 widget: checkbox - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio mutable: yes - identifier: loop title: Loop sequence of images indefinitively description: when 1 (default) loop sequences of images, when 0, play them only once type: boolean default: 1 widget: checkbox - identifier: autolength title: Automatically compute length description: Whether to automatically compute the length and out point for an image sequence. type: boolean default: 0 widget: checkbox mlt-6.20.0/src/modules/gtk2/scale_line_22_yuv_mmx.S000066400000000000000000000126021362234133600220160ustar00rootroot00000000000000/* * scale_line_22_yuv_mmx.S -- scale line in YUY2 format * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ .file "scale_line_22_yuv_mmx.S" .version "01.01" #if !defined(__MINGW32__) && !defined(__CYGWIN__) .section .note.GNU-stack,"",%progbits #endif .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 mlt-6.20.0/src/modules/jackrack/000077500000000000000000000000001362234133600164065ustar00rootroot00000000000000mlt-6.20.0/src/modules/jackrack/Makefile000066400000000000000000000030051362234133600200440ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread include ../../../config.mak include config.mak TARGET = ../libmltjackrack$(LIBSUF) GPL_OBJS = jack_rack.o \ lock_free_fifo.o \ plugin.o \ plugin_desc.o \ plugin_mgr.o \ plugin_settings.o \ process.o \ producer_ladspa.o \ filter_jackrack.o \ filter_ladspa.o OBJS = factory.o \ consumer_jack.o CFLAGS += $(shell pkg-config --cflags jack) LDFLAGS += $(shell pkg-config --libs jack) ifdef GPL OBJS += $(GPL_OBJS) CFLAGS += -DGPL CFLAGS += $(shell pkg-config --cflags libxml-2.0) CFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --cflags glib-2.0) LDFLAGS += $(LIBDL) LDFLAGS += $(shell pkg-config --libs libxml-2.0) LDFLAGS += $(shell pkg-config $(PKGCONFIG_PREFIX) --libs glib-2.0) LDFLAGS += -lm YML_FILES = *.yml BLACKLIST = blacklist.txt else YML_FILES = consumer_jack.yml BLACKLIST = dummy 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/jackrack" install -m 644 $(YML_FILES) "$(DESTDIR)$(mltdatadir)/jackrack" [ -f $(BLACKLIST) ] && install -m 644 $(BLACKLIST) "$(DESTDIR)$(mltdatadir)/jackrack" || true uninstall: rm -f "$(DESTDIR)$(moduledir)/libmltjackrack$(LIBSUF)" rm -rf "$(DESTDIR)$(mltdatadir)/jackrack" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/jackrack/blacklist.txt000066400000000000000000000000141362234133600211120ustar00rootroot00000000000000dssi-vst.so mlt-6.20.0/src/modules/jackrack/configure000077500000000000000000000023171362234133600203200ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF JACK Rack options: --gtk2-prefix=path - Override the gtk+-2.0 prefix for pkg-config EOF else pkg-config jack disable_jack=$? echo > config.mak if [ "$gpl" = "true" ] then pkg-config libxml-2.0 > /dev/null 2>&1 disable_xml2=$? ladspa_prefix=`which listplugins 2> /dev/null` if [ "$ladspa_prefix" != "" ] then ladspa_prefix=`dirname "$ladspa_prefix"` ladspa_prefix=`dirname "$ladspa_prefix"` else ladspa_prefix=`pkg-config --variable=prefix jack` fi disable_ladspa=`[ -f "$ladspa_prefix/include/ladspa.h" ] && echo 0 || echo 1` echo GPL=1 > config.mak for i in "$@" do case $i in --gtk2-prefix=* ) pkgconfig_prefix="${i#--gtk2-prefix=}" ;; esac done [ "$pkgconfig_prefix" != "" ] && echo "PKGCONFIG_PREFIX=--define-variable=prefix=\"$pkgconfig_prefix\"" >> config.mak 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 mlt-6.20.0/src/modules/jackrack/consumer_jack.c000066400000000000000000000421011362234133600213730ustar00rootroot00000000000000/* * consumer_jack.c -- a JACK audio consumer * Copyright (C) 2011-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #define BUFFER_LEN (204800 * 6) pthread_mutex_t g_activate_mutex = PTHREAD_MUTEX_INITIALIZER; /** This classes definition. */ typedef struct consumer_jack_s *consumer_jack; struct consumer_jack_s { struct mlt_consumer_s parent; jack_client_t *jack; mlt_deque queue; pthread_t thread; int joined; int running; pthread_mutex_t video_mutex; pthread_cond_t video_cond; int playing; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; int counter; jack_ringbuffer_t **ringbuffers; jack_port_t **ports; }; /** 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_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name ); static int jack_process( jack_nframes_t frames, void * data ); /** Constructor */ mlt_consumer consumer_jack_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the consumer object consumer_jack self = calloc( 1, sizeof( struct consumer_jack_s ) ); // If no malloc'd and consumer init ok if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { char name[14]; snprintf( name, sizeof( name ), "mlt%d", getpid() ); if (( self->jack = jack_client_open( name, JackNullOption, NULL ) )) { jack_set_process_callback( self->jack, jack_process, self ); // Create the queue self->queue = mlt_deque_init( ); // Get the parent consumer object mlt_consumer parent = &self->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 ); mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); // This is the initialisation of the consumer pthread_mutex_init( &self->video_mutex, NULL ); pthread_cond_init( &self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set( properties, "rescale", "nearest" ); mlt_properties_set( properties, "deinterlace_method", "onefield" ); // Default buffer for low latency mlt_properties_set_int( properties, "buffer", 1 ); // Set frequency from JACK mlt_properties_set_int( properties, "frequency", (int) jack_get_sample_rate( self->jack ) ); // Set default volume mlt_properties_set_double( properties, "volume", 1.0 ); // Ensure we don't join on a non-running object self->joined = 1; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; // Initialize the refresh handler pthread_cond_init( &self->refresh_cond, NULL ); pthread_mutex_init( &self->refresh_mutex, NULL ); mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb ); // Return the consumer produced return parent; } } // malloc or consumer init failed free( self ); // Indicate failure return NULL; } static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name ) { if ( !strcmp( name, "refresh" ) ) { consumer_jack self = parent->child; pthread_mutex_lock( &self->refresh_mutex ); self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1; pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); } } static int consumer_start( mlt_consumer parent ) { consumer_jack self = parent->child; if ( !self->running ) { consumer_stop( parent ); self->running = 1; self->joined = 0; pthread_create( &self->thread, NULL, consumer_thread, self ); } return 0; } static int consumer_stop( mlt_consumer parent ) { // Get the actual object consumer_jack self = parent->child; if ( self->running && !self->joined ) { // Kill the thread and clean up self->joined = 1; self->running = 0; // Unlatch the consumer thread pthread_mutex_lock( &self->refresh_mutex ); pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); // Cleanup the main thread #ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); // Unlatch the video thread pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); // Cleanup JACK if ( self->playing ) jack_deactivate( self->jack ); if ( self->ringbuffers ) { int n = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "channels" ); while ( n-- ) { jack_ringbuffer_free( self->ringbuffers[n] ); jack_port_unregister( self->jack, self->ports[n] ); } mlt_pool_release( self->ringbuffers ); } self->ringbuffers = NULL; if ( self->ports ) mlt_pool_release( self->ports ); self->ports = NULL; } return 0; } static int consumer_is_stopped( mlt_consumer parent ) { consumer_jack self = parent->child; return !self->running; } static int jack_process( jack_nframes_t frames, void * data ) { int error = 0; consumer_jack self = (consumer_jack) data; mlt_properties properties = MLT_CONSUMER_PROPERTIES( &self->parent ); int channels = mlt_properties_get_int( properties, "channels" ); int i; if ( !self->ringbuffers ) return 1; for ( i = 0; i < channels; i++ ) { size_t jack_size = ( frames * sizeof(float) ); size_t ring_size = jack_ringbuffer_read_space( self->ringbuffers[i] ); char *dest = jack_port_get_buffer( self->ports[i], frames ); jack_ringbuffer_read( self->ringbuffers[i], dest, ring_size < jack_size ? ring_size : jack_size ); if ( ring_size < jack_size ) memset( dest + ring_size, 0, jack_size - ring_size ); } return error; } static void initialise_jack_ports( consumer_jack self ) { int i; char mlt_name[20], con_name[30]; mlt_properties properties = MLT_CONSUMER_PROPERTIES( &self->parent ); const char **ports = NULL; // Propagate these for the Jack processing callback int channels = mlt_properties_get_int( properties, "channels" ); // Allocate buffers and ports self->ringbuffers = mlt_pool_alloc( sizeof( jack_ringbuffer_t *) * channels ); self->ports = mlt_pool_alloc( sizeof(jack_port_t *) * channels ); // Start Jack processing - required before registering ports pthread_mutex_lock( &g_activate_mutex ); jack_activate( self->jack ); pthread_mutex_unlock( &g_activate_mutex ); self->playing = 1; // Register Jack ports for ( i = 0; i < channels; i++ ) { self->ringbuffers[i] = jack_ringbuffer_create( BUFFER_LEN * sizeof(float) ); snprintf( mlt_name, sizeof( mlt_name ), "out_%d", i + 1 ); self->ports[i] = jack_port_register( self->jack, mlt_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0 ); } // Establish connections for ( i = 0; i < channels; i++ ) { snprintf( mlt_name, sizeof( mlt_name ), "%s", jack_port_name( self->ports[i] ) ); if ( mlt_properties_get( properties, con_name ) ) snprintf( con_name, sizeof( con_name ), "%s", mlt_properties_get( properties, con_name ) ); else { if ( !ports ) ports = jack_get_ports( self->jack, NULL, NULL, JackPortIsPhysical | JackPortIsInput ); if ( ports ) strncpy( con_name, ports[i], sizeof( con_name )); else snprintf( con_name, sizeof( con_name ), "system:playback_%d", i + 1); con_name[ sizeof( con_name ) - 1 ] = '\0'; } mlt_log_verbose( NULL, "JACK connect %s to %s\n", mlt_name, con_name ); jack_connect( self->jack, mlt_name, con_name ); } if ( ports ) jack_free( ports ); } static int consumer_play_audio( consumer_jack self, mlt_frame frame, int init_audio, int *duration ) { // Get the properties of this consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( &self->parent ); mlt_audio_format afmt = mlt_audio_float; // Set the preferred params of the test card signal double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" ); int channels = mlt_properties_get_int( properties, "channels" ); int frequency = mlt_properties_get_int( properties, "frequency" ); int scrub = mlt_properties_get_int( properties, "scrub_audio" ); int samples = mlt_sample_calculator( mlt_properties_get_double( properties, "fps" ), frequency, self->counter++ ); float *buffer; mlt_frame_get_audio( frame, (void**) &buffer, &afmt, &frequency, &channels, &samples ); *duration = ( ( samples * 1000 ) / frequency ); if ( mlt_properties_get_int( properties, "audio_off" ) ) { init_audio = 1; return init_audio; } if ( init_audio == 1 ) { self->playing = 0; initialise_jack_ports( self ); init_audio = 0; } if ( init_audio == 0 && ( speed == 1.0 || speed == 0.0 ) ) { int i; size_t mlt_size = samples * sizeof(float); float volume = mlt_properties_get_double( properties, "volume" ); if ( !scrub && speed == 0.0 ) volume = 0.0; if ( volume != 1.0 ) { float *p = buffer; i = samples * channels + 1; while (--i) *p++ *= volume; } // Write into output ringbuffer for ( i = 0; i < channels; i++ ) { size_t ring_size = jack_ringbuffer_write_space( self->ringbuffers[i] ); if ( ring_size >= mlt_size ) jack_ringbuffer_write( self->ringbuffers[i], (char*)( buffer + i * samples ), mlt_size ); } } return init_audio; } static int consumer_play_video( consumer_jack self, mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( &self->parent ); if ( self->running && !mlt_consumer_is_stopped( &self->parent ) ) mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); return 0; } static void *video_thread( void *arg ) { // Identify the arg consumer_jack self = 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( MLT_CONSUMER_PROPERTIES( &self->parent ), "real_time" ); // Get the current time gettimeofday( &now, NULL ); // Determine start time start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec; while ( self->running ) { // Pop the next frame pthread_mutex_lock( &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); while ( next == NULL && self->running ) { pthread_cond_wait( &self->video_cond, &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); } pthread_mutex_unlock( &self->video_mutex ); if ( !self->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 && self->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( self->queue ) < 2 ) ) consumer_play_video( self, next ); // If the queue is empty, recalculate start to allow build up again if ( real_time && ( mlt_deque_count( self->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( &self->parent ); return NULL; } /** Threaded wrapper for pipe. */ static void *consumer_thread( void *arg ) { // Identify the arg consumer_jack self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( consumer ); // Video thread pthread_t thread; // internal initialization 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 }; // int last_position = -1; pthread_mutex_lock( &self->refresh_mutex ); self->refresh_count = 0; pthread_mutex_unlock( &self->refresh_mutex ); // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame( consumer ); // Ensure that we have a frame if ( frame ) { // Get the frame properties properties = MLT_FRAME_PROPERTIES( frame ); // Get the speed of the frame double speed = mlt_properties_get_double( properties, "_speed" ); // Get refresh request for the current frame int refresh = mlt_properties_get_int( consumer_props, "refresh" ); // Clear refresh mlt_events_block( consumer_props, consumer_props ); mlt_properties_set_int( consumer_props, "refresh", 0 ); mlt_events_unblock( consumer_props, consumer_props ); // Play audio init_audio = consumer_play_audio( self, frame, init_audio, &duration ); // Determine the start time now if ( self->playing && init_video ) { // Create the video thread pthread_create( &thread, NULL, video_thread, self ); // 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 ( self->running && speed != 0 && mlt_deque_count( self->queue ) > 15 ) nanosleep( &tm, NULL ); // Push this frame to the back of the queue if ( self->running && speed ) { pthread_mutex_lock( &self->video_mutex ); mlt_deque_push_back( self->queue, frame ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); // Calculate the next playtime playtime += ( duration * 1000 ); } else if ( self->running ) { pthread_mutex_lock( &self->refresh_mutex ); if ( refresh == 0 && self->refresh_count <= 0 ) { consumer_play_video( self, frame ); pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex ); } mlt_frame_close( frame ); self->refresh_count --; pthread_mutex_unlock( &self->refresh_mutex ); } else { mlt_frame_close( frame ); frame = NULL; } // Optimisation to reduce latency if ( frame && speed == 1.0 ) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else { mlt_consumer_purge( consumer ); // last_position = -1; } } } // Kill the video thread if ( init_video == 0 ) { pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); pthread_join( thread, NULL ); } while( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close( mlt_consumer parent ) { // Get the actual object consumer_jack self = parent->child; // Stop the consumer mlt_consumer_stop( parent ); // Now clean up the rest mlt_consumer_close( parent ); // Close the queue mlt_deque_close( self->queue ); // Destroy mutexes pthread_mutex_destroy( &self->video_mutex ); pthread_cond_destroy( &self->video_cond ); pthread_mutex_destroy( &self->refresh_mutex ); pthread_cond_destroy( &self->refresh_cond ); // Disconnect from JACK jack_client_close( self->jack ); // Finally deallocate self free( self ); } mlt-6.20.0/src/modules/jackrack/consumer_jack.yml000066400000000000000000000020661362234133600217600ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: jack title: JACK version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio parameters: - identifier: channels title: Channels type: integer minimum: 1 default: 2 - identifier: out_1 title: Send L type: string - identifier: out_2 title: Send R type: string - identifier: volume title: Volume type: float minimum: 0.0 default: 1.0 - identifier: refresh description: > Applications should set this to update the video frame when paused. type: integer minimum: 0 maximum: 1 - identifier: audio_off title: Audio off type: integer description: If 1, disable audio output mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: scrub_audio title: Audio scrubbing type: integer description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/jackrack/factory.c000066400000000000000000000175461362234133600202360ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2019 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include extern mlt_consumer consumer_jack_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #ifdef GPL #include #include "plugin_mgr.h" extern mlt_filter filter_jackrack_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_ladspa_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_ladspa_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); plugin_mgr_t *g_jackrack_plugin_mgr = NULL; static void add_port_to_metadata( mlt_properties p, plugin_desc_t* desc, int j ) { LADSPA_Data sample_rate = 48000; LADSPA_PortRangeHintDescriptor hint_descriptor = desc->port_range_hints[j].HintDescriptor; mlt_properties_set( p, "title", desc->port_names[ j ] ); if ( LADSPA_IS_HINT_INTEGER( hint_descriptor ) ) { mlt_properties_set( p, "type", "integer" ); mlt_properties_set_int( p, "default", plugin_desc_get_default_control_value( desc, j, sample_rate ) ); } else if ( LADSPA_IS_HINT_TOGGLED( hint_descriptor ) ) { mlt_properties_set( p, "type", "boolean" ); mlt_properties_set_int( p, "default", plugin_desc_get_default_control_value( desc, j, sample_rate ) ); } else { mlt_properties_set( p, "type", "float" ); mlt_properties_set_double( p, "default", plugin_desc_get_default_control_value( desc, j, sample_rate ) ); } /* set upper and lower, possibly adjusted to the sample rate */ if ( LADSPA_IS_HINT_BOUNDED_BELOW( hint_descriptor ) ) { LADSPA_Data lower = desc->port_range_hints[j].LowerBound; if ( LADSPA_IS_HINT_SAMPLE_RATE( hint_descriptor ) ) lower *= sample_rate; if ( LADSPA_IS_HINT_LOGARITHMIC( hint_descriptor ) ) { if (lower < FLT_EPSILON) lower = FLT_EPSILON; } mlt_properties_set_double( p, "minimum", lower ); } if ( LADSPA_IS_HINT_BOUNDED_ABOVE( hint_descriptor ) ) { LADSPA_Data upper = desc->port_range_hints[j].UpperBound; if ( LADSPA_IS_HINT_SAMPLE_RATE( hint_descriptor ) ) upper *= sample_rate; mlt_properties_set_double( p, "maximum", upper ); } if ( LADSPA_IS_HINT_LOGARITHMIC( hint_descriptor ) ) mlt_properties_set( p, "scale", "log" ); } #endif static mlt_properties metadata( mlt_service_type type, const char *id, char *data ) { char file[ PATH_MAX ]; if( type == filter_type ) { snprintf( file, PATH_MAX, "%s/jackrack/%s", mlt_environment( "MLT_DATA" ), strncmp( id, "ladspa.", 7 ) ? data : "filter_ladspa.yml" ); } else { snprintf( file, PATH_MAX, "%s/jackrack/%s", mlt_environment( "MLT_DATA" ), strncmp( id, "ladspa.", 7 ) ? data : "producer_ladspa.yml" ); } mlt_properties result = mlt_properties_parse_yaml( file ); #ifdef GPL if ( !strncmp( id, "ladspa.", 7 ) ) { // Annotate the yaml properties with ladspa control port info. plugin_desc_t *desc = plugin_mgr_get_any_desc( g_jackrack_plugin_mgr, strtol( id + 7, NULL, 10 ) ); if ( desc ) { mlt_properties params = mlt_properties_new(); mlt_properties p; char key[20]; int i; mlt_properties_set( result, "identifier", id ); mlt_properties_set( result, "title", desc->name ); mlt_properties_set( result, "creator", desc->maker ? desc->maker : "unknown" ); mlt_properties_set( result, "description", "LADSPA plugin" ); mlt_properties_set_data( result, "parameters", params, 0, (mlt_destructor) mlt_properties_close, NULL ); for ( i = 0; i < desc->control_port_count; i++ ) { int j = desc->control_port_indicies[i]; p = mlt_properties_new(); snprintf( key, sizeof(key), "%d", mlt_properties_count( params ) ); mlt_properties_set_data( params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL ); snprintf( key, sizeof(key), "%d", j ); mlt_properties_set( p, "identifier", key ); add_port_to_metadata( p, desc, j ); mlt_properties_set( p, "mutable", "yes" ); } for ( i = 0; i < desc->status_port_count; i++ ) { int j = desc->status_port_indicies[i]; p = mlt_properties_new(); snprintf( key, sizeof(key), "%d", mlt_properties_count( params ) ); mlt_properties_set_data( params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL ); snprintf( key, sizeof(key), "%d[*]", j ); mlt_properties_set( p, "identifier", key ); add_port_to_metadata( p, desc, j ); mlt_properties_set( p, "readonly", "yes" ); } p = mlt_properties_new(); snprintf( key, sizeof(key), "%d", mlt_properties_count( params ) ); mlt_properties_set_data( params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL ); mlt_properties_set( p, "identifier", "instances" ); mlt_properties_set( p, "title", "Instances" ); mlt_properties_set( p, "description", "The number of instances of the plugin that are in use.\n" "MLT will create the number of plugins that are required " "to support the number of audio channels.\n" "Status parameters (readonly) are provided for each instance " "and are accessed by specifying the instance number after the " "identifier (starting at zero).\n" "e.g. 9[0] provides the value of status 9 for the first instance." ); mlt_properties_set( p, "type", "integer" ); mlt_properties_set( p, "readonly", "yes" ); if( type == filter_type ) { p = mlt_properties_new(); snprintf( key, sizeof(key), "%d", mlt_properties_count( params ) ); mlt_properties_set_data( params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL ); mlt_properties_set( p, "identifier", "wetness" ); mlt_properties_set( p, "title", "Wet/Dry" ); mlt_properties_set( p, "type", "float" ); mlt_properties_set_double( p, "default", 1 ); mlt_properties_set_double( p, "minimum", 0 ); mlt_properties_set_double( p, "maximum", 1 ); mlt_properties_set( p, "mutable", "yes" ); } } } #endif return result; } MLT_REPOSITORY { #ifdef GPL GSList *list; g_jackrack_plugin_mgr = plugin_mgr_new(); for ( list = g_jackrack_plugin_mgr->all_plugins; list; list = g_slist_next( list ) ) { plugin_desc_t *desc = (plugin_desc_t *) list->data; char *s = malloc( strlen( "ladpsa." ) + 21 ); sprintf( s, "ladspa.%lu", desc->id ); if( desc->has_input ) { MLT_REGISTER( filter_type, s, filter_ladspa_init ); MLT_REGISTER_METADATA( filter_type, s, metadata, NULL ); } else { MLT_REGISTER( producer_type, s, producer_ladspa_init ); MLT_REGISTER_METADATA( producer_type, s, metadata, NULL ); } free( s ); } mlt_factory_register_for_clean_up( g_jackrack_plugin_mgr, (mlt_destructor) plugin_mgr_destroy ); MLT_REGISTER( filter_type, "jack", filter_jackrack_init ); MLT_REGISTER( filter_type, "jackrack", filter_jackrack_init ); MLT_REGISTER_METADATA( filter_type, "jackrack", metadata, "filter_jackrack.yml" ); MLT_REGISTER( filter_type, "ladspa", filter_ladspa_init ); MLT_REGISTER_METADATA( filter_type, "ladspa", metadata, "filter_ladspa.yml" ); #endif MLT_REGISTER( consumer_type, "jack", consumer_jack_init ); MLT_REGISTER_METADATA( consumer_type, "jack", metadata, "consumer_jack.yml" ); } mlt-6.20.0/src/modules/jackrack/filter_jack.yml000066400000000000000000000032701362234133600214100ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: jack title: JACK version: 1 copyright: Copyright (C) 2004-2018 Meltytech, LLC license: GPLv2 language: en url: http://www.ladspa.org/ creator: Dan Dennedy tags: - Audio description: Process audio using JACK. notes: > This can be used to receive audio from JACK by connecting only input ports. It can be used to output audio to JACK by connecting only the output ports. Or, you can use it as a filter with something like JACK Rack by connecting both output and input ports to send and receive. You can configure as many channels as you need and repeat the in_1/out_1 pattern for as many channels as you have configured. If you are using a MLT consumer that uses ALSA, then you should start jackd with the dummy driver, e.g.: jackd -ddummy -r48000 -p2048. bugs: - > MLT cannot automatically adapt to the sample rate at which JACK is configured. Please make sure they are configured the same. - Does not automatically reconfigure to the number of channels requested by consumer. - Some effects have a temporal side-effect that may not work well. parameters: - identifier: client_name title: JACK client name type: string argument: yes required: yes description: > Creates a JACK client with the specified name with input and output ports. The name must be 60 characters or less. - identifier: channels title: Channels type: integer minimum: 1 default: 2 - identifier: in_1 title: Receive L type: string - identifier: in_2 title: Receive R type: string - identifier: out_1 title: Send L type: string - identifier: out_2 title: Send R type: string mlt-6.20.0/src/modules/jackrack/filter_jackrack.c000066400000000000000000000443161362234133600217000ustar00rootroot00000000000000/* * filter_jackrack.c -- filter audio through Jack and/or LADSPA plugins * Copyright (C) 2004-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "jack_rack.h" extern pthread_mutex_t g_activate_mutex; #define BUFFER_LEN 204800 * 6 static void jack_started_transmitter( mlt_listener listener, mlt_properties owner, mlt_service service, void **args ) { listener( owner, service, (mlt_position*) args[0] ); } static void jack_stopped_transmitter( mlt_listener listener, mlt_properties owner, mlt_service service, void **args ) { listener( owner, service, (mlt_position*) args[0] ); } static void jack_seek_transmitter( mlt_listener listener, mlt_properties owner, mlt_service service, void **args ) { listener( owner, service, (mlt_position*) args[0] ); } #define JACKSTATE(x) (x==JackTransportStopped?"stopped":x==JackTransportStarting?"starting":x==JackTransportRolling?"rolling":"unknown") static int jack_sync( jack_transport_state_t state, jack_position_t *jack_pos, void *arg ) { mlt_filter filter = (mlt_filter) arg; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); mlt_position position = mlt_profile_fps( profile ) * jack_pos->frame / jack_pos->frame_rate + 0.5; int result = 1; mlt_log_debug( MLT_FILTER_SERVICE(filter), "%s frame %u rate %u pos %d last_pos %d\n", JACKSTATE(state), jack_pos->frame, jack_pos->frame_rate, position, mlt_properties_get_position( properties, "_last_pos" ) ); if ( state == JackTransportStopped ) { mlt_events_fire( properties, "jack-stopped", &position, NULL ); mlt_properties_set_int( properties, "_sync_guard", 0 ); } else if ( state == JackTransportStarting ) { result = 0; if ( !mlt_properties_get_int( properties, "_sync_guard" ) ) { mlt_properties_set_int( properties, "_sync_guard", 1 ); mlt_events_fire( properties, "jack-started", &position, NULL ); } else if ( position >= mlt_properties_get_position( properties, "_last_pos" ) - 2 ) { mlt_properties_set_int( properties, "_sync_guard", 0 ); result = 1; } } else { mlt_properties_set_int( properties, "_sync_guard", 0 ); } return result; } static void on_jack_start( mlt_properties owner, mlt_properties properties ) { mlt_log_verbose( NULL, "%s\n", __FUNCTION__ ); jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL ); jack_transport_start( jack_client ); } static void on_jack_stop( mlt_properties owner, mlt_properties properties ) { mlt_log_verbose( NULL, "%s\n", __FUNCTION__ ); jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL ); jack_transport_stop( jack_client ); } static void on_jack_seek( mlt_properties owner, mlt_filter filter, mlt_position *position ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_log_verbose( MLT_FILTER_SERVICE(filter), "%s: %d\n", __FUNCTION__, *position ); mlt_properties_set_int( properties, "_sync_guard", 1 ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL ); jack_nframes_t jack_frame = jack_get_sample_rate( jack_client ); jack_frame *= *position / mlt_profile_fps( profile ); jack_transport_locate( jack_client, jack_frame ); } static void initialise_jack_ports( mlt_properties properties ) { int i; char mlt_name[67], 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 ); // Propagate 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, (mlt_destructor) jack_rack_destroy, NULL ); mlt_properties_set( properties, "_rack_client_name", rack_name ); } else { // We have to set this to something to prevent re-initialization. mlt_properties_set_data( properties, "jackrack", jack_client, 0, NULL, NULL ); } // 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, mlt_pool_release, NULL ); mlt_properties_set_data( properties, "input_buffers", input_buffers, sizeof( jack_ringbuffer_t *) * channels, mlt_pool_release, NULL ); mlt_properties_set_data( properties, "jack_output_ports", jack_output_ports, sizeof( jack_port_t *) * channels, mlt_pool_release, NULL ); mlt_properties_set_data( properties, "jack_input_ports", jack_input_ports, sizeof( jack_port_t *) * channels, mlt_pool_release, NULL ); mlt_properties_set_data( properties, "jack_output_buffers", jack_output_buffers, sizeof( float *) * channels, mlt_pool_release, NULL ); mlt_properties_set_data( properties, "jack_input_buffers", jack_input_buffers, sizeof( float *) * channels, mlt_pool_release, NULL ); // 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), (mlt_destructor) jack_ringbuffer_free, NULL ); snprintf( mlt_name, sizeof( mlt_name ), "ibuf%d", i ); mlt_properties_set_data( properties, mlt_name, input_buffers[i], BUFFER_LEN * sizeof(float), (mlt_destructor) jack_ringbuffer_free, 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 ); } } // Start Jack processing pthread_mutex_lock( &g_activate_mutex ); jack_activate( jack_client ); pthread_mutex_unlock( &g_activate_mutex ); // 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 ) { mlt_log_verbose( NULL, "JACK connect %s to %s\n", rack_name, mlt_name ); jack_connect( jack_client, rack_name, mlt_name ); } else { mlt_log_verbose( NULL, "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] ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "no buffer for output port %d\n", 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 ); if ( ring_size < jack_size ) memset( &jack_output_buffers[i][ring_size], 0, jack_size - ring_size ); // Return audio through in port jack_input_buffers[i] = jack_port_get_buffer( jack_input_ports[i], frames ); if ( ! jack_input_buffers[i] ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "no buffer for input port %d\n", 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; mlt_log_debug( MLT_FILTER_SERVICE(filter), "sync %d frame_size %d ring_size %zu jack_size %zu\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 ); } } } // Often jackd does not send the stopped event through the JackSyncCallback jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL ); jack_position_t jack_pos; jack_transport_state_t state = jack_transport_query( jack_client, &jack_pos ); int transport_state = mlt_properties_get_int( properties, "_transport_state" ); if ( state != transport_state ) { mlt_properties_set_int( properties, "_transport_state", state ); if ( state == JackTransportStopped ) jack_sync( state, &jack_pos, filter ); } return err; } /** Get the audio. */ static int jackrack_get_audio( mlt_frame frame, void **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 *format = mlt_audio_float; mlt_frame_get_audio( frame, buffer, format, &jack_frequency, channels, samples ); // TODO: Deal with sample rate differences if ( *frequency != jack_frequency ) mlt_log_error( MLT_FILTER_SERVICE( filter ), "mismatching frequencies JACK = %d actual = %d\n", jack_frequency, *frequency ); *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 float *q = (float*) *buffer; size_t size = *samples * sizeof(float); int j; // struct timespec tm = { 0, 0 }; // Write into output ringbuffer for ( j = 0; j < *channels; j++ ) { if ( jack_ringbuffer_write_space( output_buffers[j] ) >= size ) jack_ringbuffer_write( output_buffers[j], (char*)( q + j * *samples ), size ); } // Synchronization phase - wait for signal from Jack process while ( jack_ringbuffer_read_space( input_buffers[ *channels - 1 ] ) < size ) ; //pthread_cond_wait( output_ready, output_lock ); // Read from input ringbuffer for ( j = 0; j < *channels; j++, q++ ) { if ( jack_ringbuffer_read_space( input_buffers[j] ) >= size ) jack_ringbuffer_read( input_buffers[j], (char*)( q + j * *samples ), size ); } // help jack_sync() indicate when we are rolling mlt_position pos = mlt_frame_get_position( frame ); mlt_properties_set_position( filter_properties, "_last_pos", pos ); 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_data( properties, "jackrack", NULL ) ) initialise_jack_ports( properties ); } return frame; } static void filter_close( mlt_filter this ) { 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 ); 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[61]; char *jack_client_name; const char *src; jack_status_t status = 0; if ( id && arg && !strcmp( id, "jack" ) ) { snprintf( name, sizeof( name ), "%s", arg ); src = NULL; } else { snprintf( name, sizeof( name ), "mlt%d", getpid() ); src = arg; } jack_client_t *jack_client = jack_client_open( arg, JackNullOption, &status, NULL ); if ( jack_client ) { if ( status & JackNameNotUnique ) { jack_client_name = jack_get_client_name ( jack_client ); strcpy( name, jack_client_name ); } 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 ); jack_set_sync_callback( jack_client, jack_sync, this ); jack_set_sync_timeout( jack_client, 5000000 ); //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", src ); 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, mlt_pool_release, NULL ); mlt_properties_set_data( properties, "output_ready", output_ready, 0, mlt_pool_release, NULL ); mlt_properties_set_int( properties, "_sync", 1 ); mlt_properties_set_int( properties, "channels", 2 ); mlt_events_register( properties, "jack-started", (mlt_transmitter) jack_started_transmitter ); mlt_events_register( properties, "jack-stopped", (mlt_transmitter) jack_stopped_transmitter ); mlt_events_register( properties, "jack-start", NULL ); mlt_events_register( properties, "jack-stop", NULL ); mlt_events_register( properties, "jack-seek", (mlt_transmitter) jack_seek_transmitter ); mlt_events_listen( properties, properties, "jack-start", (mlt_listener) on_jack_start ); mlt_events_listen( properties, properties, "jack-stop", (mlt_listener) on_jack_stop ); mlt_events_listen( properties, this, "jack-seek", (mlt_listener) on_jack_seek ); mlt_properties_set_position( properties, "_jack_seek", -1 ); } else { mlt_log_error( NULL, "Failed to connect to JACK server\n" ); mlt_filter_close( this ); this = NULL; } } return this; } mlt-6.20.0/src/modules/jackrack/filter_jackrack.yml000066400000000000000000000035531362234133600222550ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: jackrack title: JACK version: 1 copyright: Copyright (C) 2004-2018 Meltytech, LLC license: GPLv2 language: en url: http://www.ladspa.org/ creator: Dan Dennedy tags: - Audio description: Process audio using JACK. notes: > This can be used to receive audio from JACK by connecting only input ports. It can be used to output audio to JACK by connecting only the output ports. Or, you can use it as a filter with something like JACK Rack by connecting both output and input ports to send and receive. You can configure as many channels as you need and repeat the in_1/out_1 pattern for as many channels as you have configured. If you are using a MLT consumer that uses ALSA, then you should start jackd with the dummy driver, e.g.: jackd -ddummy -r48000 -p2048. The MLT JACK client name uses the format: mlt{pid}. bugs: - > MLT cannot automatically adapt to the sample rate at which JACK is configured. Please make sure they are configured the same. - Does not automatically reconfigure to the number of channels requested by consumer. - Some effects have a temporal side-effect that may not work well. parameters: - identifier: src title: JACK Rack file type: string argument: yes description: > Creates JACK ports and runs a JACK Rack project to process audio through a stack of LADSPA filters. - identifier: client_name title: JACK client name type: string argument: yes readonly: yes description: The generated name of the JACK client. - identifier: channels title: Channels type: integer minimum: 1 default: 2 - identifier: in_1 title: Receive L type: string - identifier: in_2 title: Receive R type: string - identifier: out_1 title: Send L type: string - identifier: out_2 title: Send R type: string mlt-6.20.0/src/modules/jackrack/filter_ladspa.c000066400000000000000000000210141362234133600213610ustar00rootroot00000000000000/* * filter_ladspa.c -- filter audio through LADSPA plugins * Copyright (C) 2004-2018 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "jack_rack.h" #define BUFFER_LEN (10000) #define MAX_SAMPLE_COUNT (4096) static jack_rack_t* initialise_jack_rack( mlt_properties properties, int channels ) { jack_rack_t *jackrack = NULL; char *resource = mlt_properties_get( properties, "resource" ); if ( !resource && mlt_properties_get( properties, "src" ) ) resource = mlt_properties_get( properties, "src" ); // Start JackRack if ( resource || mlt_properties_get_int64( properties, "_pluginid" ) ) { // Create JackRack without Jack client name so that it only uses LADSPA jackrack = jack_rack_new( NULL, channels ); mlt_properties_set_data( properties, "jackrack", jackrack, 0, (mlt_destructor) jack_rack_destroy, NULL ); if ( resource ) // Load JACK Rack XML file jack_rack_open_file( jackrack, resource ); else if ( mlt_properties_get_int64( properties, "_pluginid" ) ) { // Load one LADSPA plugin by its UniqueID unsigned long id = mlt_properties_get_int64( properties, "_pluginid" ); plugin_desc_t *desc = plugin_mgr_get_any_desc( jackrack->plugin_mgr, id ); plugin_t *plugin; if ( desc && ( plugin = jack_rack_instantiate_plugin( jackrack, desc ) ) ) { plugin->enabled = TRUE; process_add_plugin( jackrack->procinfo, plugin ); mlt_properties_set_int( properties, "instances", plugin->copies ); } else { mlt_log_error( properties, "failed to load plugin %lu\n", id ); return jackrack; } if ( plugin && plugin->desc && plugin->copies == 0 ) { // Calculate the number of channels that will work with this plugin int request_channels = plugin->desc->channels; while ( request_channels < channels ) request_channels += plugin->desc->channels; if ( request_channels != channels ) { // Try to load again with a compatible number of channels. mlt_log_warning( properties, "Not compatible with %d channels. Requesting %d channels instead.\n", channels, request_channels ); jackrack = initialise_jack_rack( properties, request_channels ); } else { mlt_log_error( properties, "Invalid plugin configuration: %lu\n", id ); return jackrack; } } if ( plugin && plugin->desc && plugin->copies ) mlt_log_debug( properties, "Plugin Initialized. Channels: %lu\tCopies: %d\tTotal: %lu\n", plugin->desc->channels, plugin->copies, jackrack->channels ); } } return jackrack; } /** Get the audio. */ static int ladspa_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { int error = 0; // Get the filter service mlt_filter filter = mlt_frame_pop_audio( frame ); // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); // Check if the channel configuration has changed int prev_channels = mlt_properties_get_int( filter_properties, "_prev_channels" ); if ( prev_channels != *channels ) { if( prev_channels ) { mlt_log_info( MLT_FILTER_SERVICE(filter), "Channel configuration changed. Old: %d New: %d.\n", prev_channels, *channels ); mlt_properties_set_data( filter_properties, "jackrack", NULL, 0, (mlt_destructor) NULL, NULL ); } mlt_properties_set_int( filter_properties, "_prev_channels", *channels ); } // Initialise LADSPA if needed jack_rack_t *jackrack = mlt_properties_get_data( filter_properties, "jackrack", NULL ); if ( jackrack == NULL ) { sample_rate = *frequency; // global inside jack_rack jackrack = initialise_jack_rack( filter_properties, *channels ); } if ( jackrack && jackrack->procinfo && jackrack->procinfo->chain && mlt_properties_get_int64( filter_properties, "_pluginid" ) ) { plugin_t *plugin = jackrack->procinfo->chain; LADSPA_Data value; int i, c; mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); // Get the producer's audio *format = mlt_audio_float; mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); // Resize the buffer if necessary. if ( *channels < jackrack->channels ) { // Add extra channels to satisfy the plugin. // Extra channels in the buffer will be ignored by downstream services. int old_size = mlt_audio_format_size( *format, *samples, *channels ); int new_size = mlt_audio_format_size( *format, *samples, jackrack->channels ); uint8_t* new_buffer = mlt_pool_alloc( new_size ); memcpy( new_buffer, *buffer, old_size ); // Put silence in extra channels. memset( new_buffer + old_size, 0, new_size - old_size ); mlt_frame_set_audio( frame, new_buffer, *format, new_size, mlt_pool_release ); *buffer = new_buffer; } for ( i = 0; i < plugin->desc->control_port_count; i++ ) { // Apply the control port values char key[20]; value = plugin_desc_get_default_control_value( plugin->desc, i, sample_rate ); snprintf( key, sizeof(key), "%d", i ); if ( mlt_properties_get( filter_properties, key ) ) value = mlt_properties_anim_get_double( filter_properties, key, position, length ); for ( c = 0; c < plugin->copies; c++ ) plugin->holders[c].control_memory[i] = value; } plugin->wet_dry_enabled = mlt_properties_get( filter_properties, "wetness" ) != NULL; if ( plugin->wet_dry_enabled ) { value = mlt_properties_anim_get_double( filter_properties, "wetness", position, length ); for ( c = 0; c < jackrack->channels; c++ ) plugin->wet_dry_values[c] = value; } // Configure the buffers LADSPA_Data **input_buffers = mlt_pool_alloc( sizeof( LADSPA_Data* ) * jackrack->channels ); LADSPA_Data **output_buffers = mlt_pool_alloc( sizeof( LADSPA_Data* ) * jackrack->channels ); // Some plugins crash with too many frames (samples). // So, feed the plugin with N samples per loop iteration. int samples_offset = 0; int sample_count = MIN(*samples, MAX_SAMPLE_COUNT); for (i = 0; samples_offset < *samples; i++) { int j = 0; for (; j < jackrack->channels; j++) output_buffers[j] = input_buffers[j] = (LADSPA_Data*) *buffer + j * (*samples) + samples_offset; sample_count = MIN(*samples - samples_offset, MAX_SAMPLE_COUNT); // Do LADSPA processing error = process_ladspa( jackrack->procinfo, sample_count, input_buffers, output_buffers ); samples_offset += MAX_SAMPLE_COUNT; } mlt_pool_release( input_buffers ); mlt_pool_release( output_buffers ); // read the status port values for ( i = 0; i < plugin->desc->status_port_count; i++ ) { char key[20]; int p = plugin->desc->status_port_indicies[i]; for ( c = 0; c < plugin->copies; c++ ) { snprintf( key, sizeof(key), "%d[%d]", p, c ); value = plugin->holders[c].status_memory[i]; mlt_properties_set_double( filter_properties, key, value ); } } } else { // Nothing to do. error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); } return error; } /** 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, ladspa_get_audio ); } return frame; } /** 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; mlt_properties_set( properties, "resource", arg ); if ( !strncmp( id, "ladspa.", 7 ) ) mlt_properties_set( properties, "_pluginid", id + 7 ); } return this; } mlt-6.20.0/src/modules/jackrack/filter_ladspa.yml000066400000000000000000000012111362234133600217350ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: ladspa title: LADSPA version: 1 copyright: Copyright (C) 2004-2014 Meltytech, LLC license: GPLv2 language: en url: http://www.ladspa.org/ creator: Dan Dennedy tags: - Audio description: Process audio using LADSPA plugins. notes: > Automatically adapts to the number of channels and sampling rate of the consumer. bugs: - Some effects have a temporal side-effect that may not work well. parameters: - identifier: argument title: JACK Rack XML file type: string description: > Runs a JACK Rack project to process audio through a stack of LADSPA filters without using JACK. mlt-6.20.0/src/modules/jackrack/jack_rack.c000066400000000000000000000266611362234133600204750ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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" #include "framework/mlt_log.h" #ifndef _ #define _(x) x #endif #define _x (const xmlChar*) #define _s (const char*) extern plugin_mgr_t *g_jackrack_plugin_mgr; 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 = g_jackrack_plugin_mgr; 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 is shared and global now, so we do not destroy it with each instance // 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)) { mlt_log_info( NULL, "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) { mlt_log_error( NULL, "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) { mlt_log_warning( NULL, "%s: could not instantiate object file '%s'\n", __FUNCTION__, saved_plugin->settings->desc->object_file); 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; } if ( !saved_plugin ) return; /* 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; //mlt_log_debug( NULL, "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; //mlt_log_debug( NULL, "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; #ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif 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) { mlt_log_verbose( NULL, _("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; #ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif 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) { mlt_log_error( NULL, _("Could not parse file '%s'\n"), filename); return 1; } if (xmlStrcmp ( ((xmlDtdPtr)doc->children)->name, _x("jackrack")) != 0) { mlt_log_error( NULL, _("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 */ mlt-6.20.0/src/modules/jackrack/jack_rack.h000066400000000000000000000037411362234133600204740ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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__ */ mlt-6.20.0/src/modules/jackrack/lock_free_fifo.c000066400000000000000000000057531362234133600215200ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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; } } mlt-6.20.0/src/modules/jackrack/lock_free_fifo.h000066400000000000000000000032671362234133600215230ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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__ */ mlt-6.20.0/src/modules/jackrack/plugin.c000066400000000000000000000370461362234133600200620ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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" #include "framework/mlt_log.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 receive 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; /* clear the error report */ dlerror (); /* open the object file */ dl_handle = dlopen (desc->object_file, RTLD_NOW); dlerr = dlerror (); if (!dl_handle || dlerr) { if (!dlerr) dlerr = "unknown error"; mlt_log_warning( NULL, "%s: error opening shared object file '%s': %s\n", __FUNCTION__, desc->object_file, dlerr); return 1; } /* get the get_descriptor function */ get_descriptor = (LADSPA_Descriptor_Function) dlsym (dl_handle, "ladspa_descriptor"); dlerr = dlerror(); if (dlerr) { if (!dlerr) dlerr = "unknown error"; mlt_log_warning( NULL, "%s: error finding descriptor symbol in object file '%s': %s\n", __FUNCTION__, desc->object_file, dlerr); dlclose (dl_handle); return 1; } #ifdef __APPLE__ if (!get_descriptor (desc->index)) { void (*constructor)(void) = dlsym (dl_handle, "_init"); if (constructor) constructor(); } #endif *descriptor_ptr = get_descriptor (desc->index); if (!*descriptor_ptr) { mlt_log_warning( NULL, "%s: error finding index %lu in object file '%s'\n", __FUNCTION__, desc->index, desc->object_file); dlclose (dl_handle); return 1; } *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]) { mlt_log_panic( NULL, "Could not register jack port '%s'; aborting\n", port_name); } } g_free (plugin_name); } 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); } if (desc->status_port_count > 0) { holder->status_memory = g_malloc (sizeof (LADSPA_Data) * desc->status_port_count); } else { holder->status_memory = NULL; } for (i = 0; i < desc->status_port_count; i++) { plugin->descriptor-> connect_port (instance, desc->status_port_indicies[i], holder->status_memory + i); } 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); } if (plugin->desc->status_port_count > 0) { g_free (plugin->holders[i].status_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) mlt_log_warning( NULL, "%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) { mlt_log_warning( NULL, "%s: error closing shared object '%s': %s\n", __FUNCTION__, plugin->desc->object_file, dlerror ()); } g_free (plugin); } /* EOF */ mlt-6.20.0/src/modules/jackrack/plugin.h000066400000000000000000000054411362234133600200610ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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 #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; LADSPA_Data * status_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__ */ mlt-6.20.0/src/modules/jackrack/plugin_desc.c000066400000000000000000000313531362234133600210530ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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->maker = 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->status_port_count = 0; pd->status_port_indicies = NULL; pd->aux_channels = 0; pd->aux_are_input = TRUE; pd->has_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); g_free (pd->audio_input_port_indicies); g_free (pd->audio_output_port_indicies); g_free (pd->port_names); g_free (pd->control_port_indicies); g_free (pd->status_port_indicies); g_free (pd->audio_aux_port_indicies); pd->port_descriptors = NULL; pd->port_range_hints = NULL; pd->audio_input_port_indicies = NULL; pd->audio_output_port_indicies = NULL; pd->port_names = NULL; pd->control_port_indicies = NULL; pd->status_port_indicies = NULL; pd->audio_aux_port_indicies = 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_set_maker (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_maker (pd, descriptor->Maker); 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_maker (plugin_desc_t * pd, const char * maker) { set_string_property (pd->maker, maker); } 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 ** indices, unsigned long * current_port_count, unsigned long index) { (*current_port_count)++; if (*current_port_count == 0) *indices = g_malloc (sizeof (unsigned long) * *current_port_count); else *indices = g_realloc (*indices, sizeof (unsigned long) * *current_port_count); (*indices)[*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])) { pd->status_port_count++; if (pd->status_port_count == 0) pd->status_port_indicies = g_malloc (sizeof (unsigned long) * pd->status_port_count); else pd->status_port_indicies = g_realloc (pd->status_port_indicies, sizeof (unsigned long) * pd->status_port_count); pd->status_port_indicies[pd->status_port_count - 1] = i; } else { 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 if( icount == 0 ) { pd->channels = ocount; pd->has_input = FALSE; } else { /* deal with auxiliary 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 indices */ pd->audio_aux_port_indicies = g_malloc (sizeof (unsigned long) * pd->aux_channels); /* copy indices */ for (i = pd->channels, j = 0; i < port_count; i++, j++) pd->audio_aux_port_indicies[j] = (*port_indicies)[i]; /* shrink the main indices to only have channels indices */ *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 */ mlt-6.20.0/src/modules/jackrack/plugin_desc.h000066400000000000000000000061551362234133600210620ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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; char * maker; 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; unsigned long status_port_count; unsigned long * status_port_indicies; gboolean has_input; }; 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 (plugin_desc_t * pd); 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_maker (plugin_desc_t * pd, const char * maker); 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__ */ mlt-6.20.0/src/modules/jackrack/plugin_mgr.c000066400000000000000000000215421362234133600207210ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modifications for MLT: * Copyright (C) 2004-2016 Meltytech, LLC * * 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" #include "framework/mlt_log.h" #include "framework/mlt_factory.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 (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_LAZY); if (!dl_handle) { mlt_log_info( NULL, "%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) { mlt_log_info( NULL, "%s: error finding ladspa_descriptor symbol in object file '%s': %s\n", __FUNCTION__, filename, dlerr); dlclose (dl_handle); return; } #ifdef __APPLE__ if (!get_descriptor (0)) { void (*constructor)(void) = dlsym (dl_handle, "_init"); if (constructor) constructor(); } #endif 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) { mlt_log_info( NULL, "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 */ /* mlt_log_verbose( NULL, "Loaded plugin '%s'\n", desc->name); */ } err = dlclose (dl_handle); if (err) { mlt_log_warning( NULL, "%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) { /* mlt_log_warning( NULL, "%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 || mlt_properties_get (plugin_mgr->blacklist, dir_entry->d_name) || 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) mlt_log_warning( NULL, "%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")); #ifdef _WIN32 if (!ladspa_path) { ladspa_path = malloc (strlen (mlt_environment("MLT_APPDIR")) + strlen ("\\lib\\ladspa") + 1); strcpy (ladspa_path, mlt_environment("MLT_APPDIR")); strcat (ladspa_path, "\\lib\\ladspa"); } #elif defined(__APPLE__) && defined(RELOCATABLE) { ladspa_path = malloc( strlen (mlt_environment ("MLT_APPDIR")) + strlen ("/PlugIns/ladspa") + 1 ); strcpy (ladspa_path, mlt_environment ("MLT_APPDIR")); strcat (ladspa_path, "/PlugIns/ladspa" ); } #else if (!ladspa_path) ladspa_path = g_strdup ("/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/lib64/ladspa"); #endif for (dir = strtok (ladspa_path, MLT_DIRLIST_DELIMITER); dir; dir = strtok (NULL, MLT_DIRLIST_DELIMITER)) plugin_mgr_get_dir_plugins (plugin_mgr, dir); 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; char dirname[PATH_MAX]; pm = g_malloc (sizeof (plugin_mgr_t)); pm->all_plugins = NULL; pm->plugins = NULL; pm->plugin_count = 0; snprintf (dirname, PATH_MAX, "%s/jackrack/blacklist.txt", mlt_environment ("MLT_DATA")); pm->blacklist = mlt_properties_load (dirname); plugin_mgr_get_path_plugins (pm); if (!pm->all_plugins) mlt_log_warning( NULL, "No LADSPA plugins were found!\n\nCheck your LADSPA_PATH environment variable.\n"); else 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); mlt_properties_close(plugin_mgr->blacklist); 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 */ mlt-6.20.0/src/modules/jackrack/plugin_mgr.h000066400000000000000000000030771362234133600207310ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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" #include "framework/mlt_properties.h" typedef struct _plugin_mgr plugin_mgr_t; struct _plugin_mgr { GSList * all_plugins; GSList * plugins; unsigned long plugin_count; mlt_properties blacklist; }; 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__ */ mlt-6.20.0/src/modules/jackrack/plugin_settings.c000066400000000000000000000244411362234133600217750ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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 _GNU_SOURCE #define _GNU_SOURCE #endif #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 */ mlt-6.20.0/src/modules/jackrack/plugin_settings.h000066400000000000000000000060711362234133600220010ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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__ */ mlt-6.20.0/src/modules/jackrack/process.c000066400000000000000000000446061362234133600202420ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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 "process.h" #include "lock_free_fifo.h" #include "plugin.h" #include "jack_rack.h" #include "framework/mlt_log.h" #ifndef _ #define _(x) x #endif extern pthread_mutex_t g_activate_mutex; #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]) { mlt_log_verbose( NULL, "%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]) { mlt_log_verbose( NULL, "%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 */ if( plugin->desc->has_input ) 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) { mlt_log_error( NULL, "%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++) { if(get_first_enabled_plugin (procinfo)->desc->has_input) { procinfo->jack_input_buffers[channel] = inputs[channel]; if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose( NULL, "%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]) { mlt_log_verbose( NULL, "%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) { mlt_log_error( NULL, "%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) { mlt_log_warning( NULL, "%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) { mlt_log_info( NULL, _("Connecting to JACK server with client name '%s'\n"), procinfo->jack_client_name); procinfo->jack_client = jack_client_open (procinfo->jack_client_name, JackNullOption, NULL); if (!procinfo->jack_client) { mlt_log_warning( NULL, "%s: could not create jack client; is the jackd server running?\n", __FUNCTION__); return 1; } mlt_log_verbose( NULL, _("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); mlt_log_debug( NULL, _("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) mlt_log_warning( NULL, "%s: error connecting ports '%s' and '%s'\n", __FUNCTION__, full_port_name, jack_ports[jack_port_index]); else mlt_log_info( NULL, _("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); //mlt_log_debug( NULL, _("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) { mlt_log_error( NULL, "%s: could not register port '%s'; aborting\n", __FUNCTION__, port_name); return 1; } //mlt_log_debug( NULL, _("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); pthread_mutex_lock( &g_activate_mutex ); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); pthread_mutex_unlock( &g_activate_mutex ); 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; } mlt-6.20.0/src/modules/jackrack/process.h000066400000000000000000000043651362234133600202450ustar00rootroot00000000000000/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2014 Meltytech, LLC * * 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__ */ mlt-6.20.0/src/modules/jackrack/producer_ladspa.c000066400000000000000000000153371362234133600217320ustar00rootroot00000000000000/* * producer_ladspa.c -- LADSPA plugin producer * Copyright (C) 2013-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "jack_rack.h" #define BUFFER_LEN 10000 /** One-time initialization of jack rack. */ static jack_rack_t* initialise_jack_rack( mlt_properties properties, int channels ) { jack_rack_t *jackrack = NULL; unsigned long plugin_id = mlt_properties_get_int64( properties, "_pluginid" ); // Start JackRack if ( plugin_id ) { // Create JackRack without Jack client name so that it only uses LADSPA jackrack = jack_rack_new( NULL, channels ); mlt_properties_set_data( properties, "_jackrack", jackrack, 0, (mlt_destructor) jack_rack_destroy, NULL ); // Load one LADSPA plugin by its UniqueID plugin_desc_t *desc = plugin_mgr_get_any_desc( jackrack->plugin_mgr, plugin_id ); plugin_t *plugin; if ( desc && ( plugin = jack_rack_instantiate_plugin( jackrack, desc ) ) ) { plugin->enabled = TRUE; plugin->wet_dry_enabled = FALSE; process_add_plugin( jackrack->procinfo, plugin ); mlt_properties_set_int( properties, "instances", plugin->copies ); } else { mlt_log_error( properties, "failed to load plugin %lu\n", plugin_id ); } } return jackrack; } static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Get the producer service mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_ladspa", NULL ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); int size = 0; LADSPA_Data** output_buffers = NULL; int i = 0; // Initialize LADSPA if needed jack_rack_t *jackrack = mlt_properties_get_data( producer_properties, "_jackrack", NULL ); if ( !jackrack ) { sample_rate = *frequency; // global inside jack_rack jackrack = initialise_jack_rack( producer_properties, *channels ); } if ( jackrack ) { // Correct the returns if necessary *samples = *samples <= 0 ? 1920 : *samples; *channels = *channels <= 0 ? 2 : *channels; *frequency = *frequency <= 0 ? 48000 : *frequency; *format = mlt_audio_float; if ( jackrack->procinfo && jackrack->procinfo->chain ) { plugin_t *plugin = jackrack->procinfo->chain; LADSPA_Data value; int index, c; mlt_position position = mlt_frame_get_position( frame ); mlt_position length = mlt_producer_get_length( producer ); for ( index = 0; index < plugin->desc->control_port_count; index++ ) { // Apply the control port values char key[20]; value = plugin_desc_get_default_control_value( plugin->desc, index, sample_rate ); snprintf( key, sizeof(key), "%d", index ); if ( mlt_properties_get( producer_properties, key ) ) value = mlt_properties_anim_get_double( producer_properties, key, position, length ); for ( c = 0; c < plugin->copies; c++ ) plugin->holders[c].control_memory[index] = value; } } // Calculate the size of the buffer size = *samples * *channels * sizeof( float ); // Allocate the buffer *buffer = mlt_pool_alloc( size ); // Initialize the LADSPA output buffer. output_buffers = mlt_pool_alloc( sizeof( LADSPA_Data* ) * *channels ); for ( i = 0; i < *channels; i++ ) { output_buffers[i] = (LADSPA_Data*) *buffer + i * *samples; } // Do LADSPA processing process_ladspa( jackrack->procinfo, *samples, NULL, output_buffers ); mlt_pool_release( output_buffers ); // Set the buffer for destruction mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); if ( jackrack && jackrack->procinfo && jackrack->procinfo->chain && mlt_properties_get_int64( producer_properties, "_pluginid" ) ) { plugin_t *plugin = jackrack->procinfo->chain; LADSPA_Data value; int i, c; for ( i = 0; i < plugin->desc->status_port_count; i++ ) { // read the status port values char key[20]; int p = plugin->desc->status_port_indicies[i]; for ( c = 0; c < plugin->copies; c++ ) { snprintf( key, sizeof(key), "%d[%d]", p, c ); value = plugin->holders[c].status_memory[i]; mlt_properties_set_double( producer_properties, key, value ); } } } } 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 ) ); // Check that we created a frame and initialize it if ( *frame != NULL ) { // Obtain properties of frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); // Update timecode on the frame we're creating mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Save the producer to be used in get_audio mlt_properties_set_data( frame_properties, "_producer_ladspa", producer, 0, NULL, NULL ); // Push the get_audio method mlt_frame_push_audio( *frame, producer_get_audio ); } // Calculate the next time code mlt_producer_prepare_next( producer ); return 0; } /** Destructor for the producer. */ static void producer_close( mlt_producer producer ) { producer->close = NULL; mlt_producer_close( producer ); free( producer ); } /** Constructor for the producer. */ mlt_producer producer_ladspa_init( mlt_profile profile, mlt_service_type type, const char* id, char* arg ) { // Create a new producer object mlt_producer producer = mlt_producer_new( profile ); if ( producer != NULL ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; // Save the plugin ID. if ( !strncmp( id, "ladspa.", 7 ) ) { mlt_properties_set( properties, "_pluginid", id + 7 ); } // Make sure the plugin ID is valid. unsigned long plugin_id = mlt_properties_get_int64( properties, "_pluginid" ); if( plugin_id < 1000 || plugin_id > 0x00FFFFFF ) { producer_close( producer ); producer = NULL; } } return producer; } mlt-6.20.0/src/modules/jackrack/producer_ladspa.yml000066400000000000000000000005251362234133600223020ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: ladspa title: LADSPA version: 1 copyright: Copyright (C) 2013-2014 Meltytech, LLC license: GPLv2 language: en creator: Brian Matherly tags: - Audio description: Generate audio using LADSPA plugins. notes: > Automatically adapts to the number of channels and sampling rate of the consumer. mlt-6.20.0/src/modules/kdenlive/000077500000000000000000000000001362234133600164365ustar00rootroot00000000000000mlt-6.20.0/src/modules/kdenlive/CMakeLists.txt000066400000000000000000000004451362234133600212010ustar00rootroot00000000000000file(GLOB mltkdenlive_src *.c) add_library(mltkdenlive MODULE ${mltkdenlive_src}) target_link_libraries(mltkdenlive mlt m) install(TARGETS mltkdenlive LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/kdenlive) mlt-6.20.0/src/modules/kdenlive/Makefile000066400000000000000000000012671362234133600201040ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt include ../../../config.mak TARGET = ../libmltkdenlive$(LIBSUF) OBJS = factory.o \ filter_boxblur.o \ filter_freeze.o \ filter_wave.o \ producer_framebuffer.o 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/kdenlive" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/kdenlive" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/kdenlive/factory.c000066400000000000000000000042501362234133600202520ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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 ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/kdenlive/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } 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 ); MLT_REGISTER_METADATA( filter_type, "boxblur", metadata, "filter_boxblur.yml" ); MLT_REGISTER_METADATA( filter_type, "freeze", metadata, "filter_freeze.yml" ); MLT_REGISTER_METADATA( filter_type, "wave", metadata, "filter_wave.yml" ); MLT_REGISTER_METADATA( producer_type, "framebuffer", metadata, "producer_framebuffer.yml" ); } mlt-6.20.0/src/modules/kdenlive/filter_boxblur.c000066400000000000000000000137251362234133600216340ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include static void PreCompute(uint8_t *image, int32_t *rgba, int width, int height) { register int x, y, z; int32_t pts[4]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pts[0] = image[0]; pts[1] = image[1]; pts[2] = image[2]; pts[3] = image[3]; for (z = 0; z < 4; z++) { if (x > 0) pts[z] += rgba[-4]; if (y > 0) pts[z] += rgba[width * -4]; if (x>0 && y>0) pts[z] -= rgba[(width + 1) * -4]; *rgba++ = pts[z]; } image += 4; } } } static inline int32_t GetRGBA(int32_t *rgba, unsigned int w, unsigned int h, unsigned int x, int offsetx, unsigned int y, int offsety, unsigned int z) { int xtheo = x + offsetx; int ytheo = y + offsety; return rgba[4 * (CLAMP(xtheo, 0, w-1) + CLAMP(ytheo, 0, h-1) * w) + z]; } static void DoBoxBlur(uint8_t *image, int32_t *rgba, unsigned int width, unsigned int height, unsigned int boxw, unsigned int boxh) { register int x, y; float mul = 1.f / ((boxw*2) * (boxh*2)); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { *image++ = (GetRGBA(rgba, width, height, x, +boxw, y, +boxh, 0) + GetRGBA(rgba, width, height, x, -boxw, y, -boxh, 0) - GetRGBA(rgba, width, height, x, -boxw, y, +boxh, 0) - GetRGBA(rgba, width, height, x, +boxw, y, -boxh, 0)) * mul; *image++ = (GetRGBA(rgba, width, height, x, +boxw, y, +boxh, 1) + GetRGBA(rgba, width, height, x, -boxw, y, -boxh, 1) - GetRGBA(rgba, width, height, x, -boxw, y, +boxh, 1) - GetRGBA(rgba, width, height, x, +boxw, y, -boxh, 1)) * mul; *image++ = (GetRGBA(rgba, width, height, x, +boxw, y, +boxh, 2) + GetRGBA(rgba, width, height, x, -boxw, y, -boxh, 2) - GetRGBA(rgba, width, height, x, -boxw, y, +boxh, 2) - GetRGBA(rgba, width, height, x, +boxw, y, -boxh, 2)) * mul; *image++ = (GetRGBA(rgba, width, height, x, +boxw, y, +boxh, 3) + GetRGBA(rgba, width, height, x, -boxw, y, -boxh, 3) - GetRGBA(rgba, width, height, x, -boxw, y, +boxh, 3) - GetRGBA(rgba, width, height, x, +boxw, y, -boxh, 3)) * mul; } } } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); unsigned int boxw = 0; unsigned int boxh = 0; mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); double hori = mlt_properties_anim_get_double( properties, "hori", position, length ); double vert = mlt_properties_anim_get_double( properties, "vert", position, length ); // Get blur factor double factor = mlt_properties_get_int( properties, "start" ); if ( mlt_properties_get( properties, "end" ) ) { double end = (double) mlt_properties_get_int( properties, "end" ); factor += ( end - factor ) * mlt_filter_get_progress( filter, frame ); } // If animated property "blur" is set, use its value. char* blur_property = mlt_properties_get( properties, "blur" ); if ( blur_property ) { factor = mlt_properties_anim_get_double( properties, "blur", position, length ); } boxw = (unsigned int)(factor * hori); boxh = (unsigned int)(factor * vert); if ( boxw == 0 && boxh == 0 ) { // Don't do anything error = mlt_frame_get_image( frame, image, format, width, height, writable ); } else { // Get the image *format = mlt_image_rgb24a; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); boxw *= mlt_profile_scale_width(profile, *width); boxh *= mlt_profile_scale_height(profile, *height); if (boxw || boxh) { int size = mlt_image_format_size( *format, *width, *height, NULL ); int32_t *rgba = mlt_pool_alloc( 4 * size ); PreCompute( *image, rgba, *width, *height ); DoBoxBlur( *image, rgba, *width, *height, MAX(1, boxw), MAX(1, boxh) ); mlt_pool_release( rgba ); } } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "start", arg == NULL ? "2" : arg); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "hori", "1" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "vert", "1" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "blur", NULL ); } return filter; } mlt-6.20.0/src/modules/kdenlive/filter_boxblur.yml000066400000000000000000000025671362234133600222150ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: boxblur title: Box Blur version: 3 copyright: Leny Grisel, Jean-Baptiste Mardelle creator: Leny Grisel, Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video parameters: - identifier: start title: Size argument: yes type: integer description: > If an end size is provided, then this is the starting size, and size is interpolated over the duration of the filter from start to end size. If the keyframable property "blur" is provided, then this is ignored, and "blur" is used instead. This parameter also affects the size both horizontally and vertically simultaneously, in amounts proportional to the horizontal size and vertical size parameters. unit: pixels mutable: yes default: 2 minimum: 1 - identifier: end title: End size type: integer unit: pixels mutable: yes minimum: 1 - identifier: blur title: Size type: integer description: > If this value is set the start and end parameters are ignored. unit: pixels mutable: yes minimum: 1 - identifier: hori title: Horizontal size type: integer unit: pixels mutable: yes minimum: 0 default: 1 - identifier: vert title: Vertical size type: integer unit: pixels mutable: yes minimum: 0 default: 1 mlt-6.20.0/src/modules/kdenlive/filter_freeze.c000077500000000000000000000122411362234133600214320ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the image mlt_filter filter = mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties props = MLT_FRAME_PROPERTIES( frame ); 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_producer_get_in( mlt_frame_get_original_producer( frame ) ); mlt_position currentpos = mlt_filter_get_position( filter, 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) { mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); freeze_frame = mlt_properties_get_data( properties, "freeze_frame", NULL ); if ( !freeze_frame || mlt_properties_get_position( properties, "_frame" ) != pos ) { // freeze_frame has not been fetched yet or is not useful, so fetch it and cache it. // get parent producer mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_producer_seek( producer, pos ); // Get the frame mlt_service_get_frame( mlt_producer_service(producer), &freeze_frame, 0 ); mlt_properties freeze_properties = MLT_FRAME_PROPERTIES( freeze_frame ); 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( frame ) ); mlt_properties_set_int( freeze_properties, "progressive", mlt_properties_get_int( props, "progressive" ) ); mlt_properties_set_int( freeze_properties, "consumer_deinterlace", mlt_properties_get_int( props, "consumer_deinterlace" ) || mlt_properties_get_int( properties, "deinterlace" ) ); mlt_properties_set_data( properties, "freeze_frame", freeze_frame, 0, ( mlt_destructor )mlt_frame_close, NULL ); mlt_properties_set_position( properties, "_frame", pos ); } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Get frozen image uint8_t *buffer = NULL; int error = mlt_frame_get_image( freeze_frame, &buffer, format, width, height, 1 ); // Copy it to current frame int size = mlt_image_format_size( *format, *width, *height, NULL ); uint8_t *image_copy = mlt_pool_alloc( size ); memcpy( image_copy, buffer, size ); *image = image_copy; mlt_frame_set_image( frame, *image, size, mlt_pool_release ); uint8_t *alpha_buffer = mlt_frame_get_alpha( freeze_frame ); if ( alpha_buffer ) { int alphasize = *width * *height; uint8_t *alpha_copy = mlt_pool_alloc( alphasize ); memcpy( alpha_copy, alpha_buffer, alphasize ); mlt_frame_set_alpha( frame, alpha_copy, alphasize, mlt_pool_release ); } return error; } int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the filter on to the stack mlt_frame_push_service( frame, filter ); // 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; // Set the frame which will be chosen for freeze mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "frame", "0" ); // If freeze_after = 1, only frames after the "frame" value will be frozen mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "freeze_after", "0" ); // If freeze_before = 1, only frames before the "frame" value will be frozen mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "freeze_before", "0" ); } return filter; } mlt-6.20.0/src/modules/kdenlive/filter_freeze.yml000066400000000000000000000012471362234133600220120ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: freeze title: Freeze frame version: 1 copyright: Jean-Baptiste Mardelle creator: Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video parameters: - identifier: frame title: Frame type: time description: The time of the frame to freeze default: 0 mutable: true - identifier: freeze_after title: Freeze After type: boolean description: Whether to only freeze the frames after default: false mutable: true - identifier: freeze_before title: Freeze Before type: boolean description: Whether to only freeze the frames before default: false mutable: true mlt-6.20.0/src/modules/kdenlive/filter_wave.c000066400000000000000000000117161362234133600211170ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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[CLAMP(x+y*w, 0, w*h-1) * 4 + z]; } // the main meat of the algorithm lies here static void DoWave(uint8_t *src, int src_w, int src_h, uint8_t *dst, mlt_position position, int speed, int factor, int deformX, int deformY) { register int x, y; int decalY, decalX, z; float amplitude, phase, pulsation; register int uneven = src_w % 2; int w = (src_w - uneven ) / 2; amplitude = factor; pulsation = 0.5 / factor; // smaller means bigger period phase = position * pulsation * speed / 10; // smaller means longer for (y=0;y 0.0) { int image_size = *width * (*height) * 2; uint8_t *dst = mlt_pool_alloc (image_size); DoWave(*image, *width, (*height), dst, position, speed, factor, deformX, deformY); *image = dst; mlt_frame_set_image( frame, *image, image_size, mlt_pool_release ); } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ mlt_filter filter_wave_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if ( filter ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "start", arg == NULL ? "10" : arg); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "speed", "5"); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "deformX", "1"); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "deformY", "1"); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "wave", NULL); } return filter; } mlt-6.20.0/src/modules/kdenlive/filter_wave.yml000066400000000000000000000024431362234133600214730ustar00rootroot00000000000000schema_version: 0.2 type: filter identifier: wave title: Wave version: 2 copyright: Leny Grisel, Jean-Baptiste Mardelle creator: Leny Grisel, Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video parameters: - identifier: start title: Amplitude argument: yes type: integer description: > If an end amplitude is provided, then this is the starting amplitude, and amplitude is interpolated over the duration of the filter from start to end amplitude. If the keyframable property "wave" is provided, then this is ignored, and "wave" is used instead. This parameter also affects the pulsation period and the phase length. mutable: yes default: 10 minimum: 1 - identifier: end title: End amplitude type: integer mutable: yes minimum: 1 - identifier: wave title: Amplitude type: integer description: > If this value is set the start and end parameters are ignored. mutable: yes minimum: 1 - identifier: speed title: Speed type: integer mutable: yes default: 5 - identifier: deformX title: Deform horizontally? type: boolean mutable: yes default: 1 - identifier: deformY title: Deform vertically? type: boolean mutable: yes default: 1 mlt-6.20.0/src/modules/kdenlive/producer_framebuffer.c000066400000000000000000000326541362234133600230030ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include // Forward references. static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); /** Image stack(able) method */ static int framebuffer_get_image( mlt_frame frame, 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( frame ); int index = mlt_frame_pop_service_int( frame ); mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); // Frame properties objects mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_frame first_frame = mlt_properties_get_data( properties, "first_frame", NULL ); // Get producer parameters 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" ); int in = mlt_properties_get_position( properties, "in" ); // Determine the position mlt_position first_position = (first_frame != NULL) ? mlt_frame_get_position( first_frame ) : -1; mlt_position need_first = freeze; if ( !freeze || freeze_after || freeze_before ) { double prod_speed = mlt_properties_get_double( properties, "_speed" ); double actual_position = prod_speed * ( in + mlt_producer_position( producer ) ); if ( mlt_properties_get_int( properties, "reverse" ) ) actual_position = mlt_producer_get_playtime( producer ) - actual_position; if ( strobe < 2 ) { need_first = floor( actual_position ); } else { // Strobe effect wanted, calculate frame position need_first = floor( actual_position ); need_first -= MLT_POSITION_MOD(need_first, strobe); } if ( freeze ) { if ( freeze_after && need_first > freeze ) need_first = freeze; else if ( freeze_before && need_first < freeze ) need_first = freeze; } } if ( *format == mlt_image_none ) { // set format to the original's producer format *format = (mlt_image_format) mlt_properties_get_int( properties, "_original_format" ); } // Determine output buffer size *width = mlt_properties_get_int( frame_properties, "width" ); *height = mlt_properties_get_int( frame_properties, "height" ); int size = mlt_image_format_size( *format, *width, *height, NULL ); // Get output buffer int buffersize = 0; int alphasize = *width * *height; uint8_t *output = mlt_properties_get_data( properties, "output_buffer", &buffersize ); uint8_t *output_alpha = mlt_properties_get_data( properties, "output_alpha", NULL ); if( buffersize == 0 || buffersize != size ) { // invalidate cached frame first_position = -1; } if ( need_first != first_position ) { // invalidate cached frame first_position = -1; // Bust the cached frame mlt_properties_set_data( properties, "first_frame", NULL, 0, NULL, NULL ); first_frame = NULL; } if ( output && first_position != -1 ) { // Using the cached frame uint8_t *image_copy = mlt_pool_alloc( size ); memcpy( image_copy, output, size ); uint8_t *alpha_copy = mlt_pool_alloc( alphasize ); memcpy( alpha_copy, output_alpha, alphasize ); // Set the output image *image = image_copy; mlt_frame_set_image( frame, image_copy, size, mlt_pool_release ); mlt_frame_set_alpha( frame, alpha_copy, alphasize, mlt_pool_release ); *width = mlt_properties_get_int( properties, "_output_width" ); *height = mlt_properties_get_int( properties, "_output_height" ); *format = mlt_properties_get_int( properties, "_output_format" ); mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); return 0; } // Get the cached frame if ( first_frame == NULL ) { // Get the frame to cache from the real producer mlt_producer real_producer = mlt_properties_get_data( properties, "producer", 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 ); // Cache the frame mlt_properties_set_data( properties, "first_frame", first_frame, 0, ( mlt_destructor )mlt_frame_close, NULL ); } mlt_properties first_frame_properties = MLT_FRAME_PROPERTIES( first_frame ); // Which frames are buffered? uint8_t *first_image = mlt_properties_get_data( first_frame_properties, "image", NULL ); uint8_t *first_alpha = mlt_properties_get_data( first_frame_properties, "alpha", NULL ); if ( !first_image ) { mlt_properties_set( first_frame_properties, "rescale.interp", mlt_properties_get( frame_properties, "rescale.interp" ) ); int error = mlt_frame_get_image( first_frame, &first_image, format, width, height, writable ); if ( error != 0 ) { mlt_log_warning( MLT_PRODUCER_SERVICE( producer ), "first_image == NULL get image died\n" ); mlt_properties_set_data( properties, "first_frame", NULL, 0, NULL, NULL ); mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); return error; } output = mlt_pool_alloc( size ); memcpy( output, first_image, size ); // Let someone else clean up mlt_properties_set_data( properties, "output_buffer", output, size, mlt_pool_release, NULL ); mlt_properties_set_int( properties, "_output_width", *width ); mlt_properties_set_int( properties, "_output_height", *height ); mlt_properties_set_int( properties, "_output_format", *format ); } if ( !first_alpha ) { alphasize = *width * *height; first_alpha = mlt_frame_get_alpha_mask( first_frame ); output_alpha = mlt_pool_alloc( alphasize ); memcpy( output_alpha, first_alpha, alphasize ); mlt_properties_set_data( properties, "output_alpha", output_alpha, alphasize, mlt_pool_release, NULL ); } mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); // Create a copy uint8_t *image_copy = mlt_pool_alloc( size ); memcpy( image_copy, first_image, size ); uint8_t *alpha_copy = mlt_pool_alloc( alphasize ); memcpy( alpha_copy, first_alpha, alphasize ); // Set the output image *image = image_copy; mlt_frame_set_image( frame, image_copy, size, mlt_pool_release ); mlt_frame_set_alpha( frame, alpha_copy, alphasize, mlt_pool_release ); return 0; } static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { if ( frame ) { // Construct a new frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); // Stack the producer and producer's get image mlt_frame_push_service_int( *frame, index ); mlt_frame_push_service( *frame, producer ); mlt_frame_push_service( *frame, framebuffer_get_image ); mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES(*frame); // Get frame from the real producer mlt_frame first_frame = mlt_properties_get_data( properties, "first_frame", NULL ); if ( first_frame == NULL ) { // Get the frame to cache from the real producer mlt_producer real_producer = mlt_properties_get_data( properties, "producer", NULL ); // Get the producer speed double prod_speed = mlt_properties_get_double( properties, "_speed" ); // Seek the producer to the correct place mlt_producer_seek( real_producer, mlt_producer_position( producer ) * prod_speed ); // Get the frame mlt_service_get_frame( MLT_PRODUCER_SERVICE( real_producer ), &first_frame, index ); // Cache the frame mlt_properties_set_data( properties, "first_frame", first_frame, 0, ( mlt_destructor )mlt_frame_close, NULL ); // Find the original producer's format int width = 0; int height = 0; mlt_image_format format = mlt_image_none; uint8_t *image = NULL; int error = mlt_frame_get_image( first_frame, &image, &format, &width, &height, 0 ); if ( !error ) { // cache the original producer's pixel format mlt_properties_set_int( properties, "_original_format", (int) format ); } } mlt_properties_inherit( frame_properties, MLT_FRAME_PROPERTIES(first_frame) ); double force_aspect_ratio = mlt_properties_get_double( properties, "force_aspect_ratio" ); if ( force_aspect_ratio <= 0.0 ) force_aspect_ratio = mlt_properties_get_double( properties, "aspect_ratio" ); mlt_properties_set_double( frame_properties, "aspect_ratio", force_aspect_ratio ); // Give the returned frame temporal identity mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); mlt_properties_set_int( frame_properties, "meta.media.width", mlt_properties_get_int( properties, "width" ) ); mlt_properties_set_int( frame_properties, "meta.media.height", mlt_properties_get_int( properties, "height" ) ); mlt_properties_pass_list( frame_properties, properties, "width, height" ); } 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 producer = NULL; producer = calloc( 1, sizeof( struct mlt_producer_s ) ); if ( !producer ) return NULL; if ( mlt_producer_init( producer, NULL ) ) { free( producer ); return NULL; } // Wrap loader mlt_producer real_producer; // Check if a speed was specified. /** * Speed must be appended to the filename with '?'. To play your video at 50%: melt 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, "abnormal", props ); free( props ); if (speed == 0.0) speed = 1.0; if ( producer != NULL && real_producer != NULL) { // Get the properties of this producer mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties_set( properties, "resource", arg); // Store the producer and filter 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 ), "progressive, length, width, height, aspect_ratio" ); 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 real_properties = MLT_PRODUCER_PROPERTIES( real_producer ); const char* service = mlt_properties_get( real_properties, "mlt_service" ); if ( service && !strcmp( service, "avformat" ) ) { int n = mlt_properties_count( real_properties ); int i; for ( i = 0; i < n; i++ ) { if ( strstr( mlt_properties_get_name( real_properties, i ), "stream.frame_rate" ) ) { double source_fps = mlt_properties_get_double( real_properties, mlt_properties_get_name( real_properties, i ) ); if ( source_fps > mlt_profile_fps( profile ) ) { mlt_properties_set_double( real_properties, "force_fps", source_fps * speed ); mlt_properties_set_position( real_properties, "length", real_length ); mlt_properties_set_position( real_properties, "out", real_length - 1 ); speed = 1.0; } break; } } } } mlt_properties_set_position( properties, "out", mlt_producer_get_length( producer ) - 1 ); // Since we control the seeking, prevent it from seeking on its own mlt_producer_set_speed( real_producer, 0 ); mlt_producer_set_speed( producer, speed ); // Override the get_frame method producer->get_frame = producer_get_frame; } else { if ( producer ) mlt_producer_close( producer ); if ( real_producer ) mlt_producer_close( real_producer ); producer = NULL; } return producer; } mlt-6.20.0/src/modules/kdenlive/producer_framebuffer.yml000066400000000000000000000003041362234133600233450ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: framebuffer title: Speed version: 1 copyright: Jean-Baptiste Mardelle creator: Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video mlt-6.20.0/src/modules/kino/000077500000000000000000000000001362234133600155755ustar00rootroot00000000000000mlt-6.20.0/src/modules/kino/Makefile000066400000000000000000000017561362234133600172460ustar00rootroot00000000000000include ../../../config.mak include config.mak CFLAGS := -I../../ $(CFLAGS) CXXFLAGS := $(CFLAGS) -Wno-deprecated $(CXXFLAGS) LDFLAGS := -L../../framework -lmlt -lpthread $(LDFLAGS) TARGET = ../libmltkino.so OBJS = factory.o producer_kino.o CPPOBJS = kino_wrapper.o avi.o error.o filehandler.o riff.o 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) $(CXX) -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)$(moduledir)" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/kino/avi.cc000066400000000000000000001517021362234133600166710ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // 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 ) ); memset( dmlh, 0, sizeof( dmlh ) ); memset( &mainHdr, 0, sizeof( mainHdr ) ); memset( &streamHdr, 0, sizeof( streamHdr ) ); } /** 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; odml_list = 0; dmlh_chunk = 0; memset( &streamHdr, 0, sizeof( streamHdr ) ); } /** 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 appropriate 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() { memset( &dvinfo, 0, sizeof( dvinfo ) ); } 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; } mlt-6.20.0/src/modules/kino/avi.h000066400000000000000000000163551362234133600165370ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** 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 mlt-6.20.0/src/modules/kino/configure000077500000000000000000000014471362234133600175120ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ] then if [ "$targetos" = "Darwin" ] || [ "$targetos" = "MinGW" ] then echo "- does not build on OS X or Windows: 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.mak if [ "$lqt_disabled" = "0" ] then echo "CFLAGS += -DHAVE_LIBQUICKTIME" >> config.mak echo "HAVE_LIBQUICKTIME=1" >> config.mak else echo "- libquicktime not found: only enabling dv avi support" fi if [ "$libdv_disabled" = "0" ] then echo "CFLAGS += -DHAVE_LIBDV" >> config.mak echo "HAVE_LIBDV=1" >> config.mak fi [ "$libdv_disabled" != "0" -a "$lqt_disabled" = "0" ] && echo "- libdv not found: mov dv may not have audio" exit 0 fi mlt-6.20.0/src/modules/kino/deprecated000066400000000000000000000000001362234133600176060ustar00rootroot00000000000000mlt-6.20.0/src/modules/kino/endian_types.h000066400000000000000000000142511362234133600204330ustar00rootroot00000000000000/* * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ENDIAN_TYPES_H #define _ENDIAN_TYPES_H #include /* Needed for BYTE_ORDER and BIG/LITTLE_ENDIAN macros. */ #if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) #ifndef _BSD_SOURCE # define _BSD_SOURCE # include # undef _BSD_SOURCE #else # include #endif #else # include #endif /* !defined(__FreeBSD__) && !defined(__NetBSD__) */ #if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) #include #elif defined(__OpenBSD__) #define bswap_16(x) swap16(x) #define bswap_32(x) swap32(x) #define bswap_64(x) swap64(x) #else #define bswap_16(x) bswap16(x) #define bswap_32(x) bswap32(x) #define bswap_64(x) bswap64(x) #endif /* !defined(__FreeBSD__) && !defined(__NetBSD__) */ 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 mlt-6.20.0/src/modules/kino/error.cc000066400000000000000000000050211362234133600172330ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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; } } mlt-6.20.0/src/modules/kino/error.h000066400000000000000000000033071362234133600171020ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 mlt-6.20.0/src/modules/kino/factory.c000066400000000000000000000020431362234133600174070ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2005-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 ); } mlt-6.20.0/src/modules/kino/filehandler.cc000066400000000000000000000467241362234133600203760ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 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 */ timeStamp = 0; everyNthFrame = 0; framesToSkip = 0; maxFileSize = 0; } 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"; numBlocks = 0; } 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; if ( lseek( fd, 0, SEEK_SET ) < 0 ) return false; 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; memset( &dvinfo, 0, sizeof( dvinfo ) ); } AVIHandler::~AVIHandler() { delete audioBuffer; audioBuffer = NULL; for ( int c = 0; c < 4; c++ ) { 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 || 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 || 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 mlt-6.20.0/src/modules/kino/filehandler.h000066400000000000000000000117051362234133600202270ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 mlt-6.20.0/src/modules/kino/gpl000066400000000000000000000000001362234133600162700ustar00rootroot00000000000000mlt-6.20.0/src/modules/kino/kino_wrapper.cc000066400000000000000000000054371362234133600206150ustar00rootroot00000000000000/* * kino_wrapper.cc -- c wrapper for kino file handler * Copyright (C) 2005-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #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 ); } } mlt-6.20.0/src/modules/kino/kino_wrapper.h000066400000000000000000000026021362234133600204460ustar00rootroot00000000000000/* * kino_wrapper.h -- c wrapper for kino file handler * Copyright (C) 2005-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 mlt-6.20.0/src/modules/kino/producer_kino.c000066400000000000000000000105161362234133600206070ustar00rootroot00000000000000/* * producer_kino.c -- a DV file format parser * Copyright (C) 2005-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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( 1, sizeof( struct producer_kino_s ) ); 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 ); } } mlt-6.20.0/src/modules/kino/riff.cc000066400000000000000000000362131362234133600170370ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // C++ includes #include //#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 // 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() { type = 0; name = 0; length = 0; offset = 0; parent = 0; written = 0; } 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 = 0; off_t pos; int container = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT ); pos = lseek( fd, 0, SEEK_SET ); fail_if( pos == -1 ); /* 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; } } } mlt-6.20.0/src/modules/kino/riff.h000066400000000000000000000100231362234133600166700ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 mlt-6.20.0/src/modules/linsys/000077500000000000000000000000001362234133600161565ustar00rootroot00000000000000mlt-6.20.0/src/modules/linsys/20-linsys.rules000066400000000000000000000007111362234133600207710ustar00rootroot00000000000000# udev rules for linsys cards to give members of the video group permissions KERNEL=="sdi*[rt]x[0-9]*", MODE="0660", GROUP="video", \ RUN+="/usr/bin/find /sys/$env{DEVPATH}/ -type f -execdir /bin/chmod 0660 {} + -execdir /bin/chgrp video {} +", OPTIONS="last_rule" KERNEL=="asi*[rt]x[0-9]*", MODE="0660", GROUP="video", \ RUN+="/usr/bin/find /sys/$env{DEVPATH}/ -type f -execdir /bin/chmod 0660 {} + -execdir /bin/chgrp video {} +", OPTIONS="last_rule" mlt-6.20.0/src/modules/linsys/Makefile000077500000000000000000000012701362234133600176210ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread include ../../../config.mak include config.mak TARGET = ../libmltlinsys$(LIBSUF) OBJS = factory.o \ consumer_SDIstream.o ifdef WITH_JPEG CFLAGS += -DWITH_JPEG LDFLAGS += -ljpeg 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/linsys" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/linsys" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/linsys/configure000077500000000000000000000007131362234133600200660ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF Linsys options: --linsys-with-jpeg - Enable the option to export JPEGs (disabled by default) EOF else if [ "$targetos" = "Darwin" ] || [ "$targetos" = "MinGW" ] then echo "- does not build on OS X or Windows: disabling" touch ../disable-linsys exit 0 fi touch config.mak for i in "$@" do case $i in --linsys-with-jpeg ) echo "WITH_JPEG=1" > config.mak ;; esac done exit 0 fi mlt-6.20.0/src/modules/linsys/consumer_SDIstream.c000066400000000000000000000572261362234133600221040ustar00rootroot00000000000000/** * * MLT SDI Consumer: * request video and audio data from MLT and generate an SDI stream * * Copyright (C) Broadcasting Center Europe S.A. http://www.bce.lu * an RTL Group Company http://www.rtlgroup.com * All rights reserved. * * E-mail: support_plasec@bce.lu * * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * DESCRIPTION: * This software act as interface between the MLT Frameworkas as * MLT Consumer and the Linear Systems Ltd. SMPTE 292M and SMPTE 259M-C boards. * * Linear Systems can be contacted at http://www.linsys.ca * ********************************************************************************** * System : INTeL I686 64Bit * OS : Linux SuSE Kernel 2.6.27.39-0.2-default * Compiler: gcc 4.3.2 (c++) ********************************************************************************** * Project : MLT SDI Consumer for SD and HD * Started by : Thomas Kurpick, Dipl.Inf. (FH) ********************************************************************************** * Supported and tested boards for SD-SDI or HD-SDI Output: * PCI SDI Masterâ„¢ (model 107) * PCIe SDI Masterâ„¢ (model 159) * PCIe LP SDI Masterâ„¢ FD (model 145) * PCIe LP SDI Masterâ„¢ Quad/o (model 180) * PCIe LP HD-SDI Masterâ„¢ O (model 193) * * Note: PCIe LP HD-SDI Masterâ„¢ O (model 193) is an VidPort model and supports an * separate video and audio interface. Device file: * /dev/sdivideotx[] for active video data * /dev/sdiaudiotx[] for pcm audio data * * This mlt consumer use the following device files: * /dev/sditx[] (SD-PAL) up to 8 x AES (8 x stereo / 16 audio channels) * /dev/sdivideotx[] (HD) * /dev/sdiaudiotx[] (HD) up to 4 x AES (4 x stereo / 8 audio channels) * * ********************************************************************************** * Last modified by: * Thomas Kurpick 08.Jan.2010 * and * Dan Dennedy 10.Feb.2010 * Ver. 2.0 * See also Git commit log. * ********************************************************************************** * * Consumer properties: * 'dev_video' * 'dev_audio' * 'blanking' * Only to monitor the SDI output a beta version of jpeg-writer is implemented. * 'jpeg_files' a number for output interval * 'save_jpegs' path for image * * EXAMPLE: * * SDI boards with full frame stream (with blanking): * melt video.dv -consumer sdi:/dev/sditx0 * melt video.dv -consumer sdi:/dev/sditx0 blanking=true * melt video.dv -consumer sdi dev_video=/dev/sditx0 blanking=true * melt video.dv audio_index=all -consumer sdi dev_video=/dev/sditx0 blanking=true * * SDI boards without full frame stream (without blanking): * melt -profile atsc_1080i_50 video.mpeg audio_index=1 -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false * melt -profile atsc_1080i_50 video.mpeg audio_index=all -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false * melt -profile atsc_1080i_50 video.mpeg audio_index=all -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false jpeg_files=25 save_jpegs=channel_04.jpg * * * SDI output formats and MLT profiles: * ##################################################################################################################################################### * ########## SMPTE 274M 1920 x 1080 Image Sample Structure ############################################################################################ * ##################################################################################################################################################### * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model) * 4 1920 x 1080/60/I interlaced 30 HZ 4 x AES (8 channels) atsc_1080i_60 193 * 5 1920 x 1080/59.94/I interlaced 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_1080i_5994 193 * 6 1920 x 1080/50/I interlaced 25 HZ 4 x AES (8 channels) atsc_1080i_50 193 * 7 1920 x 1080/30/P progressive 30 HZ 4 x AES (8 channels) atsc_1080p_30 193 * 8 1920 x 1080/29.97/P progressive 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_1080p_2997 193 * 9 1920 x 1080/25/P progressive 25 HZ 4 x AES (8 channels) atsc_1080p_25 193 * 10 1920 x 1080/24/P progressive 24 HZ 4 x AES (8 channels) atsc_1080p_24 193 * 11 1920 x 1080/23.98/P progressive 24000/1001 ~ 23.98 HZ 4 x AES (8 channels) atsc_1080p_2398 193 * * ##################################################################################################################################################### * ########## SMPTE 296M 1280 × 720 Progressive Image Sample Structure ################################################################################# * ##################################################################################################################################################### * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model) * 1 1280 × 720/60 progressive 60 HZ 4 x AES (8 channels) atsc_720p_60 193 * 2 1280 × 720/59.94 progressive 60000/1001 ~ 59.97 HZ 4 x AES (8 channels) atsc_720p_5994 193 * 3 1280 × 720/50 progressive 50 HZ 4 x AES (8 channels) atsc_720p_50 193 * 4 1280 × 720/30 progressive 30 HZ 4 x AES (8 channels) atsc_720p_30 193 * 5 1280 × 720/29.97 progressive 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_720p_2997 193 * 6 1280 × 720/25 progressive 25 HZ 4 x AES (8 channels) atsc_720p_25 193 * 7 1280 × 720/24 progressive 24 HZ 4 x AES (8 channels) atsc_720p_24 193 * 8 1280 × 720/23.98 progressive 24000/1001 ~ 23.98 HZ 4 x AES (8 channels) atsc_720p_2398 193 * * ##################################################################################################################################################### * ########## SMPTE 125M 486i 29.97Hz & BT.656 576i 25Hz ############################################################################################### * ##################################################################################################################################################### * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model) * SD PAL 720 × 576/50/I interlaced 25 HZ 8 x AES (16 channels) dv_pal 180,145,159,107 * SD PAL 720 × 576/50/I interlaced 25 HZ 4 x AES (8 channels) dv_pal 193 * SD NTSC 720 × 486/59.94/I interlaced 30000/1001 ~ 29.97 HZ 8 x AES (16 channels) sdi_486i_5994 TODO:180,145,159,107 * SD NTSC 720 × 486/59.94/I interlaced 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) sdi_486i_5994 193 * **/ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_JPEG // for JPEG output #include #endif #include "sdi_generator.c" // alias for "struct consumer_SDIstream_s *" , now we can write "consumer_SDIstream". Makes it more readable... typedef struct consumer_SDIstream_s *consumer_SDIstream; struct consumer_SDIstream_s { // Most of these values are set to their defaults by the parent consumer struct mlt_consumer_s parent; // This is the basic Consumer from which we fetch our data mlt_image_format pix_fmt; // Must be mlt_image_yuv422 for SDI int width; int height; struct audio_format audio_format; /** * device file: * /dev/sditx0 * /dev/sdivideotx0 * /dev/sdiaudiotx0 **/ char *device_file_video; // Path for SDI output char *device_file_audio; // Path for exclusive SDI audio output /** * write own HANC (ancillary data) is available for: * SDI board ASSY 193 HD 'blanking=false' * SDI board ASSY 180 SD quad 'blanking=true' * SDI board ASSY 145 SD single 'blanking=true' * * 0=false, 1=true * **/ uint8_t blanking; // our audio channel pair for this frame int16_t audio_buffer[MAX_AUDIO_STREAMS][MAX_AUDIO_SAMPLES]; // The SDI audio channel pairs for this frame char *video_fmt_name; // 1080i25, 1080p25, 576i50, 486i2997, ... }; /** * Forward references to static functions. **/ 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_close(mlt_consumer parent); static void *consumer_thread(void *); static void consumer_write_JPEG(char * path, uint8_t **vBuffer, mlt_profile myProfile); int convertYCBCRtoRGB(int y1, int cb, int cr, int y2, uint8_t * target_rgb); /***************************************************************************************************** ****************************************** SDI Master Consumer ************************************** *****************************************************************************************************/ /** This is what will be called by the factory * @param profile: profile name for consumer * @param type: unused * @param *id: unused * @param *arg: pointer to output path **/ mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) { // Create the consumer object consumer_SDIstream this = calloc( 1, sizeof(struct consumer_SDIstream_s) ); // If malloc 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; // We have stuff to clean up, so override the close method parent->close = consumer_close; // Set output path for SDI, default is "/dev/sditx0" if (arg == NULL) { this->device_file_video = strdup("/dev/sditx0"); } else { this->device_file_video = strdup(arg); } // Set up start/stop/terminated callbacks parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; // Set explicit to zero or other value int i, j; for (i = 0; i < MAX_AUDIO_STREAMS; i++) { for (j = 0; j < MAX_AUDIO_SAMPLES; j++) { this->audio_buffer[i][j] = j; } } mlt_events_register( MLT_CONSUMER_PROPERTIES(parent), "consumer-fatal-error", NULL ); // Return the consumer produced return parent; } // malloc or consumer init failed free(this); // Indicate failure return NULL; } /** * Start the consumer. **/ static int consumer_start(mlt_consumer parent) { // Get the properties mlt_properties properties = mlt_consumer_properties(parent); // Get the actual object consumer_SDIstream this = parent->child; // Check that we're not already running if (!mlt_properties_get_int(properties, "running")) { // Allocate threads pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t)); // Assign the thread to properties mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL); // Set the running state mlt_properties_set_int(properties, "running", 1); // Create the the threads pthread_create(consumer_pthread, NULL, consumer_thread, this); } return 0; } /** * Stop the consumer **/ static int consumer_stop(mlt_consumer parent) { // Get the properties mlt_properties properties = mlt_consumer_properties(parent); // Check that we're running if (mlt_properties_get_int(properties, "running")) { // Get the threads pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL); // Stop the threads mlt_properties_set_int(properties, "running", 0); // Wait for termination pthread_join(*consumer_pthread, 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"); } /** * Threaded wrapper for pipe. **/ static void *consumer_thread(void *arg) { // Identify the arg consumer_SDIstream this = arg; // Get the consumer mlt_consumer consumer = &this->parent; // Convenience functionality (this is to stop melt/inigo after the end of a playout) int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "terminate_on_pause"); int terminated = 0; // save only status int save_jpegs = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "save_jpegs"); char * jpeg_folder = mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "jpeg_file"); // If no folder is specified, skip jpeg export if (jpeg_folder == NULL) { save_jpegs = 0; } if (save_jpegs > 0) mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "Saving a JPEG every %i frame.\n", save_jpegs); int counter = 0; // each second we save a Jpeg // set properties (path) for device files if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_video") != NULL) { this->device_file_video = strdup(mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_video")); } if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_audio") != NULL) { if (this->blanking == 0) { this->device_file_audio = strdup(mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_audio")); } else { // if we write HANC we do not write further audio data mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "Audio device file is set but will not be used.\n"); } } // Set additional device file defaults struct stat st; int fd = -1; if (this->device_file_video) fd = stat(this->device_file_video, &st); if (fd == -1) { free(this->device_file_video); this->device_file_video = strdup("/dev/sdivideotx0"); } else { close(fd); } if (this->device_file_audio) { fd = stat(this->device_file_audio, &st); if (fd == -1) { free(this->device_file_audio); this->device_file_audio = strdup("/dev/sdiaudiotx0"); } else { close(fd); } } else if (this->device_file_video && strstr(this->device_file_video, "sdivideotx")) { this->device_file_audio = strdup("/dev/sdiaudiotx0"); } // set blanking flag; is not nessary we write no own blanking(HANC) for HD board ASSY 193 if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking")) { // set value if (!strcmp( mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"), "false")) { this->blanking = 0; } else if (!strcmp( mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"), "true")) { this->blanking = 1; } else { this->blanking = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"); } } else if (this->device_file_video && strstr(this->device_file_video, "sdivideotx")) { this->blanking = 0; } else { // set default value without HD board, also with blanking this->blanking = 1; } // Define a frame pointer mlt_frame frame; // set Datablock number for SDI encoding int my_dbn = 1; double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps"); unsigned int count = 0; // Tell the framework how we want our audio and video int frequency = this->audio_format.sample_rate; int channels = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES(consumer), "channels" ); int samples; // set number of audio channels, linsys vidport model 193 is limited to 8 channels (4AES frames) this->audio_format.channels = 8; /* 0,2,4,6,8 */ this->audio_format.aformat = mlt_audio_s16; /* 16, 24, 32 */ this->audio_format.sample_rate = 48000; this->pix_fmt = mlt_image_yuv422; if (this->device_file_video && this->device_file_audio && !sdi_init(this->device_file_video, this->device_file_audio, this->blanking, mlt_service_profile((mlt_service) consumer), &this->audio_format)) { mlt_log_fatal( MLT_CONSUMER_SERVICE(consumer), "failed to initialize\n" ); mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-fatal-error", NULL ); mlt_consumer_stopped(consumer); return NULL; } uint8_t *video_buffer = NULL; int16_t *audio_buffer_tmp; // the upstream audio buffer // Loop until told not to while (!consumer_is_stopped(consumer) && terminated == 0) { // // Get a frame from the service if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) { // Check for termination if (terminate_on_pause && frame != NULL) { terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES( frame ), "_speed") == 0.0; if (terminated == 1) { mlt_log_verbose(MLT_CONSUMER_SERVICE(consumer), "\nEnd of playout reached, terminating\n"); consumer_stop(consumer); } } // True if mlt_consumer_rt_frame(...) successful if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) { // Get the video from this frame and save it to our video_buffer mlt_frame_get_image(frame, &video_buffer, &this->pix_fmt, &this->width, &this->height, 1); // Get the audio from this frame and save it to our audio_buffer samples = mlt_sample_calculator(fps, frequency, count++); mlt_frame_get_audio(frame, (void**) &audio_buffer_tmp, &this->audio_format.aformat, &frequency, &channels, &samples); this->audio_format.sample_rate = frequency; this->audio_format.samples = samples; /* TODO: Audio is currently hard coded to 8 channels because write 8 channels to the sdi board. The Linys SDI board has to be configured with the same number of channels! this->audio_format.channels = channels; // take given number of channels */ /* Tell the sdi_generator.c to playout our frame * 8 AES (8 x stereo channels are possible, max. 16 channels) Linsys SD board model: 107, 159, 145, 180 * 4 AES (4 x stereo channels are possible, max. 8 channels) Linsys HD board model: 193 */ if (video_buffer) { // provide mapping of audio channels int i, j = 0; int map_channels, map_start; for (i = 0; i < MAX_AUDIO_STREAMS && j < channels; i++) { char key[27]; int c; sprintf(key, "meta.map.audio.%d.channels", i); map_channels = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key); sprintf(key, "meta.map.audio.%d.start", i); map_start = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key); if (!map_channels) map_channels = channels - j; for (c = 0; c < map_channels && j < channels; c++, j++) { int16_t *src = audio_buffer_tmp + j; int16_t *dest = this->audio_buffer[(map_start + c) / 2] + (map_start + c) % 2; int s = samples + 1; while (--s) { *dest = *src; dest += 2; src += channels; } } } // generate SDI frame and playout my_dbn = sdi_playout(video_buffer, this->audio_buffer, &this->audio_format, (channels + 1) / 2, my_dbn); // write a JPEG of every X-th frame if (save_jpegs > 0 && counter >= save_jpegs) { consumer_write_JPEG(jpeg_folder, &video_buffer, mlt_service_profile((mlt_service) consumer)); counter = 0; } else if (save_jpegs > 0) { counter++; } mlt_events_fire(MLT_CONSUMER_PROPERTIES( consumer ), "consumer-frame-show", frame, NULL ); } else { mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Videobuffer was NULL, skipping playout!\n"); } } else { mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "WARNING the requested frame is not yet rendered! This will cause image disturbance!\n"); if (video_buffer) { my_dbn = sdi_playout(video_buffer, this->audio_buffer, &this->audio_format, (channels + 1) / 2, my_dbn); } else { mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Videobuffer was NULL, skipping playout!\n"); } } if (frame != NULL) mlt_frame_close(frame); } } mlt_consumer_stopped(consumer); return NULL; } /** * Callback to allow override of the close method. **/ static void consumer_close(mlt_consumer parent) { // Get the actual object consumer_SDIstream this = parent->child; free(this->device_file_video); free(this->device_file_audio); // Now clean up the rest (the close = NULL is a bit nasty but needed for now) parent->close = NULL; mlt_consumer_close(parent); // Invoke the close function of the sdi_generator to close opened files used for output sdimaster_close(); // Finally clean up this free(this); } /** * Write videobuffer as JPEG to path * @param path **/ static void consumer_write_JPEG(char * filename, uint8_t **vBuffer, mlt_profile myProfile) { #ifdef WITH_JPEG int bytes_per_pixel = 3; // or 1 for GRACYSCALE images int color_space = JCS_RGB; // or JCS_GRAYSCALE for grayscale images uint8_t * buffer_position = *vBuffer; uint8_t image_rgb[myProfile->width * myProfile->height * bytes_per_pixel]; //convert vBuffer to RGB int i; for (i = 0; i < sizeof(image_rgb) / 6; i++) { int y1 = *(buffer_position++); int cb = *(buffer_position++); int y2 = *(buffer_position++); int cr = *(buffer_position++); convertYCBCRtoRGB(y1, cb, cr, y2, &image_rgb[i * 6]); } struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; // this is a pointer to one row of image data JSAMPROW row_pointer[1]; FILE *outfile = fopen(filename, "wb"); if (!outfile) { mlt_log_error(NULL, "%s: Error opening output jpeg file %s\n!", __FILE__, filename); return; } cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo, outfile); // Setting the parameters of the output file here cinfo.image_width = myProfile->width; cinfo.image_height = myProfile->height; cinfo.input_components = bytes_per_pixel; cinfo.in_color_space = (J_COLOR_SPACE) color_space; // default compression parameters, we shouldn't be worried about these jpeg_set_defaults(&cinfo); // Now do the compression jpeg_start_compress(&cinfo, TRUE); // like reading a file, this time write one row at a time while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = &image_rgb[cinfo.next_scanline * cinfo.image_width * cinfo.input_components]; jpeg_write_scanlines(&cinfo, row_pointer, 1); } // similar to read file, clean up after we're done compressing jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); fclose(outfile); #endif } /** * converts YCbCr samples to two 32-bit RGB values * @param y1 cb cr and y2 values * @param target pointer * @return 0 upon success **/ int convertYCBCRtoRGB(int y1, int cb, int cr, int y2, uint8_t * target_rgb) { #ifdef WITH_JPEG if(y1 > 235) y1 = 235; if(y1 < 16) y1 = 16; if(y2 > 235) y2 = 235; if(y2 < 16) y2 = 16; if(cr > 240) cr = 240; if(cr < 16) cr = 16; if(cb > 240) cb = 240; if(cb < 16) cb = 16; uint8_t r1, g1, b1, r2, g2, b2; //pointer to current output buffer position uint8_t * target_pointer = target_rgb; r1 = y1 + 1.402 * (cr - 128); g1 = y1 - 0.34414 * (cb - 128) - 0.71414 * (cr - 128); b1 = y1 + 1.772 * (cb - 128); r2 = y2 + 1.402 * (cr - 128); g2 = y2 - 0.34414 * (cb - 128) - 0.71414 *(cr - 128); b2 = y2 + 1.772 * (cb - 128); *target_pointer++ = r1; *target_pointer++ = g1; *target_pointer++ = b1; *target_pointer++ = r2; *target_pointer++ = g2; *target_pointer++ = b2; return 0; #endif return 1; } mlt-6.20.0/src/modules/linsys/consumer_sdi.yml000066400000000000000000000003211362234133600213670ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: sdi title: Linear Systems SDI version: 1 copyright: Broadcasting Center Europe S.A. creator: Thomas Kurpick license: GPLv2 language: en tags: - Audio - Video mlt-6.20.0/src/modules/linsys/factory.c000066400000000000000000000011761362234133600177760ustar00rootroot00000000000000/* * factory.c for Linsys SDI consumer */ #include #include #include extern mlt_consumer consumer_SDIstream_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/linsys/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "sdi", consumer_SDIstream_init ); MLT_REGISTER_METADATA( consumer_type, "sdi", metadata, "consumer_sdi.yml" ); } mlt-6.20.0/src/modules/linsys/gpl000066400000000000000000000000001362234133600166510ustar00rootroot00000000000000mlt-6.20.0/src/modules/linsys/sdi_generator.c000066400000000000000000002360471362234133600211630ustar00rootroot00000000000000/** * * MLT SDI Consumer: * request video and audio data from MLT and generate an SDI stream * * Copyright (C) Broadcasting Center Europe S.A. http://www.bce.lu * an RTL Group Company http://www.rtlgroup.com * All rights reserved. * * E-mail: support_plasec@bce.lu * * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * DESCRIPTION: * This software act as interface between the MLT Frameworkas as * MLT Consumer and the Linear Systems Ltd. SMPTE 292M and SMPTE 259M-C boards. * * Linear Systems can be contacted at http://www.linsys.ca * ********************************************************************************** * System : INTeL I686 64Bit * OS : Linux SuSE Kernel 2.6.27.39-0.2-default * Compiler: gcc 4.3.2 (c++) ********************************************************************************** * Project : MLT SDI Consumer for SD and HD * Started by : Thomas Kurpick, Dipl.Inf. (FH) ********************************************************************************** * Supported and tested boards for SD-SDI or HD-SDI Output: * PCI SDI Masterâ„¢ (model 107) * PCIe SDI Masterâ„¢ (model 159) * PCIe LP SDI Masterâ„¢ FD (model 145) * PCIe LP SDI Masterâ„¢ Quad/o (model 180) * PCIe LP HD-SDI Masterâ„¢ O (model 193) * * Note: PCIe LP HD-SDI Masterâ„¢ O (model 193) is an VidPort model and supports an * separate video and audio interface. Device file: * /dev/sdivideotx[] for active video data * /dev/sdiaudiotx[] for pcm audio data * * This mlt consumer use the following device files: * /dev/sditx[] (SD-PAL) up to 8 x AES (8 x stereo / 16 audio channels) * /dev/sdivideotx[] (HD) * /dev/sdiaudiotx[] (HD) up to 4 x AES (4 x stereo / 8 audio channels) * * ********************************************************************************** * Last modified by: * Thomas Kurpick 08.Jan.2010 * Ver. 2.0 * ********************************************************************************** * * Consumer properties: * 'dev_video' * 'dev_audio' * 'blanking' * Only to monitor the SDI output a beta version of jpeg-writer is implemented. * 'jpeg_files' a number for output interval * 'save_jpegs' path for image * * EXAMPLE: * * SDI boards with full frame stream (with blanking): * melt video.dv -consumer sdi:/dev/sditx0 buffer=0; * melt video.dv -consumer sdi:/dev/sditx0 buffer=0 blanking=true; * melt video.dv -consumer sdi dev_video=/dev/sditx0 buffer=0 blanking=true; * melt video.dv audio_index=all -consumer sdi dev_video=/dev/sditx0 buffer=0 blanking=true; * * SDI boards without full frame stream (without blanking): * melt -profile atsc_1080i_50 video.mpeg audio_index=1 -consumer sdi dev_video=/dev/sdivideotx0 dev_sdiaudio=/dev/sdiaudiotx0 blanking=false * melt -profile atsc_1080i_50 video.mpeg audio_index=all -consumer sdi dev_video=/dev/sdivideotx0 dev_sdiaudio=/dev/sdiaudiotx0 blanking=false * melt -profile atsc_1080i_50 video.mpeg audio_index=all -consumer sdi dev_video=/dev/sdivideotx0 dev_sdiaudio=/dev/sdiaudiotx0 blanking=false jpeg_files=25 save_jpegs=channel_04.jpg * * * SDI output formats and MLT profiles: * ##################################################################################################################################################### * ########## SMPTE 274M 1920 x 1080 Image Sample Structure ############################################################################################ * ##################################################################################################################################################### * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model) * 4 1920 x 1080/60/I interlaced 30 HZ 4 x AES (8 channels) atsc_1080i_60 193 * 5 1920 x 1080/59.94/I interlaced 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_1080i_5994 193 * 6 1920 x 1080/50/I interlaced 25 HZ 4 x AES (8 channels) atsc_1080i_50 193 * 7 1920 x 1080/30/P progressive 30 HZ 4 x AES (8 channels) atsc_1080p_30 193 * 8 1920 x 1080/29.97/P progressive 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_1080p_2997 193 * 9 1920 x 1080/25/P progressive 25 HZ 4 x AES (8 channels) atsc_1080p_25 193 * 10 1920 x 1080/24/P progressive 24 HZ 4 x AES (8 channels) atsc_1080p_24 193 * 11 1920 x 1080/23.98/P progressive 24000/1001 ~ 23.98 HZ 4 x AES (8 channels) atsc_1080p_2398 193 * * ##################################################################################################################################################### * ########## SMPTE 296M 1280 × 720 Progressive Image Sample Structure ################################################################################# * ##################################################################################################################################################### * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model) * 1 1280 × 720/60 progressive 60 HZ 4 x AES (8 channels) atsc_720p_60 193 * 2 1280 × 720/59.94 progressive 60000/1001 ~ 59.97 HZ 4 x AES (8 channels) atsc_720p_5994 193 * 3 1280 × 720/50 progressive 50 HZ 4 x AES (8 channels) atsc_720p_50 193 * 4 1280 × 720/30 progressive 30 HZ 4 x AES (8 channels) atsc_720p_30 193 * 5 1280 × 720/29.97 progressive 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_720p_2997 193 * 6 1280 × 720/25 progressive 25 HZ 4 x AES (8 channels) atsc_720p_25 193 * 7 1280 × 720/24 progressive 24 HZ 4 x AES (8 channels) atsc_720p_24 193 * 8 1280 × 720/23.98 progressive 24000/1001 ~ 23.98 HZ 4 x AES (8 channels) atsc_720p_2398 193 * * ##################################################################################################################################################### * ########## SMPTE 125M 486i 29.97Hz & BT.656 576i 25Hz ############################################################################################### * ##################################################################################################################################################### * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model) * SD PAL 720 × 576/50/I interlaced 25 HZ 8 x AES (16 channels) dv_pal 180,145,159,107 * SD PAL 720 × 576/50/I interlaced 25 HZ 4 x AES (8 channels) dv_pal 193 * SD NTSC 720 × 486/59.94/I interlaced 30000/1001 ~ 29.97 HZ 8 x AES (16 channels) sdi_486i_5994 TODO:180,145,159,107 * SD NTSC 720 × 486/59.94/I interlaced 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) sdi_486i_5994 193 * **/ #include "sdi_generator.h" /*!/brief initialization of the file handlers for the playout * @param *device_video: file or SDITX device or SDIVIDEOTX device * @param *device_audio: file or SDIAUDIOTX device * @param blanking: true or false (if false the consumer write only active video data without any VANH or HANC) */ static int sdi_init(char *device_video, char *device_audio, uint8_t blanking, mlt_profile myProfile, const struct audio_format * audio_format) { // set device file device_file_video = device_video; device_file_audio = device_audio; // set flag for using of blanking with ancillary data info.blanking = blanking; // set pack method for SDI word conversion pack = pack8; //pack = pack10; //pack = pack_v210; // check format if (myProfile->width == 1920 && myProfile->height == 1080 && myProfile->frame_rate_num == 30 && myProfile->frame_rate_den == 1 && myProfile->progressive == 0) { info.fmt = &FMT_1080i60; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_274M_1080I_60HZ; } else if (myProfile->width == 1920 && myProfile->height == 1080 && myProfile->frame_rate_num == 30000 && myProfile->frame_rate_den == 1001 && myProfile->progressive == 0) { info.fmt = &FMT_1080i5994; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_274M_1080I_59_94HZ; } else if (myProfile->width == 1920 && myProfile->height == 1080 && myProfile->frame_rate_num == 25 && myProfile->frame_rate_den == 1 && myProfile->progressive == 0) { info.fmt = &FMT_1080i50; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_274M_1080I_50HZ; } else if (myProfile->width == 1920 && myProfile->height == 1080 && myProfile->frame_rate_num == 30 && myProfile->frame_rate_den == 1 && myProfile->progressive == 1) { info.fmt = &FMT_1080p30; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_274M_1080P_30HZ; } else if (myProfile->width == 1920 && myProfile->height == 1080 && myProfile->frame_rate_num == 30000 && myProfile->frame_rate_den == 1001 && myProfile->progressive == 1) { info.fmt = &FMT_1080p2997; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_274M_1080P_29_97HZ; } else if (myProfile->width == 1920 && myProfile->height == 1080 && myProfile->frame_rate_num == 25 && myProfile->frame_rate_den == 1 && myProfile->progressive == 1) { info.fmt = &FMT_1080p25; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_274M_1080P_25HZ; } else if (myProfile->width == 1920 && myProfile->height == 1080 && myProfile->frame_rate_num == 24 && myProfile->frame_rate_den == 1 && myProfile->progressive == 1) { info.fmt = &FMT_1080p24; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_274M_1080P_24HZ; } else if (myProfile->width == 1920 && myProfile->height == 1080 && myProfile->frame_rate_num == 24000 && myProfile->frame_rate_den == 1001 && myProfile->progressive == 1) { info.fmt = &FMT_1080p2398; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_274M_1080P_23_98HZ; } else if (myProfile->width == 1280 && myProfile->height == 720 && myProfile->frame_rate_num == 60 && myProfile->frame_rate_den == 1 && myProfile->progressive == 1) { info.fmt = &FMT_720p60; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_296M_720P_60HZ; } else if (myProfile->width == 1280 && myProfile->height == 720 && myProfile->frame_rate_num == 60000 && myProfile->frame_rate_den == 1001 && myProfile->progressive == 1) { info.fmt = &FMT_720p5994; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_296M_720P_59_94HZ; } else if (myProfile->width == 1280 && myProfile->height == 720 && myProfile->frame_rate_num == 50 && myProfile->frame_rate_den == 1 && myProfile->progressive == 1) { info.fmt = &FMT_720p50; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_296M_720P_50HZ; } else if (myProfile->width == 1280 && myProfile->height == 720 && myProfile->frame_rate_num == 30 && myProfile->frame_rate_den == 1 && myProfile->progressive == 1) { info.fmt = &FMT_720p30; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_296M_720P_30HZ; } else if (myProfile->width == 1280 && myProfile->height == 720 && myProfile->frame_rate_num == 30000 && myProfile->frame_rate_den == 1001 && myProfile->progressive == 1) { info.fmt = &FMT_720p2997; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_296M_720P_29_97HZ; } else if (myProfile->width == 1280 && myProfile->height == 720 && myProfile->frame_rate_num == 25 && myProfile->frame_rate_den == 1 && myProfile->progressive == 1) { info.fmt = &FMT_720p25; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_296M_720P_25HZ; } else if (myProfile->width == 1280 && myProfile->height == 720 && myProfile->frame_rate_num == 24 && myProfile->frame_rate_den == 1 && myProfile->progressive == 1) { info.fmt = &FMT_720p24; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_296M_720P_24HZ; } else if (myProfile->width == 1280 && myProfile->height == 720 && myProfile->frame_rate_num == 24000 && myProfile->frame_rate_den == 1001 && myProfile->progressive == 1) { info.fmt = &FMT_720p2398; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_296M_720P_23_98HZ; } else if (myProfile->width == 720 && myProfile->height == 576 && myProfile->frame_rate_num == 25 && myProfile->frame_rate_den == 1 && myProfile->progressive == 0) { info.fmt = &FMT_576i50; sdi_frame_mode = SDIVIDEO_CTL_BT_601_576I_50HZ; } else if (myProfile->width == 720 && myProfile->height == 486 && myProfile->frame_rate_num == 30000 && myProfile->frame_rate_den == 1001 && myProfile->progressive == 0) { info.fmt = &FMT_486i5994; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_125M_486I_59_94HZ; } else if (myProfile->width == 720 && myProfile->height == 480 && myProfile->frame_rate_num == 30000 && myProfile->frame_rate_den == 1001 && myProfile->progressive == 0) { info.fmt = &FMT_480i5994; sdi_frame_mode = SDIVIDEO_CTL_SMPTE_125M_486I_59_94HZ; } else { printf("Consumer got unknown format: %s", myProfile->description); info.fmt = &FMT_576i50; sdi_frame_mode = SDIVIDEO_CTL_BT_601_576I_50HZ; } printf("Consumer use format: %s\nProfile: %i %i %i %i %i\n", myProfile->description, myProfile->width, myProfile->height, myProfile->frame_rate_num, myProfile->frame_rate_den, myProfile->progressive); // Check if the format supports own blanking (note: model 193 supports currently only active video at the video device file) if (info.blanking && info.fmt != &FMT_576i50) { printf("SDI consumer doesn't support blanking(HANC) for the configured SD board and SDI format. Try argument: blanking=false\n"); return 0; } // if we write our own HANC we need an AES channel status bit array if (info.blanking) { // small description // http://www.sencore.com/newsletter/Nov05/DigAudioChannelStatusBits.htm // or // http://www.sencore.com/uploads/files/DigAudioChannelStatusBits.pdf // create empty AESChannelStatusBitArray int i = 0; for (i = 0; i < sizeof(AESChannelStatusBitArray) / sizeof(AESChannelStatusBitArray[0]); i++) AESChannelStatusBitArray[i] = 0; /** * Professional Format - Channel Status Bits **/ ////// Byte 0 ////// AESChannelStatusBitArray[0] = 1; // professional format AESChannelStatusBitArray[1] = 0; // PCM Format AESChannelStatusBitArray[2] = 1; // Emphasis: [100] No Emphasis AESChannelStatusBitArray[3] = 0; // ^ AESChannelStatusBitArray[4] = 0; // ^ AESChannelStatusBitArray[5] = 0; // locked AESChannelStatusBitArray[6] = 0; // sample frequency Fs: [01]48kHz, [10]44kHz, [11]32kHz AESChannelStatusBitArray[7] = 1; // ^ ////// Byte 1 ////// AESChannelStatusBitArray[8] = 0; // channel mode: [0000] not indicated, [0001]2channels, [0010]1channel mono, ... AESChannelStatusBitArray[9] = 0; // ^ AESChannelStatusBitArray[10] = 0; // ^ AESChannelStatusBitArray[11] = 1; // ^ ////// Byte 2 ////// AESChannelStatusBitArray[19] = 0; // Encoded sample word length [100]20bits, AESChannelStatusBitArray[20] = 0; // AESChannelStatusBitArray[21] = 0; // ////// Byte 3 ////// AESChannelStatusBitArray[24] = 0; // AESChannelStatusBitArray[25] = 0; // AESChannelStatusBitArray[26] = 0; // AESChannelStatusBitArray[27] = 0; // AESChannelStatusBitArray[28] = 0; // AESChannelStatusBitArray[29] = 0; // AESChannelStatusBitArray[30] = 0; // AESChannelStatusBitArray[31] = 0; // Multi Channel Mode ////// Byte 4-21 ////// //AESChannelStatusBitArray[32-179]= 0; ////// Byte 22 ////// AESChannelStatusBitArray[180] = 0; // Reliability Flags AESChannelStatusBitArray[181] = 1; // ^ AESChannelStatusBitArray[182] = 1; // ^ AESChannelStatusBitArray[183] = 1; // ^ ////// Byte 23 ////// AESChannelStatusBitArray[184] = 0; // Cyclic Redundancy Check AESChannelStatusBitArray[185] = 1; // ^ AESChannelStatusBitArray[186] = 0; // ^ AESChannelStatusBitArray[187] = 0; // ^ AESChannelStatusBitArray[188] = 1; // ^ AESChannelStatusBitArray[189] = 0; // ^ AESChannelStatusBitArray[190] = 1; // ^ AESChannelStatusBitArray[191] = 1; // ^ } // set buffer for one line of active video samples line_buffer = (uint16_t*) calloc(info.fmt->samples_per_line, sizeof(uint16_t)); // calculate and set buffer for the complete SDI frame if (info.fmt != &FMT_576i50 && info.fmt != &FMT_486i5994) { if (info.blanking) { if (pack == pack_v210) { samples = (info.fmt->samples_per_line / 96 * 48) + ((info.fmt->samples_per_line % 96) ? 48 : 0); sdi_frame_size = samples * info.fmt->lines_per_frame * 8 / 3; } else { sdi_frame_size = info.fmt->samples_per_line * info.fmt->lines_per_frame; } } else { if (pack == pack_v210) { samples = (info.fmt->active_samples_per_line / 96 * 48) + ((info.fmt->active_samples_per_line % 96) ? 48 : 0); sdi_frame_size = samples * info.fmt->active_lines_per_frame * 8 / 3; } else { sdi_frame_size = info.fmt->active_samples_per_line * info.fmt->active_lines_per_frame; } } } else { if (info.blanking) { if (pack == pack_v210) { sdi_frame_size = info.fmt->samples_per_line * 4 / 3 * info.fmt->lines_per_frame; } else if (pack == pack8) { sdi_frame_size = info.fmt->samples_per_line * info.fmt->lines_per_frame; } else { sdi_frame_size = info.fmt->samples_per_line * 10 / 8 * info.fmt->lines_per_frame; } } else { if (pack == pack_v210) { sdi_frame_size = info.fmt->active_samples_per_line * 4 / 3 * info.fmt->active_lines_per_frame; } else if (pack == pack8) { sdi_frame_size = info.fmt->active_samples_per_line * info.fmt->active_lines_per_frame; } else { sdi_frame_size = info.fmt->active_samples_per_line * 10 / 8 * info.fmt->active_lines_per_frame; } } } // (*10/8 because we store (TOTAL_SAMPLES*TOTAL_LINES) words with 10 bit in this 8 bit array) ) if (info.fmt == &FMT_576i50 && info.blanking) { sdi_frame_size = info.fmt->samples_per_line * 10 / 8 * info.fmt->lines_per_frame; } if (info.blanking) { printf("SDI frame size: %"PRIu64"\n", sdi_frame_size); } else { printf("Frame size for active video: %"PRIu64"\n", sdi_frame_size); } /** * Setup HD-SDI Master device (vidport): * * if device_file_video available then * if vidport available * 1. setup * end * 1. open device file handler * * if device_file_audio available then * 1. setup * 2. open device file handler * end * end **/ if (device_file_video != NULL) { // If we use a Linsys HD board with active video (without blanking) setup the board for the used mode if (strstr(device_file_video, "sdivideotx") != NULL && !info.blanking) { char * value; // Buffer size value = itoa(sdi_frame_size); setSDIVideoProperties(SETTING_BUFFER_SIZE_VIDEO, value, device_video); free(value); // Frame Mode value = itoa(sdi_frame_mode); setSDIVideoProperties(SETTING_FRAME_MODE, value, device_video); free(value); // Data Mode if (pack == pack8) setSDIVideoProperties(SETTING_DATA_MODE, "0", device_video); else if (pack == pack_v210) setSDIVideoProperties(SETTING_DATA_MODE, "1", device_video); } // open file handle for SDI(video) output if ((fh_sdi_video = open(device_file_video, O_WRONLY)) == -1) { perror(NULL); printf("\ncould not open video output destination: %s\n", device_file_video); return 0; } printf("SDI consumer uses video device file: %s\n", device_file_video); // Check if we have to use a separate device file for audio if (device_file_audio != NULL) { // set settings for audio device file if (strstr(device_file_audio, "sdiaudiotx") != NULL && !info.blanking) { char * value; /** * prepare sample size * MLT supports: 16bit, 32bit * LINSYS SDI boards supports: 16bit, 24bit, 32bit * we set 16bit as default **/ uint8_t sample_size = audio_format->aformat == mlt_audio_s32 ? 32 : 16; // Buffer size // audio buffer per frame (Bytes) = sample rate / frame rate * ( sample size / 1Byte ) x channels value = itoa( (uint64_t) audio_format->sample_rate / ( (uint64_t) myProfile->frame_rate_num / (uint64_t) myProfile->frame_rate_den) * (uint64_t) sample_size / 8 * (uint64_t) audio_format->channels); setSDIAudioProperties(SETTING_BUFFER_SIZE_AUDIO, value, device_audio); free(value); // channels value = itoa(audio_format->channels); setSDIAudioProperties(SETTING_CHANNELS, value, device_audio); free(value); // sample rate value = itoa(audio_format->sample_rate); setSDIAudioProperties(SETTING_SAMPEL_RATE, value, device_audio); free(value); // sample size value = itoa(sample_size); setSDIAudioProperties(SETTING_SAMPLE_SIZE, value, device_audio); free(value); } // open file handle for audio output if ((fh_sdi_audio = open(device_file_audio, O_WRONLY)) == -1) { perror(NULL); printf("\nCould not open audio output destination: %s\n", device_file_audio); return 0; } printf("SDI consumer uses audio device file: %s\n", device_file_audio); } } // set buffer for the complete SDI frame data = (uint8_t*) calloc(sdi_frame_size, sizeof(uint8_t)); return 1; } /** * Writes video and audio to specified files in SDI format * @param *vBuffer: Pointer to a video Buffer * @param aBuffer[][] * @param *audio_format: mlt audio_format * @param audio_streams: number of audio streams which have content in aBuffer (available 0-8) * @return current DBN (data block number of SDI frame) **/ static int sdi_playout(uint8_t *vBuffer, int16_t aBuffer[MAX_AUDIO_STREAMS][MAX_AUDIO_SAMPLES], const struct audio_format * audio_format, int audio_streams, int my_DBN) { // Pointer to the start of data. This is used to fill data line by line uint8_t *p = data; //******************************************************************************************* //**************** Build the SDI frame line by line **************************************** //******************************************************************************************* /* * if SDI FMT_576i50 for card ASSY 145 or ASSY 159, with access to whole SDI frame buffer * and device_file_audio must be NULL * than we write own audio data, * else * than HD for card ASSY 193 */ //if (info.fmt == &FMT_576i50 && device_file_audio == NULL && !strcmp(device_file_video, "/dev/sdivideotx0")) { if (info.fmt == &FMT_576i50 && info.blanking) { //counter for the lines int i = 0; int16_t AudioGroupCounter = 0; /*#####################################################*/ /*######## FIELD 1 #######################*/ /*#####################################################*/ info.xyz = &FIELD_1_VERT_BLANKING; // line 1-22 VERTICAL_BLANKING:23 lines SAV 0x2ac EAV 0x2d8 for (i = 1; i <= 5; i++) { create_SD_SDI_Line(line_buffer, &info, FIELD_1, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i); p = pack10(p, line_buffer, info.fmt->samples_per_line); } for (i = 6; i <= 8; i++) { create_SD_SDI_Line(line_buffer, &info, FIELD_1, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i); p = pack10(p, line_buffer, info.fmt->samples_per_line); } for (i = 9; i <= 22; i++) { create_SD_SDI_Line(line_buffer, &info, FIELD_1, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i); p = pack10(p, line_buffer, info.fmt->samples_per_line); } // line 23-310 ACTIVE: 287 lines SAV 0x200 EAV 0x274 info.xyz = &FIELD_1_ACTIVE; int f1counter = 1; // only odd lines for (i = 23; i <= 310; i++) { create_SD_SDI_Line(line_buffer, &info, FIELD_1, ACTIVE_VIDEO, vBuffer, aBuffer, i, f1counter, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i); p = pack10(p, line_buffer, info.fmt->samples_per_line); f1counter += 2; } i = 311; // line 311-312 VERTICAL_BLANKING: 2 lines SAV 0x2ac EAV 0x2d8 info.xyz = &FIELD_1_VERT_BLANKING; create_SD_SDI_Line(line_buffer, &info, FIELD_1, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i++); p = pack10(p, line_buffer, info.fmt->samples_per_line); create_SD_SDI_Line(line_buffer, &info, FIELD_1, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i++); p = pack10(p, line_buffer, info.fmt->samples_per_line); /*#####################################################*/ /*######## FIELD 2 ########################*/ /*#####################################################*/ info.xyz = &FIELD_2_VERT_BLANKING; // line 313-336 VERTICAL_BLANKING: 23 lines SAV 0x3b0 EAV 0x3c4 create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i++); p = pack10(p, line_buffer, info.fmt->samples_per_line); create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i++); p = pack10(p, line_buffer, info.fmt->samples_per_line); create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i++); p = pack10(p, line_buffer, info.fmt->samples_per_line); create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i++); p = pack10(p, line_buffer, info.fmt->samples_per_line); create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i++); p = pack10(p, line_buffer, info.fmt->samples_per_line); create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i++); p = pack10(p, line_buffer, info.fmt->samples_per_line); // `getAudioGroups2Write()`=0 for (i = 319; i <= 321; i++) { create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i); p = pack10(p, line_buffer, info.fmt->samples_per_line); } for (i = 322; i <= 335; i++) { create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i); p = pack10(p, line_buffer, info.fmt->samples_per_line); } // line 336-623 ACTIVE: 288 lines SAV 0x31c EAV 0x368 info.xyz = &FIELD_2_ACTIVE; int f2counter = 2; // only even Lines for (i = 336; i <= 623; i++) { create_SD_SDI_Line(line_buffer, &info, FIELD_2, ACTIVE_VIDEO, vBuffer, aBuffer, i, f2counter, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i); p = pack10(p, line_buffer, info.fmt->samples_per_line); f2counter += 2; } // line 624-625 VERTICAL_BLANKING: 2 lines SAV 0x3b0 EAV 0x3c4 info.xyz = &FIELD_2_VERT_BLANKING; for (i = 624; i <= 625; i++) { create_SD_SDI_Line(line_buffer, &info, FIELD_2, VERT_BLANKING, vBuffer, aBuffer, i, 0, getDBN(my_DBN++), AudioGroupCounter, getNumberOfAudioGroups2Write(i), audio_streams); AudioGroupCounter += getNumberOfAudioGroups2Write(i); p = pack10(p, line_buffer, info.fmt->samples_per_line); } } else { // use HD board without blanking // start with first even line active_video_line = 1; /* ***************************************** * *********** LINE DISTRIBUTION *********** * ***************************************** * * << decide form of scanning (interlaced || progressive) >> * if (interlaced) * << decide lines per frame (1125 || 625 || 525) >> * if(1125) 1080x1920 HD * than create lines * else if(625) 576x720 PAL * than create lines * else (525) 486x720 NTSC * than create lines * else (progressive) * << decide resolution (1125 || 750) >> * if(1125) 1080x1920 HD * than create lines * else(750) 720x1280 HD * than create lines * **/ // Generate a frame if (info.fmt->interlaced) { /**************************************** * INTERLACED ****************************************/ if (info.fmt->lines_per_frame == 1125) { if (info.blanking) { elements = info.fmt->samples_per_line; info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 1; info.ln <= 20; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } else { elements = info.fmt->active_samples_per_line; } info.xyz = &FIELD_1_ACTIVE; for (info.ln = 21; info.ln <= 560; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); active_video_line += 2; } if (info.blanking) { info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 561; info.ln <= 563; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } info.xyz = &FIELD_2_VERT_BLANKING; for (info.ln = 564; info.ln <= 583; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } // start with first odd line active_video_line = 2; info.xyz = &FIELD_2_ACTIVE; for (info.ln = 584; info.ln <= 1123; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); active_video_line += 2; } if (info.blanking) { info.xyz = &FIELD_2_VERT_BLANKING; for (info.ln = 1124; info.ln <= 1125; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } } else if (info.fmt->lines_per_frame == 625) { elements = info.fmt->active_samples_per_line; // start with first even line active_video_line = 1; /** * Generate an SDI PAL frame **/ if (info.blanking) { info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 1; info.ln <= 22; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } info.xyz = &FIELD_1_ACTIVE; for (info.ln = 23; info.ln <= 310; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); active_video_line += 2; } if (info.blanking) { info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 311; info.ln <= 312; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } info.xyz = &FIELD_2_VERT_BLANKING; for (info.ln = 313; info.ln <= 335; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } // start with first odd line active_video_line = 2; info.xyz = &FIELD_2_ACTIVE; for (info.ln = 336; info.ln <= 623; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); active_video_line += 2; } if (info.blanking) { info.xyz = &FIELD_2_VERT_BLANKING; for (info.ln = 624; info.ln <= 625; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } } else if (info.fmt->lines_per_frame == 525) { /** * Generate an SDI NTSC frame * * * 16 lines VERT_BLANKING FIELD_1_VERT_BLANKING * 1 lines VERT_BLANKING FIELD_1_ACTIVE * 3 lines ACTIVE_VIDEO FIELD_1_ACTIVE (opt. video data) * 240 lines ACTIVE_VIDEO FIELD_1_ACTIVE * 2 lines VERT_BLANKING FIELD_1_VERT_BLANKING * * 8 lines VERT_BLANKING FIELD_2_VERT_BLANKING * 9 lines VERT_BLANKING FIELD_2_VERT_BLANKING * 3 lines ACTIVE_VIDEO FIELD_2_ACTIVE (opt. video data) * 240 lines ACTIVE_VIDEO FIELD_2_ACTIVE * 4 lines VERT_BLANKING FIELD_2_VERT_BLANKING * **/ elements = info.fmt->active_samples_per_line; active_video_line = 1; if (info.blanking) { info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 1; info.ln <= 15; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } for (info.ln = 16; info.ln <= 16; info.ln++) { info.xyz = &FIELD_1_ACTIVE; mkline(line_buffer, &info, VERT_BLANKING); p = pack(p, line_buffer, elements); } } info.xyz = &FIELD_1_ACTIVE; // 480 or 486 lines if (info.fmt == &FMT_480i5994) { // 3 lines opt. video data for (info.ln = 17; info.ln <= 19; info.ln++) { mkline(line_buffer, &info, BLACK); p = pack(p, line_buffer, elements); } // 240 lines for (info.ln = 20; info.ln <= 259; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); active_video_line += 2; } } else { // 243 lines for (info.ln = 17; info.ln <= 259; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); active_video_line += 2; } } if (info.blanking) { // 2 lines vertical data info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 260; info.ln <= 261; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } // 8 lines vertical data info.xyz = &FIELD_2_VERT_BLANKING; for (info.ln = 262; info.ln <= 269; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } // 9 lines for (info.ln = 270; info.ln <= 278; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } active_video_line = 0; // 480 or 486 lines if (info.fmt == &FMT_480i5994) { // 3 lines opt. video data info.xyz = &FIELD_2_ACTIVE; for (info.ln = 279; info.ln <= 281; info.ln++) { mkline(line_buffer, &info, BLACK); p = pack(p, line_buffer, elements); } // 240 lines for (info.ln = 282; info.ln <= 521; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); active_video_line += 2; } } else { // 243 lines for (info.ln = 279; info.ln <= 521; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); active_video_line += 2; } } // 4 lines vertical data if (info.blanking) { info.xyz = &FIELD_2_VERT_BLANKING; for (info.ln = 522; info.ln <= 525; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } } } else { /**************************************** * PROGRESSIVE ****************************************/ // start with first line numerber active_video_line = 0; if (info.fmt->lines_per_frame == 1125) { if (info.blanking) { elements = info.fmt->samples_per_line; info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 1; info.ln <= 41; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } else { elements = info.fmt->active_samples_per_line; } info.xyz = &FIELD_1_ACTIVE; for (info.ln = 42; info.ln <= 1121; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line++, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); } if (info.blanking) { info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 1122; info.ln <= 1125; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } } else { if (info.blanking) { elements = info.fmt->samples_per_line; info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 1; info.ln <= 25; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } else { elements = info.fmt->active_samples_per_line; } info.xyz = &FIELD_1_ACTIVE; for (info.ln = 26; info.ln <= 745; info.ln++) { create_HD_SDI_Line(line_buffer, &info, active_video_line++, ACTIVE_VIDEO, vBuffer); p = pack(p, line_buffer, elements); } if (info.blanking) { info.xyz = &FIELD_1_VERT_BLANKING; for (info.ln = 746; info.ln <= 750; info.ln++) { create_HD_SDI_Line(line_buffer, &info, 0, VERT_BLANKING, vBuffer); p = pack(p, line_buffer, elements); } } } } } // sum of bytes that have already been written to file int bytes = 0; // store actual written bytes per 'write() int written_bytes = 0; /** * WRITE BUFFER TO FILEHANDLE **/ // Write the complete frame to output // The "while" is necessary because the sdi device file does not take the complete frame at once written_bytes = 0; while (bytes < sdi_frame_size) { if ((written_bytes = write(fh_sdi_video, data + bytes, sdi_frame_size - bytes)) < 0) { fprintf(stderr, "\nunable to write SDI video.\n"); return -1; } bytes += written_bytes; } // Check for events of the SDI board unsigned int val; if (ioctl(fh_sdi_video, SDI_IOC_TXGETEVENTS, &val) < 0) { // Maybe this is not an SDI device... //fprintf(stderr, "SDI VIDEO output:"); //perror("unable to get the transmitter event flags"); } else if (val) { if (val & SDI_EVENT_TX_BUFFER) { printf("SDI VIDEO driver transmit buffer queue underrun " "detected.\n"); fflush(stdout); } if (val & SDI_EVENT_TX_FIFO) { printf("SDI VIDEO onboard transmit FIFO underrun detected.\n"); fflush(stdout); } if (val & SDI_EVENT_TX_DATA) { printf("SDI VIDEO transmit data change detected.\n"); fflush(stdout); } } // if available write audio data if (fh_sdi_audio) { // count written bytes written_bytes = 0; // set number of samples and cut by 1600 if NTSC (handle problem of real time encoding of NTSC frequencies) size_t samples_total_per_track = audio_format->samples; uint16_t sample_number = 0; size_t channels_per_track_total = 2; uint8_t stream_number = 0; //printf("samples_total_per_track:%li\n", samples_total_per_track); // to write blockwise 2 samples of one track we must calculate the number of bytes we want to write per write-session // 2samples = 2x16Bit = 32Bit = 4Byte // 2samples = 2x32Bit = 64Bit = 8Byte // set total bytes per session size_t bytes_total = 0; bytes_total = audio_format->aformat == mlt_audio_s16 ? channels_per_track_total * sizeof(int16_t) : bytes_total; bytes_total = audio_format->aformat == mlt_audio_s32 ? channels_per_track_total * sizeof(int32_t) : bytes_total; // write all samples of all streams interleaved /** * aBuffer[track0]+sample1 * aBuffer[track0]+sample2 * aBuffer[track1]+sample1 * aBuffer[track1]+sample2 * aBuffer[track.]+sample1 * aBuffer[track.]+sample2 * * aBuffer[track0]+sample3 * aBuffer[track0]+sample4 * aBuffer[track1]+sample3 * aBuffer[track1]+sample4 * aBuffer[track.]+sample3 * aBuffer[track.]+sample4 * * aBuffer[track0]+sample5 * aBuffer[track0]+sample6 * aBuffer[track1]+sample5 * aBuffer[track1]+sample6 * aBuffer[track.]+sample5 * aBuffer[track.]+sample6 **/ int sum_written_bytes = 0; int sum_written_bytes_a = 0; int sum_written_bytes_b = 0; // write all samples per track while (sample_number < samples_total_per_track) { stream_number = 0; /** * Because we have and write a fix number of audio streams to SDI board: * we have a actual number of real audio tracks and a rest number of pseudo tracks **/ // write all streams while (stream_number < audio_streams) { // write for every stream n samples // n = number of channels per stream written_bytes = 0; while (written_bytes < bytes_total) { written_bytes += write(fh_sdi_audio, (uint8_t *) aBuffer[stream_number] + sample_number * bytes_total + written_bytes, bytes_total - written_bytes); } sum_written_bytes += written_bytes; sum_written_bytes_a += written_bytes; stream_number++; } // write pseudo tracks // now fill rest of audio tracks(AES frames) with NULL or copy of first track while (stream_number < audio_format->channels / 2) { // write for every stream n samples // n = number of channels per stream written_bytes = 0; while (written_bytes < bytes_total) { written_bytes += write(fh_sdi_audio, (uint8_t *) aBuffer[0] + sample_number * bytes_total + written_bytes, bytes_total - written_bytes); } sum_written_bytes += written_bytes; sum_written_bytes_b += written_bytes; stream_number++; } sample_number++; // Check for events of the SDI audio device unsigned int val; if (ioctl(fh_sdi_audio, SDIAUDIO_IOC_TXGETEVENTS, &val) < 0) { //Maybe this is not an SDI device... // fprintf(stderr, "SDI AUDIO output:"); // perror("unable to get the transmitter event flags"); } else if (val) { if (val & SDIAUDIO_EVENT_TX_BUFFER) { printf("SDI AUDIO driver transmit buffer queue underrun " "detected.\n"); } if (val & SDIAUDIO_EVENT_TX_FIFO) { printf("SDI AUDIO onboard transmit FIFO underrun detected.\n"); } if (val & SDIAUDIO_EVENT_TX_DATA) { printf("SDI AUDIO transmit data change detected.\n"); } } } } return getDBN(my_DBN); } // end sdimaster_playout() //**************************************************************************************** //*************************** Create Line ********************************************** //**************************************************************************************** /** generate one SDI line * @param *buf: buffer to hold the line * @param field: size of the video Buffer * @param active: v-blank or active-video * @param *video_buffer: video buffer * @param *audio_buffer2: 1.audio buffer ch1-ch2 * @param *audio_buffer1: 2.audio buffer ch2-ch3 * @param line: linenumber * @param AudioGroupCounter: count written AudioGroup * @param AudioGroups2Write: number of samples to write * @param audio_streams: number of audio streams to integrate */ static inline int create_SD_SDI_Line(uint16_t *buf, const struct line_info *info, int field, int active, uint8_t *video_buffer, int16_t audio_buffer[MAX_AUDIO_STREAMS][MAX_AUDIO_SAMPLES], int linenumber_sdiframe, int active_video_line, int my_DBN, int16_t AudioGroupCounter, int16_t AudioGroups2Write, int audio_streams) { // write line with TRS(EAV) ANC(audio) TRS(SAV) activeVideo(CbY1CrY2) // ************************************************************************* // 625 lines: | EAV | ANC | SAV | [CbY1CrY2] | // ************************************************************************* // 1728 SDI-words: | 4 | 280 | 4 | 720+360+360=1440 | // ************************************************************************* // points to current position in line uint16_t *p = buf; //######################################################################################### /* TRS Timing Reference Signal for EAV * [3ff] * [000] * [000] * [XYZ-Wort] * */ *p++ = 0x3ff; *p++ = 0x000; *p++ = 0x000; *p++ = info->xyz->eav; //######################################################################################### /* ANC Ancillary Data with AES * * [ADF][ADF][ADF][DID][DBN][DC][UDW]...[UDW][CS] * * */ // write ANC Data and get number of samples are written // step with `p` += to the number of written samples //printf("audio_streams:%i\n",audio_streams); // 1 stream, Audio Group 1 with AES Frame 1 - 2 if (audio_streams == 1) { p += writeANC(p, linenumber_sdiframe, 0x2FF, my_DBN, audio_buffer[0], audio_buffer[0], AudioGroupCounter, AudioGroups2Write); } // 2 streams, Audio Group 1 with AES Frame 1 - 2 if (audio_streams == 2) { p += writeANC(p, linenumber_sdiframe, 0x2FF, my_DBN, audio_buffer[0], audio_buffer[1], AudioGroupCounter, AudioGroups2Write); } // 3 streams, Audio Group 2 with AES Frame 1 - 4 if (audio_streams == 3) { p += writeANC(p, linenumber_sdiframe, 0x2FF, my_DBN, audio_buffer[0], audio_buffer[1], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FD, my_DBN, audio_buffer[2], audio_buffer[2], AudioGroupCounter, AudioGroups2Write); } // 4 streams, Audio Group 2 with AES Frame 1 - 4 if (audio_streams == 4) { p += writeANC(p, linenumber_sdiframe, 0x2FF, my_DBN, audio_buffer[0], audio_buffer[1], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FD, my_DBN, audio_buffer[2], audio_buffer[3], AudioGroupCounter, AudioGroups2Write); } // 5 streams, Audio Group 3 with AES Frame 1 - 6 if (audio_streams == 5) { p += writeANC(p, linenumber_sdiframe, 0x2FF, my_DBN, audio_buffer[0], audio_buffer[1], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FD, my_DBN, audio_buffer[2], audio_buffer[3], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FB, my_DBN, audio_buffer[4], audio_buffer[4], AudioGroupCounter, AudioGroups2Write); } // 6 streams, Audio Group 3 with AES Frame 1 - 6 if (audio_streams == 6) { p += writeANC(p, linenumber_sdiframe, 0x2FF, my_DBN, audio_buffer[0], audio_buffer[1], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FD, my_DBN, audio_buffer[2], audio_buffer[3], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FB, my_DBN, audio_buffer[4], audio_buffer[5], AudioGroupCounter, AudioGroups2Write); } // 7 streams, Audio Group 4 with AES Frame 1 - 7 if (audio_streams == 7) { p += writeANC(p, linenumber_sdiframe, 0x2FF, my_DBN, audio_buffer[0], audio_buffer[1], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FD, my_DBN, audio_buffer[2], audio_buffer[3], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FB, my_DBN, audio_buffer[4], audio_buffer[5], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x2F9, my_DBN, audio_buffer[6], audio_buffer[6], AudioGroupCounter, AudioGroups2Write); } // 8 streams, Audio Group 4 with AES Frame 1 - 7 if (audio_streams == 8) { p += writeANC(p, linenumber_sdiframe, 0x2FF, my_DBN, audio_buffer[0], audio_buffer[1], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FD, my_DBN, audio_buffer[2], audio_buffer[3], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x1FB, my_DBN, audio_buffer[4], audio_buffer[5], AudioGroupCounter, AudioGroups2Write); p += writeANC(p, linenumber_sdiframe, 0x2F9, my_DBN, audio_buffer[6], audio_buffer[7], AudioGroupCounter, AudioGroups2Write); } // Fill ANC data in until the end (position(p) to `ANCILLARY_DATA_SAMPLES`) while (p < (buf + ANCILLARY_DATA_SAMPLES + 4)) { // video color: black *p++ = 0x200; *p++ = 0x040; } //######################################################################################### // TRS Timing Reference Signal for SAV *p++ = 0x3ff; *p++ = 0x000; *p++ = 0x000; *p++ = info->xyz->sav; //######################################################################################### // Because we skip the first line of video, it can happen that we read too far in the buffer if (active_video_line >= info->fmt->active_lines_per_frame) { active_video_line = info->fmt->active_lines_per_frame - 1; // in SD PAL was set 575 } //Index of the start of the current line in the video_buffer int start_of_current_line = active_video_line * info->fmt->active_samples_per_line; // If VBlank then fill the line with 0x200 and 0x040 (total black) switch (active) { default: case VERT_BLANKING: while (p < (buf + info->fmt->samples_per_line)) { *p++ = 0x200; *p++ = 0x040; } break; case ACTIVE_VIDEO: // Insert the video into the line while (p < (buf + info->fmt->samples_per_line)) { // fill the rest of the line with active video // shift "<< 2" because 8 bit data in 10 bit word *p = video_buffer[start_of_current_line + ((p - 288) - buf) + 1] << 2; // Cb p++; if (*(p - 1) < 0x040) *(p - 1) = 0x040; // check values if (*(p - 1) > 0x3c0) *(p - 1) = 0x3c0; *p = video_buffer[start_of_current_line + ((p - 288) - buf) - 1] << 2; // Y1 p++; if (*(p - 1) < 0x040) *(p - 1) = 0x040; if (*(p - 1) > 0x3ac) *(p - 1) = 0x3ac; *p = video_buffer[start_of_current_line + ((p - 288) - buf) + 1] << 2; // Cr p++; if (*(p - 1) < 0x040) *(p - 1) = 0x040; if (*(p - 1) > 0x3c0) *(p - 1) = 0x3c0; *p = video_buffer[start_of_current_line + ((p - 288) - buf) - 1] << 2; // Y2 p++; if (*(p - 1) < 0x040) *(p - 1) = 0x040; if (*(p - 1) > 0x3ac) *(p - 1) = 0x3ac; } break; } return 0; } /** * create_HD_SDI_Line - generate one line * @buf: pointer to a buffer * @info: pointer to a line information structure * @active_video_line * @active: * @video_buffer: pattern * * Returns a negative error code on failure and zero on success. **/ static inline int create_HD_SDI_Line(uint16_t *buf, const struct line_info *info, uint16_t active_video_line, unsigned int active, uint8_t *video_buffer) { uint16_t *p = buf, ln; uint16_t samples = info->blanking ? info->fmt->samples_per_line : info->fmt->active_samples_per_line; if (active_video_line >= info->fmt->active_lines_per_frame) { active_video_line = info->fmt->active_lines_per_frame - 1; } int start_of_current_line = active_video_line * info->fmt->active_samples_per_line; if (info->blanking) { // write line with TRS(EAV) ANC(audio) TRS(SAV) activeVideo(CbY1CrY2) // Example SD PAL: // ************************************************************************* // 625 lines: | EAV | ANC | SAV | [CbY1CrY2] | // ************************************************************************* // 1728 SDI-words: | 4 | 280 | 4 | 720+360+360=1440 | // ************************************************************************* // write line with TRS(EAV) ANC(audio) TRS(SAV) activeVideo(CbY1CrY2) // Example HD 1080i: // ************************************************************************* // 1125 lines: | EAV | LN | CRC | ANC | SAV | [CbY1CrY2] | // ************************************************************************* // 5280 SDI-words: | 6 | 4 | 4 | 280 | 6 | 1920+720+720=3840 | // ************************************************************************* if (info->fmt == &FMT_576i50) { /* EAV */ *p++ = 0x3ff; *p++ = 0x000; *p++ = 0x000; *p++ = info->xyz->eav; } else { /* EAV */ *p++ = 1023; *p++ = 1023; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = info->xyz->eav; *p++ = info->xyz->eav; /* LN */ ln = ((info->ln & 0x07f) << 2) | (~info->ln & 0x040) << 3; *p++ = ln; *p++ = ln; ln = ((info->ln & 0x780) >> 5) | 0x200; *p++ = ln; *p++ = ln; /* CRC, added by serializer */ *p++ = 512; *p++ = 64; *p++ = 512; *p++ = 64; } /* Horizontal blanking */ while (p < (buf + info->fmt->samples_per_line - info->fmt->active_samples_per_line - 4)) { *p++ = 512; *p++ = 64; *p++ = 512; *p++ = 64; } if (info->fmt == &FMT_576i50) { /* SAV */ *p++ = 0x3ff; *p++ = 0x000; *p++ = 0x000; *p++ = info->xyz->sav; } else { /* SAV */ *p++ = 1023; *p++ = 1023; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = info->xyz->sav; *p++ = info->xyz->sav; } } switch (active) { default: case VERT_BLANKING: while (p < (buf + samples)) { *p++ = 512; *p++ = 64; *p++ = 512; *p++ = 64; } break; case ACTIVE_VIDEO: { while (p < (buf + samples)) { *p = video_buffer[start_of_current_line + (p - buf) + 1] << 2; // Cb p++; //check values, this needs a lot of resources // if (*(p - 1) < 0x040) // *(p - 1) = 0x040; // if (*(p - 1) > 0x3c0) // *(p - 1) = 0x3c0; // *p = video_buffer[start_of_current_line + (p - buf) - 1] << 2; // Y1 p++; // if (*(p - 1) < 0x040) // *(p - 1) = 0x040; // if (*(p - 1) > 0x3ac) // *(p - 1) = 0x3ac; // *p = video_buffer[start_of_current_line + (p - buf) + 1] << 2; // Cr p++; // if (*(p - 1) < 0x040) // *(p - 1) = 0x040; // if (*(p - 1) > 0x3c0) // *(p - 1) = 0x3c0; // *p = video_buffer[start_of_current_line + (p - buf) - 1] << 2; // Y2 p++; // if (*(p - 1) < 0x040) // *(p - 1) = 0x040; // if (*(p - 1) > 0x3ac) // *(p - 1) = 0x3ac; } } break; } return 0; } static int writeANC(uint16_t *p, int videoline_sdiframe, uint16_t DID, int my_DBN, int16_t *audio_buffer_A, int16_t *audio_buffer_B, int16_t AudioGroupCounter, int16_t AudioGroups2Write) { /** * ANC Ancillary Data (vgl. SMPTE 291-M page 6 ) * [ADF][ADF][ADF][DID][DBN][DC][UDW]...[UDW][CS] * **/ // save only current position for return value uint16_t *pp = p; // 16bit buffer to write temporarily 10bit word uint16_t buffer = 0; // set all explicit to zero, special the bit9 for parity // parity_counter int8_t parity_counter = 0; if (AudioGroups2Write > 0) { // 3 ADF (Ancillary Data Flag) *p++ = 0x000; *p++ = 0x3FF; *p++ = 0x3FF; // 1 DID (Data Identification) // save DID for checker() uint16_t *DID_pointer = p; *p++ = DID;// (AES Audio Data, Group // *p++ = 0x2FF; // (AES Audio Data, Group1=0x2FF) // *p++ = 0x1FD; // (AES Audio Data, Group2=0x1FD) // *p++ = 0x1FB; // (AES Audio Data, Group3=0x1FB) // *p++ = 0x2F9; // (AES Audio Data, Group4=0x2F9) // 1 DBN (Data Block Number) inactiv: 1000000000 b9,b8,b7-b0 ; SMPTE 272-M chapter15.1 // *p++ = 0x200; // 1 DBN (dynamic version0.1-beta ), should start with previous DBN of SDI-Frame // -need "previous DBN" or "current framenumber" // SDI-LINE: DBN: // [1] [1] << start sdi frame // [2] [2] // [.] [.] // [255] [255] // [256] [1] // [257] [2] // [.] [.] // [510] [255] // [511] [1] // [512] [2] // [.] [.] // [625] [115] << end sdi frame // [1] [116] << start sdi frame // Accuracy of videoline_sdiframe(1 up to 625) to 8bit (1-255) //buffer = ((videoline_sdiframe-1) % 255)+1; buffer = my_DBN; parity_counter = 0; // count binary ones for parity int i = 0; for (i = 0; i < 8; i++) { if (buffer & (1 << i)) parity_counter++; } if ((parity_counter % 2) == 0) { //else leave the 0 buffer += 512; // 10 0000 0000 // set bit8 = even parity bit and bit9 = !bit8 } else { buffer += 256; // 01 0000 0000 // set bit8 = even parity bit and bit9 = !bit8 } *p++ = buffer; // 1 DC (Data Counter) // number of UDW = AudioGroups2Write x 2AESFrames x 2channesl x 3words(X,X+1,X+2) buffer = AudioGroups2Write * 2 * 2 * 3; parity_counter = 0; // count binary ones for parity for (i = 0; i < 8; i++) { if (buffer & (1 << i)) parity_counter++; } if ((parity_counter % 2) == 0) { //else leave the 0 buffer += 512; // 10 0000 0000 // set bit8 = even parity bit and bit9 = !bit8 } else { buffer += 256; // 01 0000 0000 // set bit8 = even parity bit and bit9 = !bit8 } *p++ = buffer; int16_t sample_number = 0; int16_t counter = 0; // write subframes: // = n x 1 AudioGroup // = n x 2 x 1AESFrame // = n x 2 x 2samples // = 4 samples // = 4 x 3words while (counter < AudioGroups2Write * 2) { /* 4:3 */ // write one Audio Group with 4 x AES subframes // ( samples for ch01,ch02,ch03,ch04 or ch05,ch06,ch07,ch08 or ch09,ch10,ch11,ch12 or ch13,ch14,ch15,ch16) // and use audio_buffer_A(stereo) and audio_buffer_B(stereo) // `pack_AES_subframe()` write 3 ANC words (3*10bit), also 1 sample sample_number = (AudioGroupCounter * 2) + counter; pack_AES_subframe(p, getChannelStatusBit(sample_number / 2, 1), getZBit(sample_number / 2), 0, &audio_buffer_A[sample_number]); // left p += 3; // step 3 words sample_number = (AudioGroupCounter * 2) + counter + 1; pack_AES_subframe(p, getChannelStatusBit(sample_number / 2, 2), getZBit(sample_number / 2), 1, &audio_buffer_A[sample_number]); // right p += 3; sample_number = (AudioGroupCounter * 2) + counter; pack_AES_subframe(p, getChannelStatusBit(sample_number / 2, 3), getZBit(sample_number / 2), 2, &audio_buffer_B[sample_number]); // left p += 3; sample_number = (AudioGroupCounter * 2) + counter + 1; pack_AES_subframe(p, getChannelStatusBit(sample_number / 2, 4), getZBit(sample_number / 2), 3, &audio_buffer_B[sample_number]); // right p += 3; counter += 2; } // 1 CS (Checksum from DID - UDW) *p++ = checker(DID_pointer); // fill ANC with one dummy for videocolor black // rest until end of `ANCILLARY_DATA_SAMPLES` will be fill in a loop after call this function *p++ = 0x040; } return p - pp; } // calculate checksumm of ANC (SMPTE 272-M 15.3 Checksum (CS)) static uint16_t checker(uint16_t *DID_pointer) { // Checksumm uint16_t cs = 0x00; // DID - Datablock Identification cs += (*DID_pointer++) & 0x1FF; // 9 x LSB // DBN - Datablock Number cs += (*DID_pointer++) & 0x1FF; // 9 x LSB // DC - DataCounter cs += (*DID_pointer) & 0x1FF; // 9 x LSB // store address of DC an ad to the real value of DC // DataCounter store // ´ende´ point to DataCounter uint16_t *ende = DID_pointer; // ´ende´ point to last field ende += (*DID_pointer) & 0xFF; // without parity-Bit and ¬9-Bit DID_pointer++; // while DID_pointer point to smaller address like 'ende' while (DID_pointer <= ende) { cs += (*DID_pointer++) & 0x1FF; // 9 x LSB } // limit to 9Bit, because of overflow of sum cs = cs & 0x1FF; // set bit10 NOT bit9: // - cs invert // - & with bitmask '01 0000 0000' // - shift rest (1xbit)to left // - add to cs cs += ((~cs) & 0x100) << 1; return cs; } // end checker /** * pack 16bit in AES subframe with 3 words (30 bit) and write in ´*p´ * 10bit-words --> [X],[X+1], [X+2] implements 20bit for audio * * BIT 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 * ##### ### ### ### ### ### ### ### ### ### * [X] : [ !bit8, a5, a4, a3, a2, a1, a0, ch0, ch1, z ], * [X+1] : [ !bit8, a14, a13, a12, a11, a10, a9, a8, a7 , a6 ], * [X+2] : [ !bit8, P, C, U, V, a19, a18, a17, a16, a15 ] * * @param *p: Pointer to SDI frame buffer * @param c: value of AES subframe Channel Status Bit * @param z: value of AES subframe * @param ch: channel od AES subframe (value:0,1,2,3) * @param *audio_samplex: pointer to the audio buffer **/ static int pack_AES_subframe(uint16_t *p, int8_t c, int8_t z, int8_t ch, int16_t *audio_samplex) { /** * NOTE: WE JUST SUPPORT ONLY 16BIT SAMPLE SIZE **/ // push 16bit up to 20bit(32bit) int32_t audio_sample = *audio_samplex; audio_sample = audio_sample << 4; // Shift by 4 (louder) // parity_counter int8_t parity_counter = 0; // 16bit buffer to write 10bit of [X]word,[X+1]word,[X+2]word, uint16_t buffer = 0; //######################################################### //### WORD X ############################################ //######################################################### // word X: !bit8, a5, a4, a3, a2, a1, a0, ch1, ch0, z // SMPTE 272M s.7 buffer = z; // z bit every 192bit = 1 buffer += ch << 1; // ch1 - ch0 buffer += (audio_sample & 0x3f) << 3; // a5 - a0 buffer += ((~buffer) & 0x100) << 1; // !bit8 // write word ´X´ *p++ = buffer; // count ones int i = 0; for (i = 0; i < 9; i++) { if (buffer & 1 << i) parity_counter++; } //######################################################### //### WORD X+1 ############################################ //######################################################### // word X+1: !bit8, a14, a13, a12, a11, a10, a9, a8, a7, a6 // SMPTE 272M s.7 buffer = 0; buffer += (audio_sample >> 6) & 0x1ff; // a14 - a6 buffer += ((~buffer) & 0x100) << 1; // !bit8 // write word ´X+1´ *p++ = buffer; // count ones (zähle Einsen) i = 0; for (i = 0; i < 9; i++) { if (buffer & 1 << i) parity_counter++; } //######################################################### //### WORD X+2 ############################################ //######################################################### // word X+2: !bit8, P, C, U, V, a19, a18, a17, a16, a15 // SMPTE 272M s.7 buffer = 0; buffer += (audio_sample >> 15) & 0x01F; // a15 - a19 // default of [V][U][C] bits = `0` //buffer += 1<<5; // V (AES sample validity bit) //buffer += 1<<6; // U (AES user bit) //buffer += 1<<7; // C (AES audio channel status bit) buffer += c << 7; // C (AES audio channel status bit) // count ones (zähle Einsen) for (i = 0; i < 8; i++) { if (buffer & 1 << i) parity_counter++; } // if (!parity_counter%2) //else leave the 0 // buffer+= 1 << 8; // P (AES even parity bit) // // buffer += ((~buffer) & 0x100 )<<1; // !bit8 if ((parity_counter % 2) == 0) { //else leave the 0 buffer += 512; // 10 0000 0000 // set bit8 = even parity bit and bit9 = !bit8 } else { buffer += 256; // 01 0000 0000 // set bit8 = even parity bit and bit9 = !bit8 } *p++ = buffer; // write word ´X+2´ *p++ = buffer; return 1; } static uint8_t getZBit(int sample_number) { // start in SDI line 6 also 18samples later //sample_number+=192-18; if (sample_number % 192 == 0) { //printf("1 %i\n", sample_number); return 1; } else { //printf("0"); return 0; } } static uint8_t getChannelStatusBit(uint16_t sample_number, uint8_t ch) { // return value uint8_t AESChannelStatusBit = 0; // start in SDI line 6 also 18samples later //AESChannelStatusBit=((sample_number+192-18)%192); // interval in 192bit AESChannelStatusBit = sample_number % 192; // when mulichannelmode is true if (AESChannelStatusBitArray[31] == 1) { // set bits for channel if (AESChannelStatusBit == 30 && ch == 2) return 1; if (AESChannelStatusBit == 30 && ch == 4) return 1; if (AESChannelStatusBit == 29 && (ch == 4)) return 1; if (AESChannelStatusBit == 29 && (ch == 3)) return 1; } return AESChannelStatusBitArray[AESChannelStatusBit]; } static int16_t getNumberOfAudioGroups2Write(int linenumber) { // `4:3_VTR`-distribution if (linenumber >= 11 && linenumber <= 95) { if ((linenumber - 11) % 14 == 0) { return 4; } else { return 3; } } else if (linenumber >= 108 && linenumber <= 220) { if ((linenumber - 10) % 14 == 0) { return 4; } else { return 3; } } else if (linenumber >= 233 && linenumber <= 345) { if ((linenumber - 9) % 14 == 0) { return 4; } else { return 3; } } else if (linenumber >= 358 && linenumber <= 470) { if ((linenumber - 8) % 14 == 0) { return 4; } else { return 3; } } else if (linenumber >= 483 && linenumber <= 595) { if ((linenumber - 7) % 14 == 0) { return 4; } else { return 3; } } else if (linenumber >= 608 && linenumber <= 622) { if ((linenumber - 6) % 14 == 0) { return 4; } else { return 3; } } else { return 3; } // // `4:3`-distribution // if(linenumber<=315){ // if(linenumber>=6 && linenumber<=8){ // return 0; // } // if((linenumber+5)%10==0){ // return 4; // }else{ // return 3; // } // }else{ // if(linenumber>=319 && linenumber<=321){ // return 0; // } // if((linenumber-8)%10==0){ // return 4; // }else{ // return 3; // } // } // // full-distribution // if(linenumber<=45){ // return 4; // }else{ // return 3; // } // // fullhalf-distribution // if (linenumber==625) // return 4; // // if (linenumber%14==0) { // return 4; // } else { // return 3; // } } static uint8_t getDBN(int my_DBN) { return ((my_DBN - 1) % 255) + 1; } /** * pack8 - pack a line of 8-bit data * @outbuf: pointer to the output buffer * @inbuf: pointer to the input buffer * @count: number of elements in the buffer * * Returns a pointer to the next output location. **/ static inline uint8_t * pack8(uint8_t *outbuf, uint16_t *inbuf, size_t count) { uint16_t *inp = inbuf; uint8_t *outp = outbuf; while (inp < (inbuf + count)) { *outp++ = *inp++ >> 2; } return outp; } /** * pack10 - pack a line of 10-bit data * @outbuf: pointer to the output buffer * @inbuf: pointer to the input buffer * @count: number of elements in the buffer * * Returns a pointer to the next output location. **/ static inline uint8_t * pack10(uint8_t *outbuf, uint16_t *inbuf, size_t count) { uint16_t *inp = inbuf; uint8_t *outp = outbuf; while (inp < (inbuf + count)) { *outp++ = *inp & 0xff; *outp = *inp++ >> 8; *outp++ += (*inp << 2) & 0xfc; *outp = *inp++ >> 6; *outp++ += (*inp << 4) & 0xf0; *outp = *inp++ >> 4; *outp++ += (*inp << 6) & 0xc0; *outp++ = *inp++ >> 2; } return outp; } /** * pack_v210 - pack a line of v210 data * @outbuf: pointer to the output buffer * @inbuf: pointer to the input buffer * @count: number of elements in the buffer * * Returns a pointer to the next output location. **/ static inline uint8_t * pack_v210(uint8_t *outbuf, uint16_t *inbuf, size_t count) { uint16_t *inp = inbuf; uint8_t *outp = outbuf; count = (count / 96) * 96 + ((count % 96) ? 96 : 0); while (inp < (inbuf + count)) { *outp++ = *inp & 0xff; *outp = *inp++ >> 8; *outp++ += (*inp << 2) & 0xfc; *outp = *inp++ >> 6; *outp++ += (*inp << 4) & 0xf0; *outp++ = *inp++ >> 4; } return outp; } // Clean up static int sdimaster_close() { free(line_buffer); free(data); if (fh_sdi_audio) close(fh_sdi_audio); if (fh_sdi_video) close(fh_sdi_video); return 1; } /** * mkline - generate one line * @buf: pointer to a buffer * @info: pointer to a line information structure * @pattern: pattern * * Returns a negative error code on failure and zero on success. **/ static int mkline(unsigned short int *buf, const struct line_info *info, unsigned int pattern) { const unsigned int b = 205; unsigned short int *p = buf, *endp; unsigned int samples = info->blanking ? info->fmt->samples_per_line : info->fmt->active_samples_per_line; if (info->blanking) { /* EAV */ *p++ = 0x3ff; *p++ = 0x000; *p++ = 0x000; *p++ = info->xyz->eav; /* Horizontal blanking */ while (p < (buf + 272)) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } /* SAV */ *p++ = 0x3ff; *p++ = 0x000; *p++ = 0x000; *p++ = info->xyz->sav; } /* Active region */ endp = p; switch (pattern) { default: case VERT_BLANKING: while (p < (buf + samples)) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } break; case BLACK: /* black line (filler for FMT_480i5994 ) */ while (p < (buf + samples)) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } break; case GREEN: /* green line for test purpose */ while (p < (buf + samples)) { *p++ = 289; *p++ = 450; *p++ = 231; *p++ = 450; } break; case MAIN_SET: /* 75% gray */ endp += b + 1; while (p < endp) { *p++ = 512; *p++ = 721; *p++ = 512; *p++ = 721; } /* 75% yellow */ endp += b + 1; while (p < endp) { *p++ = 176; *p++ = 646; *p++ = 567; *p++ = 646; } /* 75% cyan */ endp += b + 1; while (p < endp) { *p++ = 625; *p++ = 525; *p++ = 176; *p++ = 525; } /* 75% green */ endp += b - 1; while (p < endp) { *p++ = 289; *p++ = 450; *p++ = 231; *p++ = 450; } /* 75% magenta */ endp += b + 1; while (p < endp) { *p++ = 735; *p++ = 335; *p++ = 793; *p++ = 335; } /* 75% red */ endp += b + 1; while (p < endp) { *p++ = 399; *p++ = 260; *p++ = 848; *p++ = 260; } /* 75% blue */ while (p < (buf + samples)) { *p++ = 848; *p++ = 139; *p++ = 457; *p++ = 139; } break; case CHROMA_SET: /* 75% blue */ endp += b + 1; while (p < endp) { *p++ = 848; *p++ = 139; *p++ = 457; *p++ = 139; } /* black */ endp += b + 1; while (p < endp) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } /* 75% magenta */ endp += b + 1; while (p < endp) { *p++ = 735; *p++ = 335; *p++ = 793; *p++ = 335; } /* black */ endp += b - 1; while (p < endp) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } /* 75% cyan */ endp += b + 1; while (p < endp) { *p++ = 625; *p++ = 525; *p++ = 176; *p++ = 525; } /* black */ endp += b + 1; while (p < endp) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } /* 75% gray */ while (p < (buf + samples)) { *p++ = 512; *p++ = 721; *p++ = 512; *p++ = 721; } break; case BLACK_SET: /* -I */ endp += 257; while (p < endp) { *p++ = 624; *p++ = 231; *p++ = 390; *p++ = 231; } /* white */ endp += 257; while (p < endp) { *p++ = 0x200; *p++ = 940; *p++ = 0x200; *p++ = 940; } /* +Q */ endp += 257; while (p < endp) { *p++ = 684; *p++ = 177; *p++ = 591; *p++ = 177; } /* black */ endp += 257; while (p < endp) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } /* blacker than black */ endp += 68; while (p < endp) { *p++ = 0x200; *p++ = 29; *p++ = 0x200; *p++ = 29; } /* black */ endp += 68 + 2; while (p < endp) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } /* whiter than black */ endp += 68; while (p < endp) { *p++ = 0x200; *p++ = 99; *p++ = 0x200; *p++ = 99; } /* black */ while (p < (buf + samples)) { *p++ = 0x200; *p++ = 0x040; *p++ = 0x200; *p++ = 0x040; } break; } return 0; } static int setSDIVideoProperties(enum sdi_setting_video_e setting, char * value, char * device) { const char fmt[] = "/sys/class/sdivideo/sdivideo%cx%i/%s"; struct stat buf; int num; char type, name[256], data[256]; char *endptr; /* Get the sysfs info */ memset(&buf, 0, sizeof(buf)); /** * Stat the file, fills the structure with info about the file * Get the major number from device node **/ if (stat(device, &buf) < 0) { fprintf(stderr, "%s: ", device); perror("unable to get the file status"); return -1; } /* Check if it is a character device or not */ if (!S_ISCHR (buf.st_mode)) { fprintf(stderr, "%s: not a character device\n", device); return -1; } /* Check the minor number to determine if it is a receive or transmit device */ type = (buf.st_rdev & 0x0080) ? 'r' : 't'; /* Get the receiver or transmitter number */ num = buf.st_rdev & 0x007f; /* Build the path to sysfs file */ snprintf(name, sizeof(name), fmt, type, num, "dev"); memset(data, 0, sizeof(data)); /* Read sysfs file (dev) */ if (util_read(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to get the device number"); return -1; } /* Compare the major number taken from sysfs file to the one taken from device node */ if (strtoul(data, &endptr, 0) != (buf.st_rdev >> 8)) { fprintf(stderr, "%s: not a SMPTE 292M/SMPTE 259M-C device\n", device); return -1; } if (*endptr != ':') { fprintf(stderr, "%s: error reading %s\n", device, name); return -1; } // Which setting do we write if (setting == SETTING_BUFFER_NUMBER_VIDEO) { snprintf(name, sizeof(name), fmt, type, num, "buffers"); snprintf(data, sizeof(data), "%s\n", value); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the number of buffers"); return -1; } printf("\tSet number of buffers = %s\n", value); } else if (setting == SETTING_BUFFER_SIZE_VIDEO) { snprintf(name, sizeof(name), fmt, type, num, "bufsize"); snprintf(data, sizeof(data), "%s\n", value); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the buffer size"); return -1; } printf("\tSet buffer size = %s Bytes\n", value); } else if (setting == SETTING_CLOCK_SOURCE) { snprintf(name, sizeof(name), fmt, type, num, "clock_source"); snprintf(data, sizeof(data), "%s\n", value); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the clock source"); return -1; } printf("\tSet clock source = %s\n", value); } else if (setting == SETTING_DATA_MODE) { snprintf(name, sizeof(name), fmt, type, num, "mode"); snprintf(data, sizeof(data), "%s\n", value); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the interface operating mode"); return -1; } printf("\tSet data mode = %s\n", value); } else if (setting == SETTING_FRAME_MODE) { snprintf(name, sizeof(name), fmt, type, num, "frame_mode"); snprintf(data, sizeof(data), "%s\n", value); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the interface frame mode"); return -1; } printf("\tSet frame mode = %s\n", value); } return 0; } static int setSDIAudioProperties(enum sdi_setting_audio_e setting, char * value, char * device) { const char fmt[] = "/sys/class/sdiaudio/sdiaudio%cx%i/%s"; struct stat buf; int num; char type, name[256], data[256]; char *endptr; /* Get the sysfs info */ memset(&buf, 0, sizeof(buf)); if (stat(device, &buf) < 0) { fprintf(stderr, "%s: ", device); perror("unable to get the file status"); return -1; } if (!S_ISCHR (buf.st_mode)) { fprintf(stderr, "%s: not a character device\n", device); return -1; } type = (buf.st_rdev & 0x0080) ? 'r' : 't'; num = buf.st_rdev & 0x007f; snprintf(name, sizeof(name), fmt, type, num, "dev"); memset(data, 0, sizeof(data)); if (util_read(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to get the device number"); return -1; } if (strtoul(data, &endptr, 0) != (buf.st_rdev >> 8)) { fprintf(stderr, "%s: not an audio device\n", device); return -1; } if (*endptr != ':') { fprintf(stderr, "%s: error reading %s\n", device, name); return -1; } if (setting == SETTING_BUFFER_NUMBER_AUDIO) { snprintf(name, sizeof(name), fmt, type, num, "buffers"); snprintf(data, sizeof(data), "%s\n", value); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the number of buffers"); return -1; } printf("\tSet number of buffers = %s\n", value); } else if (setting == SETTING_BUFFER_SIZE_AUDIO) { snprintf(name, sizeof(name), fmt, type, num, "bufsize"); snprintf(data, sizeof(data), "%s\n", value); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the buffer size"); return -1; } printf("\tSet buffer size = %s Bytes\n", value); } else if (setting == SETTING_SAMPLE_SIZE) { snprintf(name, sizeof(name), fmt, type, num, "sample_size"); snprintf(data, sizeof(data), "%s\n", value); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the interface audio sample size"); return -1; } switch (atol(value)) { case SDIAUDIO_CTL_AUDSAMP_SZ_16: printf("\tAssuming 16-bit audio.\n"); break; case SDIAUDIO_CTL_AUDSAMP_SZ_24: printf("\tAssuming 24-bit audio.\n"); break; case SDIAUDIO_CTL_AUDSAMP_SZ_32: printf("\tAssuming 32-bit audio.\n"); break; default: printf("\tSet audio sample size = %lu.\n", atol(value)); break; } } else if (setting == SETTING_SAMPEL_RATE) { snprintf(name, sizeof(name), fmt, type, num, "sample_rate"); snprintf(data, sizeof(data), "%lu\n", atol(value)); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set the interface audio sample rate"); return -1; } switch (atoi(value)) { case 32000: printf("\tAssuming 32 kHz audio.\n"); break; case 44100: printf("\tAssuming 44.1 kHz audio.\n"); break; case 48000: printf("\tAssuming 48 kHz audio.\n"); break; default: printf("\tSet audio sample rate = %lu.\n", atol(value)); break; } } else if (setting == SETTING_CHANNELS) { snprintf(name, sizeof(name), fmt, type, num, "channels"); snprintf(data, sizeof(data), "%lu\n", atol(value)); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set " "the interface audio channel enable"); return -1; } switch (atol(value)) { case SDIAUDIO_CTL_AUDCH_EN_0: printf("\tDisabling audio.\n"); break; case SDIAUDIO_CTL_AUDCH_EN_2: printf("\tAssuming 2 channels of audio.\n"); break; case SDIAUDIO_CTL_AUDCH_EN_4: printf("\tAssuming 4 channels of audio.\n"); break; case SDIAUDIO_CTL_AUDCH_EN_6: printf("\tAssuming 6 channels of audio.\n"); break; case SDIAUDIO_CTL_AUDCH_EN_8: printf("\tAssuming 8 channels of audio.\n"); break; default: printf("\tSet audio channel enable = %lu.\n", atol(value)); break; } } else if (setting == SETTING_NON_AUDIO) { snprintf(name, sizeof(name), fmt, type, num, "non_audio"); snprintf(data, sizeof(data), "0x%04lX\n", atol(value)); if (util_write(name, data, sizeof(data)) < 0) { fprintf(stderr, "%s: ", device); perror("unable to set " "the interface non-audio"); return -1; } switch (atol(value)) { case 0x0000: printf("\tPassing PCM audio.\n"); break; case 0x00ff: printf("\tPassing non-audio.\n"); break; default: printf("\tSet non-audio = 0x%04lX.\n", atol(value)); break; } } return 0; } static ssize_t util_read(const char *name, char *buf, size_t count) { ssize_t fd, ret; if ((fd = open(name, O_RDONLY)) < 0) { return fd; } ret = read(fd, buf, count); close(fd); return ret; } static ssize_t util_write(const char *name, const char *buf, size_t count) { ssize_t fd, ret; if ((fd = open(name, O_WRONLY)) < 0) { return fd; } ret = write(fd, buf, count); close(fd); return ret; } static char * itoa(uint64_t i) { if (i == 0) return strdup("0"); char * mystring = (char *) malloc(50); sprintf(mystring, "%"PRIu64, i); return mystring; } mlt-6.20.0/src/modules/linsys/sdi_generator.h000066400000000000000000000310111362234133600211500ustar00rootroot00000000000000/** * sdi_generator.h **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SDI_GENERATOR_H_ #define SDI_GENERATOR_H_ // definitions are only for SD NTSC (mkline funktion for test pattern) #define VERT_BLANKING 0 #define MAIN_SET 1 #define CHROMA_SET 2 #define BLACK_SET 3 #define BLACK 4 #define GREEN 5 // defines for SD SDI with blanking #define ANCILLARY_DATA_SAMPLES 280 #define FIELD_1 1 #define FIELD_2 2 #define VERT_BLANKING 0 #define ACTIVE_VIDEO 1 // Master SDI device #define SDI_IOC_MAGIC '=' #define SDI_IOC_TXGETEVENTS _IOR(SDI_IOC_MAGIC, 2, unsigned int) // Transmitter event flag bit locations #define SDI_EVENT_TX_BUFFER_ORDER 0 #define SDI_EVENT_TX_BUFFER (1 << SDI_EVENT_TX_BUFFER_ORDER) #define SDI_EVENT_TX_FIFO_ORDER 1 #define SDI_EVENT_TX_FIFO (1 << SDI_EVENT_TX_FIFO_ORDER) #define SDI_EVENT_TX_DATA_ORDER 2 #define SDI_EVENT_TX_DATA (1 << SDI_EVENT_TX_DATA_ORDER) // part of the linsys sdiaudio.h #define SDIAUDIO_IOC_TXGETCAP _IOR(SDIAUDIO_IOC_MAGIC, 1, unsigned int) #define SDIAUDIO_IOC_TXGETEVENTS _IOR(SDIAUDIO_IOC_MAGIC, 2, unsigned int) #define SDIAUDIO_IOC_TXGETBUFLEVEL _IOR(SDIAUDIO_IOC_MAGIC, 3, unsigned int) #define SDIAUDIO_IOC_TXGETTXD _IOR(SDIAUDIO_IOC_MAGIC, 4, int) #define SDIAUDIO_IOC_MAGIC '~' /* This ioctl magic number is currently free. See * /usr/src/linux/Documentation/ioctl-number.txt */ /* Transmitter event flag bit locations */ #define SDIAUDIO_EVENT_TX_BUFFER_ORDER 0 #define SDIAUDIO_EVENT_TX_BUFFER (1 << SDIAUDIO_EVENT_TX_BUFFER_ORDER) #define SDIAUDIO_EVENT_TX_FIFO_ORDER 1 #define SDIAUDIO_EVENT_TX_FIFO (1 << SDIAUDIO_EVENT_TX_FIFO_ORDER) #define SDIAUDIO_EVENT_TX_DATA_ORDER 2 #define SDIAUDIO_EVENT_TX_DATA (1 << SDIAUDIO_EVENT_TX_DATA_ORDER) // Filehandler for sdi output static int fh_sdi_video; static int fh_sdi_audio; #define MAX_SAMPLES_PER_LINE (2*2750) #define MAX_LINES_PER_FRAME 1125 #define MAX_AUDIO_STREAMS (8) // max. audio samples per frame #define MAX_AUDIO_SAMPLES (2002*2) /** * 23.98Hz = fix:{2002} * 24Hz = fix:{2000} * 25Hz = fix:{1920} * 29.97Hz = varies:{1601,1602,1602} * 30Hz = fix:{1600} **/ #define MAX_SDI_HEIGHT 1125 // HD-SDI #define MAX_SDI_WIDTH 2750 // HD-SDI (FMT_1080p24 has up to 2750) #define MAX_SDI_FRAMESIZE (MAX_SDI_HEIGHT*MAX_SDI_WIDTH*2) // SDI frame size, (2 Pixels are represented by 4 bytes, yuyv422) struct source_format { unsigned int lines_per_frame; unsigned int active_lines_per_frame; unsigned int samples_per_line; unsigned int active_samples_per_line; unsigned int interlaced; }; struct audio_format { mlt_audio_format aformat; // default: mlt_audio_pcm uint16_t samples; // default 2*1920 uint16_t sample_rate; // default 48000 /** * 0 channels = audio disabled, transmit only * 2 channels (stereo) * 4 channels * 6 channels * 8 channels **/ int channels; // default 2 (stereo) }; /** * SDI DEVICE FILE SETTINGS AND MODES **/ enum sdi_setting_video_e { SETTING_BUFFER_NUMBER_VIDEO = 0, SETTING_BUFFER_SIZE_VIDEO = 1, SETTING_CLOCK_SOURCE = 2, SETTING_DATA_MODE = 3, SETTING_FRAME_MODE = 4 }; enum sdi_setting_audio_e { SETTING_BUFFER_NUMBER_AUDIO = 0, SETTING_BUFFER_SIZE_AUDIO = 1, SETTING_SAMPLE_SIZE = 2, SETTING_CHANNELS = 3, SETTING_SAMPEL_RATE = 4, SETTING_NON_AUDIO = 5 }; static int sdi_frame_mode = 0; /* Frame mode settings */ #define SDIVIDEO_CTL_UNLOCKED 0 #define SDIVIDEO_CTL_SMPTE_125M_486I_59_94HZ 1 #define SDIVIDEO_CTL_BT_601_576I_50HZ 2 #define SDIVIDEO_CTL_SMPTE_260M_1035I_60HZ 5 #define SDIVIDEO_CTL_SMPTE_260M_1035I_59_94HZ 6 #define SDIVIDEO_CTL_SMPTE_295M_1080I_50HZ 7 #define SDIVIDEO_CTL_SMPTE_274M_1080I_60HZ 8 #define SDIVIDEO_CTL_SMPTE_274M_1080PSF_30HZ 9 #define SDIVIDEO_CTL_SMPTE_274M_1080I_59_94HZ 10 #define SDIVIDEO_CTL_SMPTE_274M_1080PSF_29_97HZ 11 #define SDIVIDEO_CTL_SMPTE_274M_1080I_50HZ 12 #define SDIVIDEO_CTL_SMPTE_274M_1080PSF_25HZ 13 #define SDIVIDEO_CTL_SMPTE_274M_1080PSF_24HZ 14 #define SDIVIDEO_CTL_SMPTE_274M_1080PSF_23_98HZ 15 #define SDIVIDEO_CTL_SMPTE_274M_1080P_30HZ 16 #define SDIVIDEO_CTL_SMPTE_274M_1080P_29_97HZ 17 #define SDIVIDEO_CTL_SMPTE_274M_1080P_25HZ 18 #define SDIVIDEO_CTL_SMPTE_274M_1080P_24HZ 19 #define SDIVIDEO_CTL_SMPTE_274M_1080P_23_98HZ 20 #define SDIVIDEO_CTL_SMPTE_296M_720P_60HZ 21 #define SDIVIDEO_CTL_SMPTE_296M_720P_59_94HZ 22 #define SDIVIDEO_CTL_SMPTE_296M_720P_50HZ 23 #define SDIVIDEO_CTL_SMPTE_296M_720P_30HZ 24 #define SDIVIDEO_CTL_SMPTE_296M_720P_29_97HZ 25 #define SDIVIDEO_CTL_SMPTE_296M_720P_25HZ 26 #define SDIVIDEO_CTL_SMPTE_296M_720P_24HZ 27 #define SDIVIDEO_CTL_SMPTE_296M_720P_23_98HZ 28 /* Audio sample size */ #define SDIAUDIO_CTL_AUDSAMP_SZ_16 16 /* 16 bit */ #define SDIAUDIO_CTL_AUDSAMP_SZ_24 24 /* 24 bit */ #define SDIAUDIO_CTL_AUDSAMP_SZ_32 32 /* 32 bit */ /* Audio channel enable */ #define SDIAUDIO_CTL_AUDCH_EN_0 0 /* 0 channel/disable audio */ #define SDIAUDIO_CTL_AUDCH_EN_2 2 /* 2 channel */ #define SDIAUDIO_CTL_AUDCH_EN_4 4 /* 4 channel */ #define SDIAUDIO_CTL_AUDCH_EN_6 6 /* 6 channel */ #define SDIAUDIO_CTL_AUDCH_EN_8 8 /* 8 channel */ static char * itoa(uint64_t i); static ssize_t util_read(const char *name, char *buf, size_t count); static ssize_t util_write(const char *name, const char *buf, size_t count); static int setSDIVideoProperties(enum sdi_setting_video_e setting, char * value, char * device); static int setSDIAudioProperties(enum sdi_setting_audio_e setting, char * value, char * device); // HD static const struct source_format FMT_1080i60 = { .lines_per_frame = 1125, .active_lines_per_frame = 1080, .samples_per_line = 2*2200, .active_samples_per_line = 2*1920, .interlaced = 1 }; static const struct source_format FMT_1080i5994 = { .lines_per_frame = 1125, .active_lines_per_frame = 1080, .samples_per_line = 2*2200, .active_samples_per_line = 2*1920, .interlaced = 1 }; static const struct source_format FMT_1080i50 = { .lines_per_frame = 1125, .active_lines_per_frame = 1080, .samples_per_line = 2*2640, .active_samples_per_line = 2*1920, .interlaced = 1 }; static const struct source_format FMT_1080p30 = { .lines_per_frame = 1125, .active_lines_per_frame = 1080, .samples_per_line = 2*2200, .active_samples_per_line = 2*1920, .interlaced = 0 }; static const struct source_format FMT_1080p2997 = { .lines_per_frame = 1125, .active_lines_per_frame = 1080, .samples_per_line = 2*2200, .active_samples_per_line = 2*1920, .interlaced = 0 }; static const struct source_format FMT_1080p25 = { .lines_per_frame = 1125, .active_lines_per_frame = 1080, .samples_per_line = 2*2640, .active_samples_per_line = 2*1920, .interlaced = 0 }; static const struct source_format FMT_1080p24 = { .lines_per_frame = 1125, .active_lines_per_frame = 1080, .samples_per_line = 2*2750, .active_samples_per_line = 2*1920, .interlaced = 0 }; static const struct source_format FMT_1080p2398 = { .lines_per_frame = 1125, .active_lines_per_frame = 1080, .samples_per_line = 2*2750, .active_samples_per_line = 2*1920, .interlaced = 0 }; static const struct source_format FMT_720p60 = { .lines_per_frame = 750, .active_lines_per_frame = 720, .samples_per_line = 2*1650, .active_samples_per_line = 2*1280, .interlaced = 0 }; static const struct source_format FMT_720p5994 = { .lines_per_frame = 750, .active_lines_per_frame = 720, .samples_per_line = 2*1650, .active_samples_per_line = 2*1280, .interlaced = 0 }; static const struct source_format FMT_720p50 = { .lines_per_frame = 750, .active_lines_per_frame = 720, .samples_per_line = 2*1980, .active_samples_per_line = 2*1280, .interlaced = 0 }; static const struct source_format FMT_720p30 = { .lines_per_frame = 750, .active_lines_per_frame = 720, .samples_per_line = 2*3300, .active_samples_per_line = 2*1280, .interlaced = 0 }; static const struct source_format FMT_720p2997 = { .lines_per_frame = 750, .active_lines_per_frame = 720, .samples_per_line = 2*3300, .active_samples_per_line = 2*1280, .interlaced = 0 }; static const struct source_format FMT_720p25 = { .lines_per_frame = 750, .active_lines_per_frame = 720, .samples_per_line = 2*3960, .active_samples_per_line = 2*1280, .interlaced = 0 }; static const struct source_format FMT_720p24 = { .lines_per_frame = 750, .active_lines_per_frame = 720, .samples_per_line = 2*4125, .active_samples_per_line = 2*1280, .interlaced = 0 }; static const struct source_format FMT_720p2398 = { .lines_per_frame = 750, .active_lines_per_frame = 720, .samples_per_line = 2*4125, .active_samples_per_line = 2*1280, .interlaced = 0 }; // SD PAL static const struct source_format FMT_576i50 = { .lines_per_frame = 625, .active_lines_per_frame = 576, .samples_per_line = 2*864 /*1728*/, .active_samples_per_line = 2*720 /* 720xY, 360xCb, 360xCr */, .interlaced = 1 }; // SD NTSC; 486 video lines static const struct source_format FMT_486i5994 = { .lines_per_frame = 525, .active_lines_per_frame = 486, .samples_per_line = 2*858 /*1716*/, .active_samples_per_line = 2*720 /* 720xY, 360xCb, 360xCr */, .interlaced = 1 }; // SD NTSC; 480 video lines, 6 lines opt. video data /** * sames as FMT_486i5994 but the first 6 lines will be filled with SDI-BLACK * or can be used for opt. video data (s.SMPTE) */ static const struct source_format FMT_480i5994 = { .lines_per_frame = 525, .active_lines_per_frame = 486, .samples_per_line = 2*858 /*1716*/, .active_samples_per_line = 2*720 /* 720xY, 360xCb, 360xCr */, .interlaced = 1 }; struct trs { unsigned short int sav; unsigned short int eav; }; static const struct trs FIELD_1_ACTIVE = { .sav = 0x200, .eav = 0x274 }; static const struct trs FIELD_1_VERT_BLANKING = { .sav = 0x2ac, .eav = 0x2d8 }; static const struct trs FIELD_2_ACTIVE = { .sav = 0x31c, .eav = 0x368 }; static const struct trs FIELD_2_VERT_BLANKING = { .sav = 0x3b0, .eav = 0x3c4 }; struct line_info { const struct source_format *fmt; unsigned int ln; const struct trs *xyz; uint8_t blanking; }; struct SDI_atr { int status; int *fh; uint8_t *data; size_t framesize; } SDI_atr; // 192bit for AESChannelStatusBits uint8_t AESChannelStatusBitArray[192]; // beta array //uint8_t AESChannelStatusBitArray[24]; // TODO better way for 24x8bit !!! // buffer for one sdi line uint16_t * line_buffer; // counter for active line number uint16_t active_video_line; // buffer for sdi frame size uint64_t sdi_frame_size; // buffer for the complete SDI frame uint8_t * data; static char * device_file_video; static char * device_file_audio; static struct line_info info; static uint8_t *(*pack)(uint8_t *outbuf, unsigned short int *inbuf, size_t count); static size_t elements; static unsigned int samples; // functions static int sdi_init(char *device_video, char *device_audio, uint8_t blanking, mlt_profile myProfile, const struct audio_format * audio_format); static int sdimaster_close(); static int sdi_playout(uint8_t *vBuffer, int16_t aBuffer[MAX_AUDIO_STREAMS][MAX_AUDIO_SAMPLES], const struct audio_format * audio_format, int audio_streams, int my_DBN); static int mkline(unsigned short int *buf, const struct line_info *info, unsigned int pattern); static inline int create_HD_SDI_Line(uint16_t *buf, const struct line_info *info, uint16_t active_video_line, unsigned int active, uint8_t *video_buffer); static inline int create_SD_SDI_Line(uint16_t *buf, const struct line_info *info, int field, int active, uint8_t *video_buffer, int16_t audio_buffer[MAX_AUDIO_STREAMS][MAX_AUDIO_SAMPLES], int linenumber_sdiframe, int active_video_line, int my_DBN, int16_t AudioGroupCounter, int16_t AudioGroups2Write, int audio_streams); static int writeANC(uint16_t *p, int linenumber_sdiframe, uint16_t DID, int my_DBN, int16_t *audio_buffer_A, int16_t *audio_buffer_B, int16_t AudioDataPacketCounter, int16_t AudioGroups2Write); static uint16_t checker(uint16_t *DID_pointer); static uint8_t getZBit(int sample_number); static uint8_t getChannelStatusBit(uint16_t sample_number, uint8_t ch); static int16_t getNumberOfAudioGroups2Write(int linenuber); static uint8_t getDBN(int my_DBN); static inline uint8_t *pack8(uint8_t *outbuf, uint16_t *inbuf, size_t count); // alias 'pack_uyvy()' static inline uint8_t *pack10(uint8_t *outbuf, uint16_t *inbuf, size_t count); static inline uint8_t *pack_v210(uint8_t *outbuf, uint16_t *inbuf, size_t count); static int pack_AES_subframe(uint16_t *p, int8_t c, int8_t z, int8_t ch, int16_t *audio_sample); #endif /* SDI_GENERATOR_H_ */ mlt-6.20.0/src/modules/lumas/000077500000000000000000000000001362234133600157565ustar00rootroot00000000000000mlt-6.20.0/src/modules/lumas/Makefile000066400000000000000000000021621362234133600174170ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt include ../../../config.mak all: luma create_lumas @./create_lumas luma: luma.o # When cross-compiling, use the host OS compiler to build the luma # binary because the files are generated at build time. # Strips the CROSS prefix from the C compiler variable. ifdef CROSS $(subst $(CROSS),,$(CC)) -o $@ luma.o $(LDFLAGS) else $(CC) -o $@ luma.o $(LDFLAGS) endif create_lumas: depend: luma.c $(CC) -MM $(CFLAGS) $^ 1>.depend distclean: rm -rf luma 9_16 16_9 NTSC PAL square clean: rm -f luma luma.o .executed install: all install -d "$(DESTDIR)$(mltdatadir)/lumas/9_16" install -d "$(DESTDIR)$(mltdatadir)/lumas/16_9" install -d "$(DESTDIR)$(mltdatadir)/lumas/NTSC" install -d "$(DESTDIR)$(mltdatadir)/lumas/PAL" install -d "$(DESTDIR)$(mltdatadir)/lumas/square" install -m 644 9_16/* "$(DESTDIR)$(mltdatadir)/lumas/9_16" install -m 644 16_9/* "$(DESTDIR)$(mltdatadir)/lumas/16_9" install -m 644 NTSC/* "$(DESTDIR)$(mltdatadir)/lumas/NTSC" install -m 644 PAL/* "$(DESTDIR)$(mltdatadir)/lumas/PAL" install -m 644 square/* "$(DESTDIR)$(mltdatadir)/lumas/square" mlt-6.20.0/src/modules/lumas/configure000077500000000000000000000012321362234133600176630ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF Luma options: --enable-lumas This must be explicitly enabled since they can now be generated dynamically. --luma-compress - Produce compressed (png) lumas --luma-8bpp - Produce 8 bit pgm lumas (default is 16 bit) EOF else rm -f .8bit .compress .executed for i in "$@" do case $i in --enable-lumas ) lumas_enabled=1;; --luma-compress ) touch .compress .8bit ;; --luma-8bit ) touch .8bit ;; esac done if [ "$lumas_enabled" != "1" ]; then echo "- not explicitly enabled: disabling" touch ../disable-lumas exit 0 fi fi mlt-6.20.0/src/modules/lumas/create_lumas000077500000000000000000000042001362234133600203440ustar00rootroot00000000000000#!/bin/sh [ \( -d PAL \) -a \( ! $0 -nt .executed \) ] && exit 0 if [ "$(uname -s)" = "Darwin" ]; then export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../framework elif uname -s | grep -iq mingw; then export PATH=$PATH:../../framework else export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:../../framework fi bpp=16 [ -f .8bit ] && bpp=8 for i in PAL NTSC 16_9 9_16 square do mkdir -p $i rm -f $i/*.pgm $i/*.png case $i in NTSC) w=720 h=480 ;; PAL) w=720 h=576 ;; 16_9) w=1920 h=1080 ;; 9_16) w=1080 h=1920 ;; square) w=1080 h=1080 ;; esac ./luma -w $w -h $h -bpp $bpp $i/luma01.pgm ./luma -w $w -h $h -bpp $bpp -bands $h $i/luma02.pgm ./luma -w $w -h $h -bpp $bpp -hmirror 1 $i/luma03.pgm ./luma -w $w -h $h -bpp $bpp -bands $h -vmirror 1 $i/luma04.pgm ./luma -w $w -h $h -bpp $bpp -offset 32768 -dmirror 1 $i/luma05.pgm ./luma -w $w -h $h -bpp $bpp -offset 32768 -dmirror 1 -flip 1 $i/luma06.pgm ./luma -w $w -h $h -bpp $bpp -offset 32768 -dmirror 1 -quart 1 $i/luma07.pgm ./luma -w $w -h $h -bpp $bpp -offset 32768 -dmirror 1 -quart 1 -flip 1 $i/luma08.pgm ./luma -w $w -h $h -bpp $bpp -bands 12 -rband 0 $i/luma09.pgm ./luma -w $w -h $h -bpp $bpp -bands 12 -rband 0 -rotate 1 -flop 1 $i/luma10.pgm ./luma -w $w -h $h -bpp $bpp -bands 12 -rband 1 $i/luma11.pgm ./luma -w $w -h $h -bpp $bpp -bands 12 -rband 1 -vmirror 1 $i/luma12.pgm ./luma -w $w -h $h -bpp $bpp -bands 12 -rband 1 -rotate 1 -flop 1 $i/luma13.pgm ./luma -w $w -h $h -bpp $bpp -bands 12 -rband 1 -rotate 1 -vmirror 1 $i/luma14.pgm ./luma -w $w -h $h -bpp $bpp -offset 32768 -dmirror 1 -hmirror 1 $i/luma15.pgm ./luma -w $w -h $h -bpp $bpp -type 1 $i/luma16.pgm ./luma -w $w -h $h -bpp $bpp -type 1 -bands 2 -rband 1 $i/luma17.pgm ./luma -w $w -h $h -bpp $bpp -type 2 $i/luma18.pgm ./luma -w $w -h $h -bpp $bpp -type 2 -quart 1 $i/luma19.pgm ./luma -w $w -h $h -bpp $bpp -type 2 -quart 1 -flip 1 $i/luma20.pgm ./luma -w $w -h $h -bpp $bpp -type 2 -quart 1 -bands 2 $i/luma21.pgm ./luma -w $w -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 mlt-6.20.0/src/modules/lumas/luma.c000066400000000000000000000100041362234133600170530ustar00rootroot00000000000000/* * luma.c -- image generator for transition_luma * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include int main( int argc, char **argv ) { int arg = 1; int bpp = 8; struct mlt_luma_map_s self; uint16_t *image = NULL; const char *filename = NULL; mlt_luma_map_init( &self ); for ( arg = 1; arg < argc; arg ++ ) { if ( !strcmp( argv[ arg ], "-bpp" ) ) bpp = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-type" ) ) self.type = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-w" ) ) { int tmp = atoi( argv[ ++ arg ] ); // TODO: is there an upper bound? if ( tmp ) self.w = tmp; else return 1; } else if ( !strcmp( argv[ arg ], "-h" ) ) { int tmp = atoi( argv[ ++ arg ] ); // TODO: is there an upper bound? if ( tmp ) self.h = tmp; else return 1; } else if ( !strcmp( argv[ arg ], "-bands" ) ) { int tmp = atoi( argv[ ++ arg ] ); // TODO: is there an upper bound? if ( tmp >= 0 ) self.bands = tmp; else return 1; } else if ( !strcmp( argv[ arg ], "-rband" ) ) self.rband = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-hmirror" ) ) self.hmirror = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-vmirror" ) ) self.vmirror = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-dmirror" ) ) self.dmirror = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-offset" ) ) self.offset = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-invert" ) ) self.invert = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-flip" ) ) self.flip = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-flop" ) ) self.flop = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-pflip" ) ) self.pflip = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-pflop" ) ) self.pflop = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-quart" ) ) self.quart = atoi( argv[ ++ arg ] ); else if ( !strcmp( argv[ arg ], "-rotate" ) ) self.rotate = atoi( argv[ ++ arg ] ); else filename = argv[arg]; } if ( bpp != 8 && bpp != 16 ) { fprintf( stderr, "Invalid bpp %d\n", bpp ); return 1; } mlt_pool_init(); image = mlt_luma_map_render( &self ); if ( bpp == 16 ) { uint16_t *end = image + self.w * self.h; uint16_t *p = image; uint8_t *q = ( uint8_t * )image; while ( p < end ) { *p ++ = ( *q << 8 ) + *( q + 1 ); q += 2; } if (filename) { FILE *f = fopen(filename, "wb"); if (f) { fprintf(f, "P5\x0a" ); fprintf(f, "%d %d\x0a", self.w, self.h ); fprintf(f, "65535\x0a" ); fwrite( image, self.w * self.h * sizeof( uint16_t ), 1, f ); fclose(f); } } else { printf( "P5\n" ); printf( "%d %d\n", self.w, self.h ); printf( "65535\n" ); fwrite( image, self.w * self.h * sizeof( uint16_t ), 1, stdout ); } } else { uint16_t *end = image + self.w * self.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", self.w, self.h ); printf( "255\n" ); fwrite( image, self.w * self.h, 1, stdout ); } return 0; } mlt-6.20.0/src/modules/motion_est/000077500000000000000000000000001362234133600170155ustar00rootroot00000000000000mlt-6.20.0/src/modules/motion_est/CMakeLists.txt000066400000000000000000000005311362234133600215540ustar00rootroot00000000000000if(GPL) file(GLOB mltmotion_est_src *.c) add_library(mltmotion_est MODULE ${mltmotion_est_src}) target_link_libraries(mltmotion_est mlt m) install(TARGETS mltmotion_est LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/motion_est) endif() mlt-6.20.0/src/modules/motion_est/Makefile000066400000000000000000000036271362234133600204650ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lm include ../../../config.mak TARGET = ../libmltmotion_est$(LIBSUF) OBJS = factory.o \ filter_motion_est.o \ filter_crop_detect.o \ filter_autotrack_rectangle.o \ arrow_code.o \ filter_vismv.o \ producer_slowmotion.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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/motion_est" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/motion_est" test: $(TARGET) ~/mlt-devel/mlt/src/melt/melt -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/melt/melt -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) melt 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/melt/melt -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/melt/melt -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 mlt-6.20.0/src/modules/motion_est/README000066400000000000000000000064121362234133600177000ustar00rootroot00000000000000INTRO: 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: > melt -filter motion_est To display the motion vectors as pretty arrows: > melt -filter motion_est -filter vismv If your using a movie file that contains a crop, you will get better results with this: > melt -filter crop_detect -filter motion_est -filter vismv If your computer is unable to do the above examples in real time, try this: > melt -filter motion_est -filter vismv -consumer melt real_time=0 If you'd like to see the motion vectors without the median denoising function, do this: > melt -filter motion_est denoise=0 -filter vismv To reconstruct each frame by applying the motion to the previous frame: > melt -filter motion_est show_reconstruction=1 To compare the reconstructed frame and the real frame (while paused): > melt -filter motion_est show_reconstruction=1 toggle_when_paused=1 To show the difference (residual) between the reconstructed frame the real frame: > melt -filter motion_est show_residual=1 To automatically track an object in the frame, try this: > melt -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: > melt -filter autotrack_rectangle:X,Y:WxH obscure=1 There is now a slow motion producer that does interpolation based on the motion vectors: > melt 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 mlt-6.20.0/src/modules/motion_est/arrow_code.c000066400000000000000000000101361362234133600213060ustar00rootroot00000000000000/* * /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "arrow_code.h" #include #include #include #include #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); } mlt-6.20.0/src/modules/motion_est/arrow_code.h000066400000000000000000000023451362234133600213160ustar00rootroot00000000000000/** * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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); mlt-6.20.0/src/modules/motion_est/factory.c000066400000000000000000000046321362234133600206350ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #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 ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/motion_est/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } 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 ); MLT_REGISTER_METADATA( filter_type, "motion_est", metadata, "filter_motion_est.yml" ); MLT_REGISTER_METADATA( filter_type, "vismv", metadata, "filter_vismv.yml" ); MLT_REGISTER_METADATA( filter_type, "crop_detect", metadata, "filter_crop_detect.yml" ); MLT_REGISTER_METADATA( filter_type, "autotrack_rectangle", metadata, "filter_autotrack_rectangle.yml" ); MLT_REGISTER_METADATA( producer_type, "slowmotion", metadata, "producer_slowmotion.yml" ); } mlt-6.20.0/src/modules/motion_est/filter_autotrack_rectangle.c000066400000000000000000000312101362234133600245440ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filter_motion_est.h" #include "arrow_code.h" #include #include #include #include #include #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_filter_get_position( filter, frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } // 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 ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // 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 ); // Cleanse the geometry item boundry.w = boundry.x < 0 ? boundry.w + boundry.x : boundry.w; boundry.h = boundry.y < 0 ? boundry.h + boundry.y : boundry.h; boundry.x = boundry.x < 0 ? 0 : boundry.x; boundry.y = boundry.y < 0 ? 0 : boundry.y; boundry.w = boundry.w < 0 ? 0 : boundry.w; boundry.h = boundry.h < 0 ? 0 : boundry.h; // 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); mlt_geometry_interpolate(geometry); } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); 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, "_serialize" ) == 1 ) { // Add the vector change to the list mlt_geometry key_frames = mlt_properties_get_data( filter_properties, "motion_vector_list", NULL ); if ( !key_frames ) { key_frames = mlt_geometry_init(); mlt_properties_set_data( filter_properties, "motion_vector_list", key_frames, 0, (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise ); if ( key_frames ) mlt_geometry_set_length( key_frames, mlt_filter_get_length2( filter, frame ) ); } if ( key_frames ) { struct mlt_geometry_item_s item; item.frame = (int) mlt_frame_get_position( frame ); item.key = 1; item.x = boundry.x; item.y = boundry.y; item.w = boundry.w; item.h = boundry.h; item.mix = 0; item.f[0] = item.f[1] = item.f[2] = item.f[3] = 1; item.f[4] = 0; mlt_geometry_insert( key_frames, &item ); } } 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 ) { fprintf( stderr, "%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_filter_get_position( filter, frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // 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"); // Parse the geometry if we have one mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); mlt_geometry_parse( geom, arg, length, profile->width, profile->height ); // 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_geometry_interpolate( geom ); 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); } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // 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); // Cleanse the geometry item geometry_item->w = geometry_item->x < 0 ? geometry_item->w + geometry_item->x : geometry_item->w; geometry_item->h = geometry_item->y < 0 ? geometry_item->h + geometry_item->y : geometry_item->h; geometry_item->x = geometry_item->x < 0 ? 0 : geometry_item->x; geometry_item->y = geometry_item->y < 0 ? 0 : geometry_item->y; geometry_item->w = geometry_item->w < 0 ? 0 : geometry_item->w; geometry_item->h = geometry_item->h < 0 ? 0 : geometry_item->h; 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 ); mlt_properties properties = MLT_FILTER_PROPERTIES( this ); /* apply the motion estimation filter */ mlt_filter motion_est = mlt_properties_get_data( properties, "_motion_est", NULL ); /* Pass motion_est properties */ mlt_properties_pass( MLT_FILTER_PROPERTIES( motion_est ), properties, "motion_est." ); 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( properties, "_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( properties, "_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( properties, "_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( properties, "_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 there 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... */ mlt-6.20.0/src/modules/motion_est/filter_autotrack_rectangle.yml000066400000000000000000000003011362234133600251200ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: autotrack_rectangle title: Autotrack Rectangle version: 1 copyright: Zachary Drew creator: Zachary Drew license: GPLv2 language: en tags: - Video mlt-6.20.0/src/modules/motion_est/filter_crop_detect.c000066400000000000000000000161021362234133600230210ustar00rootroot00000000000000/** * /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } // 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"); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // 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_filter_get_position(filter, 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" ); *format = mlt_image_yuv422; int xstride = 2; int ystride = 2 * *width; 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 ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); 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... */ mlt-6.20.0/src/modules/motion_est/filter_motion_est.c000066400000000000000000001024521362234133600227120ustar00rootroot00000000000000/* * /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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 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 below 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 existence 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 existence: 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 candidate 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 separately, // 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 ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // 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 *format = mlt_image_yuv422; 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 ); c->xstride = 2; c->ystride = c->xstride * *width; // 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 ) { char *property = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "bounding" ); if ( property ) { mlt_geometry geometry = mlt_geometry_init( ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); if ( geometry ) { mlt_geometry_parse( geometry, property, 0, profile->width, profile->height ); bounds = calloc( 1, sizeof(*bounds) ); mlt_properties_set_data( MLT_FILTER_PROPERTIES(filter), "bounds", bounds, sizeof(*bounds), free, NULL ); mlt_geometry_fetch( geometry, bounds, 0 ); mlt_geometry_close( geometry ); } } } 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 ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_log_verbose( MLT_FILTER_SERVICE(filter), "shot change: %d\n", c->comparison_average); mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "shot_change", 1); c->shot_change = 1; // Add the shot change to the list mlt_geometry key_frames = mlt_properties_get_data( properties, "shot_change_list", NULL ); if ( !key_frames ) { key_frames = mlt_geometry_init(); mlt_properties_set_data( properties, "shot_change_list", key_frames, 0, (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise ); if ( key_frames ) mlt_geometry_set_length( key_frames, mlt_filter_get_length2( filter, frame ) ); } if ( key_frames ) { struct mlt_geometry_item_s item; item.frame = (int) c->current_frame_position; item.x = c->comparison_average; item.f[0] = 1; item.f[1] = item.f[2] = item.f[3] = item.f[4] = 0; mlt_geometry_insert( key_frames, &item ); } } 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 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); 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; } mlt-6.20.0/src/modules/motion_est/filter_motion_est.h000066400000000000000000000026211362234133600227140ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 #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); mlt_filter filter = mlt_frame_pop_service( frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } // Get the new image *format = mlt_image_yuv422; 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_service( frame, this); 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... */ mlt-6.20.0/src/modules/motion_est/filter_vismv.yml000066400000000000000000000002701362234133600222500ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: vismv title: Visualize Motion Vectors version: 1 copyright: Zachary Drew creator: Zachary Drew license: GPLv2 language: en tags: - Video mlt-6.20.0/src/modules/motion_est/gpl000066400000000000000000000000001362234133600175100ustar00rootroot00000000000000mlt-6.20.0/src/modules/motion_est/producer_slowmotion.c000066400000000000000000000321531362234133600233020ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 below 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 existence 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 *format = mlt_image_yuv422; int size = *width * *height * 2; int xstride = 2; int ystride = 2 * *width; 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_frame_set_image( this, output, size, 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 ) { if ( !frame ) return 1; // Construct a new frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) ); mlt_properties properties = MLT_PRODUCER_PROPERTIES(this); if( *frame ) { 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( profile ); // Wrap the loader mlt_producer real_producer = mlt_factory_producer( profile, NULL, 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 ); // The loader normalised it for us already mlt_properties_set_int( properties, "loader_normalised", 1); // Store the producer and filter 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; } mlt-6.20.0/src/modules/motion_est/producer_slowmotion.yml000066400000000000000000000002621362234133600236550ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: slowmotion title: Slow Motion version: 1 copyright: Zachary Drew creator: Zachary Drew license: GPLv2 language: en tags: - Video mlt-6.20.0/src/modules/motion_est/sad_sse.h000066400000000000000000000227101362234133600206110ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #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" ((intptr_t)(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" ((intptr_t)(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" ((intptr_t)(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" ((intptr_t)(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" ((intptr_t)(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" ((intptr_t)(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" ((intptr_t)(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" ((intptr_t)(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; } mlt-6.20.0/src/modules/ndi/000077500000000000000000000000001362234133600154075ustar00rootroot00000000000000mlt-6.20.0/src/modules/ndi/Makefile000066400000000000000000000014231362234133600170470ustar00rootroot00000000000000CFLAGS += -fPIC -I../.. -Wno-deprecated -Wno-multichar LDFLAGS += -L../../framework -lmlt -lpthread include ../../../config.mak include config.mak TARGET = ../libmltndi$(LIBSUF) OBJS = factory.o consumer_ndi.o producer_ndi.o SRCS := $(OBJS:.o=.c) LDFLAGS += $(LIBDL) 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/ndi" # install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/ndi" uninstall: rm -f "$(DESTDIR)$(moduledir)/libmltndi$(LIBSUF)" rm -rf "$(DESTDIR)$(mltdatadir)/ndi" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/ndi/configure000077500000000000000000000015241362234133600173200ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF NDI SDK paths: --ndi-sdk-include= - Path to include directory --ndi-sdk-lib= - Path to library directory EOF else echo > config.mak export ndi_sdk_include= export ndi_sdk_lib= for i in "$@" do case $i in --ndi-sdk-include=* ) ndi_sdk_include="${i#--ndi-sdk-include=}" ;; --ndi-sdk-lib=* ) ndi_sdk_lib="${i#--ndi-sdk-lib=}" ;; esac done if \ [ "$ndi_sdk_include" != "" ] && [ -f "$ndi_sdk_include/Processing.NDI.Lib.h" ] && \ [ "$ndi_sdk_lib" != "" ] && \ [ -e "$ndi_sdk_lib/libndi.so" ] then echo "- NDI SDK found: enabling it" rm -f ../disable-ndi echo "CFLAGS+=-I$ndi_sdk_include " >> config.mak echo "LDFLAGS+=-L$ndi_sdk_lib -lndi " >> config.mak else echo "- NDI SDK not found: disabling" touch ../disable-ndi fi fi exit 0 mlt-6.20.0/src/modules/ndi/consumer_ndi.c000066400000000000000000000226121362234133600202430ustar00rootroot00000000000000/* * consumer_ndi.c -- output through NewTek NDI * Copyright (C) 2016 Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_FORMAT_MACROS /* see inttypes.h */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include #include #include #include #include #include #include "factory.h" #include "Processing.NDI.Lib.h" typedef struct { struct mlt_consumer_s parent; int f_running, f_exit; char* arg; pthread_t th; int count; int sliced_swab; } consumer_ndi_t; static void* consumer_ndi_feeder( void* p ) { int i; mlt_frame last = NULL; mlt_consumer consumer = p; mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); consumer_ndi_t* self = ( consumer_ndi_t* )consumer->child; mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__ ); const NDIlib_send_create_t ndi_send_desc = { self->arg, NULL, true, false }; NDIlib_send_instance_t ndi_send = NDIlib_send_create( &ndi_send_desc ); if ( !ndi_send ) { mlt_log_error( MLT_CONSUMER_SERVICE( consumer ), "%s: NDIlib_send_create failed\n", __FUNCTION__ ); return NULL; } char* ndi_con_str = malloc(NDI_CON_STR_MAX); strncpy(ndi_con_str, "", NDI_CON_STR_MAX); const NDIlib_metadata_frame_t ndi_con_type = { // The length (int)strlen(ndi_con_str), // Timecode (synthesized for us !) NDIlib_send_timecode_synthesize, // The string (char*)ndi_con_str }; NDIlib_send_add_connection_metadata(ndi_send, &ndi_con_type); while ( !self->f_exit ) { mlt_frame frame = mlt_consumer_rt_frame( consumer ); if ( frame || last ) { mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) ); int m_isKeyer = 0, width = profile->width, height = profile->height; uint8_t* image = 0; mlt_frame frm = m_isKeyer ? frame : last; mlt_image_format format = 0 ? mlt_image_rgb24a : mlt_image_yuv422; int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frm ), "rendered" ); if ( rendered && !mlt_frame_get_image( frm, &image, &format, &width, &height, 0 ) ) { mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s:%d: width=%d, height=%d\n", __FUNCTION__, __LINE__, width, height ); uint8_t* buffer = 0; int64_t timecode; int stride = width * ( m_isKeyer? 4 : 2 ); int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" ); timecode = 10000000LL * (uint64_t)self->count * (uint64_t)profile->frame_rate_den; timecode = timecode / (uint64_t)profile->frame_rate_num; const NDIlib_video_frame_t ndi_video_frame = { // Resolution width, height, // Use YCbCr video m_isKeyer ? NDIlib_FourCC_type_BGRA : NDIlib_FourCC_type_UYVY, // The frame-eate profile->frame_rate_num, profile->frame_rate_den, // The aspect ratio (double)profile->display_aspect_num / profile->display_aspect_den, // This is a progressive frame progressive ? NDIlib_frame_format_type_progressive : NDIlib_frame_format_type_interleaved, // Timecode timecode, // The video memory used for this frame buffer = (uint8_t*)malloc( height * stride ), // The line to line stride of this image stride }; if ( !m_isKeyer ) { unsigned char *arg[3] = { image, buffer }; ssize_t size = stride * height; // Normal non-keyer playout - needs byte swapping if ( !self->sliced_swab ) swab2( arg[0], arg[1], size ); else { arg[2] = (unsigned char*)size; mlt_slices_run_fifo( 0, swab_sliced, arg); } } else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) ) { // Normal keyer output int y = height + 1; uint32_t* s = (uint32_t*) image; uint32_t* d = (uint32_t*) buffer; // Need to relocate alpha channel RGBA => ARGB while ( --y ) { int x = width + 1; while ( --x ) { *d++ = ( *s << 8 ) | ( *s >> 24 ); s++; } } } else { // Keying blank frames - nullify alpha memset( buffer, 0, stride * height ); } mlt_audio_format aformat = mlt_audio_s16; int frequency = 48000; int m_channels = 2; int samples = mlt_sample_calculator( mlt_profile_fps( profile ), frequency, self->count ); int16_t *pcm = 0; if ( !mlt_frame_get_audio( frm, (void**) &pcm, &aformat, &frequency, &m_channels, &samples ) ) { // Create an audio buffer const NDIlib_audio_frame_interleaved_16s_t audio_data = { // 48kHz frequency, // Lets submit stereo although there is nothing limiting us m_channels, // samples, // Timecode timecode, // reference_level 0, // The audio data, interleaved 16bpp pcm }; NDIlib_util_send_send_audio_interleaved_16s(ndi_send, &audio_data); } // We now submit the frame. NDIlib_send_send_video(ndi_send, &ndi_video_frame); // free buf free( ndi_video_frame.p_data ); self->count++; } } if ( frame ) { if ( last ) mlt_frame_close( last ); last = frame; } mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } NDIlib_send_destroy( ndi_send ); mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__ ); if ( last ) mlt_frame_close( last ); return NULL; } static int consumer_ndi_start( mlt_consumer consumer ) { int r = 0; mlt_log_debug( MLT_CONSUMER_SERVICE( consumer ), "%s: entering\n", __FUNCTION__ ); mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Set interlacing properties mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) ); mlt_properties_set_int( properties, "top_field_first", !profile->progressive ); consumer_ndi_t* self = ( consumer_ndi_t* )consumer->child; if ( !self->f_running ) { self->sliced_swab = mlt_properties_get_int( properties, "sliced_swab" ); // set flags self->f_exit = 0; pthread_create( &self->th, NULL, consumer_ndi_feeder, consumer ); // set flags self->f_running = 1; } mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__ ); return r; } static int consumer_ndi_stop( mlt_consumer consumer ) { int r = 0; mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__ ); consumer_ndi_t* self = ( consumer_ndi_t* )consumer->child; if ( self->f_running ) { // rise flags self->f_exit = 1; // wait for thread pthread_join( self->th, NULL ); // hide flags self->f_running = 0; } mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__ ); return r; } static int consumer_ndi_is_stopped( mlt_consumer consumer ) { consumer_ndi_t* self = ( consumer_ndi_t* )consumer->child; return !self->f_running; } static void consumer_ndi_close( mlt_consumer consumer ) { consumer_ndi_t* self = ( consumer_ndi_t* )consumer->child; mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__ ); // Stop the consumer mlt_consumer_stop( consumer ); // Close the parent consumer->close = NULL; mlt_consumer_close( consumer ); // free context if ( self->arg ) free( self->arg ); free( self ); mlt_log_debug( NULL, "%s: exiting\n", __FUNCTION__ ); } /** Initialise the consumer. */ mlt_consumer consumer_ndi_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Allocate the consumer consumer_ndi_t* self = ( consumer_ndi_t* )calloc( 1, sizeof( consumer_ndi_t ) ); mlt_log_debug( NULL, "%s: entering id=[%s], arg=[%s]\n", __FUNCTION__, id, arg ); // If allocated if ( self && !mlt_consumer_init( &self->parent, self, profile ) ) { mlt_consumer parent = &self->parent; // Setup context if( arg ) self->arg = strdup( arg ); // Setup callbacks parent->close = consumer_ndi_close; parent->start = consumer_ndi_start; parent->stop = consumer_ndi_stop; parent->is_stopped = consumer_ndi_is_stopped; mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); mlt_properties_set( properties, "deinterlace_method", "onefield" ); return parent; } free( self ); return NULL; } mlt-6.20.0/src/modules/ndi/consumer_ndi.yml000066400000000000000000000010441362234133600206160ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: ndi title: NDI Output version: 1.0 creator: Maksym Veremeyenko license: LGPLv2.1 language: en tags: - Audio - Video - Network description: Audio/Video over IP output using NewTek's NDI technology parameters: - identifier: argument type: string title: NDI Source name description: Name used for discovery by NDI receivers on the network required: yes mutable: no - identifier: meta.attr.* type: string title: NDI Device Metadata required: no mutable: no mlt-6.20.0/src/modules/ndi/factory.c000066400000000000000000000065401362234133600172270ustar00rootroot00000000000000/* * factory.c -- output through NewTek NDI * Copyright (C) 2016 Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_FORMAT_MACROS /* see inttypes.h */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS //#define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include "factory.h" #include "Processing.NDI.Lib.h" void swab2( const void *from, void *to, int n ) { #if defined(USE_SSE) #define SWAB_STEP 16 int cnt = n / SWAB_STEP; __asm__ volatile ( "loop_start: \n\t" /* load */ "movdqa 0(%[from]), %%xmm0 \n\t" "add $0x10, %[from] \n\t" /* duplicate to temp registers */ "movdqa %%xmm0, %%xmm1 \n\t" /* shift right temp register */ "psrlw $8, %%xmm1 \n\t" /* shift left main register */ "psllw $8, %%xmm0 \n\t" /* compose them back */ "por %%xmm0, %%xmm1 \n\t" /* save */ "movdqa %%xmm1, 0(%[to]) \n\t" "add $0x10, %[to] \n\t" "dec %[cnt] \n\t" "jnz loop_start \n\t" : [from]"+r"(from), [to]"+r"(to), [cnt]"+r"(cnt) : : "xmm0", "xmm1" ); from = (unsigned char*) from + n - (n % SWAB_STEP); to = (unsigned char*) to + n - (n % SWAB_STEP); n = (n % SWAB_STEP); #endif swab((char*) from, (char*) to, n); }; #define SWAB_SLICED_ALIGN_POW 5 int swab_sliced( int id, int idx, int jobs, void* cookie ) { unsigned char** args = (unsigned char**)cookie; ssize_t sz = (ssize_t)args[2]; ssize_t bsz = ( ( sz / jobs + ( 1 << SWAB_SLICED_ALIGN_POW ) - 1 ) >> SWAB_SLICED_ALIGN_POW ) << SWAB_SLICED_ALIGN_POW; ssize_t offset = bsz * idx; if ( offset < sz ) { if ( ( offset + bsz ) > sz ) bsz = sz - offset; swab2( args[0] + offset, args[1] + offset, bsz ); } return 0; }; static void *create_service( mlt_profile profile, mlt_service_type type, const char *id, void *arg ) { mlt_log_debug( NULL, "%s: entering id=[%s]\n", __FUNCTION__, id ); if ( !NDIlib_initialize() ) { mlt_log_error( NULL, "%s: NDIlib_initialize failed\n", __FUNCTION__ ); return NULL; } if ( type == producer_type ) return producer_ndi_init( profile, type, id, (char*)arg ); else if ( type == consumer_type ) return consumer_ndi_init( profile, type, id, (char*)arg ); return NULL; } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "ndi", create_service ); MLT_REGISTER( producer_type, "ndi", create_service ); } mlt-6.20.0/src/modules/ndi/factory.h000066400000000000000000000024251362234133600172320ustar00rootroot00000000000000/* * factory.h -- output through NewTek NDI * Copyright (C) 2016 Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef FACTORY_H #define FACTORY_H #include #define NDI_CON_STR_MAX 32768 mlt_producer producer_ndi_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); mlt_consumer consumer_ndi_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); int swab_sliced( int id, int idx, int jobs, void* cookie ); void swab2( const void *from, void *to, int n ); #endif /* FACTORY_H */ mlt-6.20.0/src/modules/ndi/producer_ndi.c000066400000000000000000000443401362234133600202350ustar00rootroot00000000000000/* * producer_ndi.c -- output through NewTek NDI * Copyright (C) 2016 Maksym Veremeyenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __STDC_FORMAT_MACROS /* see inttypes.h */ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include #include #include #include #include #include #include #include #include #include #include "Processing.NDI.Lib.h" #include "factory.h" #define NDI_TIMEBASE 10000000LL typedef struct { mlt_producer parent; int f_running, f_exit, f_timeout; char* arg; pthread_t th; int count; mlt_deque a_queue, v_queue; pthread_mutex_t lock; pthread_cond_t cond; NDIlib_recv_instance_t recv; int v_queue_limit, a_queue_limit, v_prefill; } producer_ndi_t; static void* producer_ndi_feeder( void* p ) { mlt_producer producer = p; int ndi_src_idx; const NDIlib_source_t* ndi_srcs = NULL; NDIlib_video_frame_t* video = NULL; NDIlib_audio_frame_t audio; NDIlib_metadata_frame_t meta; producer_ndi_t* self = ( producer_ndi_t* )producer->child; mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "%s: entering\n", __FUNCTION__ ); // find the source const NDIlib_find_create_t find_create_desc = { .show_local_sources = true, .p_groups = NULL, .p_extra_ips = NULL }; // create a finder NDIlib_find_instance_t ndi_find = NDIlib_find_create2( &find_create_desc ); if ( !ndi_find ) { mlt_log_error( MLT_PRODUCER_SERVICE( producer ), "%s: NDIlib_find_create failed\n", __FUNCTION__ ); return NULL; } // wait for source mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "%s: waiting for sources, searching for [%s]\n", __FUNCTION__, self->arg ); for ( ndi_src_idx = -1; ndi_src_idx == -1 && !self->f_exit; ) { unsigned int j, f, n; // wait sources list changed f = NDIlib_find_wait_for_sources( ndi_find, 500 ); mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "%s: NDIlib_find_wait_for_sources=%d\n", __FUNCTION__, f ); if ( !f ) { mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "%s: no more sources\n", __FUNCTION__ ); break; } // check if request present in sources list ndi_srcs = NDIlib_find_get_current_sources( ndi_find, &n ); mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "%s: found %d sources\n", __FUNCTION__, n ); for ( j = 0; j < n && ndi_src_idx == -1; j++ ) if ( !strcmp( self->arg, ndi_srcs[j].p_ndi_name ) ) ndi_src_idx = j; } // exit if nothing if ( self->f_exit || ndi_src_idx == -1 ) { mlt_log_error( MLT_PRODUCER_SERVICE( producer ), "%s: exiting, self->f_exit=%d\n", __FUNCTION__, self->f_exit ); NDIlib_find_destroy( ndi_find ); return NULL; } mlt_log_debug( MLT_PRODUCER_SERVICE( producer ), "%s: source [%s] found\n", __FUNCTION__, self->arg ); // create receiver description NDIlib_recv_create_t recv_create_desc = { ndi_srcs[ ndi_src_idx ], NDIlib_recv_color_format_e_UYVY_RGBA, NDIlib_recv_bandwidth_highest, true }; // Create the receiver self->recv = NDIlib_recv_create2( &recv_create_desc ); NDIlib_find_destroy( ndi_find ); if ( !self->recv ) { mlt_log_error( MLT_PRODUCER_SERVICE( producer ), "%s: exiting, NDIlib_recv_create2 failed\n", __FUNCTION__ ); return 0; } // set tally const NDIlib_tally_t tally_state = { true, false }; NDIlib_recv_set_tally( self->recv, &tally_state ); while( !self->f_exit ) { NDIlib_frame_type_e t; NDIlib_audio_frame_interleaved_16s_t *audio_packet, *audio_packet_prev; if ( !video ) video = mlt_pool_alloc( sizeof( NDIlib_video_frame_t ) ); t = NDIlib_recv_capture(self->recv, video, &audio, &meta, 10 ); mlt_log_debug( NULL, "%s:%d: NDIlib_recv_capture=%d\n", __FILE__, __LINE__, t ); switch( t ) { case NDIlib_frame_type_none: break; case NDIlib_frame_type_video: pthread_mutex_lock( &self->lock ); mlt_deque_push_back( self->v_queue, video ); if ( mlt_deque_count( self->v_queue ) >= self->v_queue_limit ) { video = mlt_deque_pop_front( self->v_queue ); NDIlib_recv_free_video( self->recv, video ); } else video = NULL; pthread_cond_broadcast( &self->cond ); pthread_mutex_unlock( &self->lock ); break; case NDIlib_frame_type_audio: // convert to 16s interleaved audio_packet = mlt_pool_alloc( sizeof( NDIlib_audio_frame_interleaved_16s_t ) ); audio_packet->reference_level = 0; audio_packet->p_data = mlt_pool_alloc( 2 * audio.no_channels * audio.no_samples ); NDIlib_util_audio_to_interleaved_16s( &audio, audio_packet ); NDIlib_recv_free_audio( self->recv, &audio ); // store into queue pthread_mutex_lock( &self->lock ); // check if it continues prev packet audio_packet_prev = mlt_deque_pop_back( self->a_queue ); if ( audio_packet_prev ) { int64_t n = audio_packet_prev->timecode + NDI_TIMEBASE * audio_packet_prev->no_samples / audio_packet_prev->sample_rate, d = audio_packet->timecode - n; if ( d && llabs( d ) < 2 ) { mlt_log_debug( NULL, "%s:%d: audio_packet_prev->timecode=%"PRId64", audio_packet->timecode=%"PRId64", n=%"PRId64", d=%"PRId64", audio_packet_prev->no_samples=%d\n", __FILE__, __LINE__, audio_packet_prev->timecode, audio_packet->timecode, n ,d, audio_packet_prev->no_samples ); audio_packet_prev->p_data = mlt_pool_realloc( audio_packet_prev->p_data, ( audio_packet_prev->no_samples + audio_packet->no_samples ) * audio_packet->no_channels * 2); memcpy ( (unsigned char*)audio_packet_prev->p_data + 2 * audio_packet_prev->no_channels * audio_packet_prev->no_samples, audio_packet->p_data, 2 * audio_packet->no_channels * audio_packet->no_samples ); audio_packet_prev->no_samples += audio_packet->no_samples; mlt_pool_release( audio_packet->p_data ); mlt_pool_release( audio_packet ); audio_packet = NULL; } mlt_deque_push_back( self->a_queue, audio_packet_prev ); } if ( audio_packet ) mlt_deque_push_back( self->a_queue, audio_packet ); if ( mlt_deque_count( self->a_queue ) >= self->a_queue_limit ) { audio_packet = mlt_deque_pop_front( self->a_queue ); mlt_pool_release( audio_packet->p_data ); mlt_pool_release( audio_packet ); } pthread_cond_broadcast( &self->cond ); pthread_mutex_unlock( &self->lock ); break; case NDIlib_frame_type_metadata: NDIlib_recv_free_metadata( self->recv, &meta ); break; case NDIlib_frame_type_error: mlt_log_error( MLT_PRODUCER_SERVICE( producer ), "%s: NDIlib_recv_capture failed\n", __FUNCTION__ ); break; } } if ( video ) mlt_pool_release( video ); mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "%s: exiting\n", __FUNCTION__ ); return NULL; } static int get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_properties fprops = MLT_FRAME_PROPERTIES( frame ); NDIlib_recv_instance_t recv = mlt_properties_get_data( fprops, "ndi_recv", NULL ); NDIlib_audio_frame_interleaved_16s_t* audio = mlt_properties_get_data( fprops, "ndi_audio", NULL ); mlt_log_debug( NULL, "%s:%d: recv=%p, audio=%p\n", __FILE__, __LINE__, recv, audio ); if ( recv && audio ) { size_t size; *format = mlt_audio_s16; *frequency = audio->sample_rate; *channels = audio->no_channels; *samples = audio->no_samples; size = 2 * ( *samples ) * ( *channels ); *buffer = audio->p_data; mlt_frame_set_audio( frame, (uint8_t*) *buffer, *format, size, NULL ); } return 0; } static int get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { mlt_properties fprops = MLT_FRAME_PROPERTIES( frame ); NDIlib_recv_instance_t recv = mlt_properties_get_data( fprops, "ndi_recv", NULL ); NDIlib_video_frame_t* video = mlt_properties_get_data( fprops, "ndi_video", NULL ); mlt_log_debug( NULL, "%s:%d: recv=%p, video=%p\n", __FILE__, __LINE__, recv, video ); if ( recv && video ) { uint8_t *dst = NULL; size_t size; int j, dst_stride = 0, stride; *width = video->xres; *height = video->yres; if ( NDIlib_FourCC_type_UYVY == video->FourCC || NDIlib_FourCC_type_UYVA == video->FourCC ) { dst_stride = 2 * video->xres; *format = mlt_image_yuv422; } else if ( NDIlib_FourCC_type_RGBA == video->FourCC || NDIlib_FourCC_type_RGBX == video->FourCC ) { dst_stride = 4 * video->xres; *format = mlt_image_rgb24a; } else *format = mlt_image_none; size = mlt_image_format_size( *format, *width, *height, NULL ); dst = mlt_pool_alloc( size ); stride = ( dst_stride > video->line_stride_in_bytes ) ? video->line_stride_in_bytes : dst_stride; mlt_log_debug( NULL, "%s: stride=%d\n", __FUNCTION__, stride ); if ( NDIlib_FourCC_type_UYVY == video->FourCC || NDIlib_FourCC_type_UYVA == video->FourCC ) for( j = 0; j < video->yres; j++) swab2( video->p_data + j * video->line_stride_in_bytes, dst + j * dst_stride, stride ); else if ( NDIlib_FourCC_type_RGBA == video->FourCC || NDIlib_FourCC_type_RGBX == video->FourCC ) for( j = 0; j < video->yres; j++) memcpy( dst + j * dst_stride, video->p_data + j * video->line_stride_in_bytes, stride ); if ( dst ) { mlt_frame_set_image( frame, (uint8_t*) dst, size, (mlt_destructor)mlt_pool_release ); *buffer = dst; } if ( NDIlib_FourCC_type_UYVA == video->FourCC ) { uint8_t *src = video->p_data + (*height) * video->line_stride_in_bytes; size = (*width) * (*height); dst = mlt_pool_alloc( size ); dst_stride = *width; stride = ( dst_stride > ( video->line_stride_in_bytes / 2) ) ? ( video->line_stride_in_bytes / 2 ) : dst_stride; for( j = 0; j < video->yres; j++) memcpy( dst + j * dst_stride, src + j * ( video->line_stride_in_bytes / 2 ), stride ); mlt_frame_set_alpha( frame, (uint8_t*) dst, size, (mlt_destructor)mlt_pool_release ); } mlt_properties_set_int( fprops, "progressive", ( video->frame_format_type == NDIlib_frame_format_type_progressive ) ); mlt_properties_set_int( fprops, "top_field_first", ( video->frame_format_type == NDIlib_frame_format_type_interleaved ) ); NDIlib_recv_free_video( recv, video ); } return 0; } static int get_frame( mlt_producer producer, mlt_frame_ptr pframe, int index ) { mlt_frame frame = NULL; NDIlib_audio_frame_interleaved_16s_t* audio_frame = NULL; NDIlib_video_frame_t* video = NULL; double fps = mlt_producer_get_fps( producer ); mlt_position position = mlt_producer_position( producer ); producer_ndi_t* self = ( producer_ndi_t* )producer->child; pthread_mutex_lock( &self->lock ); mlt_log_debug( NULL, "%s:%d: entering %s\n", __FILE__, __LINE__, __FUNCTION__ ); // run thread if ( !self->f_running ) { // set flags self->f_exit = 0; pthread_create( &self->th, NULL, producer_ndi_feeder, producer ); // set flags self->f_running = 1; } mlt_log_debug( NULL, "%s:%d: audio_cnt=%d, video_cnt=%d\n", __FILE__, __LINE__, mlt_deque_count( self->a_queue ), mlt_deque_count( self->v_queue )); // wait for prefill if ( mlt_deque_count( self->v_queue ) < self->v_prefill ) { struct timespec tm; // Wait clock_gettime(CLOCK_REALTIME, &tm); tm.tv_nsec += self->v_prefill * 1000000000LL / fps; tm.tv_sec += tm.tv_nsec / 1000000000LL; tm.tv_nsec %= 1000000000LL; pthread_cond_timedwait( &self->cond, &self->lock, &tm ); } // pop frame to use if ( mlt_deque_count( self->v_queue ) >= self->v_prefill ) video = (NDIlib_video_frame_t*)mlt_deque_pop_front( self->v_queue ); if ( video ) { int64_t video_timecode_out, video_dur; mlt_log_debug( NULL, "%s:%d: video: timecode=%"PRId64"\n", __FILE__, __LINE__, video->timecode ); video_dur = NDI_TIMEBASE * video->frame_rate_D / video->frame_rate_N; video_timecode_out = video->timecode + video_dur; // deal with audio while ( 1 ) { NDIlib_audio_frame_interleaved_16s_t* audio_packet; int64_t audio_packet_dur, audio_packet_timecode_out, T1, T2, dst_offset, src_offset, duration; // pop audio packet audio_packet = ( NDIlib_audio_frame_interleaved_16s_t* )mlt_deque_pop_front( self->a_queue ); // check if audio present if ( !audio_packet ) break; // check if packet in a future if ( video_timecode_out < audio_packet->timecode) { // push it back to queue mlt_deque_push_front( self->a_queue, audio_packet ); break; } // calc packet audio_packet_dur = NDI_TIMEBASE * audio_packet->no_samples / audio_packet->sample_rate; audio_packet_timecode_out = audio_packet_dur + audio_packet->timecode; // check if packet in the past if ( audio_packet_timecode_out < video->timecode ) { mlt_pool_release( audio_packet->p_data ); mlt_pool_release( audio_packet ); continue; } // allocate new audio frame if ( !audio_frame ) { audio_frame = ( NDIlib_audio_frame_interleaved_16s_t* )mlt_pool_alloc ( sizeof( NDIlib_audio_frame_interleaved_16s_t ) ); audio_frame->timecode = video->timecode; audio_frame->no_channels = audio_packet->no_channels; audio_frame->sample_rate = audio_packet->sample_rate; audio_frame->no_samples = audio_packet->sample_rate * video->frame_rate_D / video->frame_rate_N; audio_frame->p_data = ( short* )mlt_pool_alloc( 2 * audio_frame->no_samples * audio_frame->no_channels ); } // copy data of overlapping region T1 = MAX( audio_packet->timecode, video->timecode ); T2 = MIN( audio_packet_timecode_out, video_timecode_out ); dst_offset = ( T1 - audio_frame->timecode ) * audio_frame->sample_rate / NDI_TIMEBASE; src_offset = ( T1 - audio_packet->timecode ) * audio_packet->sample_rate / NDI_TIMEBASE; duration = ( T2 - T1 ) * audio_frame->sample_rate / NDI_TIMEBASE;; memcpy ( (unsigned char*)audio_frame->p_data + 2 * audio_frame->no_channels * dst_offset, (unsigned char*)audio_packet->p_data + 2 * audio_packet->no_channels * src_offset, 2 * audio_packet->no_channels * duration ); // save packet back if it will be used later or clear it if ( video_timecode_out < audio_packet_timecode_out ) { // push it back to queue mlt_deque_push_front( self->a_queue, audio_packet ); break; } // free packet data mlt_pool_release( audio_packet->p_data ); mlt_pool_release( audio_packet ); } } pthread_mutex_unlock( &self->lock ); *pframe = frame = mlt_frame_init( MLT_PRODUCER_SERVICE(producer) ); if ( frame ) { mlt_properties p = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set_data( p, "ndi_recv", (void *)self->recv, 0, NULL, NULL ); if ( video ) { mlt_properties_set_data( p, "ndi_video", (void *)video, 0, mlt_pool_release, NULL ); mlt_frame_push_get_image( frame, get_image ); } else mlt_log_error( NULL, "%s:%d: NO VIDEO\n", __FILE__, __LINE__ ); if ( audio_frame ) { mlt_properties_set_data( p, "ndi_audio", (void *)audio_frame, 0, mlt_pool_release, NULL ); mlt_properties_set_data( p, "ndi_audio_data", (void *)audio_frame->p_data, 0, mlt_pool_release, NULL ); mlt_frame_push_audio( frame, (void*) get_audio ); } self->f_timeout = 0; } mlt_frame_set_position( frame, position ); // Calculate the next timecode mlt_producer_prepare_next( producer ); return 0; } static void producer_ndi_close( mlt_producer producer ) { producer_ndi_t* self = ( producer_ndi_t* )producer->child; mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "%s: entering\n", __FUNCTION__ ); if ( self->f_running ) { // rise flags self->f_exit = 1; // signal threads pthread_cond_broadcast( &self->cond ); // wait for thread pthread_join( self->th, NULL ); // hide flags self->f_running = 0; // dequeue audio frames while( mlt_deque_count( self->a_queue ) ) { NDIlib_audio_frame_interleaved_16s_t* audio = (NDIlib_audio_frame_interleaved_16s_t*)mlt_deque_pop_front( self->a_queue ); mlt_pool_release( audio->p_data ); mlt_pool_release( audio ); } // dequeue video frames while( mlt_deque_count( self->v_queue ) ) { NDIlib_video_frame_t* video = (NDIlib_video_frame_t*)mlt_deque_pop_front( self->v_queue ); NDIlib_recv_free_video( self->recv, video ); mlt_pool_release( video ); } // close receiver NDIlib_recv_destroy( self->recv ); } mlt_deque_close( self->a_queue ); mlt_deque_close( self->v_queue ); pthread_mutex_destroy( &self->lock ); pthread_cond_destroy( &self->cond ); free( producer->child ); producer->close = NULL; mlt_producer_close( producer ); mlt_log_debug( NULL, "%s: exiting\n", __FUNCTION__ ); } /** Initialise the producer. */ mlt_producer producer_ndi_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Allocate the producer producer_ndi_t* self = ( producer_ndi_t* )calloc( 1, sizeof( producer_ndi_t ) ); mlt_producer parent = ( mlt_producer )calloc( 1, sizeof( *parent ) ); mlt_log_debug( NULL, "%s: entering id=[%s], arg=[%s]\n", __FUNCTION__, id, arg ); // If allocated if ( self && !mlt_producer_init( parent, self ) ) { self->parent = parent; mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); // Setup context self->arg = strdup( arg ); pthread_mutex_init( &self->lock, NULL ); pthread_cond_init( &self->cond, NULL ); self->v_queue = mlt_deque_init(); self->a_queue = mlt_deque_init(); self->v_queue_limit = 6; self->a_queue_limit = 6; self->v_prefill = 2; // Set callbacks parent->close = (mlt_destructor) producer_ndi_close; parent->get_frame = get_frame; // These properties effectively make it infinite. mlt_properties_set_int( properties, "length", INT_MAX ); mlt_properties_set_int( properties, "out", INT_MAX - 1 ); mlt_properties_set( properties, "eof", "loop" ); return parent; } free( self ); return NULL; } mlt-6.20.0/src/modules/ndi/producer_ndi.yml000066400000000000000000000006361362234133600206140ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: ndi title: NDI Input version: 1.0 creator: Maksym Veremeyenko license: LGPLv2.1 language: en tags: - Audio - Video - Network description: Audio/Video over IP input using NewTek's NDI technology parameters: - identifier: argument type: string title: NDI Source name description: Name of the NDI Source to receive required: yes mutable: no mlt-6.20.0/src/modules/normalize/000077500000000000000000000000001362234133600166355ustar00rootroot00000000000000mlt-6.20.0/src/modules/normalize/CMakeLists.txt000066400000000000000000000005231362234133600213750ustar00rootroot00000000000000if(GPL) file(GLOB mltnormalize_src *.c) add_library(mltnormalize MODULE ${mltnormalize_src}) target_link_libraries(mltnormalize mlt m) install(TARGETS mltnormalize LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/normalize) endif() mlt-6.20.0/src/modules/normalize/Makefile000066400000000000000000000012001362234133600202660ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lm include ../../../config.mak TARGET = ../libmltnormalize$(LIBSUF) OBJS = factory.o \ filter_audiolevel.o \ filter_volume.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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/normalize" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/normalize" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/normalize/factory.c000066400000000000000000000032151362234133600204510ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include extern mlt_filter filter_audiolevel_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_volume_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/normalize/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( filter_type, "audiolevel", filter_audiolevel_init ); MLT_REGISTER( filter_type, "volume", filter_volume_init ); MLT_REGISTER_METADATA( filter_type, "audiolevel", metadata, "filter_audiolevel.yml" ); MLT_REGISTER_METADATA( filter_type, "volume", metadata, "filter_volume.yml" ); } mlt-6.20.0/src/modules/normalize/filter_audiolevel.c000066400000000000000000000104311362234133600224760ustar00rootroot00000000000000/* * filter_audiolevel.c -- get the audio level of each channel * Copyright (C) 2002 Steve Harris * Copyright (C) 2010 Marco Gittler * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #define AMPTODBFS(n) (log10(n) * 20.0) //---------------------------------------------------------------------------- // IEC standard dB scaling -- as borrowed from meterbridge (c) Steve Harris static inline double IEC_Scale(double dB) { double fScale = 1.0f; if (dB < -70.0f) fScale = 0.0f; else if (dB < -60.0f) // 0.0 .. 2.5 fScale = (dB + 70.0f) * 0.0025f; else if (dB < -50.0f) // 2.5 .. 7.5 fScale = (dB + 60.0f) * 0.005f + 0.025f; else if (dB < -40.0) // 7.5 .. 15.0 fScale = (dB + 50.0f) * 0.0075f + 0.075f; else if (dB < -30.0f) // 15.0 .. 30.0 fScale = (dB + 40.0f) * 0.015f + 0.15f; else if (dB < -20.0f) // 30.0 .. 50.0 fScale = (dB + 30.0f) * 0.02f + 0.3f; else if (dB < -0.001f || dB > 0.001f) // 50.0 .. 100.0 fScale = (dB + 20.0f) * 0.025f + 0.5f; return fScale; } static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_filter filter = mlt_frame_pop_audio( frame ); // Get the properties from the filter mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); int iec_scale = mlt_properties_get_int( filter_props, "iec_scale" ); *format = mlt_audio_s16; int error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); if ( error || !buffer ) return error; int num_channels = *channels; int num_samples = *samples > 200 ? 200 : *samples; int num_oversample = 0; int c, s; char key[ 50 ]; int16_t *pcm = (int16_t*) *buffer; for ( c = 0; c < *channels; c++ ) { double val = 0; double level = 0.0; for ( s = 0; s < num_samples; s++ ) { double sample = fabs( pcm[c + s * num_channels] / 128.0 ); val += sample; if ( sample == 128 ) num_oversample++; else num_oversample = 0; // 10 samples @max => show max signal if ( num_oversample > 10 ) { level = 1.0; break; } // if 3 samples over max => 1 peak over 0 db (0 dB = 40.0) if ( num_oversample > 3 ) level = 41.0/42.0; } // max amplitude = 40/42, 3to10 oversamples=41, more then 10 oversamples=42 if ( level == 0.0 && num_samples > 0 ) level = val / num_samples * 40.0/42.0 / 127.0; if ( iec_scale ) level = IEC_Scale( AMPTODBFS( level ) ); sprintf( key, "meta.media.audio_level.%d", c ); mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), key, level ); sprintf( key, "_audio_level.%d", c ); mlt_properties_set_double( filter_props, key, level ); mlt_log_debug( MLT_FILTER_SERVICE( filter ), "channel %d level %f\n", c, level ); } mlt_properties_set_position( filter_props, "_position", mlt_filter_get_position( filter, frame ) ); return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; } /** Constructor for the filter. */ mlt_filter filter_audiolevel_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); if ( filter ) { filter->process = filter_process; mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "iec_scale", 1 ); } return filter; } mlt-6.20.0/src/modules/normalize/filter_audiolevel.yml000066400000000000000000000017671362234133600230710ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: audiolevel title: Audio Levels version: 1 copyright: Dan Dennedy, Marco Gittler, and Steve Harris creator: Dan Dennedy contributor: - Marco Gittler - Steve Harris license: GPLv2 language: en description: Compute the audio amplitude. notes: > This filter provides the amplitude level as a percentage value in floating point. This does not do any "slowing" of the data by averaging out peaks and troughs of short duration like a VU meter. Applications can also get this data on the frame as meta.media.audio_level. where is the channel number starting with 0. tags: - Audio parameters: - identifier: iec_scale title: Use IEC 60268-18 Scale type: integer minimum: 0 maximum: 1 default: 1 widget: checkbox - identifier: _audio_level. description: > is the channel number starting with 0. This is updated on every frame with audio. readonly: yes type: float minimum: 0 maximum: 1 mlt-6.20.0/src/modules/normalize/filter_volume.c000066400000000000000000000312271362234133600216620ustar00rootroot00000000000000/* * filter_volume.c -- adjust audio volume * Copyright (C) 2003-2016 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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; 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++; } } if (j) smoothed /= j; 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, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Get the filter from the frame mlt_filter filter = mlt_frame_pop_audio( frame ); // Get the properties from the filter mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); // Get the frame's filter instance properties mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( filter ) ); // Get the parameters double gain = mlt_properties_get_double( instance_props, "gain" ); double max_gain = mlt_properties_get_double( instance_props, "max_gain" ); double limiter_level = 0.5; /* -6 dBFS */ int normalise = mlt_properties_get_int( instance_props, "normalise" ); double amplitude = mlt_properties_get_double( instance_props, "amplitude" ); int i, j; double sample; int16_t peak; // Use animated value for gain if "level" property is set char* level_property = mlt_properties_get( filter_props, "level" ); if ( level_property != NULL ) { mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); gain = mlt_properties_anim_get_double( filter_props, "level", position, length ); gain = DBFSTOAMP( gain ); } if ( mlt_properties_get( instance_props, "limiter" ) != NULL ) limiter_level = mlt_properties_get_double( instance_props, "limiter" ); // Get the producer's audio *format = normalise? mlt_audio_s16 : mlt_audio_f32le; mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); 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 ); 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 gain *= amplitude / get_smoothed_data( smooth_buffer, window ); } } else { gain *= amplitude / signal_max_power( *buffer, *channels, *samples, &peak ); } } if ( max_gain > 0 && gain > max_gain ) gain = max_gain; // Initialise filter's previous gain value to prevent an inadvertent 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; // 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 ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Ramp from the previous gain to the current gain = previous_gain; // Apply the gain if ( normalise ) { int16_t *p = *buffer; // Determine numeric limits int bytes_per_samp = (samp_width - 1) / 8 + 1; int samplemax = (1 << (bytes_per_samp * 8 - 1)) - 1; for ( i = 0; i < *samples; i++, gain += gain_step ) { for ( j = 0; j < *channels; j++ ) { sample = *p * gain; *p = ROUND( sample ); if ( gain > 1.0 && normalise ) { /* use limiter function instead of clipping */ *p = ROUND( samplemax * limiter( sample / (double) samplemax, limiter_level ) ); } p++; } } } else { float *p = *buffer; for ( i = 0; i < *samples; i++, gain += gain_step ) { for ( j = 0; j < *channels; j++, p++ ) { p[0] *= gain; } } } return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ); mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( filter ) ); double gain = 1.0; // no adjustment char *gain_str = mlt_properties_get( filter_props, "gain" ); // Parse the gain property if ( gain_str ) { char *p_orig = strdup( gain_str ); char *p = p_orig; if ( strncaseeq( p, "normalise", 9 ) ) mlt_properties_set( filter_props, "normalise", "" ); else { if ( strcmp( p, "" ) != 0 ) gain = strtod( p, &p ); while ( isspace( *p ) ) p++; /* check if "dB" is given after number */ if ( strncaseeq( p, "db", 2 ) ) gain = DBFSTOAMP( gain ); else gain = fabs( gain ); // If there is an end adjust gain to the range if ( mlt_properties_get( filter_props, "end" ) != NULL ) { double end = -1; char *p = mlt_properties_get( filter_props, "end" ); if ( strcmp( p, "" ) != 0 ) end = strtod( p, &p ); while ( isspace( *p ) ) p++; /* check if "dB" is given after number */ if ( strncaseeq( p, "db", 2 ) ) end = DBFSTOAMP( end ); else end = fabs( end ); if ( end != -1 ) gain += ( end - gain ) * mlt_filter_get_progress( filter, frame ); } } free( p_orig ); } mlt_properties_set_double( instance_props, "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 = 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 ); else gain = fabs( gain ); mlt_properties_set_double( instance_props, "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( instance_props, "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 ) { amplitude *= mlt_filter_get_progress( filter, frame ); } mlt_properties_set_int( instance_props, "normalise", 1 ); mlt_properties_set_double( instance_props, "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 ); } // Push the filter onto the stack mlt_frame_push_audio( frame, filter ); // 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 filter = calloc( 1, sizeof( struct mlt_filter_s ) ); if ( filter != NULL && mlt_filter_init( filter, NULL ) == 0 ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); filter->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" ); mlt_properties_set( properties, "level", NULL ); } return filter; } mlt-6.20.0/src/modules/normalize/filter_volume.yml000066400000000000000000000052701362234133600222400ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: volume title: Volume version: 2 copyright: Meltytech, LLC creator: Dan Denneedy license: GPLv2 language: en tags: - Audio description: > Adjust an audio stream's volume level. This filter is based on the 'normalize' utility parameters: - identifier: argument title: Gain type: string description: > The gain may be indicated as a floating point value of the gain adjustment. The gain may also be indicated as a numeric value with the suffix "dB" to adjust in terms of decibels. The gain may also be set to "normalise" to normalise the volume to the target amplitude -12dBFS. This value is discarded if value for property "level" is set. - identifier: window title: Window type: integer description: > The number of video frames over which to smooth normalisation. default: 75 mutable: yes - identifier: normalise title: Normalise type: string description: > Normalise the volume to the specified amplitude. The normalization may be indicated as a floating point value of the relative volume. The normalisation may also be indicated as a numeric value with the suffix "dB" to set the amplitude in decibels. default: -12dBFS mutable: yes - identifier: limiter title: Limiter type: string description: > Limit all samples above the specified amplitude. The limiting may be indicated as a floating point value of the relative volume. The limiting may also be indicated as a numeric value with the suffix "dB" to set the limiting amplitude in decibels. default: -6dBFS mutable: yes - identifier: max_gain title: Max gain type: string description: > A floating point or decibel value of the maximum gain that can be applied during normalisation. default: 20dB mutable: yes - identifier: end title: End gain type: string description: > A gain value just like the Gain property. This causes the gain to be interpolated from 'gain' to 'end' over the duration. This value is discarded if value for property "level" is set. mutable: yes - identifier: max_gain title: Max gain type: string description: > A floating point or decibel value of the maximum gain that can be applied during normalisation. default: 20dB mutable: yes - identifier: level title: Level type: float description: > The animated value of the gain adjustment in dB. Properties "gain" and "end" are ignored if this is set. unit: dB mutable: yes mlt-6.20.0/src/modules/normalize/gpl000066400000000000000000000000001362234133600173300ustar00rootroot00000000000000mlt-6.20.0/src/modules/oldfilm/000077500000000000000000000000001362234133600162635ustar00rootroot00000000000000mlt-6.20.0/src/modules/oldfilm/CMakeLists.txt000066400000000000000000000004371362234133600210270ustar00rootroot00000000000000file(GLOB mltoldfilm_src *.c) add_library(mltoldfilm MODULE ${mltoldfilm_src}) target_link_libraries(mltoldfilm mlt m) install(TARGETS mltoldfilm LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/oldfilm) mlt-6.20.0/src/modules/oldfilm/Makefile000066400000000000000000000014051362234133600177230ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lm 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 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/oldfilm" install -m 644 *.svg "$(DESTDIR)$(mltdatadir)/oldfilm" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/oldfilm" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/oldfilm/dust1.svg000066400000000000000000000124011362234133600200420ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/dust2.svg000066400000000000000000000525621362234133600200570ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/dust3.svg000066400000000000000000000047151362234133600200550ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/dust4.svg000066400000000000000000000074141362234133600200550ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/dust5.svg000066400000000000000000000046171362234133600200600ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/factory.c000066400000000000000000000051521362234133600201010ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 ); } mlt-6.20.0/src/modules/oldfilm/fdust.svg000066400000000000000000000107071362234133600201360ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/filter_dust.c000066400000000000000000000172741362234133600207660ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #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 < src_height; y++) { if ( y >= 0 && (y - ypos) < overlay_height ) { uint8_t *scanline_image = src + src_width * y * 2; int overlay_y = upsidedown ? ( overlay_height - ( y - ypos ) - 1 ) : ( y - ypos ); uint8_t *scanline_overlay=overlay + overlay_width * 2 * overlay_y; for ( x = xpos ; x < src_width && x - xpos < overlay_width ;x++ ) { if ( x > 0 ) { int overlay_x = mirror ? overlay_width - ( x - xpos ) -1 : ( x - xpos ); double alp = (double) * (alpha + overlay_width * overlay_y + overlay_x ) / 255.0; uint8_t* image_pixel = scanline_image + x * 2; uint8_t* overlay_pixel = scanline_overlay + overlay_x * 2; *image_pixel = (double)(*overlay_pixel) * alp + (double) *image_pixel * (1.0-alp) ; if ( xpos % 2 == 0 ) image_pixel++; else image_pixel += 3; mirror ? overlay_pixel-- : overlay_pixel++; *image_pixel = (double) (*(overlay_pixel)) * alp + (double)(*image_pixel) * ( 1.0 - alp ); } } } } } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position pos = mlt_filter_get_position( filter, frame ); mlt_position len = mlt_filter_get_length2( filter, frame ); int maxdia = mlt_properties_anim_get_int( properties, "maxdiameter", pos, len ); int maxcount = mlt_properties_anim_get_int( properties, "maxcount", pos, len ); *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Load svg 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; double position = mlt_filter_get_progress( filter, frame ); srand( position * 10000 ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); 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_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, luma_width * luma_height * 2, mlt_pool_release, NULL ); mlt_properties_set_data( properties, savename1, savealpha, luma_width * luma_height, 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 ); } else { if ( savealpha ) mlt_pool_release( savealpha ); if ( savepic ) mlt_pool_release( savepic ); } 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 ); } } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); if (piccount>0 ) return 0; if ( error == 0 && *image ) { int h = *height; int w = *width; 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; 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=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 filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "maxdiameter", "2" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "maxcount", "10" ); } return filter; } mlt-6.20.0/src/modules/oldfilm/filter_dust.yml000066400000000000000000000021701362234133600213320ustar00rootroot00000000000000schema_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 mlt-6.20.0/src/modules/oldfilm/filter_grain.c000066400000000000000000000056211362234133600211000ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position pos = mlt_filter_get_position( filter, frame ); mlt_position len = mlt_filter_get_length2( filter, frame ); *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 && *image ) { int h = *height; int w = *width; double position = mlt_filter_get_progress( filter, frame ); srand(position*10000); int noise = mlt_properties_anim_get_int( properties, "noise", pos, len ); double contrast = mlt_properties_anim_get_double( properties, "contrast", pos, len ) / 100.0; double brightness = 127.0 * (mlt_properties_anim_get_double( properties, "brightness", pos, len ) -100.0 ) / 100.0; int x = 0,y = 0,pix = 0; for ( x = 0; x < w; x++ ) { for( y = 0; y < h; y++ ) { uint8_t* pixel = (*image + (y) * w * 2 + (x) * 2 ); if (*pixel > 20) { pix = MIN ( MAX ( ( (double)*pixel -127.0 ) * contrast + 127.0 + brightness , 0 ) , 255 ) ; if ( noise > 0 ) pix -= ( rand() % noise - noise ); *pixel = MIN ( MAX ( pix , 0 ) , 255 ); } } } } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "noise", "40" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "contrast", "160" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "brightness", "70" ); } return filter; } mlt-6.20.0/src/modules/oldfilm/filter_grain.yml000066400000000000000000000024301362234133600214520ustar00rootroot00000000000000schema_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 mlt-6.20.0/src/modules/oldfilm/filter_lines.c000066400000000000000000000115451362234133600211140ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position pos = mlt_filter_get_position( filter, frame ); mlt_position len = mlt_filter_get_length2( filter, frame ); *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 && *image ) { int h = *height; int w = *width; int line_width = mlt_properties_anim_get_int( properties, "line_width", pos, len ); int num = mlt_properties_anim_get_int( properties, "num", pos, len ); double maxdarker = (double) mlt_properties_anim_get_int( properties, "darker", pos, len ); double maxlighter = (double) mlt_properties_anim_get_int( properties, "lighter", pos, len ); char buf[256]; char typebuf[256]; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale = mlt_profile_scale_width(profile, *width); if (line_width > 1 && scale > 0.0) line_width = MAX(2, lrint(line_width * scale)); if ( line_width < 1 ) return 0; double position = mlt_filter_get_progress( filter, frame ); srand(position*10000); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); while ( num-- ) { int type = (rand() % 3 ) + 1; int x1 = (double)w * rand() / RAND_MAX; int dx = rand() % line_width; int x = 0, y = 0; int ystart = rand() % h; int yend = rand() % h; sprintf( buf, "line%d", num); sprintf( typebuf, "typeline%d", num); maxlighter += rand() % 30 -15; maxdarker += rand() % 30 -15; if ( mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),buf ) ==0 ) { mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), buf, x1 ); } if ( mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ),typebuf)==0 ) { mlt_properties_set_int(MLT_FILTER_PROPERTIES( filter ),typebuf,type); } x1 = mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ), buf ); type = mlt_properties_get_int(MLT_FILTER_PROPERTIES( filter ), typebuf ); if ( position != mlt_properties_get_double(MLT_FILTER_PROPERTIES( filter ), "last_oldfilm_line_pos")) { x1 += (rand() % 11 - 5); } if ( yend < ystart) { yend=h; } for ( x = -dx ; x < dx && dx != 0 ; x++ ) { for( y = ystart; y < yend; y++ ) { if ( x + x1 < w && x + x1 > 0) { uint8_t* pixel = (*image + (y) * w * 2 + ( x + x1) * 2); double diff = 1.0 - (double) abs(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); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "line_width", 2 ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "num", 5 ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "darker" , 40 ) ; mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "lighter" , 40 ) ; } return filter; } mlt-6.20.0/src/modules/oldfilm/filter_lines.yml000066400000000000000000000031171362234133600214670ustar00rootroot00000000000000schema_version: 0.1 type: filter # consumer, filter, producer, or transition identifier: lines title: Scratchlines version: 0.2.6 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: line_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 mlt-6.20.0/src/modules/oldfilm/filter_oldfilm.c000066400000000000000000000150761362234133600214330ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include static double sinarr[] = { 0.0,0.0627587292804297,0.125270029508395,0.18728744713136,0.2485664757507,0.308865520098932, 0.3679468485397,0.425577530335206,0.481530353985902,0.535584723021826,0.587527525713892,0.637153975276265, 0.684268417247276,0.728685100865749,0.770228911401552,0.80873606055313,0.84405473219009,0.876045680894979, 0.904582780944473,0.929553523565587,0.95085946050647,0.968416592172968,0.982155698800724,0.99202261335714, 0.997978435097294,0.999999682931835,0.99807838800221,0.992222125098244,0.982453982794196,0.968812472421035, 0.951351376233828,0.930139535372831,0.90526057845426,0.876812591860795,0.844907733031696,0.809671788277164, 0.771243676860277,0.72977490330168,0.685428960066342,0.638380682987321,0.588815561967795,0.536929009678953, 0.48292559113694,0.427018217196276,0.369427305139443,0.310379909672042,0.250108827749629,0.188851680765468, 0.12684997771773,0.064348163049637,0.00159265291648683,-0.0611691363208864,-0.123689763546002,-0.18572273843423, -0.247023493251739,-0.307350347074556,-0.366465458626247,-0.424135763977612,-0.48013389541149,-0.534239077829989, -0.586237999170027,-0.635925651395529,-0.683106138750633,-0.727593450087328,-0.769212192222595,-0.807798281433749, -0.84319959036574,-0.875276547799941,-0.903902688919827,-0.928965153904073,-0.950365132881376,-0.968018255492714, -0.981854923525203,-0.991820585306115,-0.997875950775248,-0.999997146387718,-0.998175809236459,-0.992419120023356, -0.982749774749007,-0.969205895232745,-0.951840878815686,-0.930723187839362,-0.905936079729926,-0.877577278752084, -0.845758590726883,-0.810605462232336,-0.772256486024771,-0.730862854630786,-0.68658776426406,-0.639605771417098, -0.590102104664575,-0.538271934391528,-0.484319603325524,-0.428457820906457,-0.37090682467023,-0.311893511952568, -0.251650545336281,-0.190415435368805,-0.128429604166398,-0.0659374335968388,0.0, }; static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position pos = mlt_filter_get_position( filter, frame ); mlt_position len = mlt_filter_get_length2( filter, frame ); *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 && *image ) { int h = *height; int w = *width; int x = 0; int y = 0; double position = mlt_filter_get_progress( filter, frame ); srand( position * 10000); int delta = mlt_properties_anim_get_int( properties, "delta", pos, len ); int every = mlt_properties_anim_get_int( properties, "every", pos, len ); int bdu = mlt_properties_anim_get_int( properties, "brightnessdelta_up", pos, len ); int bdd = mlt_properties_anim_get_int( properties, "brightnessdelta_down", pos, len ); int bevery = mlt_properties_anim_get_int( properties, "brightnessdelta_every", pos, len ); int udu = mlt_properties_anim_get_int( properties, "unevendevelop_up", pos, len ); int udd = mlt_properties_anim_get_int( properties, "unevendevelop_down", pos, len ); int uduration = mlt_properties_anim_get_int( properties, "unevendevelop_duration", pos, len ); int diffpic = 0; if ( delta ) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); delta *= mlt_profile_scale_width(profile, *width); 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; int unevendevelop_delta = 0; if ( uduration > 0 ) { float uval = sinarr[ ( ((int)position) % uduration) * 100 / uduration ]; unevendevelop_delta = uval * ( uval > 0 ? udu : udd ); } if ( diffpic <= 0 ) { y = h; yend = 0; ydiff = -1; } else { y = 0; yend = h; ydiff = 1; } while( y != yend ) { for ( x = 0; x < w; x++ ) { uint8_t* pic = ( *image + y * w * 2 + x * 2 ); int newy = y + diffpic; if ( newy > 0 && newy < h ) { uint8_t oldval= *( pic + diffpic * w * 2 ); if ( ((int) oldval + brightdelta + unevendevelop_delta ) > 255 ) { *pic=255; } else if ( ( (int) oldval + brightdelta + unevendevelop_delta ) <0 ) { *pic=0; } else { *pic = oldval + brightdelta + unevendevelop_delta; } *( pic + 1 ) =* ( pic + diffpic * w * 2 + 1 ); } else { *pic = 0; } } y += ydiff; } } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "delta", "14" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "every", "20" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_up" , "20" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_down" , "30" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "brightnessdelta_every" , "70" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_up" , "60" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_down" , "20" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "unevendevelop_duration" , "70" ); } return filter; } mlt-6.20.0/src/modules/oldfilm/filter_oldfilm.yml000066400000000000000000000051771362234133600220130ustar00rootroot00000000000000schema_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: 30 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: '%' - identifier: unevendevelop_up title: Unevendevelop up type: integer description: Makes image n values lighter readonly: no required: yes minimum: 0 maximum: 100 default: 60 mutable: no widget: spinner - identifier: unevendevelop_down title: Unevendevelop down type: integer description: Makes image n values darker readonly: no required: yes minimum: 0 maximum: 100 default: 20 mutable: no widget: spinner - identifier: unevendevelop_duration title: Unevendevelop Duration type: integer description: Time (in frames) of a up/down cycle readonly: no required: yes minimum: 0 maximum: 10000 default: 70 mutable: no widget: spinner unit: '%' mlt-6.20.0/src/modules/oldfilm/filter_tcolor.c000066400000000000000000000052661362234133600213070ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position pos = mlt_filter_get_position( filter, frame ); mlt_position len = mlt_filter_get_length2( filter, frame ); *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 && *image ) { double over_cr = mlt_properties_anim_get_double( properties, "oversaturate_cr", pos, len )/100.0; double over_cb = mlt_properties_anim_get_double( properties, "oversaturate_cb", pos, len )/100.0; int video_width = *width; int video_height = *height; int x,y; for ( y = 0; y < video_height; y++) { for ( x = 0; x < video_width; x += 2) { uint8_t *pix = (*image + y * video_width * 2 + x * 2 + 1); uint8_t *pix1 = (*image + y * video_width * 2 + x * 2 + 3); *pix = MIN(MAX( ((double) * pix - 127.0) * over_cb + 127.0,0), 255); *pix1 = MIN(MAX( ((double) * pix1 - 127.0) * over_cr + 127.0,0), 255); } } } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } mlt_filter filter_tcolor_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "oversaturate_cr", "190" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "oversaturate_cb", "190" ); } return filter; } mlt-6.20.0/src/modules/oldfilm/filter_tcolor.yml000066400000000000000000000023151362234133600216560ustar00rootroot00000000000000schema_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 mlt-6.20.0/src/modules/oldfilm/filter_vignette.c000066400000000000000000000101001362234133600216110ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #define SIGMOD_STEPS 1000 #define POSITION_VALUE(p,s,e) (s+((double)(e-s)*p )) static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = mlt_frame_pop_service( frame ); *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( error == 0 && *image ) { float smooth, radius, cx, cy, opac; mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter ) ; mlt_position pos = mlt_filter_get_position( filter, frame ); mlt_position len = mlt_filter_get_length2( filter, frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale = mlt_profile_scale_width(profile, *width); smooth = mlt_properties_anim_get_double( filter_props, "smooth", pos, len ) * 100.0 * scale; radius = mlt_properties_anim_get_double( filter_props, "radius", pos, len ) * *width; cx = mlt_properties_anim_get_double( filter_props, "x", pos, len ) * *width; cy = mlt_properties_anim_get_double( filter_props, "y", pos, len ) * *height; opac = mlt_properties_anim_get_double( filter_props, "opacity", pos, len ); int mode = mlt_properties_get_int( filter_props , "mode" ); int video_width = *width; int video_height = *height; int x, y; int w2 = cx, h2 = cy; double delta = 1.0; double max_opac = opac; for ( y=0; y < video_height; y++ ) { int h2_pow2 = pow( y - h2, 2.0 ); for ( x = 0; x < video_width; x++ ) { uint8_t *pix = ( *image + y * video_width * 2 + x * 2 ); int dx = sqrt( h2_pow2 + pow( x - w2, 2.0 )); if (radius-smooth > dx) //center, make not darker { continue; } else if ( radius + smooth <= dx ) //max dark after smooth area { delta = 0.0; } else { // linear pos from of opacity (from 0 to 1) delta = (double) ( radius + smooth - dx ) / ( 2.0 * smooth ); if ( mode == 1 ) { //make cos for smoother non linear fade delta = (double) pow( cos((( 1.0 - delta ) * 3.14159 / 2.0 )), 3.0 ); } } delta = MAX( max_opac, delta ); *pix = (double) (*pix) * delta; *(pix+1) = ((double)(*(pix+1) - 127.0 ) * delta ) + 127.0; } } } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "smooth", 0.8 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "radius", 0.5 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "x", 0.5 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "y", 0.5 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "opacity", 0.0 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "mode", 0 ); } return filter; } mlt-6.20.0/src/modules/oldfilm/filter_vignette.yml000066400000000000000000000033171362234133600222040ustar00rootroot00000000000000schema_version: 0.1 type: filter # consumer, filter, producer, or transition identifier: vignette title: Vignette Effect version: 1.0 copyright: Copyright (C) 2008 Marco Gittler license: GPL language: en url: none creator: Marco Gittler tags: - Video # this may produce video description: > Vignette 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: smooth title: Feathering type: float readonly: no required: yes mutable: no minimum: 0.0 maximum: 1.0 default: 0.8 - identifier: radius title: Radius type: float readonly: no required: yes mutable: no minimum: 0.0 maximum: 1.0 default: 0.5 - identifier: x title: X Position type: float readonly: no required: yes mutable: no minimum: 0.0 maximum: 1.0 default: 0.5 - identifier: y title: Y Position type: float readonly: no required: yes mutable: no minimum: 0.0 maximum: 1.0 default: 0.5 - identifier: opacity title: Opacity type: float readonly: no required: yes mutable: no minimum: 0.0 maximum: 1.0 default: 0.0 - identifier: mode title: Mode type: integer description: Use linear (0) or cosinus (1) mode to fade from opac to black readonly: no required: yes mutable: no minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/oldfilm/grain.svg000066400000000000000000000075521362234133600201150ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/lines.svg000066400000000000000000000044221362234133600201200ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/oldfilm.svg000066400000000000000000000044351362234133600204400ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/tcolor.svg000066400000000000000000000057641362234133600203220ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/oldfilm/vignette.svg000066400000000000000000000063441362234133600206400ustar00rootroot00000000000000 image/svg+xml mlt-6.20.0/src/modules/opencv/000077500000000000000000000000001362234133600161275ustar00rootroot00000000000000mlt-6.20.0/src/modules/opencv/CMakeLists.txt000066400000000000000000000006071362234133600206720ustar00rootroot00000000000000find_package(OpenCV QUIET OPTIONAL_COMPONENTS tracking) if(${OpenCV_tracking_FOUND}) add_library(mltopencv MODULE factory.c filter_opencv_tracker.cpp) target_link_libraries(mltopencv mlt ${OpenCV_LIBS}) install(TARGETS mltopencv LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) install(FILES filter_opencv_tracker.yml DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/opencv) endif() mlt-6.20.0/src/modules/opencv/Makefile000066400000000000000000000014141362234133600175670ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt include ../../../config.mak include config.mak TARGET = ../libmltopencv$(LIBSUF) OBJS = factory.o CPPOBJS = filter_opencv_tracker.o CXXFLAGS += -Wno-deprecated $(CFLAGS) LDFLAGS += -L../../mlt++ -lmlt++ SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) all: $(TARGET) $(TARGET): $(OBJS) $(CPPOBJS) $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) depend: $(SRCS) $(CXX) -MM $(CXXFLAGS) $^ 1>.depend distclean: clean rm -f .depend config.h config.mak clean: rm -f $(OBJS) $(TARGET) $(CPPOBJS) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/opencv" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/opencv" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/opencv/configure000077500000000000000000000023471362234133600200440ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF OpenCV options: --enable-opencv - This module must be explicitly enabled due to a multi-threading issue: https://github.com/mltframework/mlt/issues/274 The Tracker filter requires OpenCV >= 3.1.0 and the tracking contrib module. EOF else for i in "$@" do case $i in --enable-opencv ) opencv_enabled=1;; esac done if [ "$opencv_enabled" != "1" ]; then echo "- not explicitly enabled: disabling" touch ../disable-opencv exit 0 fi pkg-config --atleast-version=3.1.0 'opencv' if [ $? -eq 0 ] then result=`pkg-config --libs opencv | grep "opencv_tracking"` if [ -z "$result" ] then echo "- OpenCV tracking contrib module NOT found, disabling OpenCV modules" touch ../disable-opencv exit 0 else echo "CFLAGS += $(pkg-config --cflags opencv)" >> config.mak echo "LDFLAGS += $(pkg-config --libs opencv)" >> config.mak fi else echo "- OpenCV >= 3.1.0 NOT found: disabling" touch ../disable-opencv exit 0 fi exit 0 fi mlt-6.20.0/src/modules/opencv/factory.c000066400000000000000000000026741362234133600177530ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2016 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 extern mlt_filter filter_tracker_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/opencv/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( filter_type, "opencv.tracker", filter_tracker_init ); MLT_REGISTER_METADATA( filter_type, "opencv.tracker", metadata, "filter_opencv_tracker.yml" ); } mlt-6.20.0/src/modules/opencv/filter_opencv_tracker.cpp000066400000000000000000000301141362234133600232040ustar00rootroot00000000000000/* * filter_tracker.cpp -- Motion tracker * Copyright (C) 2016 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 typedef struct { cv::Ptr tracker; cv::Rect2d boundingBox; char * algo; mlt_rect startRect; bool initialized; bool playback; bool analyze; int last_position; mlt_position producer_in; mlt_position producer_length; } private_data; static void property_changed( mlt_service owner, mlt_filter filter, char *name ) { private_data* pdata = (private_data*)filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); if ( !strcmp( name, "results" ) ) { mlt_properties_anim_get_int( filter_properties, "results", 0, -1 ); mlt_animation anim = mlt_properties_get_animation( filter_properties, "results" ); if ( anim && mlt_animation_key_count( anim ) > 0 ) { // We received valid analysis data pdata->initialized = true; pdata->playback = true; return; } else { // Analysis data was discarded pdata->initialized = false; pdata->producer_length = 0; pdata->playback = false; mlt_properties_set( filter_properties, "_results", NULL ); } } if ( !pdata->initialized ) { return; } if ( !strcmp( name, "rect" ) ) { // The initial rect was changed, we need to reset the tracker with this new rect mlt_rect rect = mlt_properties_get_rect( filter_properties, "rect" ); if ( rect.x != pdata->startRect.x || rect.y != pdata->startRect.y || rect.w != pdata->startRect.w || rect.h != pdata->startRect.h ) { pdata->playback = false; pdata->initialized = false; } } else if ( !strcmp( name, "algo" ) ) { char *algo = mlt_properties_get( filter_properties, "algo" ); if ( pdata->algo && *pdata->algo != '\0' && strcmp( algo, pdata->algo ) ) { pdata->playback = false; pdata->initialized = false; } } else if ( !strcmp( name, "_reset" ) ) { mlt_properties_set( filter_properties, "results", NULL ); } } static void apply( mlt_filter filter, private_data* data, int width, int height, int position, int length ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_rect rect = mlt_properties_anim_get_rect( properties, "results", position, length ); data->boundingBox.x = rect.x; data->boundingBox.y= rect.y; data->boundingBox.width = rect.w; data->boundingBox.height = rect.h; } static void analyze( mlt_filter filter, cv::Mat cvFrame, private_data* data, int width, int height, int position, int length ) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); // Create tracker and initialize it if (!data->initialized) { // Build tracker data->algo = mlt_properties_get( filter_properties, "algo" ); #if CV_VERSION_MAJOR > 3 || (CV_VERSION_MAJOR == 3 && CV_VERSION_MINOR >= 3) if ( !data->algo || *data->algo == '\0' || !strcmp(data->algo, "KCF" ) ) { data->tracker = cv::TrackerKCF::create(); } #if CV_VERSION_MAJOR > 3 || (CV_VERSION_MAJOR == 3 && CV_VERSION_MINOR >= 4 && CV_VERSION_REVISION >= 2) else if ( !strcmp(data->algo, "CSRT" ) ) { data->tracker = cv::TrackerCSRT::create(); } else if ( !strcmp(data->algo, "MOSSE" ) ) { data->tracker = cv::TrackerMOSSE::create(); } #endif else if ( !strcmp(data->algo, "MIL" ) ) { data->tracker = cv::TrackerMIL::create(); } else if ( !strcmp(data->algo, "TLD" ) ) { data->tracker = cv::TrackerTLD::create(); } else { data->tracker = cv::TrackerBoosting::create(); } #else if ( data->algo == NULL || !strcmp(data->algo, "" ) ) { data->tracker = cv::Tracker::create( "KCF" ); } else { data->tracker = cv::Tracker::create( data->algo ); } #endif // Discard previous results mlt_properties_set( filter_properties, "_results", "" ); if( data->tracker == NULL ) { fprintf( stderr, "Tracker initialized FAILED\n" ); } else { data->startRect = mlt_properties_get_rect( filter_properties, "rect" ); data->boundingBox.x = MAX( data->startRect.x, 1.0 ); data->boundingBox.y= MAX( data->startRect.y, 1.0 ); data->boundingBox.width = data->startRect.w; data->boundingBox.height = data->startRect.h; if ( data->boundingBox.width <1 ) { data->boundingBox.width = 50; } if ( data->boundingBox.height <1 ) { data->boundingBox.height = 50; } if ( data->tracker->init( cvFrame, data->boundingBox ) ) { data->initialized = true; data->analyze = true; data->last_position = position - 1; } // init anim property mlt_properties_anim_get_int( filter_properties, "_results", 0, length ); mlt_animation anim = mlt_properties_get_animation( filter_properties, "_results" ); if ( anim == NULL ) { fprintf( stderr, "animation initialized FAILED\n" ); } } } else { data->tracker->update( cvFrame, data->boundingBox ); } if( data->analyze && position != data->last_position + 1 ) { // We are in real time, do not try to store data data->analyze = false; } if ( !data->analyze ) { return; } // Store results in temp variable mlt_rect rect; rect.x = data->boundingBox.x; rect.y = data->boundingBox.y; rect.w = data->boundingBox.width; rect.h = data->boundingBox.height; rect.o = 0; int steps = mlt_properties_get_int(filter_properties, "steps"); if ( steps > 1 && position > 0 && position < length - 1 ) { if ( position % steps == 0 ) mlt_properties_anim_set_rect( filter_properties, "_results", rect, position, length, mlt_keyframe_smooth ); } else { mlt_properties_anim_set_rect( filter_properties, "_results", rect, position, length, mlt_keyframe_smooth ); } if ( position + 1 == length ) { //Analysis finished, store results mlt_animation anim = mlt_properties_get_animation( filter_properties, "_results"); mlt_properties_set( filter_properties, "results", strdup( mlt_animation_serialize( anim ) ) ); // Discard temporary data mlt_properties_set( filter_properties, "_results", (char*) NULL ); data->playback = true; } data->last_position = position; } /** Get the image. */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); int shape_width = mlt_properties_get_int( filter_properties, "shape_width" ); int blur = mlt_properties_get_int( filter_properties, "blur" ); cv::Mat cvFrame; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } if ( shape_width == 0 && blur == 0 ) { error = mlt_frame_get_image( frame, image, format, width, height, 1 ); } else { *format = mlt_image_rgb24; error = mlt_frame_get_image( frame, image, format, width, height, 1 ); cvFrame = cv::Mat( *height, *width, CV_8UC3, *image ); } private_data* data = (private_data*) filter->child; if ( data->producer_length == 0 ) { mlt_producer producer = mlt_frame_get_original_producer( frame ); if ( producer ) { data->producer_in = mlt_producer_get_in( producer ); data->producer_length = mlt_producer_get_playtime( producer ); } } if ( !data->initialized ) { mlt_properties_anim_get_int( filter_properties, "results", 0, data->producer_length ); mlt_animation anim = mlt_properties_get_animation( filter_properties, "results" ); if ( anim && mlt_animation_key_count(anim) > 0 ) { data->initialized = true; data->playback = true; } } if( data->playback ) { // Clip already analysed, don't re-process apply( filter, data, *width, *height, position, data->producer_in + data->producer_length ); } else { analyze( filter, cvFrame, data, *width, *height, position, data->producer_in + data->producer_length ); } if ( blur > 0 ) { switch ( mlt_properties_get_int( filter_properties, "blur_type" ) ) { case 1: // Gaussian Blur cv::GaussianBlur( cvFrame( data->boundingBox ), cvFrame( data->boundingBox ), cv::Size( 0, 0 ), blur ); break; case 0: default: // Median Blur ++blur; if ( blur % 2 == 0 ) { // median blur param must be odd and, minimum 3 ++blur; } cv::medianBlur( cvFrame( data->boundingBox ), cvFrame( data->boundingBox ), blur ); break; } } // Paint overlay shape if ( shape_width != 0 ) { // Get the OpenCV image mlt_color shape_color = mlt_properties_get_color( filter_properties, "shape_color" ); switch ( mlt_properties_get_int( filter_properties, "shape" ) ) { case 2: // Arrow cv::arrowedLine( cvFrame, cv::Point( data->boundingBox.x + data->boundingBox.width/2, data->boundingBox.y - data->boundingBox.height/2 ), cv::Point( data->boundingBox.x + data->boundingBox.width/2, data->boundingBox.y ), cv::Scalar( shape_color.r, shape_color.g, shape_color.b ), MAX( shape_width, 1 ), 4, 0, .2 ); break; case 1: // Ellipse { cv::RotatedRect bounding = cv::RotatedRect( cv::Point2f( data->boundingBox.x + data->boundingBox.width/2, data->boundingBox.y + data->boundingBox.height/2 ), cv::Size2f( data->boundingBox.width, data->boundingBox.height ), 0); cv::ellipse( cvFrame, bounding, cv::Scalar( shape_color.r, shape_color.g, shape_color.b ), shape_width, 1 ); } break; case 0: default: // Rectangle cv::rectangle( cvFrame, data->boundingBox, cv::Scalar( shape_color.r, shape_color.g, shape_color.b ), shape_width, 1 ); break; } } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } static void filter_close( mlt_filter filter ) { private_data* data = (private_data*) filter->child; free ( data->tracker ); free ( data ); filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_tracker_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* data = (private_data*) calloc( 1, sizeof(private_data) ); if ( filter && data) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set_int( properties, "shape_width", 1 ); mlt_properties_set_int( properties, "steps", 5 ); mlt_properties_set( properties, "algo", "KCF" ); data->initialized = false; data->playback = false; data->boundingBox.x = 0; data->boundingBox.y= 0; data->boundingBox.width = 0; data->boundingBox.height = 0; data->analyze = false; data->last_position = -1; data->producer_in = 0; data->producer_length = 0; filter->child = data; // Create a unique ID for storing data on the frame filter->close = filter_close; filter->process = filter_process; mlt_events_listen( properties, filter, "property-changed", (mlt_listener)property_changed ); } else { mlt_log_error( MLT_FILTER_SERVICE( filter ), "Filter tracker failed\n" ); if( filter ) { mlt_filter_close( filter ); } if( data ) { free( data ); } filter = NULL; } return filter; } } mlt-6.20.0/src/modules/opencv/filter_opencv_tracker.yml000066400000000000000000000063721362234133600232340ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: opencv.tracker title: OpenCV Motion Tracker copyright: Jean-Baptiste Mardelle creator: Jean-Baptiste Mardelle version: 2 license: LGPLv2.1 language: en url: tags: - Video description: Track and follow an object in the video. notes: > If used in a non-linear workflow, this filter works best with two passes. First pass performs analysis and stores the result in a property. Parallel processing (real_time < -1 or > 1) is not supported for the first pass. Second pass simply applies the results to the image. If no analysis result is found, the filter does its best to work in real time. To analyse clip, you can use with melt, use 'melt ... -consumer xml:output.mlt all=1 real_time=-1'. Analysis data is stored in a "results" property. For the second pass, you can use output.mlt as the input. parameters: - identifier: rect title: Target Rect type: string description: > The rectangle defining the object to be followed (from the 1st frame of the filter). required: no readonly: no mutable: no default: 0 0 50 50 - identifier: shape title: Shape type: integer description: > The shape to be drawn around tracked object. 0 for a rectangle, 1 for an ellipse, 2 for an arrow. readonly: no required: no minimum: 0 maximum: 5 default: 0 mutable: no - identifier: shape_width title: Shape Width type: integer description: > Decide if we want to display a shape around followed object during playback. 0 means no display, -1 means a filled shape and > 0 determines the border width. readonly: no required: no minimum: -1 maximum: 100 default: 1 mutable: no - identifier: shape_color title: Shape Color type: color description: > The color used to paint the shape around target object. readonly: no required: no default: 0xffffffff mutable: yes - identifier: blur title: Blur type: integer description: > Decide if we want to blur selected object. 0 means no blur, > 0 means blur intensity. readonly: no required: no minimum: 0 maximum: 100 default: 0 mutable: no - identifier: blur_type title: Blur Type type: integer description: > Decide which blur method is used. 0 for median blur, 1 for gaussian blur. readonly: no required: no minimum: 0 maximum: 100 default: 0 mutable: no - identifier: algo title: Tracker Algorithm type: string description: > The algorithm used for tracking (OpenCV name). Check OpenCV doc for full algorithm list, most commons are KCF, MIL, BOOSTING, TLD readonly: no required: no default: KCF mutable: no - identifier: steps title: Keyframes spacing type: integer description: > Defines the frequency of stored keyframes. A keyframe is created every steps frames. mutable: yes readonly: no required: no default: 5 minimum: 0 - identifier: results title: Analysis Results type: string description: > Set after analysis. This is an animated rect following object designated with initial rect property. mutable: no readonly: yes mlt-6.20.0/src/modules/opengl/000077500000000000000000000000001362234133600161215ustar00rootroot00000000000000mlt-6.20.0/src/modules/opengl/Makefile000066400000000000000000000037421362234133600175670ustar00rootroot00000000000000include ../../../config.mak CFLAGS := -I../.. $(CFLAGS) LDFLAGS := -L../../framework -lmlt -lm $(LDFLAGS) TARGET = ../libmltopengl$(LIBSUF) OBJS = factory.o CPPOBJS = filter_glsl_manager.o CPPOBJS += filter_movit_blur.o CPPOBJS += filter_movit_convert.o CPPOBJS += filter_movit_crop.o CPPOBJS += filter_movit_deconvolution_sharpen.o CPPOBJS += filter_movit_diffusion.o CPPOBJS += filter_movit_flip.o CPPOBJS += filter_movit_glow.o CPPOBJS += filter_movit_lift_gamma_gain.o CPPOBJS += filter_movit_mirror.o CPPOBJS += filter_movit_opacity.o CPPOBJS += filter_movit_rect.o CPPOBJS += filter_movit_resample.o CPPOBJS += filter_movit_resize.o CPPOBJS += filter_movit_saturation.o CPPOBJS += filter_movit_vignette.o CPPOBJS += filter_movit_white_balance.o CPPOBJS += mlt_movit_input.o CPPOBJS += transition_movit_luma.o CPPOBJS += transition_movit_mix.o CPPOBJS += transition_movit_overlay.o CFLAGS += -Wno-deprecated CFLAGS += $(shell pkg-config --cflags movit 2> /dev/null) CXXFLAGS += $(CFLAGS) SHADERDIR = $(shell pkg-config --variable=shaderdir movit) CXXFLAGS += -DSHADERDIR=\"$(SHADERDIR)\" LDFLAGS += -L../../mlt++ -lmlt++ ifeq ($(targetos), MinGW) CXXFLAGS += $(shell pkg-config --cflags movit) LDFLAGS += $(shell pkg-config --libs movit) -lopengl32 -lpthread else LDFLAGS += $(shell pkg-config --libs movit 2> /dev/null) ifeq ($(targetos), Darwin) CXXFLAGS += -FOpenGL LDFLAGS += -lpthread -framework OpenGL else OBJS += consumer_xgl.o LDFLAGS += -lpthread -lGL -lX11 endif endif SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) all: $(TARGET) $(TARGET): $(OBJS) $(CPPOBJS) $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) depend: $(SRCS) $(CXX) -MM $(CXXFLAGS) $^ 1>.depend distclean: clean rm -f .depend config.h config.mak clean: rm -f $(OBJS) $(TARGET) $(CPPOBJS) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/opengl" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/opengl" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/opengl/configure000077500000000000000000000003571362234133600200350ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ] then if ! $(pkg-config movit) then echo "- movit not found: disabling" touch ../disable-opengl exit 0 fi echo > config.mak case $targetos in Darwin) ;; MinGW) ;; *) ;; esac exit 0 fi mlt-6.20.0/src/modules/opengl/consumer_xgl.c000066400000000000000000000416061362234133600210010ustar00rootroot00000000000000/* * consumer_xgl.c * Copyright (C) 2012 Christophe Thommeret * Author: Christophe Thommeret * Based on Nehe's GLX port by Mihael.Vrbanec@stud.uni-karlsruhe.de * http://nehe.gamedev.net/ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define GL_GLEXT_PROTOTYPES #include #include #include #include #include #include #include #include #include #include #include #define STARTWIDTH 1280 #define STARTHEIGHT 720 extern int XInitThreads(); typedef struct consumer_xgl_s *consumer_xgl; struct consumer_xgl_s { struct mlt_consumer_s parent; mlt_properties properties; mlt_deque queue; pthread_t thread; int joined; int running; int playing; int xgl_started; }; typedef struct { pthread_t thread; int running; } thread_video; typedef struct { int width; int height; double aspect_ratio; GLuint texture; pthread_mutex_t mutex; int new; mlt_frame mlt_frame_ref; } frame_new; typedef struct { int width; int height; GLuint fbo; GLuint texture; } fbo; typedef struct { Display *dpy; int screen; Window win; GLXContext ctx; } HiddenContext; typedef struct { Display *dpy; int screen; Window win; GLXContext ctx; XSetWindowAttributes attr; int x, y; unsigned int width, height; unsigned int depth; } GLWindow; static GLWindow GLWin; static HiddenContext hiddenctx; static frame_new new_frame; static fbo fb; static thread_video vthread; static consumer_xgl xgl; static mlt_filter glsl_manager; static void* video_thread( void *arg ); static void update() { int _width = GLWin.width; int _height = GLWin.height; GLfloat left, right, top, bottom; GLfloat war = (GLfloat)_width/(GLfloat)_height; if ( war < new_frame.aspect_ratio ) { left = -1.0; right = 1.0; top = war / new_frame.aspect_ratio; bottom = -war / new_frame.aspect_ratio; } else { top = 1.0; bottom = -1.0; left = -new_frame.aspect_ratio / war; right = new_frame.aspect_ratio / war; } glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLoadIdentity(); glPushMatrix(); glTranslatef( _width/2, _height/2, 0 ); glScalef( _width/2, _height/2, 1.0 ); glBindTexture( GL_TEXTURE_2D, fb.texture ); glBegin( GL_QUADS ); glTexCoord2f( 0.0f, 0.0f ); glVertex2f( left, top ); glTexCoord2f( 0.0f, 1.0f ); glVertex2f( left, bottom ); glTexCoord2f( 1.0f, 1.0f ); glVertex2f( right, bottom ); glTexCoord2f( 1.0f, 0.0f ); glVertex2f( right, top ); glEnd(); glPopMatrix(); glXSwapBuffers( GLWin.dpy, GLWin.win ); if ( !vthread.running ) { pthread_create( &vthread.thread, NULL, video_thread, NULL ); vthread.running = 1; } } static void show_frame() { if ( (fb.width != new_frame.width) || (fb.height != new_frame.height) ) { glDeleteFramebuffers( 1, &fb.fbo ); glDeleteTextures( 1, &fb.texture ); fb.fbo = 0; fb.width = new_frame.width; fb.height = new_frame.height; glGenFramebuffers( 1, &fb.fbo ); glGenTextures( 1, &fb.texture ); glBindTexture( GL_TEXTURE_2D, fb.texture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, fb.width, fb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo ); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.texture, 0 ); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); } glPushAttrib(GL_VIEWPORT_BIT); glMatrixMode(GL_PROJECTION); glPushMatrix(); glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo ); glViewport( 0, 0, new_frame.width, new_frame.height ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0.0, new_frame.width, 0.0, new_frame.height, -1.0, 1.0 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_2D, new_frame.texture ); glBegin( GL_QUADS ); glTexCoord2f( 0.0f, 0.0f ); glVertex2f( 0.0f, 0.0f ); glTexCoord2f( 0.0f, 1.0f ); glVertex2f( 0.0f, new_frame.height ); glTexCoord2f( 1.0f, 1.0f ); glVertex2f( new_frame.width, new_frame.height ); glTexCoord2f( 1.0f, 0.0f ); glVertex2f( new_frame.width, 0.0f ); glEnd(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); mlt_events_fire( MLT_CONSUMER_PROPERTIES(&xgl->parent), "consumer-frame-show", new_frame.mlt_frame_ref, NULL ); mlt_frame_close( new_frame.mlt_frame_ref ); new_frame.mlt_frame_ref = NULL; glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopAttrib(); update(); new_frame.new = 0; } void* video_thread( void *arg ) { mlt_frame next = NULL; mlt_consumer consumer = &xgl->parent; mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( consumer ); struct timeval start, end; double duration = 0; gettimeofday( &start, NULL ); while ( vthread.running ) { // Get a frame from the attached producer next = mlt_consumer_rt_frame( consumer ); if ( !mlt_properties_get_int( MLT_FILTER_PROPERTIES( glsl_manager ), "glsl_supported" ) ) { mlt_log_error( MLT_CONSUMER_SERVICE(consumer), "OpenGL Shading Language is not supported on this machine.\n" ); xgl->running = 0; break; } // Ensure that we have a frame if ( next ) { mlt_properties properties = MLT_FRAME_PROPERTIES( next ); if ( mlt_properties_get_int( properties, "rendered" ) == 1 ) { // Get the image, width and height mlt_image_format vfmt = mlt_image_glsl_texture; int width = 0, height = 0; GLuint *image = 0; int error = mlt_frame_get_image( next, (uint8_t**) &image, &vfmt, &width, &height, 0 ); if ( !error && image && width && height && !new_frame.new ) { new_frame.width = width; new_frame.height = height; new_frame.texture = *image; new_frame.mlt_frame_ref = next; new_frame.aspect_ratio = ((double)width / (double)height) * mlt_properties_get_double( properties, "aspect_ratio" ); new_frame.new = 1; int loop = 200; while ( new_frame.new && --loop ) usleep( 500 ); } else { mlt_frame_close( next ); } new_frame.new = 0; gettimeofday( &end, NULL ); duration = 1000000.0 / mlt_properties_get_double( consumer_props, "fps" ); duration -= ( end.tv_sec * 1000000 + end.tv_usec ) - ( start.tv_sec * 1000000 + start.tv_usec ); if ( duration > 0 ) usleep( (int)duration ); gettimeofday( &start, NULL ); } else { mlt_frame_close( next ); static int dropped = 0; mlt_log_info( MLT_CONSUMER_SERVICE(consumer), "dropped video frame %d\n", ++dropped ); } } else usleep( 1000 ); } mlt_consumer_stopped( consumer ); return NULL; } static void resizeGLScene() { glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx ); if ( GLWin.height == 0 ) GLWin.height = 1; if ( GLWin.width == 0 ) GLWin.width = 1; glViewport( 0, 0, GLWin.width, GLWin.height ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0.0, GLWin.width, 0.0, GLWin.height, -1.0, 1.0 ); glMatrixMode( GL_MODELVIEW ); update(); } static void initGL( void ) { glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx ); glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); glClearDepth( 1.0f ); glDepthFunc( GL_LEQUAL ); glEnable( GL_DEPTH_TEST ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glEnable( GL_BLEND ); glShadeModel( GL_SMOOTH ); glEnable( GL_TEXTURE_2D ); glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); typedef int (*GLXSWAPINTERVALSGI) ( int ); GLXSWAPINTERVALSGI mglXSwapInterval = (GLXSWAPINTERVALSGI)glXGetProcAddressARB( (const GLubyte*)"glXSwapIntervalSGI" ); if ( mglXSwapInterval ) mglXSwapInterval( 1 ); fb.fbo = 0; fb.width = STARTWIDTH; fb.height = STARTHEIGHT; glGenFramebuffers( 1, &fb.fbo ); glGenTextures( 1, &fb.texture ); glBindTexture( GL_TEXTURE_2D, fb.texture ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, fb.width, fb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo ); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.texture, 0 ); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); resizeGLScene(); } static void createGLWindow() { const char* title = "OpenGL consumer"; int width = STARTWIDTH; int height = STARTHEIGHT; int attrListSgl[] = { GLX_RGBA, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None }; int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None }; XVisualInfo *vi; Colormap cmap; Atom wmDelete; Window winDummy; unsigned int borderDummy; GLWin.dpy = XOpenDisplay( 0 ); GLWin.screen = DefaultScreen( GLWin.dpy ); vi = glXChooseVisual( GLWin.dpy, GLWin.screen, attrListDbl ); if ( !vi ) vi = glXChooseVisual( GLWin.dpy, GLWin.screen, attrListSgl ); GLWin.ctx = glXCreateContext( GLWin.dpy, vi, 0, GL_TRUE ); cmap = XCreateColormap( GLWin.dpy, RootWindow( GLWin.dpy, vi->screen ), vi->visual, AllocNone ); GLWin.attr.colormap = cmap; GLWin.attr.border_pixel = 0; GLWin.attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; GLWin.win = XCreateWindow( GLWin.dpy, RootWindow(GLWin.dpy, vi->screen), 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &GLWin.attr ); wmDelete = XInternAtom( GLWin.dpy, "WM_DELETE_WINDOW", True ); XSetWMProtocols( GLWin.dpy, GLWin.win, &wmDelete, 1 ); XSetStandardProperties( GLWin.dpy, GLWin.win, title, title, None, NULL, 0, NULL ); XMapRaised( GLWin.dpy, GLWin.win ); glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx ); XGetGeometry( GLWin.dpy, GLWin.win, &winDummy, &GLWin.x, &GLWin.y, &GLWin.width, &GLWin.height, &borderDummy, &GLWin.depth ); // Verify GLSL works on this machine hiddenctx.ctx = glXCreateContext( GLWin.dpy, vi, GLWin.ctx, GL_TRUE ); if ( hiddenctx.ctx ) { hiddenctx.dpy = GLWin.dpy; hiddenctx.screen = GLWin.screen; hiddenctx.win = RootWindow( hiddenctx.dpy, hiddenctx.screen ); } initGL(); } static void killGLWindow() { if ( GLWin.ctx ) { if ( !glXMakeCurrent( GLWin.dpy, None, NULL ) ) { printf("Error releasing drawing context : killGLWindow\n"); } glXDestroyContext( GLWin.dpy, GLWin.ctx ); GLWin.ctx = NULL; } if ( hiddenctx.ctx ) glXDestroyContext( hiddenctx.dpy, hiddenctx.ctx ); XCloseDisplay( GLWin.dpy ); } static void run() { XEvent event; while ( xgl->running ) { while ( XPending( GLWin.dpy ) > 0 ) { XNextEvent( GLWin.dpy, &event ); switch ( event.type ) { case Expose: if ( event.xexpose.count != 0 ) break; break; case ConfigureNotify: if ( (event.xconfigure.width != GLWin.width) || (event.xconfigure.height != GLWin.height) ) { GLWin.width = event.xconfigure.width; GLWin.height = event.xconfigure.height; resizeGLScene(); } break; case KeyPress: switch ( XLookupKeysym( &event.xkey, 0 ) ) { case XK_Escape: xgl->running = 0; break; default: { mlt_producer producer = mlt_properties_get_data( xgl->properties, "transport_producer", NULL ); char keyboard[ 2 ] = " "; void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( xgl->properties, "transport_callback", NULL ); if ( callback != NULL && producer != NULL ) { keyboard[ 0 ] = ( char )XLookupKeysym( &event.xkey, 0 ); callback( producer, keyboard ); } break; } } break; case ClientMessage: if ( *XGetAtomName( GLWin.dpy, event.xclient.message_type ) == *"WM_PROTOCOLS" ) xgl->running = 0; break; default: break; } } if ( new_frame.new ) show_frame(); else usleep( 1000 ); } } void start_xgl( consumer_xgl consumer ) { xgl = consumer; pthread_mutex_init( &new_frame.mutex, NULL ); new_frame.aspect_ratio = 16.0 / 9.0; new_frame.new = 0; new_frame.width = STARTWIDTH; new_frame.height = STARTHEIGHT; new_frame.mlt_frame_ref = NULL; vthread.running = 0; xgl->xgl_started = 1; createGLWindow(); run(); if ( vthread.running ) { vthread.running = 0; pthread_join( vthread.thread, NULL ); } xgl->running = 0; } static void on_consumer_thread_started( mlt_properties owner, HiddenContext* context ) { // Initialize this thread's OpenGL state glXMakeCurrent( context->dpy, context->win, context->ctx ); mlt_events_fire( MLT_FILTER_PROPERTIES(glsl_manager), "init glsl", NULL ); } /** 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 * ); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_xgl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the consumer object consumer_xgl this = calloc( sizeof( struct consumer_xgl_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 ); // Default scaler mlt_properties_set( this->properties, "rescale", "bilinear" ); mlt_properties_set( this->properties, "deinterlace_method", "onefield" ); // default image format mlt_properties_set( this->properties, "mlt_image_format", "glsl" ); // Default buffer for low latency mlt_properties_set_int( this->properties, "buffer", 1 ); // Ensure we don't join on a non-running object this->joined = 1; this->xgl_started = 0; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; // "init glsl" is required to instantiate glsl filters. glsl_manager = mlt_factory_filter( profile, "glsl.manager", NULL ); if ( glsl_manager ) { mlt_events_listen( this->properties, &hiddenctx, "consumer-thread-started", (mlt_listener) on_consumer_thread_started ); } else { mlt_consumer_close( parent ); parent = NULL; } // Return the consumer produced return parent; } // malloc or consumer init failed free( this ); // Indicate failure return NULL; } int consumer_start( mlt_consumer parent ) { consumer_xgl this = parent->child; if ( !this->running ) { consumer_stop( parent ); this->running = 1; this->joined = 0; pthread_create( &this->thread, NULL, consumer_thread, this ); } return 0; } int consumer_stop( mlt_consumer parent ) { // Get the actual object consumer_xgl this = parent->child; if ( this->running && this->joined == 0 ) { // Kill the thread and clean up this->joined = 1; this->running = 0; if ( this->thread ) pthread_join( this->thread, NULL ); } return 0; } int consumer_is_stopped( mlt_consumer parent ) { consumer_xgl this = parent->child; return !this->running; } static void *consumer_thread( void *arg ) { // Identify the arg consumer_xgl this = arg; XInitThreads(); start_xgl( this ); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close( mlt_consumer parent ) { // Get the actual object consumer_xgl this = parent->child; // Stop the consumer ///mlt_consumer_stop( parent ); mlt_filter_close( glsl_manager ); // Now clean up the rest mlt_consumer_close( parent ); // Close the queue mlt_deque_close( this->queue ); if ( this->xgl_started ) killGLWindow(); // Finally clean up this free( this ); } mlt-6.20.0/src/modules/opengl/factory.c000066400000000000000000000150731362234133600177420ustar00rootroot00000000000000/* * Copyright (C) 2013-2019 Dan Dennedy * factory.c -- the factory method interfaces * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include extern mlt_consumer consumer_xgl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_glsl_manager_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_blur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_convert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_crop_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_deconvolution_sharpen_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_diffusion_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_flip_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_glow_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_opacity_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_rect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_saturation_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_movit_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_white_balance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_transition transition_movit_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_transition transition_movit_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_transition transition_movit_overlay_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/opengl/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { #if !defined(__APPLE__) && !defined(_WIN32) MLT_REGISTER( consumer_type, "xgl", consumer_xgl_init ); #endif MLT_REGISTER( filter_type, "glsl.manager", filter_glsl_manager_init ); MLT_REGISTER( filter_type, "movit.blur", filter_movit_blur_init ); MLT_REGISTER( filter_type, "movit.convert", filter_movit_convert_init ); MLT_REGISTER( filter_type, "movit.crop", filter_movit_crop_init ); MLT_REGISTER( filter_type, "movit.diffusion", filter_movit_diffusion_init ); MLT_REGISTER( filter_type, "movit.flip", filter_movit_flip_init ); MLT_REGISTER( filter_type, "movit.glow", filter_movit_glow_init ); MLT_REGISTER( filter_type, "movit.lift_gamma_gain", filter_lift_gamma_gain_init ); MLT_REGISTER( filter_type, "movit.mirror", filter_movit_mirror_init ); MLT_REGISTER( filter_type, "movit.opacity", filter_movit_opacity_init ); MLT_REGISTER( filter_type, "movit.rect", filter_movit_rect_init ); MLT_REGISTER( filter_type, "movit.resample", filter_movit_resample_init ); MLT_REGISTER( filter_type, "movit.resize", filter_movit_resize_init ); MLT_REGISTER( filter_type, "movit.saturation", filter_movit_saturation_init ); MLT_REGISTER( filter_type, "movit.sharpen", filter_deconvolution_sharpen_init ); MLT_REGISTER( filter_type, "movit.vignette", filter_movit_vignette_init ); MLT_REGISTER( filter_type, "movit.white_balance", filter_white_balance_init ); MLT_REGISTER( transition_type, "movit.luma_mix", transition_movit_luma_init ); MLT_REGISTER( transition_type, "movit.mix", transition_movit_mix_init ); MLT_REGISTER( transition_type, "movit.overlay", transition_movit_overlay_init ); MLT_REGISTER_METADATA( filter_type, "movit.blur", metadata, "filter_movit_blur.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.diffusion", metadata, "filter_movit_diffusion.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.flip", metadata, "filter_movit_flip.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.glow", metadata, "filter_movit_glow.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.lift_gamma_gain", metadata, "filter_movit_lift_gamma_gain.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.mirror", metadata, "filter_movit_mirror.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.opacity", metadata, "filter_movit_opacity.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.rect", metadata, "filter_movit_rect.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.saturation", metadata, "filter_movit_saturation.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.sharpen", metadata, "filter_movit_deconvolution_sharpen.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.vignette", metadata, "filter_movit_vignette.yml" ); MLT_REGISTER_METADATA( filter_type, "movit.white_balance", metadata, "filter_movit_white_balance.yml" ); MLT_REGISTER_METADATA( transition_type, "movit.luma_mix", metadata, "transition_movit_luma.yml" ); MLT_REGISTER_METADATA( transition_type, "movit.mix", metadata, "transition_movit_mix.yml" ); MLT_REGISTER_METADATA( transition_type, "movit.overlay", metadata, "transition_movit_overlay.yml" ); } mlt-6.20.0/src/modules/opengl/filter_glsl_manager.cpp000066400000000000000000000377211362234133600226370ustar00rootroot00000000000000/* * filter_glsl_manager.cpp * Copyright (C) 2011-2012 Christophe Thommeret * Copyright (C) 2013-2017 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "filter_glsl_manager.h" #include #include #include #include #include "mlt_movit_input.h" #include "mlt_flip_effect.h" #include #include extern "C" { #include } #if defined(__APPLE__) #include #elif defined(_WIN32) #include #include #else #include #endif // Texture pool may cause frames to appear out-of-order with NVIDIA // threaded optimizations. #define USE_TEXTURE_POOL 1 using namespace movit; void dec_ref_and_delete(GlslManager *p) { if (p->dec_ref() == 0) { delete p; } } GlslManager::GlslManager() : Mlt::Filter( mlt_filter_new() ) , resource_pool(new ResourcePool()) , pbo(0) , initEvent(0) , closeEvent(0) , prev_sync(NULL) { mlt_filter filter = get_filter(); if ( filter ) { // Set the mlt_filter child in case we choose to override virtual functions. filter->child = this; add_ref(mlt_global_properties()); mlt_events_register( get_properties(), "init glsl", NULL ); mlt_events_register( get_properties(), "close glsl", NULL ); initEvent = listen("init glsl", this, (mlt_listener) GlslManager::onInit); closeEvent = listen("close glsl", this, (mlt_listener) GlslManager::onClose); } } GlslManager::~GlslManager() { mlt_log_debug(get_service(), "%s\n", __FUNCTION__); cleanupContext(); // XXX If there is still a frame holding a reference to a texture after this // destructor is called, then it will crash in release_texture(). // while (texture_list.peek_back()) // delete (glsl_texture) texture_list.pop_back(); delete initEvent; delete closeEvent; if (prev_sync != NULL) { glDeleteSync( prev_sync ); } while (syncs_to_delete.count() > 0) { GLsync sync = (GLsync) syncs_to_delete.pop_front(); glDeleteSync( sync ); } delete resource_pool; } void GlslManager::add_ref(mlt_properties properties) { inc_ref(); mlt_properties_set_data(properties, "glslManager", this, 0, (mlt_destructor) dec_ref_and_delete, NULL); } GlslManager* GlslManager::get_instance() { return (GlslManager*) mlt_properties_get_data(mlt_global_properties(), "glslManager", 0); } glsl_texture GlslManager::get_texture(int width, int height, GLint internal_format) { #if USE_TEXTURE_POOL lock(); for (int i = 0; i < texture_list.count(); ++i) { glsl_texture tex = (glsl_texture) texture_list.peek(i); if (!tex->used && (tex->width == width) && (tex->height == height) && (tex->internal_format == internal_format)) { glBindTexture(GL_TEXTURE_2D, tex->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture( GL_TEXTURE_2D, 0); tex->used = 1; unlock(); return tex; } } unlock(); #endif GLuint tex = 0; glGenTextures(1, &tex); if (!tex) { return NULL; } glsl_texture gtex = new glsl_texture_s; if (!gtex) { glDeleteTextures(1, &tex); return NULL; } glBindTexture( GL_TEXTURE_2D, tex ); glTexImage2D( GL_TEXTURE_2D, 0, internal_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glBindTexture( GL_TEXTURE_2D, 0 ); gtex->texture = tex; gtex->width = width; gtex->height = height; gtex->internal_format = internal_format; gtex->used = 1; #if USE_TEXTURE_POOL lock(); texture_list.push_back(gtex); unlock(); #endif return gtex; } void GlslManager::release_texture(glsl_texture texture) { #if USE_TEXTURE_POOL texture->used = 0; #else GlslManager* g = GlslManager::get_instance(); g->lock(); g->texture_list.push_back(texture); g->unlock(); #endif } void GlslManager::delete_sync(GLsync sync) { // We do not know which thread we are called from, and we can only // delete this if we are in one with a valid OpenGL context. // Thus, store it for later deletion in render_frame_texture(). GlslManager* g = GlslManager::get_instance(); g->lock(); g->syncs_to_delete.push_back(sync); g->unlock(); } glsl_pbo GlslManager::get_pbo(int size) { lock(); if (!pbo) { GLuint pb = 0; glGenBuffers(1, &pb); if (!pb) { unlock(); return NULL; } pbo = new glsl_pbo_s; if (!pbo) { glDeleteBuffers(1, &pb); unlock(); return NULL; } pbo->pbo = pb; pbo->size = 0; } if (size > pbo->size) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo->pbo); glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, size, NULL, GL_STREAM_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); pbo->size = size; } unlock(); return pbo; } void GlslManager::cleanupContext() { lock(); while (texture_list.peek_back()) { glsl_texture texture = (glsl_texture) texture_list.peek_back(); glDeleteTextures(1, &texture->texture); delete texture; texture_list.pop_back(); } if (pbo) { glDeleteBuffers(1, &pbo->pbo); delete pbo; pbo = 0; } unlock(); } void GlslManager::onInit( mlt_properties owner, GlslManager* filter ) { mlt_log_debug( filter->get_service(), "%s\n", __FUNCTION__ ); #ifdef _WIN32 std::string path = std::string(mlt_environment("MLT_APPDIR")).append("\\share\\movit"); #elif defined(__APPLE__) && defined(RELOCATABLE) std::string path = std::string(mlt_environment("MLT_APPDIR")).append("/Resources/movit"); #else std::string path = std::string(getenv("MLT_MOVIT_PATH") ? getenv("MLT_MOVIT_PATH") : SHADERDIR); #endif bool success = init_movit( path, mlt_log_get_level() == MLT_LOG_DEBUG? MOVIT_DEBUG_ON : MOVIT_DEBUG_OFF ); filter->set( "glsl_supported", success ); } void GlslManager::onClose( mlt_properties owner, GlslManager *filter ) { filter->cleanupContext(); } void GlslManager::onServiceChanged( mlt_properties owner, mlt_service aservice ) { Mlt::Service service( aservice ); service.lock(); service.set( "movit chain", NULL, 0 ); service.unlock(); } void GlslManager::onPropertyChanged( mlt_properties owner, mlt_service service, const char* property ) { if ( property && std::string( property ) == "disable" ) onServiceChanged( owner, service ); } extern "C" { mlt_filter filter_glsl_manager_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { GlslManager* g = GlslManager::get_instance(); if (g) g->inc_ref(); else g = new GlslManager(); return g->get_filter(); } } // extern "C" static void deleteChain( GlslChain* chain ) { // The Input* is owned by the EffectChain, but the MltInput* is not. // Thus, we have to delete it here. for (std::map::iterator input_it = chain->inputs.begin(); input_it != chain->inputs.end(); ++input_it) { delete input_it->second; } delete chain->effect_chain; delete chain; } void* GlslManager::get_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, int *length ) { const char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" ); char buf[256]; snprintf( buf, sizeof(buf), "%s_%s", key, unique_id ); return mlt_properties_get_data( MLT_FRAME_PROPERTIES(frame), buf, length ); } int GlslManager::set_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise ) { const char *unique_id = mlt_properties_get( MLT_SERVICE_PROPERTIES(service), "_unique_id" ); char buf[256]; snprintf( buf, sizeof(buf), "%s_%s", key, unique_id ); return mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), buf, value, length, destroy, serialise ); } void GlslManager::set_chain( mlt_service service, GlslChain* chain ) { mlt_properties_set_data( MLT_SERVICE_PROPERTIES(service), "_movit chain", chain, 0, (mlt_destructor) deleteChain, NULL ); } GlslChain* GlslManager::get_chain( mlt_service service ) { return (GlslChain*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "_movit chain", NULL ); } Effect* GlslManager::get_effect( mlt_service service, mlt_frame frame ) { return (Effect*) get_frame_specific_data( service, frame, "_movit effect", NULL ); } Effect* GlslManager::set_effect( mlt_service service, mlt_frame frame, Effect* effect ) { set_frame_specific_data( service, frame, "_movit effect", effect, 0, NULL, NULL ); return effect; } MltInput* GlslManager::get_input( mlt_producer producer, mlt_frame frame ) { return (MltInput*) get_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input", NULL ); } MltInput* GlslManager::set_input( mlt_producer producer, mlt_frame frame, MltInput* input ) { set_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input", input, 0, NULL, NULL ); return input; } uint8_t* GlslManager::get_input_pixel_pointer( mlt_producer producer, mlt_frame frame ) { return (uint8_t*) get_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input pp", NULL ); } uint8_t* GlslManager::set_input_pixel_pointer( mlt_producer producer, mlt_frame frame, uint8_t* image ) { set_frame_specific_data( MLT_PRODUCER_SERVICE(producer), frame, "_movit input pp", image, 0, NULL, NULL ); return image; } mlt_service GlslManager::get_effect_input( mlt_service service, mlt_frame frame ) { return (mlt_service) get_frame_specific_data( service, frame, "_movit effect input", NULL ); } void GlslManager::set_effect_input( mlt_service service, mlt_frame frame, mlt_service input_service ) { set_frame_specific_data( service, frame, "_movit effect input", input_service, 0, NULL, NULL ); } void GlslManager::get_effect_secondary_input( mlt_service service, mlt_frame frame, mlt_service *input_service, mlt_frame *input_frame) { *input_service = (mlt_service) get_frame_specific_data( service, frame, "_movit effect secondary input", NULL ); *input_frame = (mlt_frame) get_frame_specific_data( service, frame, "_movit effect secondary input frame", NULL ); } void GlslManager::set_effect_secondary_input( mlt_service service, mlt_frame frame, mlt_service input_service, mlt_frame input_frame ) { set_frame_specific_data( service, frame, "_movit effect secondary input", input_service, 0, NULL, NULL ); set_frame_specific_data( service, frame, "_movit effect secondary input frame", input_frame, 0, NULL, NULL ); } void GlslManager::get_effect_third_input( mlt_service service, mlt_frame frame, mlt_service *input_service, mlt_frame *input_frame) { *input_service = (mlt_service) get_frame_specific_data( service, frame, "_movit effect third input", NULL ); *input_frame = (mlt_frame) get_frame_specific_data( service, frame, "_movit effect third input frame", NULL ); } void GlslManager::set_effect_third_input( mlt_service service, mlt_frame frame, mlt_service input_service, mlt_frame input_frame ) { set_frame_specific_data( service, frame, "_movit effect third input", input_service, 0, NULL, NULL ); set_frame_specific_data( service, frame, "_movit effect third input frame", input_frame, 0, NULL, NULL ); } int GlslManager::render_frame_texture(EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) { glsl_texture texture = get_texture( width, height, GL_RGBA8 ); if (!texture) { return 1; } GLuint fbo; glGenFramebuffers( 1, &fbo ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, fbo ); check_error(); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); lock(); while (syncs_to_delete.count() > 0) { GLsync sync = (GLsync) syncs_to_delete.pop_front(); glDeleteSync( sync ); } #if !USE_TEXTURE_POOL while (texture_list.count() > 0) { glsl_texture texture = (glsl_texture) texture_list.pop_back(); glDeleteTextures(1, &texture->texture); delete texture; } #endif unlock(); // Make sure we never have more than one frame pending at any time. // This ensures we do not swamp the GPU with so much work // that we cannot actually display the frames we generate. if (prev_sync != NULL) { glFlush(); glClientWaitSync( prev_sync, 0, GL_TIMEOUT_IGNORED ); glDeleteSync( prev_sync ); } chain->render_to_fbo( fbo, width, height ); prev_sync = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); GLsync sync = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); glDeleteFramebuffers( 1, &fbo ); check_error(); *image = (uint8_t*) &texture->texture; mlt_frame_set_image( frame, *image, 0, NULL ); mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL ); mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.fence", sync, 0, (mlt_destructor) GlslManager::delete_sync, NULL ); return 0; } int GlslManager::render_frame_rgba(EffectChain *chain, mlt_frame frame, int width, int height, uint8_t **image) { glsl_texture texture = get_texture( width, height, GL_RGBA8 ); if (!texture) { return 1; } // Use a PBO to hold the data we read back with glReadPixels(). // (Intel/DRI goes into a slow path if we don't read to PBO.) int img_size = width * height * 4; glsl_pbo pbo = get_pbo( img_size ); if (!pbo) { release_texture(texture); return 1; } // Set the FBO GLuint fbo; glGenFramebuffers( 1, &fbo ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, fbo ); check_error(); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); chain->render_to_fbo( fbo, width, height ); // Read FBO into PBO glBindFramebuffer( GL_FRAMEBUFFER, fbo ); check_error(); glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, pbo->pbo ); check_error(); glBufferData( GL_PIXEL_PACK_BUFFER_ARB, img_size, NULL, GL_STREAM_READ ); check_error(); glReadPixels( 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0) ); check_error(); // Copy from PBO uint8_t* buf = (uint8_t*) glMapBuffer( GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY ); check_error(); *image = (uint8_t*) mlt_pool_alloc( img_size ); mlt_frame_set_image( frame, *image, img_size, mlt_pool_release ); memcpy( *image, buf, img_size ); // Convert BGRA to RGBA register uint8_t *p = *image; register int n = width * height + 1; while ( --n ) { uint8_t b = p[0]; *p = p[2]; p += 2; *p = b; p += 2; } // Release PBO and FBO glUnmapBuffer( GL_PIXEL_PACK_BUFFER_ARB ); check_error(); glBindBuffer( GL_PIXEL_PACK_BUFFER_ARB, 0 ); check_error(); glBindFramebuffer( GL_FRAMEBUFFER, 0 ); check_error(); glBindTexture( GL_TEXTURE_2D, 0 ); check_error(); mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), "movit.convert.texture", texture, 0, (mlt_destructor) GlslManager::release_texture, NULL); glDeleteFramebuffers( 1, &fbo ); check_error(); return 0; } void GlslManager::lock_service( mlt_frame frame ) { Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); producer.lock(); } void GlslManager::unlock_service( mlt_frame frame ) { Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); producer.unlock(); } mlt-6.20.0/src/modules/opengl/filter_glsl_manager.h000066400000000000000000000114041362234133600222720ustar00rootroot00000000000000/* * filter_glsl_manager.h * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GLSL_MANAGER_H #define GLSL_MANAGER_H // Include a random Movit header file to get in GL.h, without including it // ourselves (which might interfere with whatever OpenGL extension library // Movit has chosen to use). #include #include #include #include #include #define MAXLISTCOUNT 1024 typedef struct glsl_list_s *glsl_list; struct glsl_list_s { void *items[MAXLISTCOUNT]; int count; int ( *add )( glsl_list, void* ); void* ( *at )( glsl_list, int ); void* ( *take_at )( glsl_list, int ); void* ( *take )( glsl_list, void* ); }; struct glsl_texture_s { int used; GLuint texture; int width; int height; GLint internal_format; }; typedef struct glsl_texture_s *glsl_texture; struct glsl_pbo_s { int size; GLuint pbo; }; typedef struct glsl_pbo_s *glsl_pbo; namespace movit { class Effect; class EffectChain; class ResourcePool; } // namespace movit class MltInput; struct GlslChain { movit::EffectChain *effect_chain; // All MltInputs in the effect chain. These are not owned by the // EffectChain (although the contained Input* is). std::map inputs; // All services owned by the effect chain and their associated Movit effect. std::map effects; // For each effect in the Movit graph, a unique identifier for the service // and whether it's disabled or not, using post-order traversal. // We need to generate the chain if and only if this has changed. std::string fingerprint; }; class GlslManager : public Mlt::Filter { public: GlslManager(); ~GlslManager(); void add_ref(mlt_properties properties); static GlslManager* get_instance(); glsl_texture get_texture(int width, int height, GLint internal_format); static void release_texture(glsl_texture); static void delete_sync(GLsync sync); glsl_pbo get_pbo(int size); void cleanupContext(); movit::ResourcePool* get_resource_pool() { return resource_pool; } static void set_chain(mlt_service, GlslChain*); static GlslChain* get_chain(mlt_service); static movit::Effect* get_effect(mlt_service, mlt_frame); static movit::Effect* set_effect(mlt_service, mlt_frame, movit::Effect*); static MltInput* get_input(mlt_producer, mlt_frame); static MltInput* set_input(mlt_producer, mlt_frame, MltInput*); static uint8_t* get_input_pixel_pointer(mlt_producer, mlt_frame); static uint8_t* set_input_pixel_pointer(mlt_producer, mlt_frame, uint8_t*); static mlt_service get_effect_input(mlt_service, mlt_frame); static void set_effect_input(mlt_service, mlt_frame, mlt_service); static void get_effect_secondary_input(mlt_service, mlt_frame, mlt_service*, mlt_frame*); static void set_effect_secondary_input(mlt_service, mlt_frame, mlt_service, mlt_frame); static void get_effect_third_input(mlt_service, mlt_frame, mlt_service*, mlt_frame*); static void set_effect_third_input(mlt_service, mlt_frame, mlt_service, mlt_frame); int render_frame_texture(movit::EffectChain*, mlt_frame, int width, int height, uint8_t **image); int render_frame_rgba(movit::EffectChain*, mlt_frame, int width, int height, uint8_t **image); static void lock_service(mlt_frame frame); static void unlock_service(mlt_frame frame); private: static void* get_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, int *length ); static int set_frame_specific_data( mlt_service service, mlt_frame frame, const char *key, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise ); static void onInit( mlt_properties owner, GlslManager* filter ); static void onClose( mlt_properties owner, GlslManager* filter ); static void onServiceChanged( mlt_properties owner, mlt_service service ); static void onPropertyChanged( mlt_properties owner, mlt_service service, const char* property ); movit::ResourcePool* resource_pool; Mlt::Deque texture_list; Mlt::Deque syncs_to_delete; glsl_pbo pbo; Mlt::Event* initEvent; Mlt::Event* closeEvent; GLsync prev_sync; }; #endif // GLSL_MANAGER_H mlt-6.20.0/src/modules/opengl/filter_movit_blur.cpp000066400000000000000000000051611362234133600223570ustar00rootroot00000000000000/* * filter_movit_blur.cpp * Copyright (C) 2013-2020 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); double radius = mlt_properties_anim_get_double( properties, "radius", mlt_filter_get_position( filter, frame ), mlt_filter_get_length2( filter, frame ) ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); radius *= mlt_profile_scale_width(profile, *width); mlt_properties_set_double( properties, "_movit.parms.float.radius", radius ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new BlurEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_movit_blur_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set_double( properties, "radius", 3 ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_blur.yml000066400000000000000000000007751362234133600224040ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.blur title: Blur (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A separable 2D blur implemented by a combination of mipmap filtering and convolution (essentially giving a convolution with a piecewise linear approximation to the true impulse response). parameters: - identifier: radius title: Radius type: float minimum: 0 default: 3 mutable: yes mlt-6.20.0/src/modules/opengl/filter_movit_convert.cpp000066400000000000000000000644521362234133600231030ustar00rootroot00000000000000/* * filter_movit_convert.cpp * Copyright (C) 2013-2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "filter_glsl_manager.h" #include #include #include "mlt_movit_input.h" #include #include "mlt_flip_effect.h" using namespace movit; static void set_movit_parameters( GlslChain *chain, mlt_service service, mlt_frame frame ); static void yuv422_to_yuv422p( uint8_t *yuv422, uint8_t *yuv422p, int width, int height ) { uint8_t *Y = yuv422p; uint8_t *U = Y + width * height; uint8_t *V = U + width * height / 2; int n = width * height / 2 + 1; while ( --n ) { *Y++ = *yuv422++; *U++ = *yuv422++; *Y++ = *yuv422++; *V++ = *yuv422++; } } static int convert_on_cpu( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { int error = 0; mlt_filter cpu_csc = (mlt_filter) mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "cpu_csc", NULL ); if ( cpu_csc ) { int (* save_fp )( mlt_frame self, uint8_t **image, mlt_image_format *input, mlt_image_format output ) = frame->convert_image; frame->convert_image = NULL; mlt_filter_process( cpu_csc, frame ); error = frame->convert_image( frame, image, format, output_format ); frame->convert_image = save_fp; } else { error = 1; } return error; } static void delete_chain( EffectChain* chain ) { delete chain; } // Copied from libavcodec, but we can not add that as a dependency to this module // simply for this. enum AVColorTransferCharacteristic { AVCOL_TRC_BT709 = 1, ///< also ITU-R BT1361 AVCOL_TRC_UNSPECIFIED = 2, AVCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM AVCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG AVCOL_TRC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC AVCOL_TRC_SMPTE240M = 7, AVCOL_TRC_LINEAR = 8, ///< "Linear transfer characteristics" AVCOL_TRC_LOG = 9, ///< "Logarithmic transfer characteristic (100:1 range)" AVCOL_TRC_LOG_SQRT = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt( 10 ) : 1 range)" AVCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4 AVCOL_TRC_BT1361_ECG = 12, ///< ITU-R BT1361 Extended Colour Gamut AVCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC) AVCOL_TRC_BT2020_10 = 14, ///< ITU-R BT2020 for 10 bit system AVCOL_TRC_BT2020_12 = 15, ///< ITU-R BT2020 for 12 bit system AVCOL_TRC_NB , ///< Not part of ABI }; // Get the gamma from the frame "color_trc" property as set by producer or this filter. static GammaCurve getGammaCurve( int color_trc ) { switch ( color_trc ) { case AVCOL_TRC_LINEAR: return GAMMA_LINEAR; case AVCOL_TRC_GAMMA22: case AVCOL_TRC_IEC61966_2_1: return GAMMA_sRGB; case AVCOL_TRC_BT2020_10: return GAMMA_REC_2020_10_BIT; case AVCOL_TRC_BT2020_12: return GAMMA_REC_2020_12_BIT; default: return GAMMA_REC_709; } } // Get the gamma from the consumer's "color_trc" property. // Also, update the frame's color_trc property with the selection. static GammaCurve getGammaCurve( mlt_properties properties ) { const char *color_trc = mlt_properties_get( properties, "consumer_color_trc" ); if ( color_trc ) { // If specified with enum or int. int n = mlt_properties_get_int( properties, "consumer_color_trc" ); switch ( n ) { case AVCOL_TRC_BT709: case AVCOL_TRC_SMPTE170M: mlt_properties_set_int( properties, "color_trc", n ); return GAMMA_REC_709; case AVCOL_TRC_LINEAR: mlt_properties_set_int( properties, "color_trc", n ); return GAMMA_LINEAR; case AVCOL_TRC_BT2020_10: mlt_properties_set_int( properties, "color_trc", n ); return GAMMA_REC_2020_10_BIT; case AVCOL_TRC_BT2020_12: mlt_properties_set_int( properties, "color_trc", n ); return GAMMA_REC_2020_12_BIT; default: // If specified by string. if ( !strcmp( color_trc, "bt709" ) ) { mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_BT709 ); return GAMMA_REC_709; } else if ( !strcmp( color_trc, "smpte170m" ) ) { mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_SMPTE170M ); return GAMMA_REC_709; } else if ( !strcmp( color_trc, "linear" ) ) { mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_LINEAR ); return GAMMA_LINEAR; } else if ( !strcmp( color_trc, "bt2020_10bit" ) ) { mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_BT2020_10 ); return GAMMA_REC_2020_10_BIT; } else if ( !strcmp( color_trc, "bt2020_12bit" ) ) { mlt_properties_set_int( properties, "color_trc", AVCOL_TRC_BT2020_12 ); return GAMMA_REC_2020_12_BIT; } break; } } return GAMMA_sRGB; } static void get_format_from_properties( mlt_properties properties, ImageFormat* image_format, YCbCrFormat* ycbcr_format ) { switch ( mlt_properties_get_int( properties, "colorspace" ) ) { case 601: ycbcr_format->luma_coefficients = YCBCR_REC_601; break; case 709: default: ycbcr_format->luma_coefficients = YCBCR_REC_709; break; } switch ( mlt_properties_get_int( properties, "color_primaries" ) ) { case 601625: image_format->color_space = COLORSPACE_REC_601_625; break; case 601525: image_format->color_space = COLORSPACE_REC_601_525; break; case 709: default: image_format->color_space = COLORSPACE_REC_709; break; } image_format->gamma_curve = getGammaCurve( mlt_properties_get_int( properties, "color_trc" ) ); if ( mlt_properties_get_int( properties, "force_full_luma" ) ) { ycbcr_format->full_range = true; } else { ycbcr_format->full_range = ( mlt_properties_get_int( properties, "full_luma" ) == 1 ); } // TODO: make new frame properties set by producers ycbcr_format->cb_x_position = ycbcr_format->cr_x_position = 0.0f; ycbcr_format->cb_y_position = ycbcr_format->cr_y_position = 0.5f; } static void build_fingerprint( mlt_service service, mlt_frame frame, std::string *fingerprint ) { if ( service == (mlt_service) -1 ) { fingerprint->append( "input" ); return; } Effect* effect = GlslManager::get_effect( service, frame ); assert( effect ); mlt_service input_a = GlslManager::get_effect_input( service, frame ); fingerprint->push_back( '(' ); build_fingerprint( input_a, frame, fingerprint ); fingerprint->push_back( ')' ); mlt_frame frame_b; mlt_service input_b; GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); if ( input_b ) { fingerprint->push_back( '(' ); build_fingerprint( input_b, frame_b, fingerprint ); fingerprint->push_back( ')' ); } GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); if ( input_b ) { fingerprint->push_back( '(' ); build_fingerprint( input_b, frame_b, fingerprint ); fingerprint->push_back( ')' ); } fingerprint->push_back( '(' ); fingerprint->append( mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_unique_id" ) ); const char* effect_fingerprint = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_movit fingerprint" ); if ( effect_fingerprint ) { fingerprint->push_back( '[' ); fingerprint->append( effect_fingerprint ); fingerprint->push_back( ']' ); } bool disable = mlt_properties_get_int( MLT_SERVICE_PROPERTIES( service ), "_movit.parms.int.disable" ); if ( disable ) { fingerprint->push_back( 'd' ); } fingerprint->push_back( ')' ); } static Effect* build_movit_chain( mlt_service service, mlt_frame frame, GlslChain *chain ) { if ( service == (mlt_service) -1 ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); MltInput* input = GlslManager::get_input( producer, frame ); GlslManager::set_input( producer, frame, NULL ); chain->effect_chain->add_input( input->get_input() ); chain->inputs.insert(std::make_pair( producer, input ) ); return input->get_input(); } Effect* effect = GlslManager::get_effect( service, frame ); assert( effect ); GlslManager::set_effect( service, frame, NULL ); mlt_service input_a = GlslManager::get_effect_input( service, frame ); mlt_service input_b, input_c; mlt_frame frame_b, frame_c; GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); GlslManager::get_effect_third_input( service, frame, &input_c, &frame_c ); Effect *effect_a = build_movit_chain( input_a, frame, chain ); if ( input_c && input_b ) { Effect *effect_b = build_movit_chain( input_b, frame_b, chain ); Effect *effect_c = build_movit_chain( input_c, frame_c, chain ); chain->effect_chain->add_effect( effect, effect_a, effect_b, effect_c ); } else if ( input_b ) { Effect *effect_b = build_movit_chain( input_b, frame_b, chain ); chain->effect_chain->add_effect( effect, effect_a, effect_b ); } else { chain->effect_chain->add_effect( effect, effect_a ); } chain->effects.insert(std::make_pair( service, effect ) ); return effect; } static void dispose_movit_effects( mlt_service service, mlt_frame frame ) { if ( service == (mlt_service) -1 ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); delete GlslManager::get_input( producer, frame ); GlslManager::set_input( producer, frame, NULL ); return; } delete GlslManager::get_effect( service, frame ); GlslManager::set_effect( service, frame, NULL ); mlt_service input_a = GlslManager::get_effect_input( service, frame ); mlt_service input_b; mlt_frame frame_b; GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); dispose_movit_effects( input_a, frame ); if ( input_b ) { dispose_movit_effects( input_b, frame_b ); } GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); if ( input_b ) { dispose_movit_effects( input_b, frame_b ); } } static void finalize_movit_chain( mlt_service leaf_service, mlt_frame frame ) { GlslChain* chain = GlslManager::get_chain( leaf_service ); std::string new_fingerprint; build_fingerprint( leaf_service, frame, &new_fingerprint ); // Build the chain if needed. if ( !chain || new_fingerprint != chain->fingerprint ) { mlt_log_debug( leaf_service, "=== CREATING NEW CHAIN (old chain=%p, leaf=%p, fingerprint=%s) ===\n", chain, leaf_service, new_fingerprint.c_str() ); mlt_profile profile = mlt_service_profile( leaf_service ); chain = new GlslChain; chain->effect_chain = new EffectChain( profile->display_aspect_num, profile->display_aspect_den, GlslManager::get_instance()->get_resource_pool() ); chain->fingerprint = new_fingerprint; build_movit_chain( leaf_service, frame, chain ); set_movit_parameters( chain, leaf_service, frame ); chain->effect_chain->add_effect( new Mlt::VerticalFlip ); ImageFormat output_format; output_format.color_space = COLORSPACE_sRGB; output_format.gamma_curve = getGammaCurve( MLT_FRAME_PROPERTIES(frame) ); chain->effect_chain->add_output(output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); chain->effect_chain->set_dither_bits(8); chain->effect_chain->finalize(); GlslManager::set_chain( leaf_service, chain ); } else { // Delete all the created Effect instances to avoid memory leaks. dispose_movit_effects( leaf_service, frame ); } } static void set_movit_parameters( GlslChain *chain, mlt_service service, mlt_frame frame ) { if ( service == (mlt_service) -1 ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); MltInput* input = chain->inputs[ producer ]; if (input) input->set_pixel_data( GlslManager::get_input_pixel_pointer( producer, frame ) ); return; } Effect* effect = chain->effects[ service ]; mlt_service input_a = GlslManager::get_effect_input( service, frame ); set_movit_parameters( chain, input_a, frame ); mlt_service input_b; mlt_frame frame_b; GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); if ( input_b ) { set_movit_parameters( chain, input_b, frame_b ); } GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); if ( input_b ) { set_movit_parameters( chain, input_b, frame_b ); } mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); int count = mlt_properties_count( properties ); for (int i = 0; i < count; ++i) { const char *name = mlt_properties_get_name( properties, i ); if (strncmp(name, "_movit.parms.float.", strlen("_movit.parms.float.")) == 0 && mlt_properties_get_value( properties, i )) { bool ok = effect->set_float(name + strlen("_movit.parms.float."), mlt_properties_get_double( properties, name )); assert(ok); } if (strncmp(name, "_movit.parms.int.", strlen("_movit.parms.int.")) == 0 && mlt_properties_get_value( properties, i )) { bool ok = effect->set_int(name + strlen("_movit.parms.int."), mlt_properties_get_int( properties, name )); assert(ok); } if (strncmp(name, "_movit.parms.vec3.", strlen("_movit.parms.vec3.")) == 0 && strcmp(name + strlen(name) - 3, "[0]") == 0 && mlt_properties_get_value( properties, i )) { float val[3]; char *name_copy = strdup(name); char *index_char = name_copy + strlen(name_copy) - 2; val[0] = mlt_properties_get_double( properties, name_copy ); *index_char = '1'; val[1] = mlt_properties_get_double( properties, name_copy ); *index_char = '2'; val[2] = mlt_properties_get_double( properties, name_copy ); index_char[-1] = '\0'; bool ok = effect->set_vec3(name_copy + strlen("_movit.parms.vec3."), val); assert(ok); free(name_copy); } if (strncmp(name, "_movit.parms.vec4.", strlen("_movit.parms.vec4.")) == 0 && strcmp(name + strlen(name) - 3, "[0]") == 0 && mlt_properties_get_value( properties, i )) { float val[4]; char *name_copy = strdup(name); char *index_char = name_copy + strlen(name_copy) - 2; val[0] = mlt_properties_get_double( properties, name_copy ); *index_char = '1'; val[1] = mlt_properties_get_double( properties, name_copy ); *index_char = '2'; val[2] = mlt_properties_get_double( properties, name_copy ); *index_char = '3'; val[3] = mlt_properties_get_double( properties, name_copy ); index_char[-1] = '\0'; bool ok = effect->set_vec4(name_copy + strlen("_movit.parms.vec4."), val); assert(ok); free(name_copy); } } } static void dispose_pixel_pointers( GlslChain *chain, mlt_service service, mlt_frame frame ) { if ( service == (mlt_service) -1 ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); MltInput* input = chain->inputs[ producer ]; if (input) input->invalidate_pixel_data(); mlt_pool_release( GlslManager::get_input_pixel_pointer( producer, frame ) ); return; } mlt_service input_a = GlslManager::get_effect_input( service, frame ); dispose_pixel_pointers( chain, input_a, frame ); mlt_service input_b; mlt_frame frame_b; GlslManager::get_effect_secondary_input( service, frame, &input_b, &frame_b ); if ( input_b ) { dispose_pixel_pointers( chain, input_b, frame_b ); } GlslManager::get_effect_third_input( service, frame, &input_b, &frame_b ); if ( input_b ) { dispose_pixel_pointers( chain, input_b, frame_b ); } } static int movit_render( EffectChain *chain, mlt_frame frame, mlt_image_format *format, mlt_image_format output_format, int width, int height, uint8_t **image ) { GlslManager* glsl = GlslManager::get_instance(); int error; if ( output_format == mlt_image_glsl_texture ) { error = glsl->render_frame_texture( chain, frame, width, height, image ); } else { error = glsl->render_frame_rgba( chain, frame, width, height, image ); if ( !error && output_format != mlt_image_rgb24a ) { *format = mlt_image_rgb24a; error = convert_on_cpu( frame, image, format, output_format ); } } return error; } // Create an MltInput for an image with the given format and dimensions. static MltInput* create_input( mlt_properties properties, mlt_image_format format, int aspect_width, int aspect_height, int width, int height ) { MltInput* input = new MltInput( format ); if ( format == mlt_image_rgb24a || format == mlt_image_opengl ) { // TODO: Get the color space if available. input->useFlatInput( FORMAT_RGBA_POSTMULTIPLIED_ALPHA, width, height ); } else if ( format == mlt_image_rgb24 ) { // TODO: Get the color space if available. input->useFlatInput( FORMAT_RGB, width, height ); } else if ( format == mlt_image_yuv420p ) { ImageFormat image_format = {}; YCbCrFormat ycbcr_format = {}; get_format_from_properties( properties, &image_format, &ycbcr_format ); ycbcr_format.chroma_subsampling_x = ycbcr_format.chroma_subsampling_y = 2; input->useYCbCrInput( image_format, ycbcr_format, width, height ); } else if ( format == mlt_image_yuv422 ) { ImageFormat image_format = {}; YCbCrFormat ycbcr_format = {}; get_format_from_properties( properties, &image_format, &ycbcr_format ); ycbcr_format.chroma_subsampling_x = 2; ycbcr_format.chroma_subsampling_y = 1; input->useYCbCrInput( image_format, ycbcr_format, width, height ); } return input; } // Make a copy of the given image (allocated using mlt_pool_alloc) suitable // to pass as pixel pointer to an MltInput (created using create_input // with the same parameters), and return that pointer. static uint8_t* make_input_copy( mlt_image_format format, uint8_t *image, int width, int height ) { // We use height-1 because mlt_image_format_size() uses height + 1. // XXX Remove -1 when mlt_image_format_size() is changed. int img_size = mlt_image_format_size( format, width, height - 1, NULL ); uint8_t* img_copy = (uint8_t*) mlt_pool_alloc( img_size ); if ( format == mlt_image_yuv422 ) { yuv422_to_yuv422p( image, img_copy, width, height ); } else { memcpy( img_copy, image, img_size ); } return img_copy; } static int convert_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, mlt_image_format output_format ) { // Nothing to do! if ( *format == output_format ) return 0; mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_log_debug( NULL, "filter_movit_convert: %s -> %s (%d)\n", mlt_image_format_name( *format ), mlt_image_format_name( output_format ), mlt_frame_get_position( frame ) ); // Use CPU if glsl not initialized or not supported. GlslManager* glsl = GlslManager::get_instance(); if ( !glsl || !glsl->get_int("glsl_supported" ) ) return convert_on_cpu( frame, image, format, output_format ); // Do non-GL image conversions on a CPU-based image converter. if ( *format != mlt_image_glsl && output_format != mlt_image_glsl && output_format != mlt_image_glsl_texture ) return convert_on_cpu( frame, image, format, output_format ); int error = 0; int width = mlt_properties_get_int( properties, "width" ); int height = mlt_properties_get_int( properties, "height" ); GlslManager::get_instance()->lock_service( frame ); // If we're at the beginning of a series of Movit effects, store the input // sent into the chain. if ( output_format == mlt_image_glsl ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); MltInput *input = create_input( properties, *format, profile->width, profile->height, width, height ); GlslManager::set_input( producer, frame, input ); uint8_t *img_copy = make_input_copy( *format, *image, width, height ); GlslManager::set_input_pixel_pointer( producer, frame, img_copy ); *image = (uint8_t *) -1; mlt_frame_set_image( frame, *image, 0, NULL ); } // If we're at the _end_ of a series of Movit effects, render the chain. if ( *format == mlt_image_glsl ) { mlt_service leaf_service = (mlt_service) *image; if ( leaf_service == (mlt_service) -1 ) { // Something on the way requested conversion to mlt_glsl, // but never added an effect. Don't build a Movit chain; // just do the conversion and we're done. mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); MltInput *input = GlslManager::get_input( producer, frame ); *image = GlslManager::get_input_pixel_pointer( producer, frame ); *format = input->get_format(); delete input; GlslManager::get_instance()->unlock_service( frame ); return convert_on_cpu( frame, image, format, output_format ); } // Construct the chain unless we already have a good one. finalize_movit_chain( leaf_service, frame ); // Set per-frame parameters now that we know which Effect instances to set them on. // (finalize_movit_chain may already have done this, though, but twice doesn't hurt.) GlslChain *chain = GlslManager::get_chain( leaf_service ); set_movit_parameters( chain, leaf_service, frame ); error = movit_render( chain->effect_chain, frame, format, output_format, width, height, image ); dispose_pixel_pointers( chain, leaf_service, frame ); } // If we've been asked to render some frame directly to a texture (without any // effects in-between), we create a new mini-chain to do so. if ( *format != mlt_image_glsl && output_format == mlt_image_glsl_texture ) { // We might already have a texture from a previous conversion from mlt_image_glsl. glsl_texture texture = (glsl_texture) mlt_properties_get_data( properties, "movit.convert.texture", NULL ); // XXX: requires a special property set on the frame by the app for now // because we do not have reliable way to clear the texture property // when a downstream filter has changed image. if ( texture && mlt_properties_get_int( properties, "movit.convert.use_texture") ) { *image = (uint8_t*) &texture->texture; mlt_frame_set_image( frame, *image, 0, NULL ); } else { // Use a separate chain to convert image in RAM to OpenGL texture. // Use cached chain if available and compatible. Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) ); EffectChain *chain = (EffectChain*) producer.get_data( "movit.convert.chain" ); MltInput *input = (MltInput*) producer.get_data( "movit.convert.input" ); int w = producer.get_int( "movit.convert.width" ); int h = producer.get_int( "movit.convert.height" ); mlt_image_format f = (mlt_image_format) producer.get_int( "movit.convert.format" ); if ( !chain || !input || width != w || height != h || *format != f ) { chain = new EffectChain( width, height, GlslManager::get_instance()->get_resource_pool() ); input = create_input( properties, *format, width, height, width, height ); chain->add_input( input->get_input() ); chain->add_effect( new Mlt::VerticalFlip() ); ImageFormat movit_output_format; movit_output_format.color_space = COLORSPACE_sRGB; movit_output_format.gamma_curve = getGammaCurve( properties ); chain->add_output(movit_output_format, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); chain->set_dither_bits(8); chain->finalize(); producer.set( "movit.convert.chain", chain, 0, (mlt_destructor) delete_chain ); producer.set( "movit.convert.input", input, 0, NULL ); producer.set( "movit.convert.width", width ); producer.set( "movit.convert.height", height ); producer.set( "movit.convert.format", *format ); } if ( *format == mlt_image_yuv422 ) { // We need to convert to planar, which make_input_copy() will do for us. uint8_t *planar = make_input_copy( *format, *image, width, height ); input->set_pixel_data( planar ); error = movit_render( chain, frame, format, output_format, width, height, image ); mlt_pool_release( planar ); } else { input->set_pixel_data( *image ); error = movit_render( chain, frame, format, output_format, width, height, image ); } } } GlslManager::get_instance()->unlock_service( frame ); mlt_properties_set_int( properties, "format", output_format ); *format = output_format; return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { // Set a default colorspace on the frame if not yet set by the producer. // The producer may still change it during get_image. // This way we do not have to modify each producer to set a valid colorspace. mlt_properties properties = MLT_FRAME_PROPERTIES(frame); if ( mlt_properties_get_int( properties, "colorspace" ) <= 0 ) mlt_properties_set_int( properties, "colorspace", mlt_service_profile( MLT_FILTER_SERVICE(filter) )->colorspace ); frame->convert_image = convert_image; mlt_filter cpu_csc = (mlt_filter) mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), "cpu_csc", NULL ); mlt_properties_inc_ref( MLT_FILTER_PROPERTIES(cpu_csc) ); mlt_properties_set_data( properties, "cpu_csc", cpu_csc, 0, (mlt_destructor) mlt_filter_close, NULL ); return frame; } static mlt_filter create_filter( mlt_profile profile, const char *effect ) { mlt_filter filter; char *id = strdup( effect ); char *arg = strchr( id, ':' ); if ( arg != NULL ) *arg ++ = '\0'; // The swscale and avcolor_space filters require resolution as arg to test compatibility if ( !strcmp( effect, "avcolor_space" ) ) filter = mlt_factory_filter( profile, id, &profile->width ); else filter = mlt_factory_filter( profile, id, arg ); if ( filter ) mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "_loader", 1 ); free( id ); return filter; } extern "C" { mlt_filter filter_movit_convert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); #ifdef _WIN32 // XXX avcolor_space is crashing on Windows in this context! mlt_filter cpu_csc = NULL; #else mlt_filter cpu_csc = create_filter( profile, "avcolor_space" ); #endif if ( !cpu_csc ) cpu_csc = create_filter( profile, "imageconvert" ); if ( cpu_csc ) mlt_properties_set_data( MLT_FILTER_PROPERTIES( filter ), "cpu_csc", cpu_csc, 0, (mlt_destructor) mlt_filter_close, NULL ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_crop.cpp000066400000000000000000000116161362234133600223600ustar00rootroot00000000000000/* * filter_movit_crop.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include #include "optional_effect.h" using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); mlt_image_format requested_format = *format; // Correct width/height if necessary *width = mlt_properties_get_int( properties, "crop.original_width" ); *height = mlt_properties_get_int( properties, "crop.original_height" ); if ( *width == 0 || *height == 0 ) { *width = mlt_properties_get_int( properties, "meta.media.width" ); *height = mlt_properties_get_int( properties, "meta.media.height" ); } if ( *width == 0 || *height == 0 ) { *width = profile->width; *height = profile->height; } mlt_properties_set_int( properties, "rescale_width", *width ); mlt_properties_set_int( properties, "rescale_height", *height ); // Get the image as requested // *format = (mlt_image_format) mlt_properties_get_int( MLT_PRODUCER_PROPERTIES(producer), "_movit image_format" ); // This is needed to prevent conversion to mlt_image_glsl after producer, // deinterlace, or fieldorder, The latter two can force output of // an image after it had already been converted to glsl. *format = mlt_image_none; error = mlt_frame_get_image( frame, image, format, width, height, writable ); // Skip processing if requested. if ( requested_format == mlt_image_none ) return error; if ( !error && *format != mlt_image_glsl && frame->convert_image ) { // Pin the requested format to the first one returned. // mlt_properties_set_int( MLT_PRODUCER_PROPERTIES(producer), "_movit image_format", *format ); error = frame->convert_image( frame, image, format, mlt_image_glsl ); } if ( !error ) { double left = mlt_properties_get_double( properties, "crop.left" ); double right = mlt_properties_get_double( properties, "crop.right" ); double top = mlt_properties_get_double( properties, "crop.top" ); double bottom = mlt_properties_get_double( properties, "crop.bottom" ); int owidth = *width - left - right; int oheight = *height - top - bottom; owidth = owidth < 0 ? 0 : owidth; oheight = oheight < 0 ? 0 : oheight; mlt_log_debug( MLT_FILTER_SERVICE(filter), "%dx%d -> %dx%d\n", *width, *height, owidth, oheight); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_properties_set_int( properties, "_movit.parms.int.width", owidth ); mlt_properties_set_int( properties, "_movit.parms.int.height", oheight ); mlt_properties_set_double( properties, "_movit.parms.float.left", -left ); mlt_properties_set_double( properties, "_movit.parms.float.top", -top ); bool disable = ( *width == owidth && *height == oheight ); mlt_properties_set_int( properties, "_movit.parms.int.disable", disable ); GlslManager::get_instance()->unlock_service( frame ); } GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); Effect* effect = GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new OptionalEffect ); assert(effect); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); RGBATuple border_color( 0.0f, 0.0f, 0.0f, 1.0f ); bool ok = effect->set_vec4( "border_color", (float*) &border_color ); assert(ok); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" mlt_filter filter_movit_crop_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); filter->process = process; } return filter; } mlt-6.20.0/src/modules/opengl/filter_movit_deconvolution_sharpen.cpp000066400000000000000000000077571362234133600260400ustar00rootroot00000000000000/* * filter_deconvolution_sharpen.cpp * Copyright (C) 2013-2020 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); int matrix_size = mlt_properties_anim_get_int( properties, "matrix_size", position, length ); double circle_radius = mlt_properties_anim_get_double( properties, "circle_radius", position, length ); double gaussian_radius = mlt_properties_anim_get_double( properties, "gaussian_radius", position, length ); double scale = mlt_profile_scale_width(mlt_service_profile(MLT_FILTER_SERVICE(filter)), *width); circle_radius *= scale; gaussian_radius *= scale; mlt_properties_set_int( properties, "_movit.parms.int.matrix_size", matrix_size ); mlt_properties_set_double( properties, "_movit.parms.float.circle_radius", circle_radius ); mlt_properties_set_double( properties, "_movit.parms.float.gaussian_radius", gaussian_radius ); mlt_properties_set_double( properties, "_movit.parms.float.correlation", mlt_properties_anim_get_double( properties, "correlation", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.float.noise", mlt_properties_anim_get_double( properties, "noise", position, length ) ); // DeconvolutionSharpenEffect compiles the matrix size into the shader, // so we need to regenerate the chain if this changes. char fingerprint[256]; snprintf( fingerprint, sizeof( fingerprint ), "s=%d", matrix_size ); mlt_properties_set( properties, "_movit fingerprint", fingerprint ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new DeconvolutionSharpenEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_deconvolution_sharpen_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set_int( properties, "matrix_size", 5 ); mlt_properties_set_double( properties, "circle_radius", 2.0 ); mlt_properties_set_double( properties, "gaussian_radius", 0.0 ); mlt_properties_set_double( properties, "correlation", 0.95 ); mlt_properties_set_double( properties, "noise", 0.01 ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_deconvolution_sharpen.yml000066400000000000000000000025731362234133600260460ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.sharpen title: Deconvolution Sharpen (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > Deconvolution Sharpen is a filter that sharpens by way of deconvolution (i.e., trying to reverse the blur kernel, as opposed to just boosting high frequencies), more specifically by FIR Wiener filters. It is the same algorithm as used by the (now largely abandoned) Refocus plug-in for GIMP, and I suspect the same as in Photoshop's “Smart Sharpen†filter. The effect gives generally better results than unsharp masking, but can be very GPU intensive, and requires a fair bit of tweaking to get good results without ringing and/or excessive noise. parameters: - identifier: matrix_size title: Matrix Size type: integer minimum: 0 maximum: 25 default: 5 mutable: yes - identifier: circle_radius title: Circle Radius type: float minimum: 0 default: 2 mutable: yes - identifier: gaussian_radius title: Gaussian Radius type: float minimum: 0 default: 0 mutable: yes - identifier: correlation title: Correlation type: float minimum: 0 default: 0.95 mutable: yes - identifier: noise title: Noise Level type: float minimum: 0 default: 0.01 mutable: yes mlt-6.20.0/src/modules/opengl/filter_movit_diffusion.cpp000066400000000000000000000053751362234133600234100ustar00rootroot00000000000000/* * filter_movit_diffusion.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_properties_set_double( properties, "_movit.parms.float.radius", mlt_properties_anim_get_double( properties, "radius", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.float.blurred_mix_amount", mlt_properties_anim_get_double( properties, "mix", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new DiffusionEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_movit_diffusion_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set_double( properties, "radius", 3.0 ); mlt_properties_set_double( properties, "mix", 0.3 ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_diffusion.yml000066400000000000000000000020211362234133600234100ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.diffusion title: Diffusion (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > There are many different effects that go under the name of "diffusion", seemingly all of the inspired by the effect you get when you put a diffusion filter in front of your camera lens. The effect most people want is a general flattening/smoothing of the light, and reduction of fine detail (most notably, blemishes in people's skin), without ruining edges, which a regular blur would do. We do a relatively simple version, sometimes known as "white diffusion", where we first blur the picture, and then overlay it on the original using the original as a matte. parameters: - identifier: radius title: Blur Radius type: float minimum: 0.0 default: 3.0 mutable: yes - identifier: mix title: Blurriness type: float minimum: 0.0 maximum: 1.0 default: 0.3 mutable: yes mlt-6.20.0/src/modules/opengl/filter_movit_flip.cpp000066400000000000000000000040761362234133600223510ustar00rootroot00000000000000/* * filter_movit_mirror.cpp * Copyright (C) 2019 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include #include "mlt_flip_effect.h" using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new Mlt::VerticalFlip ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_movit_flip_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_flip.yml000066400000000000000000000003341362234133600223610ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.flip title: Flip (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en description: A simple vertical flip. tags: - Video mlt-6.20.0/src/modules/opengl/filter_movit_glow.cpp000066400000000000000000000061301362234133600223600ustar00rootroot00000000000000/* * filter_movit_glow.cpp * Copyright (C) 2013-2020 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); GlslManager::get_instance()->lock_service( frame ); double radius = mlt_properties_anim_get_double( properties, "radius", position, length ); radius *= mlt_profile_scale_width(mlt_service_profile(MLT_FILTER_SERVICE(filter)), *width); mlt_properties_set_double( properties, "_movit.parms.float.radius", radius ); mlt_properties_set_double( properties, "_movit.parms.float.blurred_mix_amount", mlt_properties_anim_get_double( properties, "blur_mix", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.float.highlight_cutoff", mlt_properties_anim_get_double( properties, "highlight_cutoff", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new GlowEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_movit_glow_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set_double( properties, "radius", 20.0 ); mlt_properties_set_double( properties, "blur_mix", 1.0 ); mlt_properties_set_double( properties, "highlight_cutoff", 0.2 ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_glow.yml000066400000000000000000000013571362234133600224050ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.glow title: Glow (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > Cut out the highlights of the image (everything above a certain threshold), blur them, and overlay them onto the original image. parameters: - identifier: radius title: Radius type: float minimum: 0.0 default: 20.0 mutable: yes - identifier: blur_mix title: Highlight Blurriness type: float minimum: 0.0 maximum: 1.0 default: 1.0 mutable: yes - identifier: highlight_cutoff title: Highlight Cutoff Threshold type: float minimum: 0.0 maximum: 1.0 default: 0.2 mutable: yes mlt-6.20.0/src/modules/opengl/filter_movit_lift_gamma_gain.cpp000066400000000000000000000102451362234133600245100ustar00rootroot00000000000000/* * filter_lift_gamma_gain.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_properties_set_double( properties, "_movit.parms.vec3.lift[0]", mlt_properties_anim_get_double( properties, "lift_r", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.vec3.lift[1]", mlt_properties_anim_get_double( properties, "lift_g", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.vec3.lift[2]", mlt_properties_anim_get_double( properties, "lift_b", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.vec3.gamma[0]", mlt_properties_anim_get_double( properties, "gamma_r", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.vec3.gamma[1]", mlt_properties_anim_get_double( properties, "gamma_g", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.vec3.gamma[2]", mlt_properties_anim_get_double( properties, "gamma_b", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.vec3.gain[0]", mlt_properties_anim_get_double( properties, "gain_r", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.vec3.gain[1]", mlt_properties_anim_get_double( properties, "gain_g", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.vec3.gain[2]", mlt_properties_anim_get_double( properties, "gain_b", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new LiftGammaGainEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set_double( properties, "lift_r", 0.0 ); mlt_properties_set_double( properties, "lift_g", 0.0 ); mlt_properties_set_double( properties, "lift_b", 0.0 ); mlt_properties_set_double( properties, "gamma_r", 1.0 ); mlt_properties_set_double( properties, "gamma_g", 1.0 ); mlt_properties_set_double( properties, "gamma_b", 1.0 ); mlt_properties_set_double( properties, "gain_r", 1.0 ); mlt_properties_set_double( properties, "gain_g", 1.0 ); mlt_properties_set_double( properties, "gain_b", 1.0 ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_lift_gamma_gain.yml000066400000000000000000000040331362234133600245250ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.lift_gamma_gain title: Lift, Gamma, and Gain (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A simple lift/gamma/gain effect, used for color grading. notes: > Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, although all parameters affect the entire curve. Mathematically speaking, it is a bit unusual to look at gamma as a color, but it works pretty well in practice. The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). The lift is actually a case where we actually would _not_ want linear light; since black by definition becomes equal to the lift color, we want lift to be pretty close to black, but in linear light that means lift affects the rest of the curve relatively little. Thus, we actually convert to gamma 2.2 before lift, and then back again afterwards. (Gain and gamma are, up to constants, commutative with the de-gamma operation.) parameters: - identifier: lift_r title: Lift Red type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: lift_g title: Lift Green type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: lift_b title: Lift Blue type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: gamma_r title: Gamma Red type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gamma_g title: Gamma Green type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gamma_b title: Gamma Blue type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_r title: Gain Red type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_g title: Gain Green type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_b title: Gain Blue type: float minimum: 0.0 default: 1.0 mutable: yes mlt-6.20.0/src/modules/opengl/filter_movit_mirror.cpp000066400000000000000000000040451362234133600227250ustar00rootroot00000000000000/* * filter_movit_mirror.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new MirrorEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_movit_mirror_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_mirror.yml000066400000000000000000000003471362234133600227450ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.mirror title: Mirror (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en description: A simple horizontal mirroring. tags: - Video mlt-6.20.0/src/modules/opengl/filter_movit_opacity.cpp000066400000000000000000000057351362234133600230720ustar00rootroot00000000000000/* * filter_movit_opacity.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); double opacity = mlt_properties_anim_get_double( properties, "opacity", position, length ); double alpha = mlt_properties_anim_get_double( properties, "alpha", position, length ); mlt_properties_set_double( properties, "_movit.parms.vec4.factor[0]", opacity ); mlt_properties_set_double( properties, "_movit.parms.vec4.factor[1]", opacity ); mlt_properties_set_double( properties, "_movit.parms.vec4.factor[2]", opacity ); mlt_properties_set_double( properties, "_movit.parms.vec4.factor[3]", alpha >= 0 ? alpha : opacity ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new MultiplyEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" mlt_filter filter_movit_opacity_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set( properties, "opacity", arg? arg : "1" ); mlt_properties_set_double( properties, "alpha", -1.0 ); filter->process = process; } return filter; } mlt-6.20.0/src/modules/opengl/filter_movit_opacity.yml000066400000000000000000000023021362234133600230740ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.opacity title: Opacity (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: Adjust the opacity of an image through the alpha channel. notes: > When used in some transitions this will cause this video to be mixed with the other video. If the video this is applied to already has an alpha channel, then this preserves that by multiplying the opacity level with the alpha channel. This filter is especially handy when used in conjunction with movit.overlay. You can also use this to fade a clip from and to black. parameters: - identifier: opacity title: Opacity type: float minimum: 0 maximum: 1 default: 1 mutable: yes - identifier: alpha title: Alpha description: > When this is less than zero, the alpha component of the Movit multiply effect follows opacity. Otherwise, you can set this to another value to control the alpha component independently. If you set this to 1, then the opacity parameter can be used to fade to and from black. type: float minimum: -1 maximum: 1 default: -1 mutable: yes mlt-6.20.0/src/modules/opengl/filter_movit_rect.cpp000066400000000000000000000043171362234133600223520ustar00rootroot00000000000000/* * filter_movit_rect.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "filter_glsl_manager.h" using namespace movit; static mlt_frame process( mlt_filter filter, mlt_frame frame ) { // Drive the resize and resample filters on the b_frame for these composite parameters mlt_properties properties = MLT_FILTER_PROPERTIES(filter); mlt_properties frame_props = MLT_FRAME_PROPERTIES(frame); mlt_properties_set( frame_props, "resize.rect", mlt_properties_get( properties, "rect" ) ); mlt_properties_set( frame_props, "resize.fill", mlt_properties_get( properties, "fill" ) ); mlt_properties_set( frame_props, "distort", mlt_properties_get( properties, "distort" ) ); mlt_properties_set( frame_props, "resize.halign", mlt_properties_get( properties, "halign" ) ); mlt_properties_set( frame_props, "resize.valign", mlt_properties_get( properties, "valign" ) ); return frame; } extern "C" mlt_filter filter_movit_rect_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set( MLT_FILTER_PROPERTIES(filter), "rect", arg ); mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "fill", 1 ); mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "distort", 0 ); filter->process = process; } return filter; } mlt-6.20.0/src/modules/opengl/filter_movit_rect.yml000066400000000000000000000030221362234133600223610ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.rect title: Position and Size (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > Change the coordinates and scale to fit within a rectangle. parameters: - identifier: rect title: Rectangle type: rect description: > Keyframable rectangle specification. mutable: yes - identifier: distort title: Ignore aspect ratio description: > Determines whether the image aspect ratio will be distorted while scaling to completely fill the geometry rectangle. type: boolean default: 0 mutable: yes widget: checkbox - identifier: fill title: Upscale to Fill description: > Determines whether the image will be scaled up to fill the rectangle or whether the size will be constrained to 100% of the profile resolution. type: integer default: 1 minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - center - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo mlt-6.20.0/src/modules/opengl/filter_movit_resample.cpp000066400000000000000000000076561362234133600232360ustar00rootroot00000000000000/* * filter_movit_resample.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include #include "optional_effect.h" using namespace movit; static int 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 ); mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); // Correct width/height if necessary if ( *width == 0 || *height == 0 ) { *width = profile->width; *height = profile->height; } int iwidth = *width; int iheight = *height; double factor = mlt_properties_get_double( filter_properties, "factor" ); factor = factor > 0 ? factor : 1.0; int owidth = *width * factor; int oheight = *height * factor; // If meta.media.width/height exist, we want that as minimum information if ( mlt_properties_get_int( properties, "meta.media.width" ) ) { iwidth = mlt_properties_get_int( properties, "meta.media.width" ); iheight = mlt_properties_get_int( properties, "meta.media.height" ); } mlt_properties_set_int( properties, "rescale_width", *width ); mlt_properties_set_int( properties, "rescale_height", *height ); // Deinterlace if height is changing to prevent fields mixing on interpolation if ( iheight != oheight ) mlt_properties_set_int( properties, "consumer_deinterlace", 1 ); GlslManager::get_instance()->lock_service( frame ); mlt_properties_set_int( filter_properties, "_movit.parms.int.width", owidth ); mlt_properties_set_int( filter_properties, "_movit.parms.int.height", oheight ); bool disable = ( iwidth == owidth && iheight == oheight ); mlt_properties_set_int( filter_properties, "_movit.parms.int.disable", disable ); *width = owidth; *height = oheight; GlslManager::get_instance()->unlock_service( frame ); // Get the image as requested if ( *format != mlt_image_none ) *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, &iwidth, &iheight, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); Effect *effect = GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new OptionalEffect ); // This needs to be something else than 0x0 at chain finalization time. bool ok = effect->set_int("width", owidth); ok |= effect->set_int("height", oheight); assert( ok ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" mlt_filter filter_movit_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); filter->process = process; } return filter; } mlt-6.20.0/src/modules/opengl/filter_movit_resize.cpp000066400000000000000000000167211362234133600227200ustar00rootroot00000000000000/* * filter_movit_resize.cpp * Copyright (C) 2013-2020 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "filter_glsl_manager.h" #include #include #include "optional_effect.h" using namespace movit; static float alignment_parse( char* align ) { int ret = 0.0f; if ( align == NULL ); else if ( isdigit( align[ 0 ] ) ) ret = atoi( align ); else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' ) ret = 1.0f; else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' ) ret = 2.0f; return ret; } static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); // Retrieve the aspect ratio double aspect_ratio = mlt_frame_get_aspect_ratio( frame ); double consumer_aspect = mlt_profile_sar( profile ); // Correct Width/height if necessary if ( *width == 0 || *height == 0 ) { *width = profile->width; *height = profile->height; } // Assign requested width/height from our subordinate int owidth = *width; int oheight = *height; // Use a mlt_rect to compute position and size mlt_rect rect; rect.x = rect.y = 0.0; if ( mlt_properties_get( properties, "resize.rect" ) ) { mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); rect = mlt_properties_anim_get_rect( properties, "resize.rect", position, length ); if ( strchr( mlt_properties_get( properties, "resize.rect" ), '%' ) ) { rect.x *= profile->width; rect.w *= profile->width; rect.y *= profile->height; rect.h *= profile->height; } double scale = mlt_profile_scale_width(profile, *width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); rect.y *= scale; rect.h *= scale; if ( !mlt_properties_get_int( properties, "resize.fill" ) ) { int x = mlt_properties_get_int( properties, "meta.media.width" ); owidth = lrintf( rect.w > x ? x : rect.w ); x = mlt_properties_get_int( properties, "meta.media.height" ); oheight = lrintf( rect.h > x ? x : rect.h ); } else { owidth = lrintf( rect.w ); oheight = lrintf( rect.h ); } } // Check for the special case - no aspect ratio means no problem :-) if ( aspect_ratio == 0.0 ) aspect_ratio = consumer_aspect; // Reset the aspect ratio mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio ); // Skip processing if requested. char *rescale = mlt_properties_get( properties, "rescale.interp" ); if ( *format == mlt_image_none || ( rescale && !strcmp( rescale, "none" ) ) ) return mlt_frame_get_image( frame, image, format, width, height, writable ); if ( mlt_properties_get_int( properties, "distort" ) == 0 ) { // Normalise the input and out display aspect int normalised_width = profile->width; int normalised_height = profile->height; int real_width = mlt_properties_get_int( properties, "meta.media.width" ); int real_height = mlt_properties_get_int( properties, "meta.media.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 = consumer_aspect * owidth / oheight; // Optimised for the input_ar > output_ar case (e.g. widescreen on standard) int scaled_width = lrint( ( 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 = lrint( ( output_ar * normalised_height ) / input_ar ); } // Now calculate the actual image size that we want owidth = lrint( scaled_width * owidth / normalised_width ); oheight = lrint( scaled_height * oheight / normalised_height ); mlt_log_debug( MLT_FILTER_SERVICE(filter), "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); // Tell frame we have conformed the aspect to the consumer mlt_frame_set_aspect_ratio( frame, consumer_aspect ); } mlt_properties_set_int( properties, "distort", 0 ); // Now get the image *format = mlt_image_glsl; error = mlt_frame_get_image( frame, image, format, &owidth, &oheight, writable ); // Offset the position according to alignment if ( mlt_properties_get( properties, "resize.rect" ) ) { // default top left if rect supplied float w = float( rect.w - owidth ); float h = float( rect.h - oheight ); rect.x += w * alignment_parse( mlt_properties_get( properties, "resize.halign" ) ) / 2.0f; rect.y += h * alignment_parse( mlt_properties_get( properties, "resize.valign" ) ) / 2.0f; } else { // default center if no rect float w = float( *width - owidth ); float h = float( *height - oheight ); rect.x = w * 0.5f; rect.y = h * 0.5f; } if ( !error ) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_properties_set_int( filter_properties, "_movit.parms.int.width", *width ); mlt_properties_set_int( filter_properties, "_movit.parms.int.height", *height ); mlt_properties_set_double( filter_properties, "_movit.parms.float.left", rect.x ); mlt_properties_set_double( filter_properties, "_movit.parms.float.top", rect.y ); bool disable = ( *width == owidth && *height == oheight && rect.x == 0 && rect.y == 0 ); mlt_properties_set_int( filter_properties, "_movit.parms.int.disable", disable ); GlslManager::get_instance()->unlock_service( frame ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new OptionalEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" mlt_filter filter_movit_resize_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); filter->process = process; } return filter; } mlt-6.20.0/src/modules/opengl/filter_movit_saturation.cpp000066400000000000000000000051021362234133600235770ustar00rootroot00000000000000/* * filter_movit_saturation.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_properties_set_double( properties, "_movit.parms.float.saturation", mlt_properties_anim_get_double( properties, "saturation", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new SaturationEffect() ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_movit_saturation_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set( properties, "saturation", arg? arg : "1.0" ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_saturation.yml000066400000000000000000000012141362234133600236160ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.saturation title: Saturation (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A simple desaturation/saturation effect. We use the Rec. 709 definition of luminance (in linear light, of course) and linearly interpolate between that (saturation=0) and the original signal (saturation=1). Extrapolating that curve further (ie., saturation > 1) gives us increased saturation if so desired. parameters: - identifier: saturation title: Saturation type: float minimum: 0 default: 1 mutable: yes mlt-6.20.0/src/modules/opengl/filter_movit_vignette.cpp000066400000000000000000000054641362234133600232460ustar00rootroot00000000000000/* * filter_movit_vignette.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_properties_set_double( properties, "_movit.parms.float.radius", mlt_properties_anim_get_double( properties, "radius", position, length ) ); mlt_properties_set_double( properties, "_movit.parms.float.inner_radius", mlt_properties_anim_get_double( properties, "inner_radius", position, length ) ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new VignetteEffect() ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_movit_vignette_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); filter->process = process; mlt_properties_set_double( MLT_FILTER_PROPERTIES(filter), "radius", 0.3 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES(filter), "inner_radius", 0.3 ); } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_vignette.yml000066400000000000000000000011531362234133600232540ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.vignette title: Vignette (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A circular vignette, falling off as cos² of the distance from the center (the classic formula for approximating a real lens). parameters: - identifier: radius title: Outer Radius type: float minimum: 0.0 maximum: 1.0 default: 0.3 mutable: yes - identifier: inner_radius title: Inner Radius type: float minimum: 0.0 maximum: 1.0 default: 0.3 mutable: yes mlt-6.20.0/src/modules/opengl/filter_movit_white_balance.cpp000066400000000000000000000066661362234133600242130ustar00rootroot00000000000000/* * filter_white_balance.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "filter_glsl_manager.h" #include using namespace movit; static double srgb8_to_linear(int c) { double x = c / 255.0f; if (x < 0.04045f) { return (1.0/12.92f) * x; } else { return pow((x + 0.055) * (1.0/1.055f), 2.4); } } static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); GlslManager::get_instance()->lock_service( frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); int color_int = mlt_properties_anim_get_int( properties, "neutral_color", position, length ); RGBTriplet color( srgb8_to_linear((color_int >> 24) & 0xff), srgb8_to_linear((color_int >> 16) & 0xff), srgb8_to_linear((color_int >> 8) & 0xff) ); mlt_properties_set_double( properties, "_movit.parms.vec3.neutral_color[0]", color.r ); mlt_properties_set_double( properties, "_movit.parms.vec3.neutral_color[1]", color.g ); mlt_properties_set_double( properties, "_movit.parms.vec3.neutral_color[2]", color.b ); double output_color_temperature = mlt_properties_anim_get_double( properties, "color_temperature", position, length ); mlt_properties_set_double( properties, "_movit.parms.float.output_color_temperature", output_color_temperature ); GlslManager::get_instance()->unlock_service( frame ); *format = mlt_image_glsl; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); GlslManager::set_effect_input( MLT_FILTER_SERVICE( filter ), frame, (mlt_service) *image ); GlslManager::set_effect( MLT_FILTER_SERVICE( filter ), frame, new WhiteBalanceEffect ); *image = (uint8_t *) MLT_FILTER_SERVICE( filter ); return error; } static mlt_frame process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } extern "C" { mlt_filter filter_white_balance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( filter = mlt_filter_new() ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); glsl->add_ref( properties ); mlt_properties_set( properties, "neutral_color", arg? arg : "#7f7f7f" ); mlt_properties_set_double( properties, "color_temperature", 6500.0 ); filter->process = process; } return filter; } } mlt-6.20.0/src/modules/opengl/filter_movit_white_balance.yml000066400000000000000000000010721362234133600242140ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: movit.white_balance title: White Balance (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: Color correction in LMS color space. parameters: - identifier: neutral_color title: Neutral Color type: string widget: color default: 0x7f7f7f00 mutable: yes - identifier: color_temperature title: Color Temperature type: float minimum: 1000.0 maximum: 15000.0 default: 6500.0 unit: Kelvin mutable: yes mlt-6.20.0/src/modules/opengl/mlt_flip_effect.h000066400000000000000000000025461362234133600214230ustar00rootroot00000000000000/* * mlt_flip_effect.h * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MLT_FLIP_EFFECT_H #define MLT_FLIP_EFFECT_H namespace Mlt { class VerticalFlip : public movit::Effect { public: VerticalFlip() {} virtual std::string effect_type_id() const { return "MltVerticalFlip"; } std::string output_fragment_shader() { return "vec4 FUNCNAME(vec2 tc) { tc.y = 1.0 - tc.y; return INPUT(tc); }\n"; } virtual bool needs_linear_light() const { return false; } virtual bool needs_srgb_primaries() const { return false; } AlphaHandling alpha_handling() const { return DONT_CARE_ALPHA_TYPE; } }; } // namespace Mlt #endif // MLT_FLIP_EFFECT_H mlt-6.20.0/src/modules/opengl/mlt_movit_input.cpp000066400000000000000000000044411362234133600220610ustar00rootroot00000000000000/* * mlt_movit_input.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mlt_movit_input.h" using namespace movit; MltInput::MltInput( mlt_image_format format ) : m_format(format) , input(0) , isRGB(true) { } MltInput::~MltInput() { } void MltInput::useFlatInput(MovitPixelFormat pix_fmt, unsigned width, unsigned height) { if (!input) { m_width = width; m_height = height; ImageFormat image_format; image_format.color_space = COLORSPACE_sRGB; image_format.gamma_curve = GAMMA_sRGB; input = new FlatInput(image_format, pix_fmt, GL_UNSIGNED_BYTE, width, height); } } void MltInput::useYCbCrInput(const ImageFormat& image_format, const YCbCrFormat& ycbcr_format, unsigned width, unsigned height) { if (!input) { m_width = width; m_height = height; input = new YCbCrInput(image_format, ycbcr_format, width, height); isRGB = false; m_ycbcr_format = ycbcr_format; } } void MltInput::set_pixel_data(const unsigned char* data) { assert(input); if (isRGB) { FlatInput* flat = (FlatInput*) input; flat->set_pixel_data(data); } else { YCbCrInput* ycbcr = (YCbCrInput*) input; ycbcr->set_pixel_data(0, data); ycbcr->set_pixel_data(1, &data[m_width * m_height]); ycbcr->set_pixel_data(2, &data[m_width * m_height + (m_width / m_ycbcr_format.chroma_subsampling_x * m_height / m_ycbcr_format.chroma_subsampling_y)]); } } void MltInput::invalidate_pixel_data() { assert(input); if (isRGB) { FlatInput* flat = (FlatInput*) input; flat->invalidate_pixel_data(); } else { YCbCrInput* ycbcr = (YCbCrInput*) input; ycbcr->invalidate_pixel_data(); } } mlt-6.20.0/src/modules/opengl/mlt_movit_input.h000066400000000000000000000034721362234133600215310ustar00rootroot00000000000000/* * mlt_movit_input.h * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MLT_MOVIT_INPUT_H #define MLT_MOVIT_INPUT_H #include #include #include #include class MltInput { public: MltInput( mlt_image_format format ); ~MltInput(); void useFlatInput(movit::MovitPixelFormat pix_fmt, unsigned width, unsigned height); void useYCbCrInput(const movit::ImageFormat& image_format, const movit::YCbCrFormat& ycbcr_format, unsigned width, unsigned height); void set_pixel_data(const unsigned char* data); void invalidate_pixel_data(); movit::Input *get_input() { return input; } // The original pixel format that was used to create this MltInput, // in case we change our mind later and want to convert on the CPU instead. mlt_image_format get_format() const { return m_format; } private: mlt_image_format m_format; unsigned m_width, m_height; // Note: Owned by the EffectChain, so should not be deleted by us. movit::Input *input; bool isRGB; movit::YCbCrFormat m_ycbcr_format; }; #endif // MLT_MOVIT_INPUT_H mlt-6.20.0/src/modules/opengl/optional_effect.h000066400000000000000000000015061362234133600214350ustar00rootroot00000000000000#ifndef OPTIONAL_EFFECT_H #define OPTIONAL_EFFECT_H #include #include #include // A wrapper effect that, at rewrite time, can remove itself entirely from the loop. // It does so if "disable" is set to a nonzero value at finalization time. template class OptionalEffect : public T { public: OptionalEffect() : disable(0) { this->register_int("disable", &disable); } virtual std::string effect_type_id() const { return "OptionalEffect[" + T::effect_type_id() + "]"; } virtual void rewrite_graph(movit::EffectChain *graph, movit::Node *self) { if (disable) { assert(self->incoming_links.size() == 1); graph->replace_sender(self, self->incoming_links[0]); self->disabled = true; } else { T::rewrite_graph(graph, self); } } private: int disable; }; #endif mlt-6.20.0/src/modules/opengl/transition_movit_luma.cpp000066400000000000000000000172161362234133600232620ustar00rootroot00000000000000/* * transition_movit_luma.cpp * Copyright (C) 2014-2017 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include #include #include #include #include #include "mlt_movit_input.h" using namespace movit; static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error; // Get the transition object mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); mlt_service service = MLT_TRANSITION_SERVICE( transition ); // Get the b frame from the stack mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); mlt_frame c_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); // Get the properties of the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); mlt_service_lock( service ); // Get the transition parameters mlt_position position = mlt_transition_get_position( transition, a_frame ); mlt_position length = mlt_transition_get_length( transition ); int reverse = mlt_properties_get_int( properties, "reverse" ); double mix = mlt_transition_get_progress( transition, a_frame ); double inverse = 1.0 - mix; double softness = mlt_properties_anim_get_double( properties, "softness", position, length ); if ( c_frame ) { // Set the Movit parameters. mlt_properties_set( properties, "_movit.parms.float.strength_first", NULL ); mlt_properties_set( properties, "_movit.parms.float.strength_second", NULL ); mlt_properties_set_double( properties, "_movit.parms.float.progress", reverse ? inverse : mix ); mlt_properties_set_double( properties, "_movit.parms.float.transition_width", 1.0 / (softness + 1.0e-4) ); mlt_properties_set_int( properties, "_movit.parms.int.inverse", !mlt_properties_get_int( properties, "invert" ) ); uint8_t *a_image, *b_image, *c_image; // Get the images. *format = mlt_image_glsl; error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); error = mlt_frame_get_image( c_frame, &c_image, format, width, height, writable ); GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); GlslManager::set_effect_third_input( service, a_frame, (mlt_service) c_image, c_frame ); GlslManager::set_effect( service, a_frame, new LumaMixEffect() ); } else { // Set the Movit parameters. mlt_properties_set( properties, "_movit.parms.int.inverse", NULL ); mlt_properties_set( properties, "_movit.parms.float.progress", NULL ); mlt_properties_set( properties, "_movit.parms.float.transition_width", NULL ); mlt_properties_set_double( properties, "_movit.parms.float.strength_first", reverse ? mix : inverse ); mlt_properties_set_double( properties, "_movit.parms.float.strength_second", reverse ? inverse : mix ); uint8_t *a_image, *b_image; // Get the two images. *format = mlt_image_glsl; error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); GlslManager::set_effect( service, a_frame, new MixEffect() ); } *image = (uint8_t *) service; mlt_service_unlock( service ); return error; } static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); // Obtain the wipe producer. char *resource = mlt_properties_get( properties, "resource" ); char *last_resource = mlt_properties_get( properties, "_resource" ); mlt_producer producer = (mlt_producer) mlt_properties_get_data( properties, "instance", NULL ); // If we haven't created the wipe producer or it has changed if ( resource ) if ( !producer || strcmp( resource, last_resource ) ) { char temp[ 512 ]; // Store the last resource now mlt_properties_set( properties, "_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 = mlt_fopen( temp, "r" ); if ( test == NULL ) { strcat( temp, ".png" ); test = mlt_fopen( temp, "r" ); } if ( test ) fclose( test ); else strcpy( temp, "colour:0x00000080" ); resource = temp; } mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) ); producer = mlt_factory_producer( profile, NULL, resource ); if ( producer != NULL ) { mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" ); } mlt_properties_set_data( properties, "instance", producer, 0, (mlt_destructor) mlt_producer_close, NULL ); } // We may still not have a producer in which case, we do nothing if ( producer ) { mlt_frame wipe = NULL; mlt_position position = mlt_transition_get_position( transition, a_frame ); mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), properties, "producer." ); mlt_producer_seek( producer, position ); if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &wipe, 0 ) == 0 ) { char name[64]; snprintf( name, sizeof(name), "movit.luma %s", mlt_properties_get( properties, "_unique_id" ) ); mlt_properties_set_data( MLT_FRAME_PROPERTIES(a_frame), name, wipe, 0, (mlt_destructor) mlt_frame_close, NULL ); mlt_properties_set_int( MLT_FRAME_PROPERTIES(wipe), "distort", 1 ); mlt_frame_push_frame( a_frame, wipe ); } else { mlt_frame_push_frame( a_frame, NULL ); } } else { mlt_frame_push_frame( a_frame, NULL ); } mlt_frame_push_frame( a_frame, b_frame ); mlt_frame_push_service( a_frame, transition ); mlt_frame_push_get_image( a_frame, get_image ); return a_frame; } extern "C" mlt_transition transition_movit_luma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_transition transition = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( transition = mlt_transition_new() ) ) { transition->process = process; mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "resource", arg ); // Inform apps and framework that this is a video only transition mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 ); } return transition; } mlt-6.20.0/src/modules/opengl/transition_movit_luma.yml000066400000000000000000000022251362234133600232730ustar00rootroot00000000000000schema_version: 0.2 type: transition identifier: movit.luma_mix title: Wipe (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: A generic dissolve and wipe transition processor. parameters: - identifier: resource argument: yes title: Wipe File description: Gradient image or dissolve if not supplied. type: string mutable: yes - identifier: softness title: Softness description: The blurriness of the edges of the transition. type: float minimum: 0 maximum: 1 default: 0 mutable: yes - identifier: reverse title: Reverse type: integer mutable: yes description: Reverse the direction of the transition. default: 0 minimum: 0 maximum: 1 widget: checkbox - identifier: invert title: Invert type: integer mutable: yes description: Invert the wipe. default: 0 minimum: 0 maximum: 1 widget: checkbox - identifier: producer.* title: Producer mutable: yes description: > Properties may be set on the encapsulated producer that reads resource. readonly: no mlt-6.20.0/src/modules/opengl/transition_movit_mix.cpp000066400000000000000000000073441362234133600231220ustar00rootroot00000000000000/* * transition_movit_mix.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include #include #include #include #include "mlt_movit_input.h" #include "mlt_flip_effect.h" using namespace movit; static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error; // Get the b frame from the stack mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); // Get the transition object mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); mlt_service service = MLT_TRANSITION_SERVICE( transition ); mlt_service_lock( service ); // Get the properties of the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); // Get the transition parameters mlt_position position = mlt_transition_get_position( transition, a_frame ); mlt_position length = mlt_transition_get_length( transition ); int reverse = mlt_properties_get_int( properties, "reverse" ); const char* mix_str = mlt_properties_get( properties, "mix" ); double mix = ( mix_str && strlen( mix_str ) > 0 ) ? mlt_properties_anim_get_double( properties, "mix", position, length ) : mlt_transition_get_progress( transition, a_frame ); double inverse = 1.0 - mix; // Set the Movit parameters. mlt_properties_set_double( properties, "_movit.parms.float.strength_first", reverse ? mix : inverse ); mlt_properties_set_double( properties, "_movit.parms.float.strength_second", reverse ? inverse : mix ); uint8_t *a_image, *b_image; // Get the two images. *format = mlt_image_glsl; error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); GlslManager::set_effect( service, a_frame, new MixEffect() ); *image = (uint8_t *) service; mlt_service_unlock( service ); return error; } static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { mlt_frame_push_service( a_frame, transition ); mlt_frame_push_frame( a_frame, b_frame ); mlt_frame_push_get_image( a_frame, get_image ); return a_frame; } extern "C" mlt_transition transition_movit_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_transition transition = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( transition = mlt_transition_new() ) ) { transition->process = process; mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "mix", arg ); // Inform apps and framework that this is a video only transition mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 ); } return transition; } mlt-6.20.0/src/modules/opengl/transition_movit_mix.yml000066400000000000000000000014671362234133600231410ustar00rootroot00000000000000schema_version: 0.1 type: transition identifier: movit.mix title: Dissolve (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: A simple video cross-fade or mixing effect. parameters: - identifier: argument title: Mix Level description: Performs a dissolve if a mix level is not supplied. type: float minimum: 0 maximum: 1 mutable: yes - identifier: mix title: Mix Level description: Performs a dissolve if a mix level is not supplied. type: float minimum: 0 maximum: 1 mutable: yes - identifier: reverse title: Reverse type: integer mutable: yes description: > Reverse the direction of the transition. default: 0 minimum: 0 maximum: 1 widget: checkbox mlt-6.20.0/src/modules/opengl/transition_movit_overlay.cpp000066400000000000000000000053731362234133600240060ustar00rootroot00000000000000/* * transition_movit_overlay.cpp * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "filter_glsl_manager.h" #include #include #include #include using namespace movit; static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error; // Get the b frame from the stack mlt_frame b_frame = (mlt_frame) mlt_frame_pop_frame( a_frame ); // Get the transition object mlt_transition transition = (mlt_transition) mlt_frame_pop_service( a_frame ); mlt_service service = MLT_TRANSITION_SERVICE( transition ); mlt_service_lock( service ); uint8_t *a_image, *b_image; // Get the two images. *format = mlt_image_glsl; error = mlt_frame_get_image( a_frame, &a_image, format, width, height, writable ); error = mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); GlslManager::set_effect_input( service, a_frame, (mlt_service) a_image ); GlslManager::set_effect_secondary_input( service, a_frame, (mlt_service) b_image, b_frame ); GlslManager::set_effect( service, a_frame, new OverlayEffect ); *image = (uint8_t *) service; mlt_service_unlock( service ); return error; } static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { mlt_frame_push_service( a_frame, transition ); mlt_frame_push_frame( a_frame, b_frame ); mlt_frame_push_get_image( a_frame, get_image ); return a_frame; } extern "C" mlt_transition transition_movit_overlay_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_transition transition = NULL; GlslManager* glsl = GlslManager::get_instance(); if ( glsl && ( transition = mlt_transition_new() ) ) { transition->process = process; // Inform apps and framework that this is a video only transition mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 ); } return transition; } mlt-6.20.0/src/modules/opengl/transition_movit_overlay.yml000066400000000000000000000004531362234133600240170ustar00rootroot00000000000000schema_version: 0.1 type: transition identifier: movit.overlay title: Overlay (GLSL) version: 1 copyright: Dan Dennedy creator: Steinar H. Gunderson license: GPLv2 language: en tags: - Video description: > A simple video overlay or alpha-compositing effect using the Porter-Duff over operation. mlt-6.20.0/src/modules/plus/000077500000000000000000000000001362234133600156205ustar00rootroot00000000000000mlt-6.20.0/src/modules/plus/CMakeLists.txt000066400000000000000000000027541362234133600203700ustar00rootroot00000000000000set(mltplus_src consumer_blipflash.c factory.c filter_affine.c filter_charcoal.c filter_dynamictext.c filter_dynamic_loudness.c filter_invert.c filter_lift_gamma_gain.c filter_loudness.c filter_loudness_meter.c filter_lumakey.c filter_rgblut.c filter_sepia.c filter_spot_remover.c filter_text.c filter_timer.c producer_blipflash.c producer_count.c transition_affine.c) set(mltplus_lib mlt m Threads::Threads) find_package(FFTW QUIET) if(TARGET FFTW3::fftw3) add_library(fftw ALIAS FFTW3::fftw3) else() pkg_check_modules(fftw3 IMPORTED_TARGET GLOBAL fftw3) if(TARGET PkgConfig::fftw3) add_library(fftw ALIAS PkgConfig::fftw3) endif() endif() if(TARGET fftw) list(APPEND mltplus_src filter_dance.c filter_fft.c) list(APPEND mltplus_lib fftw) endif() pkg_check_modules(libebur128 IMPORTED_TARGET libebur128) if(TARGET PkgConfig::libebur128) list(APPEND mltplus_lib PkgConfig::libebur128) set(mltplus_inc "") else() list(APPEND mltplus_src ebur128/ebur128.c) set(mltplus_inc ${CMAKE_CURRENT_SOURCE_DIR}/ebur128 ${CMAKE_CURRENT_SOURCE_DIR}/ebur128/queue) endif() add_library(mltplus MODULE ${mltplus_src}) target_link_libraries(mltplus ${mltplus_lib}) target_include_directories(mltplus PRIVATE ${mltplus_inc}) install(TARGETS mltplus LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/plus) mlt-6.20.0/src/modules/plus/Makefile000066400000000000000000000022541362234133600172630ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS := -L../../framework -lmlt -lm -lpthread $(LDFLAGS) include ../../../config.mak include config.mak TARGET = ../libmltplus$(LIBSUF) OBJS = consumer_blipflash.o \ factory.o \ filter_affine.o \ filter_charcoal.o \ filter_dynamictext.o \ filter_dynamic_loudness.o \ filter_invert.o \ filter_lift_gamma_gain.o \ filter_loudness.o \ filter_loudness_meter.o \ filter_lumakey.o \ filter_rgblut.o \ filter_sepia.o \ filter_spot_remover.o \ filter_text.o \ filter_timer.o \ producer_blipflash.o \ producer_count.o \ transition_affine.o ifdef USE_FFTW OBJS += filter_dance.o \ filter_fft.o endif ifdef USE_INTERNAL_LIBEBUR128 OBJS += ebur128/ebur128.o 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/plus" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/plus" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/plus/configure000077500000000000000000000016401362234133600175300ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ] then echo > config.mak if pkg-config fftw3 then FFTWPC=fftw3 elif pkg-config fftw then FFTWPC=fftw fi if [ "$FFTWPC" != "" ] then echo "USE_FFTW=1" >> config.mak echo "CFLAGS += -DUSE_FFTW" >> config.mak echo "CFLAGS += $(pkg-config --cflags $FFTWPC)" >> config.mak echo "LDFLAGS += $(pkg-config --libs $FFTWPC)" >> config.mak else echo "- fftw not found: disable fft and dance filters" fi pkg-config libebur128 if [ $? -eq 0 ] then echo "- libebur128 found: using external libebur128" echo "CFLAGS += $(pkg-config --cflags libebur128)" >> config.mak echo "LDFLAGS += $(pkg-config --libs libebur128)" >> config.mak else echo "- libebur128 not found: using internal libebur128" echo "USE_INTERNAL_LIBEBUR128=1" >> config.mak echo "CFLAGS += -DUSE_INTERNAL_LIBEBUR128" >> config.mak echo "CFLAGS += -Iebur128 -Iebur128/queue" >> config.mak fi exit 0 fi mlt-6.20.0/src/modules/plus/consumer_blipflash.c000066400000000000000000000265411362234133600216530ustar00rootroot00000000000000/* * consumer_blipflash.c -- a consumer to measure A/V sync from a blip/flash * source * Copyright (C) 2013 Brian Matherly * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // mlt Header files #include #include #include // System header files #include #include #include #include #include // Private constants #define SAMPLE_FREQ 48000 #define FLASH_LUMA_THRESHOLD 150 #define BLIP_THRESHOLD 0.5 // Private types typedef struct { int64_t flash_history[2]; int flash_history_count; int64_t blip_history[2]; int blip_history_count; int blip_in_progress; int samples_since_blip; int blip; int flash; int sample_offset; FILE* out_file; int report_frames; } avsync_stats; // Forward references. static int consumer_start( mlt_consumer consumer ); static int consumer_stop( mlt_consumer consumer ); static int consumer_is_stopped( mlt_consumer consumer ); static void *consumer_thread( void *arg ); static void consumer_close( mlt_consumer consumer ); /** Initialize the consumer. */ mlt_consumer consumer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Allocate the consumer mlt_consumer consumer = mlt_consumer_new( profile ); mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES( consumer ); avsync_stats* stats = NULL; // If memory allocated and initializes without error if ( consumer != NULL ) { // Set up start/stop/terminated callbacks consumer->close = consumer_close; consumer->start = consumer_start; consumer->stop = consumer_stop; consumer->is_stopped = consumer_is_stopped; stats = mlt_pool_alloc( sizeof( avsync_stats ) ); stats->flash_history_count = 0; stats->blip_history_count = 0; stats->blip_in_progress = 0; stats->samples_since_blip = 0; stats->blip = 0; stats->flash = 0; stats->sample_offset = INT_MAX; stats->report_frames = 0; stats->out_file = stdout; if ( arg != NULL ) { FILE* out_file = mlt_fopen( arg, "w" ); if ( out_file != NULL ) stats->out_file = out_file; } mlt_properties_set_data( consumer_properties, "_stats", stats, 0, NULL, NULL ); mlt_properties_set( consumer_properties, "report", "blip" ); } // Return this return consumer; } /** Start the consumer. */ static int consumer_start( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // 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, consumer ); } return 0; } /** Stop the consumer. */ static int consumer_stop( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // 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 if ( thread ) pthread_join( *thread, NULL ); } return 0; } /** Determine if the consumer is stopped. */ static int consumer_is_stopped( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); return !mlt_properties_get_int( properties, "_running" ); } static void detect_flash( mlt_frame frame, mlt_position pos, double fps, avsync_stats* stats ) { int width = 0; int height = 0; mlt_image_format format = mlt_image_yuv422; uint8_t* image = NULL; int error = mlt_frame_get_image( frame, &image, &format, &width, &height, 0 ); if ( !error && format == mlt_image_yuv422 && image != NULL ) { int i, j = 0; int y_accumulator = 0; // Add up the luma values from 4 samples in 4 different quadrants. for( i = 1; i < 3; i++ ) { int x = ( width / 3 ) * i; x = x - x % 2; // Make sure this is a luma sample for( j = 1; j < 3; j++ ) { int y = ( height / 3 ) * j; y_accumulator += image[ y * height * 2 + x * 2 ]; } } // If the average luma value is > 150, assume it is a flash. stats->flash = ( y_accumulator / 4 ) > FLASH_LUMA_THRESHOLD; } if( stats->flash ) { stats->flash_history[1] = stats->flash_history[0]; stats->flash_history[0] = mlt_sample_calculator_to_now( fps, SAMPLE_FREQ, pos ); if( stats->flash_history_count < 2 ) { stats->flash_history_count++; } } } static void detect_blip( mlt_frame frame, mlt_position pos, double fps, avsync_stats* stats ) { int frequency = SAMPLE_FREQ; int channels = 1; int samples = mlt_sample_calculator( fps, frequency, pos ); mlt_audio_format format = mlt_audio_float; float* buffer = NULL; int error = mlt_frame_get_audio( frame, (void**) &buffer, &format, &frequency, &channels, &samples ); if ( !error && format == mlt_audio_float && buffer != NULL ) { int i = 0; for( i = 0; i < samples; i++ ) { if( !stats->blip_in_progress ) { if( buffer[i] > BLIP_THRESHOLD || buffer[i] < -BLIP_THRESHOLD ) { // This sample must start a blip stats->blip_in_progress = 1; stats->samples_since_blip = 0; stats->blip_history[1] = stats->blip_history[0]; stats->blip_history[0] = mlt_sample_calculator_to_now( fps, SAMPLE_FREQ, pos ); stats->blip_history[0] += i; if( stats->blip_history_count < 2 ) { stats->blip_history_count++; } stats->blip = 1; } } else { if( buffer[i] > -BLIP_THRESHOLD && buffer[i] < BLIP_THRESHOLD ) { if( ++stats->samples_since_blip > frequency / 1000 ) { // One ms of silence means the blip is over stats->blip_in_progress = 0; stats->samples_since_blip = 0; } } else { stats->samples_since_blip = 0; } } } } } static void calculate_sync( avsync_stats* stats ) { if( stats->blip || stats->flash ) { if( stats->flash_history_count > 0 && stats->blip_history_count > 0 && stats->blip_history[0] == stats->flash_history[0] ) { // The flash and blip occurred at the same time. stats->sample_offset = 0; } if( stats->flash_history_count > 1 && stats->blip_history_count > 0 && stats->blip_history[0] <= stats->flash_history[0] && stats->blip_history[0] >= stats->flash_history[1] ) { // The latest blip occurred between two flashes if( stats->flash_history[0] - stats->blip_history[0] > stats->blip_history[0] - stats->flash_history[1] ) { // Blip is closer to the previous flash. // F1---B0--------F0 // ^----^ // Video leads audio (negative number). stats->sample_offset = (int)(stats->flash_history[1] - stats->blip_history[0] ); } else { // Blip is closer to the current flash. // F1--------B0---F0 // ^----^ // Audio leads video (positive number). stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[0]); } } else if( stats->blip_history_count > 1 && stats->flash_history_count > 0 && stats->flash_history[0] <= stats->blip_history[0] && stats->flash_history[0] >= stats->blip_history[1] ) { // The latest flash occurred between two blips if( stats->blip_history[0] - stats->flash_history[0] > stats->flash_history[0] - stats->blip_history[1] ) { // Flash is closer to the previous blip. // B1---F0--------B0 // ^----^ // Audio leads video (positive number). stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[1]); } else { // Flash is closer to the latest blip. // B1--------F0---B0 // ^----^ // Video leads audio (negative number). stats->sample_offset = (int)(stats->flash_history[0] - stats->blip_history[0] ); } } } } static void report_results( avsync_stats* stats, mlt_position pos ) { if( stats->report_frames || stats->blip ) { if( stats->sample_offset == INT_MAX ) { fprintf( stats->out_file, MLT_POSITION_FMT "\t??\n", pos ); } else { // Convert to milliseconds. double ms_offset = (double)stats->sample_offset * 1000.0 / (double)SAMPLE_FREQ; fprintf( stats->out_file, MLT_POSITION_FMT "\t%02.02f\n", pos, ms_offset ); } } stats->blip = 0; stats->flash = 0; } /** The main thread - the argument is simply the consumer. */ static void *consumer_thread( void *arg ) { // Map the argument to the object mlt_consumer consumer = arg; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // 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( consumer ); // 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 ) { avsync_stats* stats = mlt_properties_get_data( properties, "_stats", NULL ); double fps = mlt_properties_get_double( properties, "fps" ); mlt_position pos = mlt_frame_get_position( frame ); if( !strcmp( mlt_properties_get( properties, "report" ), "frame" ) ) { stats->report_frames = 1; } else { stats->report_frames = 0; } detect_flash( frame, pos, fps, stats ); detect_blip( frame, pos, fps, stats ); calculate_sync( stats ); report_results( stats, pos ); // 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( consumer ); return NULL; } /** Close the consumer. */ static void consumer_close( mlt_consumer consumer ) { mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES( consumer ); avsync_stats* stats = mlt_properties_get_data( consumer_properties, "_stats", NULL ); // Stop the consumer mlt_consumer_stop( consumer ); // Close the file if( stats->out_file != stdout ) { fclose( stats->out_file ); } // Clean up memory mlt_pool_release( stats ); // Close the parent mlt_consumer_close( consumer ); // Free the memory free( consumer ); } mlt-6.20.0/src/modules/plus/consumer_blipflash.yml000066400000000000000000000014361362234133600222260ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: blipflash title: Blip Flash version: 1 copyright: Brian Matherly creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video - Audio description: > Calculate the A/V sync for a blip flash source. Sync can be recalculated whenever a blip or a flash is detected. parameters: - identifier: argument title: Report File type: string description: > The file to report the results to. If empty, the results will be reported to standard out. required: no widget: filesave - identifier: report title: Report Style type: string description: > When to report sync - every frame or only when blips occur. default: blip values: - blip - frame mutable: yes widget: combo mlt-6.20.0/src/modules/plus/ebur128/000077500000000000000000000000001362234133600170105ustar00rootroot00000000000000mlt-6.20.0/src/modules/plus/ebur128/COPYING000066400000000000000000000020431362234133600200420ustar00rootroot00000000000000Copyright (c) 2011 Jan Kokemüller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mlt-6.20.0/src/modules/plus/ebur128/ebur128.c000066400000000000000000001410101362234133600203410ustar00rootroot00000000000000/* See COPYING file for copyright and license details. */ #include "ebur128.h" #include #include #include /* You may have to define _USE_MATH_DEFINES if you use MSVC */ #include #include /* This can be replaced by any BSD-like queue implementation. */ #include #define CHECK_ERROR(condition, errorcode, goto_point) \ if ((condition)) { \ errcode = (errorcode); \ goto goto_point; \ } STAILQ_HEAD(ebur128_double_queue, ebur128_dq_entry); struct ebur128_dq_entry { double z; STAILQ_ENTRY(ebur128_dq_entry) entries; }; #define ALMOST_ZERO 0.000001 typedef struct { // Data structure for polyphase FIR interpolator unsigned int factor; // Interpolation factor of the interpolator unsigned int taps; // Taps (prefer odd to increase zero coeffs) unsigned int channels; // Number of channels unsigned int delay; // Size of delay buffer struct { unsigned int count; // Number of coefficients in this subfilter unsigned int* index; // Delay index of corresponding filter coeff double* coeff; // List of subfilter coefficients }* filter; // List of subfilters (one for each factor) float** z; // List of delay buffers (one for each channel) unsigned int zi; // Current delay buffer index } interpolator; struct ebur128_state_internal { /** Filtered audio data (used as ring buffer). */ double* audio_data; /** Size of audio_data array. */ size_t audio_data_frames; /** Current index for audio_data. */ size_t audio_data_index; /** How many frames are needed for a gating block. Will correspond to 400ms * of audio at initialization, and 100ms after the first block (75% overlap * as specified in the 2011 revision of BS1770). */ unsigned long needed_frames; /** The channel map. Has as many elements as there are channels. */ int* channel_map; /** How many samples fit in 100ms (rounded). */ unsigned long samples_in_100ms; /** BS.1770 filter coefficients (nominator). */ double b[5]; /** BS.1770 filter coefficients (denominator). */ double a[5]; /** BS.1770 filter state. */ double v[5][5]; /** Linked list of block energies. */ struct ebur128_double_queue block_list; unsigned long block_list_max; unsigned long block_list_size; /** Linked list of 3s-block energies, used to calculate LRA. */ struct ebur128_double_queue short_term_block_list; unsigned long st_block_list_max; unsigned long st_block_list_size; int use_histogram; unsigned long *block_energy_histogram; unsigned long *short_term_block_energy_histogram; /** Keeps track of when a new short term block is needed. */ size_t short_term_frame_counter; /** Maximum sample peak, one per channel */ double* sample_peak; double* prev_sample_peak; /** Maximum true peak, one per channel */ double* true_peak; double* prev_true_peak; interpolator* interp; float* resampler_buffer_input; size_t resampler_buffer_input_frames; float* resampler_buffer_output; size_t resampler_buffer_output_frames; /** The maximum window duration in ms. */ unsigned long window; unsigned long history; }; static double relative_gate = -10.0; /* Those will be calculated when initializing the library */ static double relative_gate_factor; static double minus_twenty_decibels; static double histogram_energies[1000]; static double histogram_energy_boundaries[1001]; static interpolator* interp_create(unsigned int taps, unsigned int factor, unsigned int channels) { interpolator* interp = calloc(1, sizeof(interpolator)); unsigned int j = 0; interp->taps = taps; interp->factor = factor; interp->channels = channels; interp->delay = (interp->taps + interp->factor - 1) / interp->factor; // Initialize the filter memory // One subfilter per interpolation factor. interp->filter = calloc(interp->factor, sizeof(*interp->filter)); for (j = 0; j < interp->factor; j++) { interp->filter[j].index = calloc(interp->delay, sizeof(unsigned int)); interp->filter[j].coeff = calloc(interp->delay, sizeof(double)); } // One delay buffer per channel. interp->z = calloc(interp->channels, sizeof(float*)); for (j = 0; j < interp->channels; j++) { interp->z[j] = calloc( interp->delay, sizeof(float) ); } // Calculate the filter coefficients for (j = 0; j < interp->taps; j++) { // Calculate sinc double m = (double)j - (double)(interp->taps - 1) / 2.0; double c = 1.0; if (fabs(m) > ALMOST_ZERO) { c = sin(m * M_PI / interp->factor) / (m * M_PI / interp->factor); } // Apply Hanning window c *= 0.5 * (1 - cos(2 * M_PI * j / (interp->taps - 1))); if (fabs(c) > ALMOST_ZERO) { // Ignore any zero coeffs. // Put the coefficient into the correct subfilter unsigned int f = j % interp->factor; unsigned int t = interp->filter[f].count++; interp->filter[f].coeff[t] = c; interp->filter[f].index[t] = j / interp->factor; } } return interp; } static void interp_destroy(interpolator* interp) { unsigned int j = 0; if (!interp) return; for (j = 0; j < interp->factor; j++) { free(interp->filter[j].index); free(interp->filter[j].coeff); } free(interp->filter); for (j = 0; j < interp->channels; j++) { free(interp->z[j]); } free(interp->z); free(interp); } static void interp_process(interpolator* interp, size_t frames, float* in, float* out) { size_t frame = 0; unsigned int chan = 0; unsigned int f = 0; unsigned int t = 0; unsigned int out_stride = interp->channels * interp->factor; float* outp = 0; double acc = 0; double c = 0; for (frame = 0; frame < frames; frame++) { for (chan = 0; chan < interp->channels; chan++) { // Add sample to delay buffer interp->z[chan][interp->zi] = *in++; // Apply coefficients outp = out + chan; for (f = 0; f < interp->factor; f++) { acc = 0.0; for (t = 0; t < interp->filter[f].count; t++) { int i = (int)interp->zi - (int)interp->filter[f].index[t]; if (i < 0) i += interp->delay; c = interp->filter[f].coeff[t]; acc += interp->z[chan][i] * c; } *outp = (float)acc; outp += interp->channels; } } out += out_stride; interp->zi++; if (interp->zi == interp->delay) interp->zi = 0; } } static void ebur128_init_filter(ebur128_state* st) { int i, j; double f0 = 1681.974450955533; double G = 3.999843853973347; double Q = 0.7071752369554196; double K = tan(M_PI * f0 / (double) st->samplerate); double Vh = pow(10.0, G / 20.0); double Vb = pow(Vh, 0.4996667741545416); double pb[3] = {0.0, 0.0, 0.0}; double pa[3] = {1.0, 0.0, 0.0}; double rb[3] = {1.0, -2.0, 1.0}; double ra[3] = {1.0, 0.0, 0.0}; double a0 = 1.0 + K / Q + K * K ; pb[0] = (Vh + Vb * K / Q + K * K) / a0; pb[1] = 2.0 * (K * K - Vh) / a0; pb[2] = (Vh - Vb * K / Q + K * K) / a0; pa[1] = 2.0 * (K * K - 1.0) / a0; pa[2] = (1.0 - K / Q + K * K) / a0; /* fprintf(stderr, "%.14f %.14f %.14f %.14f %.14f\n", b1[0], b1[1], b1[2], a1[1], a1[2]); */ f0 = 38.13547087602444; Q = 0.5003270373238773; K = tan(M_PI * f0 / (double) st->samplerate); ra[1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K); ra[2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K); /* fprintf(stderr, "%.14f %.14f\n", a2[1], a2[2]); */ st->d->b[0] = pb[0] * rb[0]; st->d->b[1] = pb[0] * rb[1] + pb[1] * rb[0]; st->d->b[2] = pb[0] * rb[2] + pb[1] * rb[1] + pb[2] * rb[0]; st->d->b[3] = pb[1] * rb[2] + pb[2] * rb[1]; st->d->b[4] = pb[2] * rb[2]; st->d->a[0] = pa[0] * ra[0]; st->d->a[1] = pa[0] * ra[1] + pa[1] * ra[0]; st->d->a[2] = pa[0] * ra[2] + pa[1] * ra[1] + pa[2] * ra[0]; st->d->a[3] = pa[1] * ra[2] + pa[2] * ra[1]; st->d->a[4] = pa[2] * ra[2]; for (i = 0; i < 5; ++i) { for (j = 0; j < 5; ++j) { st->d->v[i][j] = 0.0; } } } static int ebur128_init_channel_map(ebur128_state* st) { size_t i; st->d->channel_map = (int*) malloc(st->channels * sizeof(int)); if (!st->d->channel_map) return EBUR128_ERROR_NOMEM; if (st->channels == 4) { st->d->channel_map[0] = EBUR128_LEFT; st->d->channel_map[1] = EBUR128_RIGHT; st->d->channel_map[2] = EBUR128_LEFT_SURROUND; st->d->channel_map[3] = EBUR128_RIGHT_SURROUND; } else if (st->channels == 5) { st->d->channel_map[0] = EBUR128_LEFT; st->d->channel_map[1] = EBUR128_RIGHT; st->d->channel_map[2] = EBUR128_CENTER; st->d->channel_map[3] = EBUR128_LEFT_SURROUND; st->d->channel_map[4] = EBUR128_RIGHT_SURROUND; } else { for (i = 0; i < st->channels; ++i) { switch (i) { case 0: st->d->channel_map[i] = EBUR128_LEFT; break; case 1: st->d->channel_map[i] = EBUR128_RIGHT; break; case 2: st->d->channel_map[i] = EBUR128_CENTER; break; case 3: st->d->channel_map[i] = EBUR128_UNUSED; break; case 4: st->d->channel_map[i] = EBUR128_LEFT_SURROUND; break; case 5: st->d->channel_map[i] = EBUR128_RIGHT_SURROUND; break; default: st->d->channel_map[i] = EBUR128_UNUSED; break; } } } return EBUR128_SUCCESS; } static int ebur128_init_resampler(ebur128_state* st) { int errcode = EBUR128_SUCCESS; if (st->samplerate < 96000) { st->d->interp = interp_create(49, 4, st->channels); CHECK_ERROR(!st->d->interp, EBUR128_ERROR_NOMEM, exit) } else if (st->samplerate < 192000) { st->d->interp = interp_create(49, 2, st->channels); CHECK_ERROR(!st->d->interp, EBUR128_ERROR_NOMEM, exit) } else { st->d->resampler_buffer_input = NULL; st->d->resampler_buffer_output = NULL; st->d->interp = NULL; goto exit; } st->d->resampler_buffer_input_frames = st->d->samples_in_100ms * 4; st->d->resampler_buffer_input = malloc(st->d->resampler_buffer_input_frames * st->channels * sizeof(float)); CHECK_ERROR(!st->d->resampler_buffer_input, EBUR128_ERROR_NOMEM, free_interp) st->d->resampler_buffer_output_frames = st->d->resampler_buffer_input_frames * st->d->interp->factor; st->d->resampler_buffer_output = malloc (st->d->resampler_buffer_output_frames * st->channels * sizeof(float)); CHECK_ERROR(!st->d->resampler_buffer_output, EBUR128_ERROR_NOMEM, free_input) return errcode; free_interp: interp_destroy(st->d->interp); st->d->interp = NULL; free_input: free(st->d->resampler_buffer_input); st->d->resampler_buffer_input = NULL; exit: return errcode; } static void ebur128_destroy_resampler(ebur128_state* st) { free(st->d->resampler_buffer_input); st->d->resampler_buffer_input = NULL; free(st->d->resampler_buffer_output); st->d->resampler_buffer_output = NULL; interp_destroy(st->d->interp); st->d->interp = NULL; } void ebur128_get_version(int* major, int* minor, int* patch) { *major = EBUR128_VERSION_MAJOR; *minor = EBUR128_VERSION_MINOR; *patch = EBUR128_VERSION_PATCH; } ebur128_state* ebur128_init(unsigned int channels, unsigned long samplerate, int mode) { int result; int errcode; ebur128_state* st; unsigned int i; size_t j; st = (ebur128_state*) malloc(sizeof(ebur128_state)); CHECK_ERROR(!st, 0, exit) st->d = (struct ebur128_state_internal*) malloc(sizeof(struct ebur128_state_internal)); CHECK_ERROR(!st->d, 0, free_state) st->channels = channels; errcode = ebur128_init_channel_map(st); CHECK_ERROR(errcode, 0, free_internal) st->d->sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->sample_peak, 0, free_channel_map) st->d->prev_sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->prev_sample_peak, 0, free_sample_peak) st->d->true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->true_peak, 0, free_prev_sample_peak) st->d->prev_true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->prev_true_peak, 0, free_true_peak) for (i = 0; i < channels; ++i) { st->d->sample_peak[i] = 0.0; st->d->prev_sample_peak[i] = 0.0; st->d->true_peak[i] = 0.0; st->d->prev_true_peak[i] = 0.0; } st->d->use_histogram = mode & EBUR128_MODE_HISTOGRAM ? 1 : 0; st->d->history = ULONG_MAX; st->samplerate = samplerate; st->d->samples_in_100ms = (st->samplerate + 5) / 10; st->mode = mode; if ((mode & EBUR128_MODE_S) == EBUR128_MODE_S) { st->d->window = 3000; } else if ((mode & EBUR128_MODE_M) == EBUR128_MODE_M) { st->d->window = 400; } else { goto free_prev_true_peak; } st->d->audio_data_frames = st->samplerate * st->d->window / 1000; if (st->d->audio_data_frames % st->d->samples_in_100ms) { /* round up to multiple of samples_in_100ms */ st->d->audio_data_frames = st->d->audio_data_frames + st->d->samples_in_100ms - (st->d->audio_data_frames % st->d->samples_in_100ms); } st->d->audio_data = (double*) malloc(st->d->audio_data_frames * st->channels * sizeof(double)); CHECK_ERROR(!st->d->audio_data, 0, free_true_peak) for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) { st->d->audio_data[i] = 0.0; } ebur128_init_filter(st); if (st->d->use_histogram) { st->d->block_energy_histogram = malloc(1000 * sizeof(unsigned long)); CHECK_ERROR(!st->d->block_energy_histogram, 0, free_audio_data) for (i = 0; i < 1000; ++i) { st->d->block_energy_histogram[i] = 0; } } else { st->d->block_energy_histogram = NULL; } if (st->d->use_histogram) { st->d->short_term_block_energy_histogram = malloc(1000 * sizeof(unsigned long)); CHECK_ERROR(!st->d->short_term_block_energy_histogram, 0, free_block_energy_histogram) for (i = 0; i < 1000; ++i) { st->d->short_term_block_energy_histogram[i] = 0; } } else { st->d->short_term_block_energy_histogram = NULL; } STAILQ_INIT(&st->d->block_list); st->d->block_list_size = 0; st->d->block_list_max = st->d->history / 100; STAILQ_INIT(&st->d->short_term_block_list); st->d->st_block_list_size = 0; st->d->st_block_list_max = st->d->history / 3000; st->d->short_term_frame_counter = 0; result = ebur128_init_resampler(st); CHECK_ERROR(result, 0, free_short_term_block_energy_histogram) /* the first block needs 400ms of audio data */ st->d->needed_frames = st->d->samples_in_100ms * 4; /* start at the beginning of the buffer */ st->d->audio_data_index = 0; /* initialize static constants */ relative_gate_factor = pow(10.0, relative_gate / 10.0); minus_twenty_decibels = pow(10.0, -20.0 / 10.0); histogram_energy_boundaries[0] = pow(10.0, (-70.0 + 0.691) / 10.0); if (st->d->use_histogram) { for (i = 0; i < 1000; ++i) { histogram_energies[i] = pow(10.0, ((double) i / 10.0 - 69.95 + 0.691) / 10.0); } for (i = 1; i < 1001; ++i) { histogram_energy_boundaries[i] = pow(10.0, ((double) i / 10.0 - 70.0 + 0.691) / 10.0); } } return st; free_short_term_block_energy_histogram: free(st->d->short_term_block_energy_histogram); free_block_energy_histogram: free(st->d->block_energy_histogram); free_audio_data: free(st->d->audio_data); free_prev_true_peak: free(st->d->prev_true_peak); free_true_peak: free(st->d->true_peak); free_prev_sample_peak: free(st->d->prev_sample_peak); free_sample_peak: free(st->d->sample_peak); free_channel_map: free(st->d->channel_map); free_internal: free(st->d); free_state: free(st); exit: return NULL; } void ebur128_destroy(ebur128_state** st) { struct ebur128_dq_entry* entry; free((*st)->d->block_energy_histogram); free((*st)->d->short_term_block_energy_histogram); free((*st)->d->audio_data); free((*st)->d->channel_map); free((*st)->d->sample_peak); free((*st)->d->prev_sample_peak); free((*st)->d->true_peak); free((*st)->d->prev_true_peak); while (!STAILQ_EMPTY(&(*st)->d->block_list)) { entry = STAILQ_FIRST(&(*st)->d->block_list); STAILQ_REMOVE_HEAD(&(*st)->d->block_list, entries); free(entry); } while (!STAILQ_EMPTY(&(*st)->d->short_term_block_list)) { entry = STAILQ_FIRST(&(*st)->d->short_term_block_list); STAILQ_REMOVE_HEAD(&(*st)->d->short_term_block_list, entries); free(entry); } ebur128_destroy_resampler(*st); free((*st)->d); free(*st); *st = NULL; } static void ebur128_check_true_peak(ebur128_state* st, size_t frames) { size_t c, i; interp_process(st->d->interp, frames, st->d->resampler_buffer_input, st->d->resampler_buffer_output); for (c = 0; c < st->channels; ++c) { for (i = 0; i < st->d->resampler_buffer_output_frames; ++i) { if (st->d->resampler_buffer_output[i * st->channels + c] > st->d->prev_true_peak[c]) { st->d->prev_true_peak[c] = st->d->resampler_buffer_output[i * st->channels + c]; } else if (-st->d->resampler_buffer_output[i * st->channels + c] > st->d->prev_true_peak[c]) { st->d->prev_true_peak[c] = -st->d->resampler_buffer_output[i * st->channels + c]; } } } } #ifdef __SSE2_MATH__ #include #define TURN_ON_FTZ \ unsigned int mxcsr = _mm_getcsr(); \ _mm_setcsr(mxcsr | _MM_FLUSH_ZERO_ON); #define TURN_OFF_FTZ _mm_setcsr(mxcsr); #define FLUSH_MANUALLY #else #warning "manual FTZ is being used, please enable SSE2 (-msse2 -mfpmath=sse)" #define TURN_ON_FTZ #define TURN_OFF_FTZ #define FLUSH_MANUALLY \ st->d->v[ci][4] = fabs(st->d->v[ci][4]) < DBL_MIN ? 0.0 : st->d->v[ci][4]; \ st->d->v[ci][3] = fabs(st->d->v[ci][3]) < DBL_MIN ? 0.0 : st->d->v[ci][3]; \ st->d->v[ci][2] = fabs(st->d->v[ci][2]) < DBL_MIN ? 0.0 : st->d->v[ci][2]; \ st->d->v[ci][1] = fabs(st->d->v[ci][1]) < DBL_MIN ? 0.0 : st->d->v[ci][1]; #endif #define EBUR128_FILTER(type, min_scale, max_scale) \ static void ebur128_filter_##type(ebur128_state* st, const type* src, \ size_t frames) { \ static double scaling_factor = -((double) min_scale) > (double) max_scale ? \ -((double) min_scale) : (double) max_scale; \ double* audio_data = st->d->audio_data + st->d->audio_data_index; \ size_t i, c; \ \ TURN_ON_FTZ \ \ if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK) { \ for (c = 0; c < st->channels; ++c) { \ double max = 0.0; \ for (i = 0; i < frames; ++i) { \ if (src[i * st->channels + c] > max) { \ max = src[i * st->channels + c]; \ } else if (-src[i * st->channels + c] > max) { \ max = -1.0 * src[i * st->channels + c]; \ } \ } \ max /= scaling_factor; \ if (max > st->d->prev_sample_peak[c]) st->d->prev_sample_peak[c] = max; \ } \ } \ if ((st->mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK && \ st->d->interp) { \ for (c = 0; c < st->channels; ++c) { \ for (i = 0; i < frames; ++i) { \ st->d->resampler_buffer_input[i * st->channels + c] = \ (float) (src[i * st->channels + c] / scaling_factor); \ } \ } \ ebur128_check_true_peak(st, frames); \ } \ for (c = 0; c < st->channels; ++c) { \ int ci = st->d->channel_map[c] - 1; \ if (ci < 0) continue; \ else if (ci == EBUR128_DUAL_MONO - 1) ci = 0; /*dual mono */ \ for (i = 0; i < frames; ++i) { \ st->d->v[ci][0] = (double) (src[i * st->channels + c] / scaling_factor) \ - st->d->a[1] * st->d->v[ci][1] \ - st->d->a[2] * st->d->v[ci][2] \ - st->d->a[3] * st->d->v[ci][3] \ - st->d->a[4] * st->d->v[ci][4]; \ audio_data[i * st->channels + c] = \ st->d->b[0] * st->d->v[ci][0] \ + st->d->b[1] * st->d->v[ci][1] \ + st->d->b[2] * st->d->v[ci][2] \ + st->d->b[3] * st->d->v[ci][3] \ + st->d->b[4] * st->d->v[ci][4]; \ st->d->v[ci][4] = st->d->v[ci][3]; \ st->d->v[ci][3] = st->d->v[ci][2]; \ st->d->v[ci][2] = st->d->v[ci][1]; \ st->d->v[ci][1] = st->d->v[ci][0]; \ } \ FLUSH_MANUALLY \ } \ TURN_OFF_FTZ \ } EBUR128_FILTER(short, SHRT_MIN, SHRT_MAX) EBUR128_FILTER(int, INT_MIN, INT_MAX) EBUR128_FILTER(float, -1.0f, 1.0f) EBUR128_FILTER(double, -1.0, 1.0) static double ebur128_energy_to_loudness(double energy) { return 10 * (log(energy) / log(10.0)) - 0.691; } static size_t find_histogram_index(double energy) { size_t index_min = 0; size_t index_max = 1000; size_t index_mid; do { index_mid = (index_min + index_max) / 2; if (energy >= histogram_energy_boundaries[index_mid]) { index_min = index_mid; } else { index_max = index_mid; } } while (index_max - index_min != 1); return index_min; } static int ebur128_calc_gating_block(ebur128_state* st, size_t frames_per_block, double* optional_output) { size_t i, c; double sum = 0.0; double channel_sum; for (c = 0; c < st->channels; ++c) { if (st->d->channel_map[c] == EBUR128_UNUSED) continue; channel_sum = 0.0; if (st->d->audio_data_index < frames_per_block * st->channels) { for (i = 0; i < st->d->audio_data_index / st->channels; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } for (i = st->d->audio_data_frames - (frames_per_block - st->d->audio_data_index / st->channels); i < st->d->audio_data_frames; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } } else { for (i = st->d->audio_data_index / st->channels - frames_per_block; i < st->d->audio_data_index / st->channels; ++i) { channel_sum += st->d->audio_data[i * st->channels + c] * st->d->audio_data[i * st->channels + c]; } } if (st->d->channel_map[c] == EBUR128_Mp110 || st->d->channel_map[c] == EBUR128_Mm110 || st->d->channel_map[c] == EBUR128_Mp060 || st->d->channel_map[c] == EBUR128_Mm060 || st->d->channel_map[c] == EBUR128_Mp090 || st->d->channel_map[c] == EBUR128_Mm090) { channel_sum *= 1.41; } else if (st->d->channel_map[c] == EBUR128_DUAL_MONO) { channel_sum *= 2.0; } sum += channel_sum; } sum /= (double) frames_per_block; if (optional_output) { *optional_output = sum; return EBUR128_SUCCESS; } else if (sum >= histogram_energy_boundaries[0]) { if (st->d->use_histogram) { ++st->d->block_energy_histogram[find_histogram_index(sum)]; } else { struct ebur128_dq_entry* block; if (st->d->block_list_size == st->d->block_list_max) { block = STAILQ_FIRST(&st->d->block_list); STAILQ_REMOVE_HEAD(&st->d->block_list, entries); } else { block = (struct ebur128_dq_entry*) malloc(sizeof(struct ebur128_dq_entry)); if (!block) return EBUR128_ERROR_NOMEM; st->d->block_list_size++; } block->z = sum; STAILQ_INSERT_TAIL(&st->d->block_list, block, entries); } return EBUR128_SUCCESS; } else { return EBUR128_SUCCESS; } } int ebur128_set_channel(ebur128_state* st, unsigned int channel_number, int value) { if (channel_number >= st->channels) { return 1; } if (value == EBUR128_DUAL_MONO && (st->channels != 1 || channel_number != 0)) { fprintf(stderr, "EBUR128_DUAL_MONO only works with mono files!\n"); return 1; } st->d->channel_map[channel_number] = value; return 0; } int ebur128_change_parameters(ebur128_state* st, unsigned int channels, unsigned long samplerate) { int errcode = EBUR128_SUCCESS; size_t j; if (channels == st->channels && samplerate == st->samplerate) { return EBUR128_ERROR_NO_CHANGE; } free(st->d->audio_data); st->d->audio_data = NULL; if (channels != st->channels) { unsigned int i; free(st->d->channel_map); st->d->channel_map = NULL; free(st->d->sample_peak); st->d->sample_peak = NULL; free(st->d->prev_sample_peak); st->d->prev_sample_peak = NULL; free(st->d->true_peak); st->d->true_peak = NULL; free(st->d->prev_true_peak); st->d->prev_true_peak = NULL; st->channels = channels; errcode = ebur128_init_channel_map(st); CHECK_ERROR(errcode, EBUR128_ERROR_NOMEM, exit) st->d->sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->sample_peak, EBUR128_ERROR_NOMEM, exit) st->d->prev_sample_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->prev_sample_peak, EBUR128_ERROR_NOMEM, exit) st->d->true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->true_peak, EBUR128_ERROR_NOMEM, exit) st->d->prev_true_peak = (double*) malloc(channels * sizeof(double)); CHECK_ERROR(!st->d->prev_true_peak, EBUR128_ERROR_NOMEM, exit) for (i = 0; i < channels; ++i) { st->d->sample_peak[i] = 0.0; st->d->prev_sample_peak[i] = 0.0; st->d->true_peak[i] = 0.0; st->d->prev_true_peak[i] = 0.0; } } if (samplerate != st->samplerate) { st->samplerate = samplerate; st->d->samples_in_100ms = (st->samplerate + 5) / 10; ebur128_init_filter(st); } st->d->audio_data_frames = st->samplerate * st->d->window / 1000; if (st->d->audio_data_frames % st->d->samples_in_100ms) { /* round up to multiple of samples_in_100ms */ st->d->audio_data_frames = st->d->audio_data_frames + st->d->samples_in_100ms - (st->d->audio_data_frames % st->d->samples_in_100ms); } st->d->audio_data = (double*) malloc(st->d->audio_data_frames * st->channels * sizeof(double)); CHECK_ERROR(!st->d->audio_data, EBUR128_ERROR_NOMEM, exit) for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) { st->d->audio_data[j] = 0.0; } ebur128_destroy_resampler(st); errcode = ebur128_init_resampler(st); CHECK_ERROR(errcode, EBUR128_ERROR_NOMEM, exit) /* the first block needs 400ms of audio data */ st->d->needed_frames = st->d->samples_in_100ms * 4; /* start at the beginning of the buffer */ st->d->audio_data_index = 0; /* reset short term frame counter */ st->d->short_term_frame_counter = 0; exit: return errcode; } int ebur128_set_max_window(ebur128_state* st, unsigned long window) { int errcode = EBUR128_SUCCESS; size_t j; if ((st->mode & EBUR128_MODE_S) == EBUR128_MODE_S && window < 3000) { window = 3000; } else if ((st->mode & EBUR128_MODE_M) == EBUR128_MODE_M && window < 400) { window = 400; } if (window == st->d->window) { return EBUR128_ERROR_NO_CHANGE; } st->d->window = window; free(st->d->audio_data); st->d->audio_data = NULL; st->d->audio_data_frames = st->samplerate * st->d->window / 1000; if (st->d->audio_data_frames % st->d->samples_in_100ms) { /* round up to multiple of samples_in_100ms */ st->d->audio_data_frames = st->d->audio_data_frames + st->d->samples_in_100ms - (st->d->audio_data_frames % st->d->samples_in_100ms); } st->d->audio_data = (double*) malloc(st->d->audio_data_frames * st->channels * sizeof(double)); CHECK_ERROR(!st->d->audio_data, EBUR128_ERROR_NOMEM, exit) for (j = 0; j < st->d->audio_data_frames * st->channels; ++j) { st->d->audio_data[j] = 0.0; } /* the first block needs 400ms of audio data */ st->d->needed_frames = st->d->samples_in_100ms * 4; /* start at the beginning of the buffer */ st->d->audio_data_index = 0; /* reset short term frame counter */ st->d->short_term_frame_counter = 0; exit: return errcode; } int ebur128_set_max_history(ebur128_state* st, unsigned long history) { if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA && history < 3000) { history = 3000; } else if ((st->mode & EBUR128_MODE_M) == EBUR128_MODE_M && history < 400) { history = 400; } if (history == st->d->history) { return EBUR128_ERROR_NO_CHANGE; } st->d->history = history; st->d->block_list_max = st->d->history / 100; st->d->st_block_list_max = st->d->history / 3000; while (st->d->block_list_size > st->d->block_list_max) { struct ebur128_dq_entry* block = STAILQ_FIRST(&st->d->block_list); STAILQ_REMOVE_HEAD(&st->d->block_list, entries); free(block); st->d->block_list_size--; } while (st->d->st_block_list_size > st->d->st_block_list_max) { struct ebur128_dq_entry* block = STAILQ_FIRST(&st->d->short_term_block_list); STAILQ_REMOVE_HEAD(&st->d->short_term_block_list, entries); free(block); st->d->st_block_list_size--; } return EBUR128_SUCCESS; } static int ebur128_energy_shortterm(ebur128_state* st, double* out); #define EBUR128_ADD_FRAMES(type) \ int ebur128_add_frames_##type(ebur128_state* st, \ const type* src, size_t frames) { \ size_t src_index = 0; \ unsigned int c = 0; \ for (c = 0; c < st->channels; c++) { \ st->d->prev_sample_peak[c] = 0.0; \ st->d->prev_true_peak[c] = 0.0; \ } \ while (frames > 0) { \ if (frames >= st->d->needed_frames) { \ ebur128_filter_##type(st, src + src_index, st->d->needed_frames); \ src_index += st->d->needed_frames * st->channels; \ frames -= st->d->needed_frames; \ st->d->audio_data_index += st->d->needed_frames * st->channels; \ /* calculate the new gating block */ \ if ((st->mode & EBUR128_MODE_I) == EBUR128_MODE_I) { \ if (ebur128_calc_gating_block(st, st->d->samples_in_100ms * 4, NULL)) {\ return EBUR128_ERROR_NOMEM; \ } \ } \ if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA) { \ st->d->short_term_frame_counter += st->d->needed_frames; \ if (st->d->short_term_frame_counter == st->d->samples_in_100ms * 30) { \ struct ebur128_dq_entry* block; \ double st_energy; \ ebur128_energy_shortterm(st, &st_energy); \ if (st_energy >= histogram_energy_boundaries[0]) { \ if (st->d->use_histogram) { \ ++st->d->short_term_block_energy_histogram[ \ find_histogram_index(st_energy)];\ } else { \ if (st->d->st_block_list_size == st->d->st_block_list_max) { \ block = STAILQ_FIRST(&st->d->short_term_block_list); \ STAILQ_REMOVE_HEAD(&st->d->short_term_block_list, entries); \ } else { \ block = (struct ebur128_dq_entry*) \ malloc(sizeof(struct ebur128_dq_entry)); \ if (!block) return EBUR128_ERROR_NOMEM; \ st->d->st_block_list_size++; \ } \ block->z = st_energy; \ STAILQ_INSERT_TAIL(&st->d->short_term_block_list, \ block, entries); \ } \ } \ st->d->short_term_frame_counter = st->d->samples_in_100ms * 20; \ } \ } \ /* 100ms are needed for all blocks besides the first one */ \ st->d->needed_frames = st->d->samples_in_100ms; \ /* reset audio_data_index when buffer full */ \ if (st->d->audio_data_index == st->d->audio_data_frames * st->channels) {\ st->d->audio_data_index = 0; \ } \ } else { \ ebur128_filter_##type(st, src + src_index, frames); \ st->d->audio_data_index += frames * st->channels; \ if ((st->mode & EBUR128_MODE_LRA) == EBUR128_MODE_LRA) { \ st->d->short_term_frame_counter += frames; \ } \ st->d->needed_frames -= frames; \ frames = 0; \ } \ } \ for (c = 0; c < st->channels; c++) { \ if (st->d->prev_sample_peak[c] > st->d->sample_peak[c]) { \ st->d->sample_peak[c] = st->d->prev_sample_peak[c]; \ } \ if (st->d->prev_true_peak[c] > st->d->true_peak[c]) { \ st->d->true_peak[c] = st->d->prev_true_peak[c]; \ } \ } \ return EBUR128_SUCCESS; \ } EBUR128_ADD_FRAMES(short) EBUR128_ADD_FRAMES(int) EBUR128_ADD_FRAMES(float) EBUR128_ADD_FRAMES(double) static int ebur128_calc_relative_threshold(ebur128_state* st, size_t* above_thresh_counter, double* relative_threshold) { struct ebur128_dq_entry* it; size_t i; *relative_threshold = 0.0; *above_thresh_counter = 0; if (st->d->use_histogram) { for (i = 0; i < 1000; ++i) { *relative_threshold += st->d->block_energy_histogram[i] * histogram_energies[i]; *above_thresh_counter += st->d->block_energy_histogram[i]; } } else { STAILQ_FOREACH(it, &st->d->block_list, entries) { ++*above_thresh_counter; *relative_threshold += it->z; } } if (*above_thresh_counter != 0) { *relative_threshold /= (double) *above_thresh_counter; *relative_threshold *= relative_gate_factor; } return EBUR128_SUCCESS; } static int ebur128_gated_loudness(ebur128_state** sts, size_t size, double* out) { struct ebur128_dq_entry* it; double gated_loudness = 0.0; double relative_threshold = 0.0; size_t above_thresh_counter = 0; size_t i, j, start_index; for (i = 0; i < size; i++) { if (sts[i] && (sts[i]->mode & EBUR128_MODE_I) != EBUR128_MODE_I) { return EBUR128_ERROR_INVALID_MODE; } } for (i = 0; i < size; i++) { if (!sts[i]) continue; ebur128_calc_relative_threshold(sts[i], &above_thresh_counter, &relative_threshold); } if (!above_thresh_counter) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } above_thresh_counter = 0; if (relative_threshold < histogram_energy_boundaries[0]) { start_index = 0; } else { start_index = find_histogram_index(relative_threshold); if (relative_threshold > histogram_energies[start_index]) { ++start_index; } } for (i = 0; i < size; i++) { if (!sts[i]) continue; if (sts[i]->d->use_histogram) { for (j = start_index; j < 1000; ++j) { gated_loudness += sts[i]->d->block_energy_histogram[j] * histogram_energies[j]; above_thresh_counter += sts[i]->d->block_energy_histogram[j]; } } else { STAILQ_FOREACH(it, &sts[i]->d->block_list, entries) { if (it->z >= relative_threshold) { ++above_thresh_counter; gated_loudness += it->z; } } } } if (!above_thresh_counter) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } gated_loudness /= (double) above_thresh_counter; *out = ebur128_energy_to_loudness(gated_loudness); return EBUR128_SUCCESS; } int ebur128_relative_threshold(ebur128_state* st, double* out) { double relative_threshold; size_t above_thresh_counter; if (st && (st->mode & EBUR128_MODE_I) != EBUR128_MODE_I) return EBUR128_ERROR_INVALID_MODE; ebur128_calc_relative_threshold(st, &above_thresh_counter, &relative_threshold); if (!above_thresh_counter) { *out = -70.0; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(relative_threshold); return EBUR128_SUCCESS; } int ebur128_loudness_global(ebur128_state* st, double* out) { return ebur128_gated_loudness(&st, 1, out); } int ebur128_loudness_global_multiple(ebur128_state** sts, size_t size, double* out) { return ebur128_gated_loudness(sts, size, out); } static int ebur128_energy_in_interval(ebur128_state* st, size_t interval_frames, double* out) { if (interval_frames > st->d->audio_data_frames) { return EBUR128_ERROR_INVALID_MODE; } ebur128_calc_gating_block(st, interval_frames, out); return EBUR128_SUCCESS; } static int ebur128_energy_shortterm(ebur128_state* st, double* out) { return ebur128_energy_in_interval(st, st->d->samples_in_100ms * 30, out); } int ebur128_loudness_momentary(ebur128_state* st, double* out) { double energy; int error = ebur128_energy_in_interval(st, st->d->samples_in_100ms * 4, &energy); if (error) { return error; } else if (energy <= 0.0) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(energy); return EBUR128_SUCCESS; } int ebur128_loudness_shortterm(ebur128_state* st, double* out) { double energy; int error = ebur128_energy_shortterm(st, &energy); if (error) { return error; } else if (energy <= 0.0) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(energy); return EBUR128_SUCCESS; } int ebur128_loudness_window(ebur128_state* st, unsigned long window, double* out) { double energy; size_t interval_frames = st->samplerate * window / 1000; int error = ebur128_energy_in_interval(st, interval_frames, &energy); if (error) { return error; } else if (energy <= 0.0) { *out = -HUGE_VAL; return EBUR128_SUCCESS; } *out = ebur128_energy_to_loudness(energy); return EBUR128_SUCCESS; } static int ebur128_double_cmp(const void *p1, const void *p2) { const double* d1 = (const double*) p1; const double* d2 = (const double*) p2; return (*d1 > *d2) - (*d1 < *d2); } /* EBU - TECH 3342 */ int ebur128_loudness_range_multiple(ebur128_state** sts, size_t size, double* out) { size_t i, j; struct ebur128_dq_entry* it; double* stl_vector; size_t stl_size; double* stl_relgated; size_t stl_relgated_size; double stl_power, stl_integrated; /* High and low percentile energy */ double h_en, l_en; int use_histogram = 0; for (i = 0; i < size; ++i) { if (sts[i]) { if ((sts[i]->mode & EBUR128_MODE_LRA) != EBUR128_MODE_LRA) { return EBUR128_ERROR_INVALID_MODE; } if (i == 0 && sts[i]->mode & EBUR128_MODE_HISTOGRAM) { use_histogram = 1; } else if (use_histogram != !!(sts[i]->mode & EBUR128_MODE_HISTOGRAM)) { return EBUR128_ERROR_INVALID_MODE; } } } if (use_histogram) { unsigned long hist[1000] = { 0 }; size_t percentile_low, percentile_high; size_t index; stl_size = 0; stl_power = 0.0; for (i = 0; i < size; ++i) { if (!sts[i]) continue; for (j = 0; j < 1000; ++j) { hist[j] += sts[i]->d->short_term_block_energy_histogram[j]; stl_size += sts[i]->d->short_term_block_energy_histogram[j]; stl_power += sts[i]->d->short_term_block_energy_histogram[j] * histogram_energies[j]; } } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } stl_power /= stl_size; stl_integrated = minus_twenty_decibels * stl_power; if (stl_integrated < histogram_energy_boundaries[0]) { index = 0; } else { index = find_histogram_index(stl_integrated); if (stl_integrated > histogram_energies[index]) { ++index; } } stl_size = 0; for (j = index; j < 1000; ++j) { stl_size += hist[j]; } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } percentile_low = (size_t) ((stl_size - 1) * 0.1 + 0.5); percentile_high = (size_t) ((stl_size - 1) * 0.95 + 0.5); stl_size = 0; j = index; while (stl_size <= percentile_low) { stl_size += hist[j++]; } l_en = histogram_energies[j - 1]; while (stl_size <= percentile_high) { stl_size += hist[j++]; } h_en = histogram_energies[j - 1]; *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en); return EBUR128_SUCCESS; } else { stl_size = 0; for (i = 0; i < size; ++i) { if (!sts[i]) continue; STAILQ_FOREACH(it, &sts[i]->d->short_term_block_list, entries) { ++stl_size; } } if (!stl_size) { *out = 0.0; return EBUR128_SUCCESS; } stl_vector = (double*) malloc(stl_size * sizeof(double)); if (!stl_vector) return EBUR128_ERROR_NOMEM; for (j = 0, i = 0; i < size; ++i) { if (!sts[i]) continue; STAILQ_FOREACH(it, &sts[i]->d->short_term_block_list, entries) { stl_vector[j] = it->z; ++j; } } qsort(stl_vector, stl_size, sizeof(double), ebur128_double_cmp); stl_power = 0.0; for (i = 0; i < stl_size; ++i) { stl_power += stl_vector[i]; } stl_power /= (double) stl_size; stl_integrated = minus_twenty_decibels * stl_power; stl_relgated = stl_vector; stl_relgated_size = stl_size; while (stl_relgated_size > 0 && *stl_relgated < stl_integrated) { ++stl_relgated; --stl_relgated_size; } if (stl_relgated_size) { h_en = stl_relgated[(size_t) ((stl_relgated_size - 1) * 0.95 + 0.5)]; l_en = stl_relgated[(size_t) ((stl_relgated_size - 1) * 0.1 + 0.5)]; free(stl_vector); *out = ebur128_energy_to_loudness(h_en) - ebur128_energy_to_loudness(l_en); return EBUR128_SUCCESS; } else { free(stl_vector); *out = 0.0; return EBUR128_SUCCESS; } } } int ebur128_loudness_range(ebur128_state* st, double* out) { return ebur128_loudness_range_multiple(&st, 1, out); } int ebur128_sample_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) != EBUR128_MODE_SAMPLE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } else if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = st->d->sample_peak[channel_number]; return EBUR128_SUCCESS; } int ebur128_prev_sample_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_SAMPLE_PEAK) != EBUR128_MODE_SAMPLE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } else if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = st->d->prev_sample_peak[channel_number]; return EBUR128_SUCCESS; } int ebur128_true_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_TRUE_PEAK) != EBUR128_MODE_TRUE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } else if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = st->d->true_peak[channel_number] > st->d->sample_peak[channel_number] ? st->d->true_peak[channel_number] : st->d->sample_peak[channel_number]; return EBUR128_SUCCESS; } int ebur128_prev_true_peak(ebur128_state* st, unsigned int channel_number, double* out) { if ((st->mode & EBUR128_MODE_TRUE_PEAK) != EBUR128_MODE_TRUE_PEAK) { return EBUR128_ERROR_INVALID_MODE; } else if (channel_number >= st->channels) { return EBUR128_ERROR_INVALID_CHANNEL_INDEX; } *out = st->d->prev_true_peak[channel_number] > st->d->prev_sample_peak[channel_number] ? st->d->prev_true_peak[channel_number] : st->d->prev_sample_peak[channel_number]; return EBUR128_SUCCESS; } mlt-6.20.0/src/modules/plus/ebur128/ebur128.h000066400000000000000000000361541362234133600203620ustar00rootroot00000000000000/* See COPYING file for copyright and license details. */ #ifndef EBUR128_H_ #define EBUR128_H_ /** \file ebur128.h * \brief libebur128 - a library for loudness measurement according to * the EBU R128 standard. */ #ifdef __cplusplus extern "C" { #endif #define EBUR128_VERSION_MAJOR 1 #define EBUR128_VERSION_MINOR 2 #define EBUR128_VERSION_PATCH 0 #include /* for size_t */ /** \enum channel * Use these values when setting the channel map with ebur128_set_channel(). * See definitions in ITU R-REC-BS 1770-4 */ enum channel { EBUR128_UNUSED = 0, /**< unused channel (for example LFE channel) */ EBUR128_LEFT, EBUR128_Mp030 = 1, /**< itu M+030 */ EBUR128_RIGHT, EBUR128_Mm030 = 2, /**< itu M-030 */ EBUR128_CENTER, EBUR128_Mp000 = 3, /**< itu M+000 */ EBUR128_LEFT_SURROUND, EBUR128_Mp110 = 4, /**< itu M+110 */ EBUR128_RIGHT_SURROUND, EBUR128_Mm110 = 5, /**< itu M-110 */ EBUR128_DUAL_MONO, /**< a channel that is counted twice */ EBUR128_MpSC, /**< itu M+SC */ EBUR128_MmSC, /**< itu M-SC */ EBUR128_Mp060, /**< itu M+060 */ EBUR128_Mm060, /**< itu M-060 */ EBUR128_Mp090, /**< itu M+090 */ EBUR128_Mm090, /**< itu M-090 */ EBUR128_Mp135, /**< itu M+135 */ EBUR128_Mm135, /**< itu M-135 */ EBUR128_Mp180, /**< itu M+180 */ EBUR128_Up000, /**< itu U+000 */ EBUR128_Up030, /**< itu U+030 */ EBUR128_Um030, /**< itu U-030 */ EBUR128_Up045, /**< itu U+045 */ EBUR128_Um045, /**< itu U-030 */ EBUR128_Up090, /**< itu U+090 */ EBUR128_Um090, /**< itu U-090 */ EBUR128_Up110, /**< itu U+110 */ EBUR128_Um110, /**< itu U-110 */ EBUR128_Up135, /**< itu U+135 */ EBUR128_Um135, /**< itu U-135 */ EBUR128_Up180, /**< itu U+180 */ EBUR128_Tp000, /**< itu T+000 */ EBUR128_Bp000, /**< itu B+000 */ EBUR128_Bp045, /**< itu B+045 */ EBUR128_Bm045 /**< itu B-045 */ }; /** \enum error * Error return values. */ enum error { EBUR128_SUCCESS = 0, EBUR128_ERROR_NOMEM, EBUR128_ERROR_INVALID_MODE, EBUR128_ERROR_INVALID_CHANNEL_INDEX, EBUR128_ERROR_NO_CHANGE }; /** \enum mode * Use these values in ebur128_init (or'ed). Try to use the lowest possible * modes that suit your needs, as performance will be better. */ enum mode { /** can call ebur128_loudness_momentary */ EBUR128_MODE_M = (1 << 0), /** can call ebur128_loudness_shortterm */ EBUR128_MODE_S = (1 << 1) | EBUR128_MODE_M, /** can call ebur128_loudness_global_* and ebur128_relative_threshold */ EBUR128_MODE_I = (1 << 2) | EBUR128_MODE_M, /** can call ebur128_loudness_range */ EBUR128_MODE_LRA = (1 << 3) | EBUR128_MODE_S, /** can call ebur128_sample_peak */ EBUR128_MODE_SAMPLE_PEAK = (1 << 4) | EBUR128_MODE_M, /** can call ebur128_true_peak */ EBUR128_MODE_TRUE_PEAK = (1 << 5) | EBUR128_MODE_M | EBUR128_MODE_SAMPLE_PEAK, /** uses histogram algorithm to calculate loudness */ EBUR128_MODE_HISTOGRAM = (1 << 6) }; /** forward declaration of ebur128_state_internal */ struct ebur128_state_internal; /** \brief Contains information about the state of a loudness measurement. * * You should not need to modify this struct directly. */ typedef struct { int mode; /**< The current mode. */ unsigned int channels; /**< The number of channels. */ unsigned long samplerate; /**< The sample rate. */ struct ebur128_state_internal* d; /**< Internal state. */ } ebur128_state; /** \brief Get library version number. Do not pass null pointers here. * * @param major major version number of library * @param minor minor version number of library * @param patch patch version number of library */ void ebur128_get_version(int* major, int* minor, int* patch); /** \brief Initialize library state. * * @param channels the number of channels. * @param samplerate the sample rate. * @param mode see the mode enum for possible values. * @return an initialized library state. */ ebur128_state* ebur128_init(unsigned int channels, unsigned long samplerate, int mode); /** \brief Destroy library state. * * @param st pointer to a library state. */ void ebur128_destroy(ebur128_state** st); /** \brief Set channel type. * * The default is: * - 0 -> EBUR128_LEFT * - 1 -> EBUR128_RIGHT * - 2 -> EBUR128_CENTER * - 3 -> EBUR128_UNUSED * - 4 -> EBUR128_LEFT_SURROUND * - 5 -> EBUR128_RIGHT_SURROUND * * @param st library state. * @param channel_number zero based channel index. * @param value channel type from the "channel" enum. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_set_channel(ebur128_state* st, unsigned int channel_number, int value); /** \brief Change library parameters. * * Note that the channel map will be reset when setting a different number of * channels. The current unfinished block will be lost. * * @param st library state. * @param channels new number of channels. * @param samplerate new sample rate. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM on memory allocation error. The state will be * invalid and must be destroyed. * - EBUR128_ERROR_NO_CHANGE if channels and sample rate were not changed. */ int ebur128_change_parameters(ebur128_state* st, unsigned int channels, unsigned long samplerate); /** \brief Set the maximum window duration. * * Set the maximum duration that will be used for ebur128_window_loudness(). * Note that this destroys the current content of the audio buffer. * * @param st library state. * @param window duration of the window in ms. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM on memory allocation error. The state will be * invalid and must be destroyed. * - EBUR128_ERROR_NO_CHANGE if window duration not changed. */ int ebur128_set_max_window(ebur128_state* st, unsigned long window); /** \brief Set the maximum history. * * Set the maximum history that will be stored for loudness integration. * More history provides more accurate results, but requires more resources. * * Applies to ebur128_loudness_range() and ebur128_loudness_global() when * EBUR128_MODE_HISTOGRAM is not set. * * Default is ULONG_MAX (at least ~50 days). * Minimum is 3000ms for EBUR128_MODE_LRA and 400ms for EBUR128_MODE_M. * * @param st library state. * @param history duration of history in ms. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NO_CHANGE if history not changed. */ int ebur128_set_max_history(ebur128_state* st, unsigned long history); /** \brief Add frames to be processed. * * @param st library state. * @param src array of source frames. Channels must be interleaved. * @param frames number of frames. Not number of samples! * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM on memory allocation error. */ int ebur128_add_frames_short(ebur128_state* st, const short* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_int(ebur128_state* st, const int* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_float(ebur128_state* st, const float* src, size_t frames); /** \brief See \ref ebur128_add_frames_short */ int ebur128_add_frames_double(ebur128_state* st, const double* src, size_t frames); /** \brief Get global integrated loudness in LUFS. * * @param st library state. * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not been set. */ int ebur128_loudness_global(ebur128_state* st, double* out); /** \brief Get global integrated loudness in LUFS across multiple instances. * * @param sts array of library states. * @param size length of sts * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not been set. */ int ebur128_loudness_global_multiple(ebur128_state** sts, size_t size, double* out); /** \brief Get momentary loudness (last 400ms) in LUFS. * * @param st library state. * @param out momentary loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. */ int ebur128_loudness_momentary(ebur128_state* st, double* out); /** \brief Get short-term loudness (last 3s) in LUFS. * * @param st library state. * @param out short-term loudness in LUFS. -HUGE_VAL if result is negative * infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_S" has not been set. */ int ebur128_loudness_shortterm(ebur128_state* st, double* out); /** \brief Get loudness of the specified window in LUFS. * * window must not be larger than the current window set in st. * The current window can be changed by calling ebur128_set_max_window(). * * @param st library state. * @param window window in ms to calculate loudness. * @param out loudness in LUFS. -HUGE_VAL if result is negative infinity. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if window larger than current window in st. */ int ebur128_loudness_window(ebur128_state* st, unsigned long window, double* out); /** \brief Get loudness range (LRA) of programme in LU. * * Calculates loudness range according to EBU 3342. * * @param st library state. * @param out loudness range (LRA) in LU. Will not be changed in case of * error. EBUR128_ERROR_NOMEM or EBUR128_ERROR_INVALID_MODE will be * returned in this case. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM in case of memory allocation error. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_LRA" has not been set. */ int ebur128_loudness_range(ebur128_state* st, double* out); /** \brief Get loudness range (LRA) in LU across multiple instances. * * Calculates loudness range according to EBU 3342. * * @param sts array of library states. * @param size length of sts * @param out loudness range (LRA) in LU. Will not be changed in case of * error. EBUR128_ERROR_NOMEM or EBUR128_ERROR_INVALID_MODE will be * returned in this case. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_NOMEM in case of memory allocation error. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_LRA" has not been set. */ int ebur128_loudness_range_multiple(ebur128_state** sts, size_t size, double* out); /** \brief Get maximum sample peak from all frames that have been processed. * * The equation to convert to dBFS is: 20 * log10(out) * * @param st library state * @param channel_number channel to analyse * @param out maximum sample peak in float format (1.0 is 0 dBFS) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_SAMPLE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_sample_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get maximum sample peak from the last call to add_frames(). * * The equation to convert to dBFS is: 20 * log10(out) * * @param st library state * @param channel_number channel to analyse * @param out maximum sample peak in float format (1.0 is 0 dBFS) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_SAMPLE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_prev_sample_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get maximum true peak from all frames that have been processed. * * Uses an implementation defined algorithm to calculate the true peak. Do not * try to compare resulting values across different versions of the library, * as the algorithm may change. * * The current implementation uses a custom polyphase FIR interpolator to * calculate true peak. Will oversample 4x for sample rates < 96000 Hz, 2x for * sample rates < 192000 Hz and leave the signal unchanged for 192000 Hz. * * The equation to convert to dBTP is: 20 * log10(out) * * @param st library state * @param channel_number channel to analyse * @param out maximum true peak in float format (1.0 is 0 dBTP) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_TRUE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_true_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get maximum true peak from the last call to add_frames(). * * Uses an implementation defined algorithm to calculate the true peak. Do not * try to compare resulting values across different versions of the library, * as the algorithm may change. * * The current implementation uses a custom polyphase FIR interpolator to * calculate true peak. Will oversample 4x for sample rates < 96000 Hz, 2x for * sample rates < 192000 Hz and leave the signal unchanged for 192000 Hz. * * The equation to convert to dBTP is: 20 * log10(out) * * @param st library state * @param channel_number channel to analyse * @param out maximum true peak in float format (1.0 is 0 dBTP) * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_TRUE_PEAK" has not * been set. * - EBUR128_ERROR_INVALID_CHANNEL_INDEX if invalid channel index. */ int ebur128_prev_true_peak(ebur128_state* st, unsigned int channel_number, double* out); /** \brief Get relative threshold in LUFS. * * @param st library state * @param out relative threshold in LUFS. * @return * - EBUR128_SUCCESS on success. * - EBUR128_ERROR_INVALID_MODE if mode "EBUR128_MODE_I" has not * been set. */ int ebur128_relative_threshold(ebur128_state* st, double* out); #ifdef __cplusplus } #endif #endif /* EBUR128_H_ */ mlt-6.20.0/src/modules/plus/ebur128/queue/000077500000000000000000000000001362234133600201345ustar00rootroot00000000000000mlt-6.20.0/src/modules/plus/ebur128/queue/sys/000077500000000000000000000000001362234133600207525ustar00rootroot00000000000000mlt-6.20.0/src/modules/plus/ebur128/queue/sys/queue.h000066400000000000000000000533671362234133600222650ustar00rootroot00000000000000/*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 * $FreeBSD: src/sys/sys/queue.h,v 1.68.2.4 2011/05/24 16:06:26 mdf Exp $ */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ #include /* * This file defines four types of data structures: singly-linked lists, * singly-linked tail queues, lists and tail queues. * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A singly-linked tail queue is headed by a pair of pointers, one to the * head of the list and the other to the tail of the list. The elements are * singly linked for minimum space and pointer manipulation overhead at the * expense of O(n) removal for arbitrary elements. New elements can be added * to the list after an existing element, at the head of the list, or at the * end of the list. Elements being removed from the head of the tail queue * should use the explicit macro for this purpose for optimum efficiency. * A singly-linked tail queue may only be traversed in the forward direction. * Singly-linked tail queues are ideal for applications with large datasets * and few or no removals or for implementing a FIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * For details on the use of these macros, see the queue(3) manual page. * * * SLIST LIST STAILQ TAILQ * _HEAD + + + + * _HEAD_INITIALIZER + + + + * _ENTRY + + + + * _INIT + + + + * _EMPTY + + + + * _FIRST + + + + * _NEXT + + + + * _PREV - - - + * _LAST - - + + * _FOREACH + + + + * _FOREACH_SAFE + + + + * _FOREACH_REVERSE - - - + * _FOREACH_REVERSE_SAFE - - - + * _INSERT_HEAD + + + + * _INSERT_BEFORE - + - + * _INSERT_AFTER + + + + * _INSERT_TAIL - - + + * _CONCAT - - + + * _REMOVE_AFTER + - + - * _REMOVE_HEAD + - + - * _REMOVE + + + + * _SWAP + + + + * */ #ifdef QUEUE_MACRO_DEBUG /* Store the last 2 places the queue element or head was altered */ struct qm_trace { char * lastfile; int lastline; char * prevfile; int prevline; }; #define TRACEBUF struct qm_trace trace; #define TRASHIT(x) do {(x) = (void *)-1;} while (0) #define QMD_SAVELINK(name, link) void **name = (void *)&(link) #define QMD_TRACE_HEAD(head) do { \ (head)->trace.prevline = (head)->trace.lastline; \ (head)->trace.prevfile = (head)->trace.lastfile; \ (head)->trace.lastline = __LINE__; \ (head)->trace.lastfile = __FILE__; \ } while (0) #define QMD_TRACE_ELEM(elem) do { \ (elem)->trace.prevline = (elem)->trace.lastline; \ (elem)->trace.prevfile = (elem)->trace.lastfile; \ (elem)->trace.lastline = __LINE__; \ (elem)->trace.lastfile = __FILE__; \ } while (0) #else #define QMD_TRACE_ELEM(elem) #define QMD_TRACE_HEAD(head) #define QMD_SAVELINK(name, link) #define TRACEBUF #define TRASHIT(x) #endif /* QUEUE_MACRO_DEBUG */ /* * Singly-linked List declarations. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List functions. */ #define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_FOREACH(var, head, field) \ for ((var) = SLIST_FIRST((head)); \ (var); \ (var) = SLIST_NEXT((var), field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST((head)); \ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ (var) = (tvar)) #define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ for ((varp) = &SLIST_FIRST((head)); \ ((var) = *(varp)) != NULL; \ (varp) = &SLIST_NEXT((var), field)) #define SLIST_INIT(head) do { \ SLIST_FIRST((head)) = NULL; \ } while (0) #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ SLIST_NEXT((slistelm), field) = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ SLIST_FIRST((head)) = (elm); \ } while (0) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_REMOVE(head, elm, type, field) do { \ QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ if (SLIST_FIRST((head)) == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = SLIST_FIRST((head)); \ while (SLIST_NEXT(curelm, field) != (elm)) \ curelm = SLIST_NEXT(curelm, field); \ SLIST_REMOVE_AFTER(curelm, field); \ } \ TRASHIT(*oldnext); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ SLIST_NEXT(elm, field) = \ SLIST_NEXT(SLIST_NEXT(elm, field), field); \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ } while (0) /* * Singly-linked Tail queue declarations. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first;/* first element */ \ struct type **stqh_last;/* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue functions. */ #define STAILQ_CONCAT(head1, head2) do { \ if (!STAILQ_EMPTY((head2))) { \ *(head1)->stqh_last = (head2)->stqh_first; \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_INIT((head2)); \ } \ } while (0) #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) #define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_FOREACH(var, head, field) \ for((var) = STAILQ_FIRST((head)); \ (var); \ (var) = STAILQ_NEXT((var), field)) #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = STAILQ_FIRST((head)); \ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ (var) = (tvar)) #define STAILQ_INIT(head) do { \ STAILQ_FIRST((head)) = NULL; \ (head)->stqh_last = &STAILQ_FIRST((head)); \ } while (0) #define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ STAILQ_NEXT((tqelm), field) = (elm); \ } while (0) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ STAILQ_FIRST((head)) = (elm); \ } while (0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ STAILQ_NEXT((elm), field) = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ } while (0) #define STAILQ_LAST(head, type, field) \ (STAILQ_EMPTY((head)) ? \ NULL : \ ((struct type *)(void *) \ ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) #define STAILQ_REMOVE(head, elm, type, field) do { \ QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ if (STAILQ_FIRST((head)) == (elm)) { \ STAILQ_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = STAILQ_FIRST((head)); \ while (STAILQ_NEXT(curelm, field) != (elm)) \ curelm = STAILQ_NEXT(curelm, field); \ STAILQ_REMOVE_AFTER(head, curelm, field); \ } \ TRASHIT(*oldnext); \ } while (0) #define STAILQ_REMOVE_AFTER(head, elm, field) do { \ if ((STAILQ_NEXT(elm, field) = \ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ } while (0) #define STAILQ_REMOVE_HEAD(head, field) do { \ if ((STAILQ_FIRST((head)) = \ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ (head)->stqh_last = &STAILQ_FIRST((head)); \ } while (0) #define STAILQ_SWAP(head1, head2, type) do { \ struct type *swap_first = STAILQ_FIRST(head1); \ struct type **swap_last = (head1)->stqh_last; \ STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_FIRST(head2) = swap_first; \ (head2)->stqh_last = swap_last; \ if (STAILQ_EMPTY(head1)) \ (head1)->stqh_last = &STAILQ_FIRST(head1); \ if (STAILQ_EMPTY(head2)) \ (head2)->stqh_last = &STAILQ_FIRST(head2); \ } while (0) /* * List declarations. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List functions. */ #if (defined(_KERNEL) && defined(INVARIANTS)) #define QMD_LIST_CHECK_HEAD(head, field) do { \ if (LIST_FIRST((head)) != NULL && \ LIST_FIRST((head))->field.le_prev != \ &LIST_FIRST((head))) \ panic("Bad list head %p first->prev != head", (head)); \ } while (0) #define QMD_LIST_CHECK_NEXT(elm, field) do { \ if (LIST_NEXT((elm), field) != NULL && \ LIST_NEXT((elm), field)->field.le_prev != \ &((elm)->field.le_next)) \ panic("Bad link elm %p next->prev != elm", (elm)); \ } while (0) #define QMD_LIST_CHECK_PREV(elm, field) do { \ if (*(elm)->field.le_prev != (elm)) \ panic("Bad link elm %p prev->next != elm", (elm)); \ } while (0) #else #define QMD_LIST_CHECK_HEAD(head, field) #define QMD_LIST_CHECK_NEXT(elm, field) #define QMD_LIST_CHECK_PREV(elm, field) #endif /* (_KERNEL && INVARIANTS) */ #define LIST_EMPTY(head) ((head)->lh_first == NULL) #define LIST_FIRST(head) ((head)->lh_first) #define LIST_FOREACH(var, head, field) \ for ((var) = LIST_FIRST((head)); \ (var); \ (var) = LIST_NEXT((var), field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST((head)); \ (var) && ((tvar) = LIST_NEXT((var), field), 1); \ (var) = (tvar)) #define LIST_INIT(head) do { \ LIST_FIRST((head)) = NULL; \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ QMD_LIST_CHECK_NEXT(listelm, field); \ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ LIST_NEXT((listelm), field)->field.le_prev = \ &LIST_NEXT((elm), field); \ LIST_NEXT((listelm), field) = (elm); \ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ QMD_LIST_CHECK_PREV(listelm, field); \ (elm)->field.le_prev = (listelm)->field.le_prev; \ LIST_NEXT((elm), field) = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ QMD_LIST_CHECK_HEAD((head), field); \ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ LIST_FIRST((head)) = (elm); \ (elm)->field.le_prev = &LIST_FIRST((head)); \ } while (0) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_REMOVE(elm, field) do { \ QMD_SAVELINK(oldnext, (elm)->field.le_next); \ QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ QMD_LIST_CHECK_NEXT(elm, field); \ QMD_LIST_CHECK_PREV(elm, field); \ if (LIST_NEXT((elm), field) != NULL) \ LIST_NEXT((elm), field)->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = LIST_NEXT((elm), field); \ TRASHIT(*oldnext); \ TRASHIT(*oldprev); \ } while (0) #define LIST_SWAP(head1, head2, type, field) do { \ struct type *swap_tmp = LIST_FIRST((head1)); \ LIST_FIRST((head1)) = LIST_FIRST((head2)); \ LIST_FIRST((head2)) = swap_tmp; \ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ } while (0) /* * Tail queue declarations. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ TRACEBUF \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ TRACEBUF \ } /* * Tail queue functions. */ #if (defined(_KERNEL) && defined(INVARIANTS)) #define QMD_TAILQ_CHECK_HEAD(head, field) do { \ if (!TAILQ_EMPTY(head) && \ TAILQ_FIRST((head))->field.tqe_prev != \ &TAILQ_FIRST((head))) \ panic("Bad tailq head %p first->prev != head", (head)); \ } while (0) #define QMD_TAILQ_CHECK_TAIL(head, field) do { \ if (*(head)->tqh_last != NULL) \ panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ } while (0) #define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ if (TAILQ_NEXT((elm), field) != NULL && \ TAILQ_NEXT((elm), field)->field.tqe_prev != \ &((elm)->field.tqe_next)) \ panic("Bad link elm %p next->prev != elm", (elm)); \ } while (0) #define QMD_TAILQ_CHECK_PREV(elm, field) do { \ if (*(elm)->field.tqe_prev != (elm)) \ panic("Bad link elm %p prev->next != elm", (elm)); \ } while (0) #else #define QMD_TAILQ_CHECK_HEAD(head, field) #define QMD_TAILQ_CHECK_TAIL(head, headname) #define QMD_TAILQ_CHECK_NEXT(elm, field) #define QMD_TAILQ_CHECK_PREV(elm, field) #endif /* (_KERNEL && INVARIANTS) */ #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ QMD_TRACE_HEAD(head1); \ QMD_TRACE_HEAD(head2); \ } \ } while (0) #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_FOREACH(var, head, field) \ for ((var) = TAILQ_FIRST((head)); \ (var); \ (var) = TAILQ_NEXT((var), field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST((head)); \ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for ((var) = TAILQ_LAST((head), headname); \ (var); \ (var) = TAILQ_PREV((var), headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST((head), headname); \ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ (var) = (tvar)) #define TAILQ_INIT(head) do { \ TAILQ_FIRST((head)) = NULL; \ (head)->tqh_last = &TAILQ_FIRST((head)); \ QMD_TRACE_HEAD(head); \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ QMD_TAILQ_CHECK_NEXT(listelm, field); \ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ TAILQ_NEXT((elm), field)->field.tqe_prev = \ &TAILQ_NEXT((elm), field); \ else { \ (head)->tqh_last = &TAILQ_NEXT((elm), field); \ QMD_TRACE_HEAD(head); \ } \ TAILQ_NEXT((listelm), field) = (elm); \ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ QMD_TRACE_ELEM(&(elm)->field); \ QMD_TRACE_ELEM(&listelm->field); \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ QMD_TAILQ_CHECK_PREV(listelm, field); \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ TAILQ_NEXT((elm), field) = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ QMD_TRACE_ELEM(&(elm)->field); \ QMD_TRACE_ELEM(&listelm->field); \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ QMD_TAILQ_CHECK_HEAD(head, field); \ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ TAILQ_FIRST((head))->field.tqe_prev = \ &TAILQ_NEXT((elm), field); \ else \ (head)->tqh_last = &TAILQ_NEXT((elm), field); \ TAILQ_FIRST((head)) = (elm); \ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ QMD_TRACE_HEAD(head); \ QMD_TRACE_ELEM(&(elm)->field); \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ QMD_TAILQ_CHECK_TAIL(head, field); \ TAILQ_NEXT((elm), field) = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &TAILQ_NEXT((elm), field); \ QMD_TRACE_HEAD(head); \ QMD_TRACE_ELEM(&(elm)->field); \ } while (0) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_REMOVE(head, elm, field) do { \ QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ QMD_TAILQ_CHECK_NEXT(elm, field); \ QMD_TAILQ_CHECK_PREV(elm, field); \ if ((TAILQ_NEXT((elm), field)) != NULL) \ TAILQ_NEXT((elm), field)->field.tqe_prev = \ (elm)->field.tqe_prev; \ else { \ (head)->tqh_last = (elm)->field.tqe_prev; \ QMD_TRACE_HEAD(head); \ } \ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ TRASHIT(*oldnext); \ TRASHIT(*oldprev); \ QMD_TRACE_ELEM(&(elm)->field); \ } while (0) #define TAILQ_SWAP(head1, head2, type, field) do { \ struct type *swap_first = (head1)->tqh_first; \ struct type **swap_last = (head1)->tqh_last; \ (head1)->tqh_first = (head2)->tqh_first; \ (head1)->tqh_last = (head2)->tqh_last; \ (head2)->tqh_first = swap_first; \ (head2)->tqh_last = swap_last; \ if ((swap_first = (head1)->tqh_first) != NULL) \ swap_first->field.tqe_prev = &(head1)->tqh_first; \ else \ (head1)->tqh_last = &(head1)->tqh_first; \ if ((swap_first = (head2)->tqh_first) != NULL) \ swap_first->field.tqe_prev = &(head2)->tqh_first; \ else \ (head2)->tqh_last = &(head2)->tqh_first; \ } while (0) #ifdef _KERNEL /* * XXX insque() and remque() are an old way of handling certain queues. * They bogusly assumes that all queue heads look alike. */ struct quehead { struct quehead *qh_link; struct quehead *qh_rlink; }; #ifdef __CC_SUPPORTS___INLINE static __inline void insque(void *a, void *b) { struct quehead *element = (struct quehead *)a, *head = (struct quehead *)b; element->qh_link = head->qh_link; element->qh_rlink = head; head->qh_link = element; element->qh_link->qh_rlink = element; } static __inline void remque(void *a) { struct quehead *element = (struct quehead *)a; element->qh_link->qh_rlink = element->qh_rlink; element->qh_rlink->qh_link = element->qh_link; element->qh_rlink = 0; } #else /* !__CC_SUPPORTS___INLINE */ void insque(void *a, void *b); void remque(void *a); #endif /* __CC_SUPPORTS___INLINE */ #endif /* _KERNEL */ #endif /* !_SYS_QUEUE_H_ */ mlt-6.20.0/src/modules/plus/factory.c000066400000000000000000000145051362234133600174400ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include extern mlt_consumer consumer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); 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_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_dynamic_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_loudness_meter_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_lumakey_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_rgblut_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_filter filter_spot_remover_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_text_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_timer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_count_init( const char *arg ); extern mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #ifdef USE_FFTW extern mlt_filter filter_dance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_fft_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #endif static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/plus/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "blipflash", consumer_blipflash_init ); MLT_REGISTER( filter_type, "affine", filter_affine_init ); MLT_REGISTER( filter_type, "charcoal", filter_charcoal_init ); MLT_REGISTER( filter_type, "dynamictext", filter_dynamictext_init ); MLT_REGISTER( filter_type, "dynamic_loudness", filter_dynamic_loudness_init ); MLT_REGISTER( filter_type, "invert", filter_invert_init ); MLT_REGISTER( filter_type, "lift_gamma_gain", filter_lift_gamma_gain_init ); MLT_REGISTER( filter_type, "loudness", filter_loudness_init ); MLT_REGISTER( filter_type, "loudness_meter", filter_loudness_meter_init ); MLT_REGISTER( filter_type, "lumakey", filter_lumakey_init ); MLT_REGISTER( filter_type, "rgblut", filter_rgblut_init ); MLT_REGISTER( filter_type, "sepia", filter_sepia_init ); MLT_REGISTER( filter_type, "spot_remover", filter_spot_remover_init ); MLT_REGISTER( filter_type, "text", filter_text_init ); MLT_REGISTER( filter_type, "timer", filter_timer_init ); MLT_REGISTER( producer_type, "blipflash", producer_blipflash_init ); MLT_REGISTER( producer_type, "count", producer_count_init ); MLT_REGISTER( transition_type, "affine", transition_affine_init ); #ifdef USE_FFTW MLT_REGISTER( filter_type, "dance", filter_dance_init ); MLT_REGISTER( filter_type, "fft", filter_fft_init ); #endif MLT_REGISTER_METADATA( consumer_type, "blipflash", metadata, "consumer_blipflash.yml" ); MLT_REGISTER_METADATA( filter_type, "affine", metadata, "filter_affine.yml" ); MLT_REGISTER_METADATA( filter_type, "charcoal", metadata, "filter_charcoal.yml" ); MLT_REGISTER_METADATA( filter_type, "dynamictext", metadata, "filter_dynamictext.yml" ); MLT_REGISTER_METADATA( filter_type, "dynamic_loudness", metadata, "filter_dynamic_loudness.yml" ); MLT_REGISTER_METADATA( filter_type, "invert", metadata, "filter_invert.yml" ); MLT_REGISTER_METADATA( filter_type, "lift_gamma_gain", metadata, "filter_lift_gamma_gain.yml" ); MLT_REGISTER_METADATA( filter_type, "loudness", metadata, "filter_loudness.yml" ); MLT_REGISTER_METADATA( filter_type, "loudness_meter", metadata, "filter_loudness_meter.yml" ); MLT_REGISTER_METADATA( filter_type, "lumakey", metadata, "filter_lumakey.yml" ); MLT_REGISTER_METADATA( filter_type, "rgblut", metadata, "filter_rgblut.yml" ); MLT_REGISTER_METADATA( filter_type, "sepia", metadata, "filter_sepia.yml" ); MLT_REGISTER_METADATA( filter_type, "spot_remover", metadata, "filter_spot_remover.yml" ); MLT_REGISTER_METADATA( filter_type, "text", metadata, "filter_text.yml" ); MLT_REGISTER_METADATA( filter_type, "timer", metadata, "filter_timer.yml" ); MLT_REGISTER_METADATA( producer_type, "blipflash", metadata, "producer_blipflash.yml" ); MLT_REGISTER_METADATA( producer_type, "count", metadata, "producer_count.yml" ); MLT_REGISTER_METADATA( transition_type, "affine", metadata, "transition_affine.yml" ); #ifdef USE_FFTW MLT_REGISTER_METADATA( filter_type, "dance", metadata, "filter_dance.yml" ); MLT_REGISTER_METADATA( filter_type, "fft", metadata, "filter_fft.yml" ); #endif } mlt-6.20.0/src/modules/plus/filter_affine.c000066400000000000000000000116631362234133600205700ustar00rootroot00000000000000/* * filter_affine.c -- affine filter * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 ) { // Get the filter mlt_filter filter = mlt_frame_pop_service( frame ); // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Get the image int error = 0; *format = mlt_image_rgb24a; mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); 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; mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); if ( producer == NULL ) { char *background = mlt_properties_get( properties, "background" ); producer = mlt_factory_producer( profile, NULL, background ); mlt_properties_set_data( properties, "producer", producer, 0, (mlt_destructor)mlt_producer_close, NULL ); } if ( transition == NULL ) { transition = mlt_factory_transition( profile, "affine", NULL ); mlt_properties_set_data( properties, "transition", transition, 0, (mlt_destructor)mlt_transition_close, NULL ); if ( transition ) mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "b_alpha", 1 ); } if ( producer != NULL && transition != NULL ) { mlt_position position = mlt_filter_get_position( filter, frame ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_position in = mlt_filter_get_in( filter ); mlt_position out = mlt_filter_get_out( filter ); double consumer_ar = mlt_profile_sar( profile ); mlt_transition_set_in_and_out( transition, in, out ); if ( out > 0 ) { mlt_properties_set_position( MLT_PRODUCER_PROPERTIES( producer ), "length", out - in + 1 ); mlt_producer_set_in_and_out( producer, in, out ); } mlt_producer_seek( producer, in + 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_frame_set_position( a_frame, in + position ); // Set the rescale interpolation to match the frame mlt_properties_set( MLT_FRAME_PROPERTIES( a_frame ), "rescale.interp", mlt_properties_get( frame_properties, "rescale.interp" ) ); // Special case - aspect_ratio = 0 if ( mlt_frame_get_aspect_ratio( frame ) == 0 ) mlt_frame_set_aspect_ratio( frame, consumer_ar ); if ( mlt_frame_get_aspect_ratio( a_frame ) == 0 ) mlt_frame_set_aspect_ratio( a_frame, consumer_ar ); // Add the affine transition onto the frame stack mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); mlt_transition_process( transition, a_frame, frame ); if ( mlt_properties_get_int( properties, "use_normalised" ) ) { // Use the normalised width & height mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); *width = profile->width; *height = profile->height; } 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_frame_set_image( frame, *image, *width * *height * 4, NULL ); mlt_frame_set_alpha( frame, mlt_frame_get_alpha_mask( a_frame ), *width * *height, NULL ); } else { mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "background", arg ? arg : "colour:0" ); } return filter; } mlt-6.20.0/src/modules/plus/filter_affine.yml000066400000000000000000000021611362234133600211400ustar00rootroot00000000000000schema_version: 0.2 type: filter identifier: affine title: Transform version: 4 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: background argument: yes title: Background description: > The specification for a producer to act as the background image. type: string default: colour:0 - identifier: producer.* title: Producer properties description: A property and its value to apply to the producer. type: properties mutable: yes - identifier: transition.* title: Transition properties description: > A property and its value to apply to the transition. This is the primary means to use this filter. See the "affine" transition for details. type: properties service-name: transition.affine mutable: yes - identifier: use_normalised title: Use normalized description: > Use the profile's video resolution when requesting the image from the filter's producer instead of the resolution requested by the consumer. type: boolean default: 0 mutable: yes mlt-6.20.0/src/modules/plus/filter_charcoal.c000066400000000000000000000132461362234133600211130ustar00rootroot00000000000000/* * filter_charcoal.c -- charcoal filter * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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 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 ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); // Get the image *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { // Get the charcoal scatter value int x_scatter = mlt_properties_anim_get_double( properties, "x_scatter", position, length ); int y_scatter = mlt_properties_anim_get_double( properties, "y_scatter", position, length ); float scale = mlt_properties_anim_get_double( properties, "scale" ,position, length); float mix = mlt_properties_anim_get_double( properties, "mix", position, length); int invert = mlt_properties_anim_get_int( properties, "invert", position, length); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale_x = mlt_profile_scale_width(profile, *width); double scale_y = mlt_profile_scale_height(profile, *height); if (scale_x > 0.0 || scale_y > 0.0) { x_scatter = MAX(1, lrint(x_scatter * scale_x)); y_scatter = MAX(1, lrint(y_scatter * scale_y)); } // 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_frame_set_image( frame, *image, *width * *height * 2, mlt_pool_release ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "x_scatter", 1 ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "y_scatter", 1 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "scale", 1.5 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "mix", 0.0 ); } return filter; } mlt-6.20.0/src/modules/plus/filter_charcoal.yml000066400000000000000000000013231362234133600214630ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: charcoal title: Charcoal version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: x_scatter title: Line Width type: integer default: 1 minimum: 1 mutable: yes unit: pixels - identifier: y_scatter title: Line Height type: integer default: 1 minimum: 1 mutable: yes unit: pixels - identifier: scale title: Contrast type: float default: 1.5 mutable: yes - identifier: mix title: Color type: float default: 0 mutable: yes - identifier: invert title: Invert type: boolean default: 0 mutable: yes mlt-6.20.0/src/modules/plus/filter_dance.c000066400000000000000000000242551362234133600204130ustar00rootroot00000000000000/* * filter_dance.c -- animate images size and position to the audio * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include // calloc(), free() #include // sin() #include // strdup() // Private Constants static const double PI = 3.14159265358979323846; // Private Types typedef struct { mlt_filter affine; mlt_filter fft; char* mag_prop_name; int rel_pos; double phase; int preprocess_warned; } private_data; static double apply( double positive, double negative, double mag, double max_range ) { if( mag == 0.0 ) { return 0.0; } else if( mag > 0.0 && positive > 0.0 ) { return positive * mag * max_range; } else if( mag < 0.0 && negative > 0.0 ) { return negative * mag * max_range; } else if( positive ) { return positive * fabs( mag ) * max_range; } else if ( negative ) { return negative * -fabs( mag ) * max_range; } return 0.0; } static int filter_get_audio( mlt_frame frame, void** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_filter filter = (mlt_filter)mlt_frame_pop_audio( frame ); mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); // Create the FFT filter the first time. if( !pdata->fft ) { pdata->fft = mlt_factory_filter( profile, "fft", NULL ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( pdata->fft ), "window_size", mlt_properties_get_int( filter_properties, "window_size" ) ); if( !pdata->fft ) { mlt_log_warning( MLT_FILTER_SERVICE(filter), "Unable to create FFT.\n" ); return 1; } } mlt_properties fft_properties = MLT_FILTER_PROPERTIES( pdata->fft ); double low_freq = mlt_properties_get_int( filter_properties, "frequency_low" ); double hi_freq = mlt_properties_get_int( filter_properties, "frequency_high" ); double threshold = mlt_properties_get_int( filter_properties, "threshold" ); double osc = mlt_properties_get_int( filter_properties, "osc" ); float peak = 0; // The service must stay locked while using the private data mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // Perform FFT processing on the frame mlt_filter_process( pdata->fft, frame ); mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); float* bins = mlt_properties_get_data( fft_properties, "bins", NULL ); double window_level = mlt_properties_get_double( fft_properties, "window_level" ); if( bins && window_level == 1.0 ) { // Find the peak FFT magnitude in the configured range of frequencies int bin_count = mlt_properties_get_int( fft_properties, "bin_count" ); double bin_width = mlt_properties_get_double( fft_properties, "bin_width" ); int bin = 0; for( bin = 0; bin < bin_count; bin++ ) { double F = bin_width * (double)bin; if( F >= low_freq && F <= hi_freq ) { if( bins[bin] > peak ) { peak = bins[bin]; } } } } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Scale the magnitude to dB and apply oscillation double dB = peak > 0.0 ? 20 * log10( peak ) : -1000.0; double mag = 0.0; if( dB >= threshold ) { // Scale to range 0.0-1.0 mag = 1 - (dB / threshold); if( osc != 0 ) { // Apply the oscillation double fps = mlt_profile_fps( profile ); double t = pdata->rel_pos / fps; mag = mag * sin( 2 * PI * osc * t + pdata->phase ); } pdata->rel_pos++; } else { pdata->rel_pos = 1; // Alternate the phase so that the dancing alternates directions to the beat. pdata->phase = pdata->phase ? 0 : PI; mag = 0; } // Save the magnitude as a property on the frame to be used in get_image() mlt_properties_set_double( MLT_FRAME_PROPERTIES(frame), pdata->mag_prop_name, mag ); return 0; } /** Get the image. */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame ); private_data* pdata = (private_data*)filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); if( mlt_properties_get( frame_properties, pdata->mag_prop_name ) ) { double mag = mlt_properties_get_double( frame_properties, pdata->mag_prop_name ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); // scale_x and scale_y are in the range 0.0 to x.0 with: // 0.0 = the largest possible // < 1.0 = increase size (zoom in) // 1.0 = no scaling // > 1.0 = decrease size (zoom out) double initial_zoom = mlt_properties_get_double( filter_properties, "initial_zoom" ); double zoom = mlt_properties_get_double( filter_properties, "zoom" ); double scale_xy = (100.0 / initial_zoom ) - ( fabs(mag) * (zoom / 100.0) ); if( scale_xy < 0.1 ) scale_xy = 0.1; // ox is in the range -width to +width with: // > 0 = offset to the left // 0 = no offset // < 0 = offset to the right double left = mlt_properties_get_double( filter_properties, "left" ); double right = mlt_properties_get_double( filter_properties, "right" ); double ox = apply( left, right, mag, (double)profile->width / 100.0 ); // oy is in the range -height to +height with: // > 0 = offset up // 0 = no offset // < 0 = offset down double up = mlt_properties_get_double( filter_properties, "up" ); double down = mlt_properties_get_double( filter_properties, "down" ); double oy = apply( up, down, mag, (double)profile->height / 100.0 ); // fix_rotate_x is in the range -360 to +360 with: // > 0 = rotate clockwise // 0 = no rotation // < 0 = rotate anticlockwise double counterclockwise = mlt_properties_get_double( filter_properties, "counterclockwise" ); double clockwise = mlt_properties_get_double( filter_properties, "clockwise" ); double fix_rotate_x = apply( clockwise, counterclockwise, mag, 1.0 ); // Perform the affine. mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); mlt_properties affine_properties = MLT_FILTER_PROPERTIES( pdata->affine ); mlt_properties_set_double( affine_properties, "transition.scale_x", scale_xy ); mlt_properties_set_double( affine_properties, "transition.scale_y", scale_xy ); mlt_properties_set_double( affine_properties, "transition.ox", ox ); mlt_properties_set_double( affine_properties, "transition.oy", oy ); mlt_properties_set_double( affine_properties, "transition.fix_rotate_x", fix_rotate_x ); mlt_filter_process( pdata->affine, frame ); error = mlt_frame_get_image( frame, image, format, width, height, 0 ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } else { if ( pdata->preprocess_warned++ == 2 ) { // This filter depends on the consumer processing the audio before the // video. mlt_log_warning( MLT_FILTER_SERVICE(filter), "Audio not preprocessed. Unable to dance.\n" ); } mlt_frame_get_image( frame, image, format, width, height, 0 ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } static void filter_close( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if ( pdata ) { mlt_filter_close( pdata->affine ); mlt_filter_close( pdata->fft ); free( pdata->mag_prop_name ); free( pdata ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ mlt_filter filter_dance_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); mlt_filter affine_filter = mlt_factory_filter( profile, "affine", "colour:0x00000000" ); if ( filter && pdata && affine_filter ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set_int( properties, "_filter_private", 1 ); mlt_properties_set_int( properties, "frequency_low", 20 ); mlt_properties_set_int( properties, "frequency_high", 20000 ); mlt_properties_set_double( properties, "threshold", -30.0 ); mlt_properties_set_double( properties, "osc", 5.0 ); mlt_properties_set_double( properties, "initial_zoom", 100.0 ); mlt_properties_set_double( properties, "zoom", 0.0 ); mlt_properties_set_double( properties, "left", 0.0 ); mlt_properties_set_double( properties, "right", 0.0 ); mlt_properties_set_double( properties, "up", 0.0 ); mlt_properties_set_double( properties, "down", 0.0 ); mlt_properties_set_double( properties, "clockwise", 0.0 ); mlt_properties_set_double( properties, "counterclockwise", 0.0 ); mlt_properties_set_int( properties, "window_size", 2048 ); // Create a unique ID for storing data on the frame pdata->mag_prop_name = calloc( 1, 20 ); snprintf( pdata->mag_prop_name, 20, "fft_mag.%p", filter ); pdata->mag_prop_name[20 - 1] = '\0'; pdata->affine = affine_filter; pdata->fft = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter dance failed\n" ); if( filter ) { mlt_filter_close( filter ); } if( affine_filter ) { mlt_filter_close( affine_filter ); } if( pdata ) { free( pdata ); } filter = NULL; } return filter; } mlt-6.20.0/src/modules/plus/filter_dance.yml000066400000000000000000000114471362234133600207710ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: dance title: Dance version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that moves the image around proportional to the magnitude of the audio spectrum. parameters: - identifier: frequency_low title: Low Frequency type: integer description: > The low end of the frequency range to be used to influence the image motion. mutable: yes readonly: no default: 20 unit: Hz - identifier: frequency_high title: High Frequency type: integer description: > The high end of the frequency range to be used to influence the image motion. mutable: yes readonly: no default: 20000 unit: Hz - identifier: threshold title: Level Threshold type: float description: > The minimum amplitude of sound that must occur within the frequency range to cause the image to move. motion. mutable: yes readonly: no default: -30 minimum: -100 maximum: 0 unit: dB - identifier: osc title: Oscillation type: float description: > Oscillation can be useful to make the image move back and forth during long periods of sound. A value of 0 specifies no oscillation. mutable: yes readonly: no default: 5 minimum: 0 unit: Hz - identifier: initial_zoom title: Initial Zoom type: float description: | The amount to zoom the image before any motion occurs. This can be used to avoid black on the sides of the image when it moves. 100% = no zoom < 100% = zoom out (make the image smaller) > 100% = zoom in (make the image larger) mutable: yes readonly: no default: 100 minimum: 0 maximum: 5000 unit: '%' - identifier: zoom title: Zoom type: float description: | The amount that the audio affects the zoom of the image. < 0% = Image will zoom out (get smaller) with more sound 0% = no zoom > 0% = Image will zoom in (get larger) with more sound mutable: yes readonly: no default: 0 minimum: -100 maximum: 100 unit: '%' - identifier: left title: Left type: float description: | The amount that the audio affects the left offset of the image. 0% = no left offset > 0% = Image will move left with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 100 unit: '%' - identifier: right title: Right type: float description: | The amount that the audio affects the right offset of the image. 0% = no right offset > 0% = Image will move right with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 100 unit: '%' - identifier: up title: Up type: float description: | The amount that the audio affects the upward offset of the image. 0% = no upward offset > 0% = Image will move up with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 100 unit: '%' - identifier: down title: Down type: float description: | The amount that the audio affects the downward offset of the image. 0% = no downward offset > 0% = Image will move down with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 100 unit: '%' - identifier: clockwise title: Clockwise type: float description: | The amount that the audio affects the clockwise rotation of the image. 0% = no clockwise rotation > 0% = Image will rotate clockwise with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 360 unit: degrees - identifier: counterclockwise title: Counterclockwise type: float description: | The amount that the audio affects the counterclockwise rotation of the image. 0% = no counterclockwise rotation > 0% = Image will rotate counterclockwise with more sound mutable: yes readonly: no default: 0 minimum: 0 maximum: 360 unit: degrees - identifier: window_size title: Window Size type: integer description: > The number of samples that the FFT will be performed on. If window_size is less than the number of samples in a frame, extra samples will be ignored. If window_size is more than the number of samples in a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 2048 mlt-6.20.0/src/modules/plus/filter_dynamic_loudness.c000066400000000000000000000171441362234133600227000ustar00rootroot00000000000000/* * filter_loudness.c -- normalize audio according to EBU R128 * Copyright (C) 2014 Brian Matherly * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include typedef struct { ebur128_state* r128; double target_gain; double start_gain; double end_gain; int reset; unsigned int time_elapsed_ms; mlt_position prev_o_pos; } private_data; static void property_changed( mlt_service owner, mlt_filter filter, char *name ) { private_data* pdata = (private_data*)filter->child; if ( !strcmp( name, "window" ) ) { pdata->reset = 1; } } static void check_for_reset( mlt_filter filter, int channels, int frequency ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; if( pdata->reset ) { if( pdata->r128 ) { ebur128_destroy( &pdata->r128 ); } pdata->r128 = 0; pdata->target_gain = 0.0; pdata->start_gain = 0.0; pdata->end_gain = 0.0; pdata->reset = 0; pdata->time_elapsed_ms = 0; pdata->prev_o_pos = -1; mlt_properties_set_double( properties, "out_gain", 0.0 ); mlt_properties_set_double( properties, "in_loudness", -100.0 ); mlt_properties_set_int( properties, "reset_count", mlt_properties_get_int( properties, "reset_count") + 1 ); } if( !pdata->r128 ) { pdata->r128 = ebur128_init( channels, frequency, EBUR128_MODE_I ); ebur128_set_max_window( pdata->r128, 400 ); ebur128_set_max_history( pdata->r128, mlt_properties_get_int( properties, "window" ) * 1000.0 ); } } static void analyze_audio( mlt_filter filter, void* buffer, int samples, int frequency ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; int result = -1; double in_loudness = 0.0; mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); double fps = mlt_profile_fps( profile ); ebur128_add_frames_float( pdata->r128, buffer, samples ); if( pdata->time_elapsed_ms < 400 ) { // Waiting for first program loudness measurement. // Use window loudness as initial guess. result = ebur128_loudness_window( pdata->r128, pdata->time_elapsed_ms, &in_loudness ); pdata->time_elapsed_ms += samples * 1000 / frequency; } else { result = ebur128_loudness_global( pdata->r128, &in_loudness ); } if( result == EBUR128_SUCCESS && in_loudness != HUGE_VAL && in_loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "in_loudness", in_loudness ); double target_loudness = mlt_properties_get_double( properties, "target_loudness" ); pdata->target_gain = target_loudness - in_loudness; // Make sure gain limits are not exceeded. double max_gain = mlt_properties_get_double( properties, "max_gain" ); double min_gain = mlt_properties_get_double( properties, "min_gain" ); if( pdata->target_gain > max_gain ) { pdata->target_gain = max_gain; } else if ( pdata->target_gain < min_gain ) { pdata->target_gain = min_gain; } } // Make sure gain does not change too quickly. pdata->start_gain = pdata->end_gain; pdata->end_gain = pdata->target_gain; double max_frame_gain = mlt_properties_get_double( properties, "max_rate" ) / fps; if( pdata->start_gain - pdata->end_gain > max_frame_gain ) { pdata->end_gain = pdata->start_gain - max_frame_gain; } else if( pdata->end_gain - pdata->start_gain > max_frame_gain ) { pdata->end_gain = pdata->start_gain + max_frame_gain; } mlt_properties_set_double( properties, "out_gain", pdata->end_gain ); } static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_filter filter = mlt_frame_pop_audio( frame ); private_data* pdata = (private_data*)filter->child; mlt_position o_pos = mlt_frame_original_position( frame ); // Get the producer's audio *format = mlt_audio_f32le; mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); if( abs( o_pos - pdata->prev_o_pos ) > 1 ) { // Assume this is a new clip and restart // Use original position so that transitions between clips are detected. pdata->reset = 1; mlt_log_info( MLT_FILTER_SERVICE( filter ), "Reset. Old Pos: %d\tNew Pos: %d\n", pdata->prev_o_pos, o_pos ); } check_for_reset( filter, *channels, *frequency ); if( o_pos != pdata->prev_o_pos ) { // Only analyze the audio is the producer is not paused. analyze_audio( filter, *buffer, *samples, *frequency ); } double start_coeff = pdata->start_gain > -90.0 ? pow(10.0, pdata->start_gain / 20.0) : 0.0; double end_coeff = pdata->end_gain > -90.0 ? pow(10.0, pdata->end_gain / 20.0) : 0.0; double coeff_factor = pow( (end_coeff / start_coeff), 1.0 / (double)*samples ); double coeff = start_coeff; float* p = *buffer; int s = 0; int c = 0; for( s = 0; s < *samples; s++ ) { coeff = coeff * coeff_factor; for ( c = 0; c < *channels; c++ ) { *p = *p * coeff; p++; } } pdata->prev_o_pos = o_pos; mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; } /** Destructor for the filter. */ static void filter_close( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if( pdata ) { if( pdata->r128 ) { ebur128_destroy( &pdata->r128 ); } free( pdata ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ mlt_filter filter_dynamic_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); if( filter && pdata ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set( properties, "target_loudness", "-23.0" ); mlt_properties_set( properties, "window", "3.0" ); mlt_properties_set( properties, "max_gain", "15.0" ); mlt_properties_set( properties, "min_gain", "-15.0" ); mlt_properties_set( properties, "max_rate", "3.0" ); mlt_properties_set( properties, "in_loudness", "-100.0" ); mlt_properties_set( properties, "out_gain", "0.0" ); mlt_properties_set( properties, "reset_count", "0" ); pdata->target_gain = 0.0; pdata->start_gain = 0.0; pdata->end_gain = 0.0; pdata->r128 = 0; pdata->reset = 1; pdata->time_elapsed_ms = 0; pdata->prev_o_pos = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; mlt_events_listen( properties, filter, "property-changed", (mlt_listener)property_changed ); } else { if( filter ) { mlt_filter_close( filter ); filter = NULL; } free( pdata ); } return filter; } mlt-6.20.0/src/modules/plus/filter_dynamic_loudness.yml000066400000000000000000000047341362234133600232600ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: dynamic_loudness title: Dynamic Loudness version: 1 copyright: Brian Matherly creator: Brian Matherly license: LGPLv2.1 language: en tags: - Audio description: Dynamically correct audio loudness as recommended by EBU R128. notes: > This filter adjusts the level of the audio based on the loudness of the input. It performs loudness measurement over a specified sliding window of time. Then, it adjusts the gain on the output based on the difference between the measured loudness and the target loudness in order to achieve the desired loudness. parameters: - identifier: target_loudness title: Target Program Loudness type: float description: > The target program loudness in LUFS (Loudness Units Full Scale). readonly: no mutable: yes default: -23.0 minimum: -50.0 maximum: -10.0 unit: LUFS - identifier: window title: Measurement Window type: float description: > The duration of time in seconds over which the loudness is calculated. readonly: no mutable: yes default: 3.0 minimum: 1 maximum: 100000 unit: seconds - identifier: max_gain title: Maximum Gain Increase type: float description: > The maximum amount that the gain will be increased by the filter. readonly: no mutable: yes default: 15 minimum: 0 maximum: 30 unit: dB - identifier: min_gain title: Maximum Gain Decrease type: float description: > The maximum amount that the gain will be decreased by the filter. readonly: no mutable: yes default: -15 minimum: 0 maximum: -30 unit: dB - identifier: in_loudness title: Input Program Loudness type: float description: > The program loudness measured on the input over the duration of the window. readonly: yes unit: LUFS - identifier: out_gain title: Output Gain type: float description: > The amount of gain applied to the last frame. Updated with each new frame. readonly: yes unit: dB - identifier: reset_count title: Reset Count type: integer description: > The number of times the filter has reset the loudness measurement. The measurement is reset whenever the filter detects a discontinuity in the frame sequence. It also resets when it detects that the producer has changed. readonly: yes mlt-6.20.0/src/modules/plus/filter_dynamictext.c000066400000000000000000000233071362234133600216670ustar00rootroot00000000000000/* * filter_dynamictext.c -- dynamic text overlay filter * Copyright (C) 2011-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include // for stat() #include // for stat() #include // for stat() #include // for strftime() and gtime() #define MAX_TEXT_LEN 512 /** Get the next token and indicate whether it is enclosed in "# #". */ static int get_next_token(char* str, int* pos, char* token, int* is_keyword) { int token_pos = 0; int str_len = strlen( str ); if( (*pos) >= str_len || str[*pos] == '\0' ) { return 0; } if( str[*pos] == '#' ) { *is_keyword = 1; (*pos)++; } else { *is_keyword = 0; } while( *pos < str_len && token_pos < MAX_TEXT_LEN - 1) { if( str[*pos] == '\\' && str[(*pos) + 1] == '#' ) { // Escape Sequence - "#" preceded by "\" - copy the # into the token. token[token_pos] = '#'; token_pos++; (*pos)++; // skip "\" (*pos)++; // skip "#" } else if( str[*pos] == '#' ) { if( *is_keyword ) { // Found the end of the keyword (*pos)++; } break; } else { token[token_pos] = str[*pos]; token_pos++; (*pos)++; } } token[token_pos] = '\0'; return 1; } static void get_timecode_str( mlt_filter filter, mlt_frame frame, char* text, mlt_time_format time_format ) { mlt_position frames = mlt_frame_get_position( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); char *s = mlt_properties_frames_to_time( properties, frames, time_format ); if ( s ) strncat( text, s, MAX_TEXT_LEN - strlen( text ) - 1 ); } static void get_frame_str( mlt_filter filter, mlt_frame frame, char* text ) { int pos = mlt_frame_get_position( frame ); char s[12]; snprintf( s, sizeof( s ) - 1, "%d", pos ); strncat( text, s, MAX_TEXT_LEN - strlen( text ) - 1 ); } static void get_filedate_str( const char* keyword, mlt_filter filter, mlt_frame frame, char* text ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); char* filename = mlt_properties_get( producer_properties, "resource"); struct stat file_info; if( !stat(filename, &file_info)) { const char *format = "%Y/%m/%d"; int n = strlen( "filedate" ) + 1; struct tm* time_info = gmtime( &(file_info.st_mtime) ); char *date = calloc( 1, MAX_TEXT_LEN ); if ( strlen( keyword ) > n ) format = &keyword[n]; strftime( date, MAX_TEXT_LEN, format, time_info ); strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1); free( date ); } } static void get_localfiledate_str( const char* keyword, mlt_filter filter, mlt_frame frame, char* text ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); char* filename = mlt_properties_get( producer_properties, "resource" ); struct stat file_info; if( !stat( filename, &file_info ) ) { const char *format = "%Y/%m/%d"; int n = strlen( "localfiledate" ) + 1; struct tm* time_info = localtime( &(file_info.st_mtime) ); char *date = calloc( 1, MAX_TEXT_LEN ); if ( strlen( keyword ) > n ) format = &keyword[n]; strftime( date, MAX_TEXT_LEN, format, time_info ); strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1); free( date ); } } static void get_localtime_str( const char* keyword, char* text ) { const char *format = "%Y/%m/%d %H:%M:%S"; int n = strlen( "localtime" ) + 1; time_t now = time( NULL ); struct tm* time_info = localtime( &now ); char *date = calloc( 1, MAX_TEXT_LEN ); if ( strlen( keyword ) > n ) format = &keyword[n]; strftime( date, MAX_TEXT_LEN, format, time_info ); strncat( text, date, MAX_TEXT_LEN - strlen( text ) - 1); free( date ); } static void get_resource_str( mlt_filter filter, mlt_frame frame, char* text ) { mlt_producer producer = mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); strncat( text, mlt_properties_get( producer_properties, "resource" ), MAX_TEXT_LEN - strlen( text ) - 1 ); } static void get_createdate_str( const char* keyword, mlt_filter filter, mlt_frame frame, char* text ) { time_t creation_date = (time_t)(mlt_producer_get_creation_time( mlt_frame_get_original_producer( frame ) ) / 1000); const char *format = "%Y/%m/%d"; int n = strlen( "createdate" ) + 1; if ( strlen( keyword ) > n ) format = &keyword[n]; strftime( text, MAX_TEXT_LEN - strlen( text ) - 1, format, localtime( &creation_date ) ); } /** Perform substitution for keywords that are enclosed in "# #". */ static void substitute_keywords(mlt_filter filter, char* result, char* value, mlt_frame frame) { char keyword[MAX_TEXT_LEN] = ""; int pos = 0; int is_keyword = 0; while ( get_next_token(value, &pos, keyword, &is_keyword) ) { if(!is_keyword) { strncat( result, keyword, MAX_TEXT_LEN - strlen( result ) - 1 ); } else if ( !strcmp( keyword, "timecode" ) || !strcmp( keyword, "smpte_df" ) ) { get_timecode_str( filter, frame, result, mlt_time_smpte_df ); } else if ( !strcmp( keyword, "smpte_ndf" ) ) { get_timecode_str( filter, frame, result, mlt_time_smpte_ndf ); } else if ( !strcmp( keyword, "frame" ) ) { get_frame_str( filter, frame, result ); } else if ( !strncmp( keyword, "filedate", 8 ) ) { get_filedate_str( keyword, filter, frame, result ); } else if ( !strncmp( keyword, "localfiledate", 13 ) ) { get_localfiledate_str( keyword, filter, frame, result ); } else if ( !strncmp( keyword, "localtime", 9 ) ) { get_localtime_str( keyword, result ); } else if ( !strcmp( keyword, "resource" ) ) { get_resource_str( filter, frame, result ); } else if ( !strncmp( keyword, "createdate", 10 ) ) { get_createdate_str( keyword, filter, frame, result ); } else { // replace keyword with property value from this frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); char *frame_value = mlt_properties_get( frame_properties, keyword ); if( frame_value ) { strncat( result, frame_value, MAX_TEXT_LEN - strlen(result) - 1 ); } } } } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); char* dynamic_text = mlt_properties_get( properties, "argument" ); if ( !dynamic_text || !strcmp( "", dynamic_text ) ) return frame; mlt_filter text_filter = mlt_properties_get_data( properties, "_text_filter", NULL ); mlt_properties text_filter_properties = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE(text_filter)); // Apply keyword substitution before passing the text to the filter. char* result = calloc( 1, MAX_TEXT_LEN ); substitute_keywords( filter, result, dynamic_text, frame ); mlt_properties_set( text_filter_properties, "argument", result ); free( result ); mlt_properties_pass_list( text_filter_properties, properties, "geometry family size weight style fgcolour bgcolour olcolour pad halign valign outline" ); mlt_filter_set_in_and_out( text_filter, mlt_filter_get_in( filter ), mlt_filter_get_out( filter ) ); return mlt_filter_process( text_filter, frame ); } /** Constructor for the filter. */ mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); mlt_filter text_filter = mlt_factory_filter( profile, "qtext", NULL ); if( !text_filter ) text_filter = mlt_factory_filter( profile, "text", NULL ); if( !text_filter ) mlt_log_warning( MLT_FILTER_SERVICE(filter), "Unable to create text filter.\n" ); if ( filter && text_filter ) { mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); // Register the text filter for reuse/destruction mlt_properties_set_data( my_properties, "_text_filter", text_filter, 0, ( mlt_destructor )mlt_filter_close, NULL ); // Assign default values mlt_properties_set( my_properties, "argument", arg ? arg: "#timecode#" ); mlt_properties_set( my_properties, "geometry", "0%/0%:100%x100%:100%" ); mlt_properties_set( my_properties, "family", "Sans" ); mlt_properties_set( my_properties, "size", "48" ); mlt_properties_set( my_properties, "weight", "400" ); mlt_properties_set( my_properties, "style", "normal" ); mlt_properties_set( my_properties, "fgcolour", "0x000000ff" ); mlt_properties_set( my_properties, "bgcolour", "0x00000020" ); mlt_properties_set( my_properties, "olcolour", "0x00000000" ); mlt_properties_set( my_properties, "pad", "0" ); mlt_properties_set( my_properties, "halign", "left" ); mlt_properties_set( my_properties, "valign", "top" ); mlt_properties_set( my_properties, "outline", "0" ); mlt_properties_set_int( my_properties, "_filter_private", 1 ); filter->process = filter_process; } else { if( filter ) { mlt_filter_close( filter ); } if( text_filter ) { mlt_filter_close( text_filter ); } filter = NULL; } return filter; } mlt-6.20.0/src/modules/plus/filter_dynamictext.yml000066400000000000000000000114231362234133600222420ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: dynamictext title: Dynamic text version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: Overlay dynamic text onto the video notes: > The dynamic text filter will search for keywords in the text to be overlaid and will replace those keywords on a frame-by-frame basis. parameters: - identifier: argument title: Dynamic text type: string description: | The text to overlay. May include keywords enclosed in "#". Keywords include: * #createdate# - Best guess of file creation date * #smpte_df# - SMPTE drop-frame timecode of the frame * #smpte_ndf# - SMPTE non-drop-frame timecode of the frame * #timecode# - same as #smpte_df# * #frame# - frame number of the frame * #filedate# - modification date of the file (GMT) * #localfiledate# - modification date of the file (local) * #localtime# - current system date and time * #resource# - resource of the producer that produced the frame Timecode keywords are based on the framerate and position of the frame. Time-based keywords can include a strftime format string to customize the output as long as you put some delimiter except "#" between the keyword and the format string and the keyword comes first. For example, "#localtime %I:%M:%S %p#" shows only the time in 12-hour format. Keywords may also be any frame property (e.g. #meta.media.0.codec.frame_rate#) The # may be escaped with "\". required: yes readonly: no default: > #trick to escape "#" character #timecode# widget: text - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo mlt-6.20.0/src/modules/plus/filter_fft.c000066400000000000000000000213211362234133600201070ustar00rootroot00000000000000/* * filter_fft.c -- perform fft on audio * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include // calloc(), free() #include // memset(), memmove() #include // sqrt() #include // Private Constants static const float MAX_S16_AMPLITUDE = 32768.0; static const int MIN_WINDOW_SIZE = 500; static const double PI = 3.14159265358979323846; // Private Types typedef struct { int initialized; unsigned int window_size; double* fft_in; fftw_complex* fft_out; fftw_plan fft_plan; int bin_count; int sample_buff_count; float* sample_buff; float* hann; float* out_bins; mlt_position expected_pos; } private_data; static int initFft( mlt_filter filter ) { int error = 0; private_data* private = (private_data*)filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); if( private->window_size < MIN_WINDOW_SIZE ) { private->window_size = mlt_properties_get_int( filter_properties, "window_size" ); if( private->window_size >= MIN_WINDOW_SIZE ) { private->initialized = 1; private->bin_count = private->window_size / 2 + 1; private->sample_buff_count = 0; private->out_bins = mlt_pool_alloc( private->bin_count * sizeof(*private->out_bins)); // Initialize sample buffer private->sample_buff = mlt_pool_alloc( private->window_size * sizeof(*private->sample_buff)); memset( private->sample_buff, 0, sizeof(*private->sample_buff) * private->window_size ); // Initialize fftw variables private->fft_in = fftw_alloc_real( private->window_size ); private->fft_out = fftw_alloc_complex( private->bin_count ); private->fft_plan = fftw_plan_dft_r2c_1d( private->window_size, private->fft_in, private->fft_out, FFTW_ESTIMATE ); // Initialize the hanning window function private->hann = mlt_pool_alloc( private->window_size * sizeof(*private->hann)); int i = 0; for ( i = 0; i < private->window_size; i++ ) { private->hann[i] = 0.5 * (1 - cos( 2 * PI * i / private->window_size ) ); } mlt_properties_set_int( filter_properties, "bin_count", private->bin_count ); mlt_properties_set_data( filter_properties, "bins", private->out_bins, 0, 0, 0 ); } if( private->window_size < MIN_WINDOW_SIZE || !private->fft_in || !private->fft_out || !private->fft_plan ) { mlt_log_error( MLT_FILTER_SERVICE( filter ), "Unable to initialize FFT\n" ); error = 1; private->window_size = 0; } } return error; } static int filter_get_audio( mlt_frame frame, void** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_filter filter = (mlt_filter)mlt_frame_pop_audio( frame ); mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); private_data* private = (private_data*)filter->child; int c = 0; int s = 0; // Sanity if ( *format != mlt_audio_s16 && *format != mlt_audio_float ) { *format = mlt_audio_float; } // Get the audio mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); // The service must stay locked while using the private FFT data mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); if( !private->initialized ) { private->expected_pos = mlt_frame_get_position( frame ); } if( !initFft( filter ) ) { if( private->expected_pos != mlt_frame_get_position( frame ) ) { // Reset the sample buffer when seeking occurs. memset( private->sample_buff, 0, sizeof(*private->sample_buff) * private->window_size ); private->sample_buff_count = 0; mlt_log_info( MLT_FILTER_SERVICE(filter), "Buffer Reset %d:%d\n", private->expected_pos, mlt_frame_get_position( frame ) ); private->expected_pos = mlt_frame_get_position( frame ); } int new_samples = 0; int old_samples = 0; if( *samples >= private->window_size ) { // Ignore samples that don't fit in the window new_samples = private->window_size; old_samples = 0; } else { new_samples = *samples; // Shift the previous samples (discarding oldest samples) old_samples = private->window_size - new_samples; memmove( private->sample_buff, private->sample_buff + new_samples, sizeof(*private->sample_buff) * old_samples); } // Zero out the space for the new samples memset( private->sample_buff + old_samples, 0, sizeof(*private->sample_buff) * new_samples ); // Copy the new samples into the sample buffer if( *format == mlt_audio_s16 ) { int16_t* aud = (int16_t*)*buffer; // For each sample, add all channels for( c = 0; c < *channels; c++ ) { for( s = 0; s < new_samples; s++ ) { double sample = aud[s * *channels + c]; // Scale to +/-1 sample /= MAX_S16_AMPLITUDE; sample /= (double)*channels; private->sample_buff[old_samples + s] += sample; } } } else if( *format == mlt_audio_float ) { float* aud = (float*)*buffer; // For each sample, add all channels for( c = 0; c < *channels; c++ ) { for( s = 0; s < new_samples; s++ ) { double sample = aud[c * *samples + s]; sample /= (double)*channels; private->sample_buff[old_samples + s] += sample; } } } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Unsupported format %d\n", *format ); } private->sample_buff_count += *samples; if( private->sample_buff_count > private->window_size ) { private->sample_buff_count = private->window_size; } // Copy samples to fft input while applying window function for (s = 0; s < private->window_size; s++) { private->fft_in[s] = private->sample_buff[s] * private->hann[s]; } // Perform the FFT fftw_execute( private->fft_plan ); // Convert to magnitudes int bin = 0; for( bin = 0; bin < private->bin_count; bin++ ) { // Convert FFT output to magnitudes private->out_bins[bin] = sqrt( private->fft_out[bin][0] * private->fft_out[bin][0] + private->fft_out[bin][1] * private->fft_out[bin][1] ); // Scale to 0.0 - 1.0 private->out_bins[bin] = (4.0 * private->out_bins[bin]) / (float)private->window_size; } private->expected_pos++; } mlt_properties_set_double( filter_properties, "bin_width", (double)*frequency / (double)private->window_size ); mlt_properties_set_double( filter_properties, "window_level", (double)private->sample_buff_count / (double)private->window_size ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; } static void filter_close( mlt_filter filter ) { private_data* private = (private_data*)filter->child; if ( private ) { fftw_free( private->fft_in ); fftw_free( private->fft_out ); fftw_destroy_plan( private->fft_plan ); mlt_pool_release( private->sample_buff ); mlt_pool_release( private->hann ); mlt_pool_release( private->out_bins ); free( private ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ mlt_filter filter_fft_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* private = (private_data*)calloc( 1, sizeof(private_data) ); if( filter && private ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set_int( properties, "_filter_private", 1 ); mlt_properties_set_int( properties, "window_size", 2048 ); mlt_properties_set_double( properties, "window_level", 0.0 ); mlt_properties_set_double( properties, "bin_width", 0.0 ); mlt_properties_set_int( properties, "bin_count", 0 ); mlt_properties_set_data( properties, "bins", 0, 0, 0, 0 ); memset( private, 0, sizeof(private_data) ); filter->close = filter_close; filter->process = filter_process; filter->child = private; } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter FFT failed\n" ); if( filter ) { mlt_filter_close( filter ); } if( private ) { free( private ); } filter = NULL; } return filter; } mlt-6.20.0/src/modules/plus/filter_fft.yml000066400000000000000000000036461362234133600205000ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: fft title: FFT version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Audio description: > An audio filter that computes the FFT of the audio. This filter does not modify the audio or the image. It only computes the FFT and stores the result in the "bins" property of the filter. parameters: - identifier: window_size title: Window Size type: integer description: > The number of samples that the transform will be performed on. If window_size is less than the number of samples in a frame, extra samples will be ignored. If window_size is more than the number of samples in a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 2048 - identifier: window_level title: Window Level type: float description: > The level of the sample window. 0 indicates that there are no samples in the window. 1.0 indicates that the window is full. The transform of a window that is not full may show frequency spikes that are not really present in the audio. readonly: yes minimum: 0 maximum: 1.0 - identifier: bin_width title: Bin Width type: float description: > The width of each bin in Hz. readonly: yes unit: Hz - identifier: bin_count title: Bin Count type: integer description: > The number of bins that are output from the transform. readonly: yes - identifier: bins title: Output Bins description: > A pointer to an array of floats that represent the magnitude of the output of the transform. bin[i] = sqrt( real[i]^2 + imag[i]^2 ) readonly: yes mlt-6.20.0/src/modules/plus/filter_invert.c000066400000000000000000000045771362234133600206550ustar00rootroot00000000000000/* * filter_invert.c -- invert filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the image mlt_filter filter = mlt_frame_pop_service( frame ); int mask = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "alpha" ); *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { 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( frame ); int size = *width * *height; memset( alpha, mask, size ); } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/plus/filter_invert.yml000066400000000000000000000002551362234133600212210ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: invert title: Invert version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video mlt-6.20.0/src/modules/plus/filter_lift_gamma_gain.c000066400000000000000000000167701362234133600224420ustar00rootroot00000000000000/* * filter_lift_gamma_gain.cpp * Copyright (C) 2014 Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include typedef struct { uint8_t rlut[256]; uint8_t glut[256]; uint8_t blut[256]; double rlift, glift, blift; double rgamma, ggamma, bgamma; double rgain, ggain, bgain; } private_data; static void refresh_lut( mlt_filter filter, mlt_frame frame ) { private_data* self = (private_data*)filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); double rlift = mlt_properties_anim_get_double( properties, "lift_r", position, length ); double glift = mlt_properties_anim_get_double( properties, "lift_g", position, length ); double blift = mlt_properties_anim_get_double( properties, "lift_b", position, length ); double rgamma = mlt_properties_anim_get_double( properties, "gamma_r", position, length ); double ggamma = mlt_properties_anim_get_double( properties, "gamma_g", position, length ); double bgamma = mlt_properties_anim_get_double( properties, "gamma_b", position, length ); double rgain = mlt_properties_anim_get_double( properties, "gain_r", position, length ); double ggain = mlt_properties_anim_get_double( properties, "gain_g", position, length ); double bgain = mlt_properties_anim_get_double( properties, "gain_b", position, length ); // Only regenerate the LUT if something changed. if( self->rlift != rlift || self->glift != glift || self->blift != blift || self->rgamma != rgamma || self->ggamma != ggamma || self->bgamma != bgamma || self->rgain != rgain || self->ggain != ggain || self->bgain != bgain ) { int i = 0; for( i = 0; i < 256; i++ ) { // Convert to gamma 2.2 double gamma22 = pow( (double)i / 255.0, 1.0 / 2.2 ); double r = gamma22; double g = gamma22; double b = gamma22; // Apply lift r += rlift * ( 1.0 - r ); g += glift * ( 1.0 - g ); b += blift * ( 1.0 - b ); // Clamp negative values r = MAX( r, 0.0 ); g = MAX( g, 0.0 ); b = MAX( b, 0.0 ); // Apply gamma r = pow( r, 2.2 / rgamma ); g = pow( g, 2.2 / ggamma ); b = pow( b, 2.2 / bgamma ); // Apply gain r *= pow( rgain, 1.0 / rgamma ); g *= pow( ggain, 1.0 / ggamma ); b *= pow( bgain, 1.0 / bgamma ); // Clamp values r = CLAMP( r, 0.0, 1.0 ); g = CLAMP( g, 0.0, 1.0 ); b = CLAMP( b, 0.0, 1.0 ); // Update LUT self->rlut[ i ] = (int)(r * 255.0); self->glut[ i ] = (int)(g * 255.0); self->blut[ i ] = (int)(b * 255.0); } // Store the values that created the LUT so that // changes can be detected. self->rlift = rlift; self->glift = glift; self->blift = blift; self->rgamma = rgamma; self->ggamma = ggamma; self->bgamma = bgamma; self->rgain = rgain; self->ggain = ggain; self->bgain = bgain; } } static void apply_lut( mlt_filter filter, uint8_t* image, mlt_image_format format, int width, int height ) { private_data* self = (private_data*)filter->child; uint8_t* rlut = malloc( sizeof(self->rlut) ); uint8_t* glut = malloc( sizeof(self->glut) ); uint8_t* blut = malloc( sizeof(self->blut) ); int total = width * height + 1; uint8_t* sample = image; // Copy the LUT so that we can be frame-thread safe. mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); memcpy( rlut, self->rlut, sizeof(self->rlut) ); memcpy( glut, self->glut, sizeof(self->glut) ); memcpy( blut, self->blut, sizeof(self->blut) ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); switch( format ) { case mlt_image_rgb24: while( --total ) { *sample = rlut[ *sample ]; sample++; *sample = glut[ *sample ]; sample++; *sample = blut[ *sample ]; sample++; } break; case mlt_image_rgb24a: while( --total ) { *sample = rlut[ *sample ]; sample++; *sample = glut[ *sample ]; sample++; *sample = blut[ *sample ]; sample++; sample++; // Skip alpha } break; default: mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid image format: %s\n", mlt_image_format_name( format ) ); break; } free( rlut ); free( glut ); free( blut ); } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); int error = 0; // Regenerate the LUT if necessary mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); refresh_lut( filter, frame ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Make sure the format is acceptable if( *format != mlt_image_rgb24 && *format != mlt_image_rgb24a ) { *format = mlt_image_rgb24; } // Get the image writable = 1; error = mlt_frame_get_image( frame, image, format, width, height, writable ); // Apply the LUT if( !error ) { apply_lut( filter, *image, *format, *width, *height ); } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } static void filter_close( mlt_filter filter ) { private_data* self = (private_data*)filter->child; free( self ); filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* self = (private_data*)calloc( 1, sizeof(private_data) ); int i = 0; if ( filter && self ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Initialize self data for( i = 0; i < 256; i++ ) { self->rlut[i] = i; self->glut[i] = i; self->blut[i] = i; } self->rlift = self->glift = self->blift = 0.0; self->rgamma = self->ggamma = self->bgamma = 1.0; self->rgain = self->ggain = self->bgain = 1.0; // Initialize filter properties mlt_properties_set_double( properties, "lift_r", self->rlift ); mlt_properties_set_double( properties, "lift_g", self->glift ); mlt_properties_set_double( properties, "lift_b", self->blift ); mlt_properties_set_double( properties, "gamma_r", self->rgamma ); mlt_properties_set_double( properties, "gamma_g", self->ggamma ); mlt_properties_set_double( properties, "gamma_b", self->bgamma ); mlt_properties_set_double( properties, "gain_r", self->rgain ); mlt_properties_set_double( properties, "gain_g", self->ggain ); mlt_properties_set_double( properties, "gain_b", self->bgain ); filter->close = filter_close; filter->process = filter_process; filter->child = self; } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter lift_gamma_gain init failed\n" ); mlt_filter_close( filter ); filter = NULL; free( self ); } return filter; } mlt-6.20.0/src/modules/plus/filter_lift_gamma_gain.yml000066400000000000000000000040051362234133600230050ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: lift_gamma_gain title: Lift, Gamma, and Gain version: 1 copyright: Brian Matherly creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: > A simple lift/gamma/gain effect, used for color grading. notes: > Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, although all parameters affect the entire curve. Mathematically speaking, it is a bit unusual to look at gamma as a color, but it works pretty well in practice. The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). The lift is a case where we actually would _not_ want linear light; since black by definition becomes equal to the lift color, we want lift to be pretty close to black, but in linear light that means lift affects the rest of the curve relatively little. Thus, we actually convert to gamma 2.2 before lift, and then back again afterwards. (Gain and gamma are, up to constants, commutative with the de-gamma operation.) parameters: - identifier: lift_r title: Lift Red type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: lift_g title: Lift Green type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: lift_b title: Lift Blue type: float minimum: 0.0 default: 0.0 mutable: yes - identifier: gamma_r title: Gamma Red type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gamma_g title: Gamma Green type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gamma_b title: Gamma Blue type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_r title: Gain Red type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_g title: Gain Green type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: gain_b title: Gain Blue type: float minimum: 0.0 default: 1.0 mutable: yes mlt-6.20.0/src/modules/plus/filter_loudness.c000066400000000000000000000161371362234133600211750ustar00rootroot00000000000000/* * filter_loudness.c -- normalize audio according to EBU R128 * Copyright (C) 2014 Brian Matherly * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #define MAX_RESULT_SIZE 512 typedef struct { ebur128_state* state; } analyze_data; typedef struct { double in_loudness; double in_range; double in_peak; } apply_data; typedef struct { analyze_data* analyze; apply_data* apply; mlt_position last_position; } private_data; static void destroy_analyze_data( mlt_filter filter ) { private_data* private = (private_data*)filter->child; ebur128_destroy( &private->analyze->state ); free( private->analyze ); private->analyze = NULL; } static void init_analyze_data( mlt_filter filter, int channels, int samplerate ) { private_data* private = (private_data*)filter->child; private->analyze = (analyze_data*)calloc( 1, sizeof(analyze_data) ); private->analyze->state = ebur128_init( (unsigned int)channels, (unsigned long)samplerate, EBUR128_MODE_I | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK ); private->last_position = 0; } static void destroy_apply_data( mlt_filter filter ) { private_data* private = (private_data*)filter->child; free( private->apply ); private->apply = NULL; } static void init_apply_data( mlt_filter filter ) { private_data* private = (private_data*)filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); char* results = mlt_properties_get( properties, "results" ); int scan_return = 0; private->apply = (apply_data*)calloc( 1, sizeof(apply_data) ); scan_return = sscanf( results,"L: %lf\tR: %lf\tP %lf", &private->apply->in_loudness, &private->apply->in_range, &private->apply->in_peak ); if( scan_return != 3 ) { mlt_log_error( MLT_FILTER_SERVICE( filter ), "Unable to load results: %s\n", results ); destroy_apply_data( filter ); return; } else { mlt_log_info( MLT_FILTER_SERVICE( filter ), "Loaded Results: L: %lf\tR: %lf\tP %lf\n", private->apply->in_loudness, private->apply->in_range, private->apply->in_peak ); } } static void analyze( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { private_data* private = (private_data*)filter->child; mlt_position pos = mlt_filter_get_position( filter, frame ); // If any frames are skipped, analysis data will be incomplete. if( private->analyze && pos != private->last_position + 1 ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "Analysis Failed: Bad frame sequence\n" ); destroy_analyze_data( filter ); } // Analyze Audio if( !private->analyze && pos == 0 ) { init_analyze_data( filter, *channels, *frequency ); } if( private->analyze ) { ebur128_add_frames_float( private->analyze->state, *buffer, *samples ); if ( pos + 1 == mlt_filter_get_length2( filter, frame ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); double loudness = 0.0; double range = 0.0; double tmpPeak = 0.0; double peak = 0.0; int i = 0; char result[MAX_RESULT_SIZE]; ebur128_loudness_global( private->analyze->state, &loudness ); ebur128_loudness_range( private->analyze->state, &range ); for ( i = 0; i < *channels; i++ ) { ebur128_sample_peak( private->analyze->state, i, &tmpPeak ); if( tmpPeak > peak ) { peak = tmpPeak; } } snprintf( result, MAX_RESULT_SIZE, "L: %lf\tR: %lf\tP %lf", loudness, range, peak ); result[ MAX_RESULT_SIZE - 1 ] = '\0'; mlt_log_info( MLT_FILTER_SERVICE( filter ), "Stored results: %s\n", result ); mlt_properties_set( properties, "results", result ); destroy_analyze_data( filter ); } private->last_position = pos; } } static void apply( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { private_data* private = (private_data*)filter->child; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); // Analyze Audio if( !private->apply ) { init_apply_data( filter ); } if( private->apply ) { double target_db = mlt_properties_get_double( properties, "program" ); double delta_db = target_db - private->apply->in_loudness; double coeff = delta_db > -90.0 ? pow(10.0, delta_db / 20.0) : 0.0; float* p = *buffer; int count = *samples * *channels; while( count-- ) { *p = *p * coeff; p++; } } } /** Get the audio. */ static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_filter filter = mlt_frame_pop_audio( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // Get the producer's audio *format = mlt_audio_f32le; mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); char* results = mlt_properties_get( properties, "results" ); if( results && strcmp( results, "" ) ) { apply( filter, frame, buffer, format, frequency, channels, samples ); } else { analyze( filter, frame, buffer, format, frequency, channels, samples ); } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; } /** Destructor for the filter. */ static void filter_close( mlt_filter filter ) { private_data* private = (private_data*)filter->child; if ( private ) { if ( private->analyze ) { destroy_analyze_data( filter ); } free( private ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); private_data* data = (private_data*)calloc( 1, sizeof(private_data) ); if ( filter && data ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set( properties, "program", "-23.0" ); data->analyze = NULL; filter->close = filter_close; filter->process = filter_process; filter->child = data; } else { if( filter ) { mlt_filter_close( filter ); filter = NULL; } if( data ) { free( data ); } } return filter; } mlt-6.20.0/src/modules/plus/filter_loudness.yml000066400000000000000000000023011362234133600215400ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: loudness title: Loudness version: 1 copyright: Brian Matherly creator: Brian Matherly license: LGPLv2.1 language: en tags: - Audio description: Correct audio loudness as recommended by EBU R128. notes: > This filter requires two passes. The first pass performs analysis and stores the result in the "results" property. The second pass applies the results to the audio in order to achieve the desired loudness over the range of the filter. parameters: - identifier: results title: Analysis Results type: string description: > Set after analysis. Used during application. Loudness information about the original audio. When results are not supplied, the filter computes the results and stores them in this property when the last frame has been processed. mutable: no - identifier: program title: Target Program Loudness type: float description: > Used during application. The target program loudness in LUFS (Loudness Units Full Scale). readonly: no mutable: yes default: -23.0 minimum: -50.0 maximum: -10.0 unit: LUFS mlt-6.20.0/src/modules/plus/filter_loudness_meter.c000066400000000000000000000223031362234133600223610ustar00rootroot00000000000000/* * filter_loudness_meter.c -- measure audio loudness according to EBU R128 * Copyright (C) 2016 Brian Matherly * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include typedef struct { ebur128_state* r128; int reset; mlt_position prev_pos; } private_data; static void property_changed( mlt_service owner, mlt_filter filter, char *name ) { private_data* pdata = (private_data*)filter->child; if ( !strcmp( name, "reset" ) || !strcmp( name, "calc_program" ) || !strcmp( name, "calc_shortterm" ) || !strcmp( name, "calc_momentary" ) || !strcmp( name, "calc_range" ) || !strcmp( name, "calc_peak" ) || !strcmp( name, "calc_true_peak" ) ) { pdata->reset = 1; } } static void check_for_reset( mlt_filter filter, int channels, int frequency ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; if( pdata->reset ) { if( pdata->r128 ) { ebur128_destroy( &pdata->r128 ); } pdata->r128 = 0; pdata->reset = 0; pdata->prev_pos = -1; mlt_events_block( properties, filter ); mlt_properties_set( properties, "frames_processed", "0" ); mlt_properties_set( properties, "program", "-100.0" ); mlt_properties_set( properties, "shortterm", "-100.0" ); mlt_properties_set( properties, "momentary", "-100.0" ); mlt_properties_set( properties, "range", "-1.0" ); mlt_properties_set_int( properties, "reset_count", mlt_properties_get_int( properties, "reset_count") + 1 ); mlt_properties_set_int( properties, "reset", 0 ); mlt_events_unblock( properties, filter ); } if( !pdata->r128 ) { int mode = EBUR128_MODE_HISTOGRAM; if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_program" ) ) { mode |= EBUR128_MODE_I; } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_shortterm" ) ) { mode |= EBUR128_MODE_S; } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_momentary" ) ) { mode |= EBUR128_MODE_M; } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_range" ) ) { mode |= EBUR128_MODE_LRA; } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_peak" ) ) { mode |= EBUR128_MODE_SAMPLE_PEAK; } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_true_peak" ) ) { mode |= EBUR128_MODE_TRUE_PEAK; } pdata->r128 = ebur128_init( channels, frequency, mode ); } } static void analyze_audio( mlt_filter filter, void* buffer, int samples ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; int result = -1; double loudness = 0.0; ebur128_add_frames_float( pdata->r128, buffer, samples ); if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_program" ) ) { result = ebur128_loudness_global( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "program", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_shortterm" ) ) { result = ebur128_loudness_shortterm( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "shortterm", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_momentary" ) ) { result = ebur128_loudness_momentary( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "momentary", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_range" ) ) { double range = 0; result = ebur128_loudness_range( pdata->r128, &range ); if( result == EBUR128_SUCCESS && range != HUGE_VAL && range != -HUGE_VAL ) { mlt_properties_set_double( properties, "range", range ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_peak" ) ) { double prev_peak = 0.0; double max_peak = 0.0; int c = 0; for( c = 0; c < pdata->r128->channels; c++ ) { double peak; result = ebur128_sample_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak ) { max_peak = peak; } result = ebur128_prev_sample_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak ) { prev_peak = peak; } } mlt_properties_set_double( properties, "max_peak", 20 * log10(max_peak) ); mlt_properties_set_double( properties, "peak", 20 * log10(prev_peak) ); } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_true_peak" ) ) { double prev_peak = 0.0; double max_peak = 0.0; int c = 0; for( c = 0; c < pdata->r128->channels; c++ ) { double peak; result = ebur128_true_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak ) { max_peak = peak; } result = ebur128_prev_true_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak ) { prev_peak = peak; } } mlt_properties_set_double( properties, "max_true_peak", 20 * log10(max_peak) ); mlt_properties_set_double( properties, "true_peak", 20 * log10(prev_peak) ); } mlt_properties_set_position( properties, "frames_processed", mlt_properties_get_position( properties, "frames_processed" ) + 1 ); } static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_filter filter = mlt_frame_pop_audio( frame ); private_data* pdata = (private_data*)filter->child; mlt_position pos = mlt_frame_get_position( frame ); // Get the producer's audio *format = mlt_audio_f32le; mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); check_for_reset( filter, *channels, *frequency ); if( pos != pdata->prev_pos ) { // Only analyze the audio if the producer is not paused. analyze_audio( filter, *buffer, *samples ); } pdata->prev_pos = pos; mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, filter_get_audio ); return frame; } /** Destructor for the filter. */ static void filter_close( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if( pdata ) { if( pdata->r128 ) { ebur128_destroy( &pdata->r128 ); } free( pdata ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ mlt_filter filter_loudness_meter_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); if( filter && pdata ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set_int( properties, "calc_program", 1 ); mlt_properties_set_int( properties, "calc_shortterm", 1 ); mlt_properties_set_int( properties, "calc_momentary", 1 ); mlt_properties_set_int( properties, "calc_range", 1 ); mlt_properties_set_int( properties, "calc_peak", 1 ); mlt_properties_set_int( properties, "calc_true_peak", 1 ); mlt_properties_set( properties, "program", "-100.0" ); mlt_properties_set( properties, "shortterm", "-100.0" ); mlt_properties_set( properties, "momentary", "-100.0" ); mlt_properties_set( properties, "range", "-1.0" ); mlt_properties_set( properties, "peak", "-100.0" ); mlt_properties_set( properties, "max_peak", "-100.0" ); mlt_properties_set( properties, "true_peak", "-100.0" ); mlt_properties_set( properties, "max_true_peak", "-100.0" ); mlt_properties_set( properties, "reset", "1" ); mlt_properties_set( properties, "reset_count", "0" ); mlt_properties_set( properties, "frames_processed", "0" ); pdata->r128 = 0; pdata->reset = 1; pdata->prev_pos = -1; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; mlt_events_listen( properties, filter, "property-changed", (mlt_listener)property_changed ); } else { if( filter ) { mlt_filter_close( filter ); filter = NULL; } free( pdata ); } return filter; } mlt-6.20.0/src/modules/plus/filter_loudness_meter.yml000066400000000000000000000070401362234133600227410ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: loudness_meter title: Loudness Meter version: 1 copyright: Brian Matherly creator: Brian Matherly license: LGPLv2.1 language: en tags: - Audio description: Measure audio loudness as recommended by EBU R128. parameters: - identifier: calc_program title: Calculate Program Loudness type: boolean description: > Whether to calculate program (integrated) loudness. readonly: no mutable: yes default: 1 - identifier: calc_shortterm title: Calculate Short-term Loudness type: boolean description: > Whether to calculate short-term loudness. readonly: no mutable: yes default: 1 - identifier: calc_momentary title: Calculate momentary Loudness type: boolean description: > Whether to calculate momentary loudness. readonly: no mutable: yes default: 1 - identifier: calc_range title: Calculate loudness range type: boolean description: > Whether to calculate loudness range. readonly: no mutable: yes default: 1 - identifier: calc_peak title: Calculate the peak sample level type: boolean description: > Whether to calculate the peak sample level. readonly: no mutable: yes default: 1 - identifier: calc_true_peak title: Calculate the true peak level type: boolean description: > Whether to calculate the true peak level. readonly: no mutable: yes default: 1 - identifier: program title: Program Loudness type: float description: The measured program loudness since the last reset. readonly: yes unit: LUFS - identifier: shortterm title: Short-term Loudness type: float description: The measured short-term loudness. readonly: yes unit: LUFS - identifier: momentary title: Momentary Loudness type: float description: The measured momentary loudness. readonly: yes unit: LUFS - identifier: range title: Loudness Range type: float description: The measured loudness range since the last reset. readonly: yes unit: LUFS - identifier: peak title: Peak type: float description: The measured peak sample value for the last frame that was processed. readonly: yes unit: dBFS - identifier: max_peak title: Max Peak type: float description: The measured peak sample value that has been received since the last reset. readonly: yes unit: dBFS - identifier: true_peak title: True Peak type: float description: The measured true peak value for the last frame that was processed. readonly: yes unit: dBTP - identifier: max_true_peak title: Max True Peak type: float description: The measured true peak value that has been received since the last reset. readonly: yes unit: dBTP - identifier: reset title: Reset type: boolean description: > Reset the measurement. Automatically resets back to 0 after the reset is complete. readonly: no mutable: yes default: 1 - identifier: reset_count title: Reset Count type: integer description: > The number of times the measurement has been reset. The filter is reset whenever reset is set to 1 or whenever a parameter is changed. readonly: yes - identifier: frames_processed title: Frames Processed type: integer description: > The number of frames that have been processed since the last reset. readonly: yes mlt-6.20.0/src/modules/plus/filter_lumakey.c000066400000000000000000000103711362234133600210020ustar00rootroot00000000000000/* * filter_lumakey.c -- Luma Key filter for luma based image compositing * Copyright (C) 2014 Janne Liljeblad * Author: Janne Liljeblad * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include static int clamp( int value, int low, int high) { if (value < low) value = low; if (value > high) value = high; return value; } /** Builds a lookup table that maps luma values to opacity values. */ static void fill_opa_lut(int lgg_lut[], int prelevel, int postlevel, int slope_start, int slope_end ) { int i; // Prelevel plateau for( i = 0; i < slope_start; i++ ) lgg_lut[ i ] = prelevel; // Value transition if ( slope_start != slope_end ) { double value = prelevel; double value_step = (double) (postlevel - prelevel) / (double) (slope_end - slope_start); for( i = slope_start; i < slope_end + 1; i++ ) { lgg_lut[ i ] = (int)value; value += value_step; } } // Postlevel plateau for( i = slope_end; i < 256; i++ ) lgg_lut[ i ] = postlevel; } /** Do image filtering. */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the image mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); *format = mlt_image_rgb24a; int error = mlt_frame_get_image( frame, image, format, width, height, 0 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { // Get values and force accepted ranges int threshold = mlt_properties_anim_get_int( properties, "threshold", position, length ); int slope = mlt_properties_anim_get_int( properties, "slope", position, length ); int prelevel = mlt_properties_anim_get_int( properties, "prelevel", position, length ); int postlevel = mlt_properties_anim_get_int( properties, "postlevel", position, length ); threshold = clamp( threshold, 0, 255 ); slope = clamp( slope, 0, 128 ); prelevel = clamp( prelevel, 0, 255 ); postlevel = clamp( postlevel, 0, 255 ); int slope_start = clamp( threshold - slope, 0, 255 ); int slope_end = clamp( threshold + slope, 0, 255 ); // Build lut int opa_lut[256]; fill_opa_lut( opa_lut, prelevel, postlevel, slope_start, slope_end ); // Values for calculating visual luma from RGB double R = 0.3; double G = 0.59; double B = 0.11; // Filter int i = *width * *height + 1; uint8_t *sample = *image; uint8_t r, g, b; while ( --i ) { r = *sample ++; g = *sample ++; b = *sample ++; *sample ++ = opa_lut[(int) (R * r + g * G + b * B) ]; } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ mlt_filter filter_lumakey_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "threshold", "128" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "slope", "0" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "prelevel", "0" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "postlevel", "255" ); } return filter; } mlt-6.20.0/src/modules/plus/filter_lumakey.yml000066400000000000000000000030141362234133600213550ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: lumakey title: Lumakey version: 1 copyright: Janne Liljeblad creator: Janne Liljeblad license: LGPLv2.1 language: en tags: - Video description: > This filter modifies image's alpha channel as a function of its luma value. This is used together with a compositor to combine two images so that bright or dark areas of source image are overwritten on top of the destination image. parameters: - identifier: threshold title: Threshold type: integer minimum: 0 maximum: 255 default: 128 mutable: yes description: > Luma value that defines the center point of transition from prelevel to postlevel opacity value. - identifier: slope title: Slope type: integer minimum: 0 maximum: 128 default: 0 mutable: yes description: > This defines the width of the transition from prelevel to postlevel luma value. Start point of transition in opacity value is threshold - slope and end point is thresholt + slope, values are forced in range 0 - 255. - identifier: prelevel title: Pre-Threshold Luma Level type: integer minimum: 0 maximum: 255 default: 0 mutable: yes description: > Opacity value before the transition in opacity value begins. - identifier: postlevel title: Post-Threshold Luma Level type: integer minimum: 0 maximum: 255 default: 255 mutable: yes description: > Opacity value after the transition in opacity value ends. mlt-6.20.0/src/modules/plus/filter_rgblut.c000066400000000000000000000064221362234133600206340ustar00rootroot00000000000000/* * filter_rgblut.c -- generic RGB look-up table filter with string interface * Copyright (C) 2014 Janne Liljeblad * Author: Janne Liljeblad * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include /** Fill channel lut with integers parsed from property string. */ static void fill_channel_lut(int lut[], char* channel_table_str) { mlt_tokeniser tokeniser = mlt_tokeniser_init(); mlt_tokeniser_parse_new( tokeniser, channel_table_str, ";" ); // Only create lut from string if tokens count exactly right if ( tokeniser->count == 256 ) { // Fill lut with token values int i; int val; for( i = 0; i < 256; i++ ) { val = atoi(tokeniser->tokens[i]); lut[i] = val; } } else { // Fill lut with linear no-op table int i; for( i = 0; i < 256; i++ ) { lut[i] = i; } } mlt_tokeniser_close( tokeniser ); } /** 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 image mlt_filter filter = mlt_frame_pop_service( frame ); *format = mlt_image_rgb24; int error = mlt_frame_get_image( frame, image, format, width, height, 0 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { // Create lut tables from properties for each RGB channel char* r_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "R_table" ); int r_lut[256]; fill_channel_lut( r_lut, r_str ); char* g_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "G_table" ); int g_lut[256]; fill_channel_lut( g_lut, g_str ); char* b_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "B_table" ); int b_lut[256]; fill_channel_lut( b_lut, b_str ); // Apply look-up tables into image int i = *width * *height + 1; uint8_t *p = *image; uint8_t *r = *image; while ( --i ) { *p ++ = r_lut[ *r ++ ]; *p ++ = g_lut[ *r ++ ]; *p ++ = b_lut[ *r ++ ]; } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ mlt_filter filter_rgblut_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/plus/filter_rgblut.yml000066400000000000000000000031141362234133600212060ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: rgblut title: RGBLUT version: 1 copyright: Janne Liljeblad creator: Janne Liljeblad license: LGPLv2.1 language: en tags: - Video description: Converts input strings with exactly 256 semicolon separated integer values in range 0 - 255 to look-up tables that are then used to modify R, G, B values. This creates a generic string interface for color correction. parameters: - identifier: R_table title: Red channel look-up table type: string description: > Value is tokenised using semicolon separator into exactly 256 integer values in range 0 - 255 and a look-up table for red channel values is created and applied to image. If tokenising of value fails a linear table that returns input values unchanged is used instead. - identifier: G_table title: Green channel look-up table type: string description: > Value is tokenised using semicolon separator into exactly 256 integer values in range 0 - 255 and a look-up table for green channel values is created and applied to image. If tokenising of value fails a linear table that returns input values unchanged is used instead. - identifier: B_table title: Blue channel look-up table type: string description: > Value is tokenised using semicolon separator into exactly 256 integer values in range 0 - 255 and a look-up table for green channel values is created and applied to image. If tokenising of value fails a linear table that returns input values unchanged is used instead. mlt-6.20.0/src/modules/plus/filter_sepia.c000066400000000000000000000053071362234133600204370ustar00rootroot00000000000000/* * filter_sepia.c -- sepia filter * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 ) { // Get the filter mlt_filter filter = mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); // Get the image *format = mlt_image_yuv422; int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Only process if we have no error and a valid colour space if ( error == 0 && *image ) { // 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_anim_get_int( properties, "u", position, length ); int v = mlt_properties_anim_get_int( properties, "v", position, length ); // 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 filter, mlt_frame frame ) { // Push the frame filter mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "u", "75" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "v", "150" ); } return filter; } mlt-6.20.0/src/modules/plus/filter_sepia.yml000066400000000000000000000007521362234133600210150ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: sepia title: Sepia version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video description: > Apply a color tint. Default values give a sepia tone like an old photograph. parameters: - identifier: u type: integer title: Yellow-Blue minimum: 0 maximum: 255 default: 75 - identifier: v type: integer title: Cyan-Red minimum: 0 maximum: 255 default: 150 mlt-6.20.0/src/modules/plus/filter_spot_remover.c000066400000000000000000000157031362234133600220630ustar00rootroot00000000000000/* * filter_remover.c -- filter to interpolate pixels to cover an area * Copyright (c) 2018-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include /** Scale a rectangle by the specified factors. */ mlt_rect scale_rect( mlt_rect rect, double x_scale, double y_scale ) { rect.x = rect.x / x_scale; rect.y = rect.y / y_scale; rect.w = rect.w / x_scale; rect.h = rect.h / y_scale; return rect; } /** Constrain a rect to be within the max dimensions with an additional 1 pixel * padding. */ mlt_rect constrain_rect( mlt_rect rect, int max_x, int max_y ) { rect.x = round( rect.x ); rect.y = round( rect.y ); rect.w = round( rect.w ); rect.h = round( rect.h ); if ( rect.x < 0 ) { rect.w = rect.w + rect.x - 1; rect.x = 1; } if ( rect.y < 0 ) { rect.h = rect.h + rect.y - 1; rect.y = 1; } if ( rect.x + rect.w < 0 ) { rect.w = 0; } if ( rect.y + rect.h < 0 ) { rect.h = 0; } if ( rect.x < 1 ) { rect.x = 1; } if ( rect.y < 1 ) { rect.y = 1; } if ( rect.x + rect.w > max_x - 1 ) { rect.w = max_x - rect.x - 1; } if ( rect.y + rect.h > max_y - 1 ) { rect.h = max_y - rect.y - 1; } return rect; } /** Perform spot removal on a channel. * * Values within the rectangle are replaced with interpolated values. * Each value is an interpolation of the first values outside of the rect on * the top, bottom, left and right of the value being interpolated. * * \param chan a pointer to the first value in the channel * \param rowCount the number of values in each line (row) * \param step the space between values in each line * \param rect the area to be removed */ static void remove_spot_channel( uint8_t *chan, int rowCount, int step, mlt_rect rect ) { int yStop = rect.y + rect.h; int xStop = rect.x + rect.w; int rowSize = rowCount * step; int y; for ( y = rect.y; y < yStop; y++ ) { uint8_t* xValueL = chan + ( y * rowSize ) + ( ( (int)rect.x - 1 ) * step ); uint8_t* xValueR = xValueL + ( (int)rect.w * step ); uint8_t* p = chan + ( y * rowSize ) + ( (int)rect.x * step ); double yRatio = 1.0 - ( ( y - rect.y ) / rect.h ); int x; for ( x = rect.x; x < xStop; x++ ) { uint8_t* yValueT = chan + ( ( (int)rect.y - 1 ) * rowSize ) + ( x * step ); uint8_t* yValueB = yValueT + (int)rect.h * rowSize; double xRatio = 1.0 - ( ( x - rect.x ) / rect.w ); unsigned int xValueInterp = ( *xValueL * xRatio ) + ( *xValueR * ( 1.0 - xRatio ) ); unsigned int yValueInterp = ( *yValueT * yRatio ) + ( *yValueB * ( 1.0 - yRatio ) ); unsigned int value = ( xValueInterp + yValueInterp ) / 2; if ( value > 255 ) value = 255; *p = value; p += step; } } } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame ); mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); char* rect_str = mlt_properties_get( filter_properties, "rect" ); if ( !rect_str ) { mlt_log_warning( MLT_FILTER_SERVICE(filter), "rect property not set\n" ); return mlt_frame_get_image( frame, image, format, width, height, writable ); } mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_rect rect = mlt_properties_anim_get_rect( filter_properties, "rect", position, length ); if ( strchr( rect_str, '%' ) ) { rect.x *= profile->width; rect.w *= profile->width; rect.y *= profile->height; rect.h *= profile->height; } double scale = mlt_profile_scale_width(profile, *width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); rect.y *= scale; rect.h *= scale; rect = constrain_rect( rect, profile->width * scale, profile->height * scale); if ( rect.w < 1 || rect.h < 1 ) { mlt_log_info( MLT_FILTER_SERVICE(filter), "rect invalid\n" ); return mlt_frame_get_image( frame, image, format, width, height, writable ); } switch( *format ) { case mlt_image_rgb24a: case mlt_image_rgb24: case mlt_image_yuv422: case mlt_image_yuv420p: // These formats are all supported break; default: *format = mlt_image_rgb24a; break; } error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if (error) return error; int i; switch( *format ) { case mlt_image_rgb24a: for ( i = 0; i < 4; i++ ) { remove_spot_channel( *image + i, *width, 4, rect ); } break; case mlt_image_rgb24: for ( i = 0; i < 3; i++ ) { remove_spot_channel( *image + i, *width, 3, rect ); } break; case mlt_image_yuv422: // Y remove_spot_channel( *image, *width, 2, rect ); // U remove_spot_channel( *image + 1, *width / 2, 4, constrain_rect( scale_rect( rect, 2, 1 ), *width / 2, *height ) ); // V remove_spot_channel( *image + 3, *width / 2, 4, constrain_rect( scale_rect( rect, 2, 1 ), *width / 2, *height ) ); break; case mlt_image_yuv420p: // Y remove_spot_channel( *image, *width, 1, rect ); // U remove_spot_channel( *image + (*width * *height), *width / 2, 1, constrain_rect( scale_rect( rect, 2, 2 ), *width / 2, *height / 2 ) ); // V remove_spot_channel( *image + (*width * *height * 5 / 4), *width / 2, 1, constrain_rect( scale_rect( rect, 2, 2 ), *width / 2, *height / 2 ) ); break; default: return 1; } uint8_t *alpha = mlt_frame_get_alpha( frame ); if ( alpha && *format != mlt_image_rgb24a ) { remove_spot_channel( alpha, *width, 1, rect ); } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } mlt_filter filter_spot_remover_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); if ( filter ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set( properties, "rect", "0% 0% 10% 10%" ); filter->process = filter_process; } else { mlt_log_error( NULL, "Filter spot_remover initialization failed\n" ); } return filter; } mlt-6.20.0/src/modules/plus/filter_spot_remover.yml000066400000000000000000000012621362234133600224350ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: spot_remover title: Spot Remover version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: > Replace an area with interpolated pixels. The new pixel values are interpolated from the nearest pixels immediately outside of the specified area. parameters: - identifier: rect title: Rectangle description: > Defines the rectangle of the area that will be removed. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 10% 10%" readonly: no mutable: yes mlt-6.20.0/src/modules/plus/filter_text.c000066400000000000000000000233311362234133600203170ustar00rootroot00000000000000/* * filter_text.c -- text overlay filter * Copyright (C) 2018-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include static void property_changed( mlt_service owner, mlt_filter filter, char *name ) { if( !strcmp( "geometry", name ) || !strcmp( "family", name ) || !strcmp( "size", name ) || !strcmp( "weight", name ) || !strcmp( "style", name ) || !strcmp( "fgcolour", name ) || !strcmp( "bgcolour", name ) || !strcmp( "olcolour", name ) || !strcmp( "pad", name ) || !strcmp( "halign", name ) || !strcmp( "valign", name ) || !strcmp( "outline", name ) ) { mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "_reset", 1 ); } } static void setup_producer( mlt_producer producer, mlt_properties my_properties ) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); // Pass the properties to the text producer mlt_properties_set( producer_properties, "family", mlt_properties_get( my_properties, "family" ) ); mlt_properties_set( producer_properties, "size", mlt_properties_get( my_properties, "size" ) ); mlt_properties_set( producer_properties, "weight", mlt_properties_get( my_properties, "weight" ) ); mlt_properties_set( producer_properties, "style", mlt_properties_get( my_properties, "style" ) ); mlt_properties_set( producer_properties, "fgcolour", mlt_properties_get( my_properties, "fgcolour" ) ); mlt_properties_set( producer_properties, "bgcolour", mlt_properties_get( my_properties, "bgcolour" ) ); mlt_properties_set( producer_properties, "olcolour", mlt_properties_get( my_properties, "olcolour" ) ); mlt_properties_set( producer_properties, "pad", mlt_properties_get( my_properties, "pad" ) ); mlt_properties_set( producer_properties, "outline", mlt_properties_get( my_properties, "outline" ) ); mlt_properties_set( producer_properties, "align", mlt_properties_get( my_properties, "halign" ) ); } static void setup_transition( mlt_filter filter, mlt_transition transition, mlt_frame frame, mlt_properties my_properties ) { mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_service_lock( MLT_TRANSITION_SERVICE(transition) ); mlt_rect rect = mlt_properties_anim_get_rect( my_properties, "geometry", position, length ); if (mlt_properties_get(my_properties, "geometry") && strchr(mlt_properties_get(my_properties, "geometry"), '%')) { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); rect.x *= profile->width; rect.y *= profile->height; rect.w *= profile->width; rect.h *= profile->height; } mlt_properties_set_rect( transition_properties, "rect", rect ); mlt_properties_set( transition_properties, "halign", mlt_properties_get( my_properties, "halign" ) ); mlt_properties_set( transition_properties, "valign", mlt_properties_get( my_properties, "valign" ) ); mlt_service_unlock( MLT_TRANSITION_SERVICE(transition) ); } static mlt_properties get_filter_properties( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = mlt_frame_get_unique_properties( frame, MLT_FILTER_SERVICE(filter) ); if ( !properties ) properties = MLT_FILTER_PROPERTIES(filter); return properties; } /** Get the image. */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = mlt_frame_pop_service( frame ); char* argument = (char*)mlt_frame_pop_service( frame ); mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties properties = get_filter_properties( filter, frame ); mlt_producer producer = mlt_properties_get_data( my_properties, "_producer", NULL ); mlt_transition transition = mlt_properties_get_data( my_properties, "_transition", NULL ); mlt_frame b_frame = 0; mlt_position position = 0; // Configure this filter mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); if( mlt_properties_get_int( my_properties, "_reset" ) ) { setup_producer( producer, properties ); setup_transition( filter, transition, frame, properties ); } mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "text", argument ); // Make sure the producer is in the correct position position = mlt_filter_get_position( filter, frame ); mlt_producer_seek( producer, position ); // Get the b frame and process with transition if successful if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &b_frame, 0 ) == 0 ) { // This lock needs to also protect the producer properties from being // modified in setup_producer() while also being used in mlt_service_get_frame(). mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // 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_int( b_props, "consumer_deinterlace", mlt_properties_get_int( a_props, "consumer_deinterlace" ) ); // Apply all filters that are attached to this filter to the b frame mlt_service_apply_filters( MLT_FILTER_SERVICE( filter ), b_frame, 0 ); // Process the frame mlt_transition_process( transition, frame, b_frame ); // Get the image error = mlt_frame_get_image( frame, image, format, width, height, writable ); // Close the temporary frames mlt_frame_close( b_frame ); } else { mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } free( argument ); return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = get_filter_properties( filter, frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); char* argument = mlt_properties_anim_get( properties, "argument", position, length ); if ( !argument || !strcmp( "", argument ) ) return frame; // Save the text to be used by get_image() to support parallel processing // when this filter is encapsulated by other filters. mlt_frame_push_service( frame, strdup( argument ) ); // Push the filter on to the stack mlt_frame_push_service( frame, filter ); // 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_text_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); mlt_transition transition = mlt_factory_transition( profile, "affine", NULL ); mlt_producer producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "qtext:" ); // Use pango if qtext is not available. if( !producer ) producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "pango:" ); if( !producer ) mlt_log_warning( MLT_FILTER_SERVICE(filter), "QT or GTK modules required for text.\n" ); if ( filter && transition && producer ) { mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); // Register the transition for reuse/destruction mlt_properties_set_int( MLT_TRANSITION_PROPERTIES(transition), "fill", 0 ); mlt_properties_set_int( MLT_TRANSITION_PROPERTIES(transition), "b_scaled", 1 ); mlt_properties_set_data( my_properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); // Register the producer for reuse/destruction mlt_properties_set_data( my_properties, "_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); // Ensure that we loop mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" ); // Listen for property changes. mlt_events_listen( MLT_FILTER_PROPERTIES(filter), filter, "property-changed", (mlt_listener)property_changed ); // Assign default values mlt_properties_set( my_properties, "argument", arg ? arg: "text" ); mlt_properties_set( my_properties, "geometry", "0%/0%:100%x100%:100%" ); mlt_properties_set( my_properties, "family", "Sans" ); mlt_properties_set( my_properties, "size", "48" ); mlt_properties_set( my_properties, "weight", "400" ); mlt_properties_set( my_properties, "style", "normal" ); mlt_properties_set( my_properties, "fgcolour", "0x000000ff" ); mlt_properties_set( my_properties, "bgcolour", "0x00000020" ); mlt_properties_set( my_properties, "olcolour", "0x00000000" ); mlt_properties_set( my_properties, "pad", "0" ); mlt_properties_set( my_properties, "halign", "left" ); mlt_properties_set( my_properties, "valign", "top" ); mlt_properties_set( my_properties, "outline", "0" ); mlt_properties_set_int( my_properties, "_reset", 1 ); mlt_properties_set_int( my_properties, "_filter_private", 1 ); filter->process = filter_process; } else { if( filter ) { mlt_filter_close( filter ); } if( transition ) { mlt_transition_close( transition ); } if( producer ) { mlt_producer_close( producer ); } filter = NULL; } return filter; } mlt-6.20.0/src/modules/plus/filter_text.yml000066400000000000000000000067351362234133600207070ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: text title: Text version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: Overlay text onto the video parameters: - identifier: argument title: Text type: string description: | The text to overlay. required: yes argument: yes readonly: no default: text widget: text - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo mlt-6.20.0/src/modules/plus/filter_timer.c000066400000000000000000000145631362234133600204620ustar00rootroot00000000000000/* * filter_timer.c -- timer text overlay filter * Copyright (C) 2018-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #define MAX_TEXT_LEN 512 double time_to_seconds( char* time ) { int hours = 0; int mins = 0; double secs = 0; if ( time ) sscanf( time, "%d:%d:%lf", &hours, &mins, &secs ); return ( hours * 60.0 * 60.0 ) + ( mins * 60.0 ) + secs; } static void get_timer_str( mlt_filter filter, mlt_frame frame, char* text ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position current_frame = mlt_filter_get_position( filter, frame ); char* direction = mlt_properties_get( properties, "direction" ); double timer_start = time_to_seconds( mlt_properties_get( properties, "start" ) ); double timer_duration = time_to_seconds( mlt_properties_get( properties, "duration" ) ); double timer_offset = time_to_seconds( mlt_properties_get( properties, "offset" ) ); double value = time_to_seconds( mlt_properties_frames_to_time( properties, current_frame, mlt_time_clock ) ); if ( timer_duration <= 0.0 ) { // "duration" of zero means entire length of the filter. mlt_position filter_length = mlt_filter_get_length2( filter, frame ) - 1; double filter_duration = time_to_seconds( mlt_properties_frames_to_time( properties, filter_length, mlt_time_clock ) ); timer_duration = filter_duration - timer_start; } if ( value < timer_start ) { // Hold at 0 until start time. value = 0.0; } else { value = value - timer_start; if ( value > timer_duration ) { // Hold at duration after the timer has elapsed. value = timer_duration; } } // Apply direction. if ( direction && !strcmp( direction, "down" ) ) { value = timer_duration - value; } // Apply offset value += timer_offset; int hours = value / ( 60 * 60 ); int mins = ( value / 60 ) - ( hours * 60 ); double secs = value - (double)( mins * 60 ) - (double)( hours * 60 * 60 ); char* format = mlt_properties_get( properties, "format" ); if ( !strcmp( format, "HH:MM:SS" ) ) { snprintf( text, MAX_TEXT_LEN, "%02d:%02d:%02d", hours, mins, (int)floor(secs) ); } else if ( !strcmp( format, "HH:MM:SS.S" ) ) { snprintf( text, MAX_TEXT_LEN, "%02d:%02d:%04.1f", hours, mins, floor(secs * 10.0) / 10.0 ); } else if ( !strcmp( format, "MM:SS" ) ) { snprintf( text, MAX_TEXT_LEN, "%02d:%02d", hours * 60 + mins, (int)floor(secs) ); } else if ( !strcmp( format, "MM:SS.SS" ) ) { snprintf( text, MAX_TEXT_LEN, "%02d:%05.2f", hours * 60 + mins, floor(secs * 100.0) / 100.0 ); } else if ( !strcmp( format, "SS" ) ) { snprintf( text, MAX_TEXT_LEN, "%02d", (int)floor(value) ); } else if ( !strcmp( format, "SS.S" ) ) { snprintf( text, MAX_TEXT_LEN, "%04.1f", floor(value * 10.0) / 10.0 ); } else if ( !strcmp( format, "SS.SS" ) ) { snprintf( text, MAX_TEXT_LEN, "%05.2f", floor(value * 100.0) / 100.0 ); } } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_filter text_filter = mlt_properties_get_data( properties, "_text_filter", NULL ); mlt_properties text_filter_properties = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE(text_filter)); char* result = calloc( 1, MAX_TEXT_LEN ); get_timer_str( filter, frame, result ); mlt_properties_set( text_filter_properties, "argument", result ); free( result ); mlt_properties_pass_list( text_filter_properties, properties, "geometry family size weight style fgcolour bgcolour olcolour pad halign valign outline" ); mlt_filter_set_in_and_out( text_filter, mlt_filter_get_in( filter ), mlt_filter_get_out( filter ) ); return mlt_filter_process( text_filter, frame ); } /** Constructor for the filter. */ mlt_filter filter_timer_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); mlt_filter text_filter = mlt_factory_filter( profile, "qtext", NULL ); if( !text_filter ) text_filter = mlt_factory_filter( profile, "text", NULL ); if( !text_filter ) mlt_log_warning( MLT_FILTER_SERVICE(filter), "Unable to create text filter.\n" ); if ( filter && text_filter ) { mlt_properties my_properties = MLT_FILTER_PROPERTIES( filter ); // Register the text filter for reuse/destruction mlt_properties_set_data( my_properties, "_text_filter", text_filter, 0, ( mlt_destructor )mlt_filter_close, NULL ); // Assign default values mlt_properties_set( my_properties, "format", "SS.SS" ); mlt_properties_set( my_properties, "start", "00:00:00.000" ); mlt_properties_set( my_properties, "duration", "00:10:00.000" ); mlt_properties_set( my_properties, "offset", "00:00:00.000" ); mlt_properties_set( my_properties, "direction", "up" ); mlt_properties_set( my_properties, "geometry", "0%/0%:100%x100%:100%" ); mlt_properties_set( my_properties, "family", "Sans" ); mlt_properties_set( my_properties, "size", "48" ); mlt_properties_set( my_properties, "weight", "400" ); mlt_properties_set( my_properties, "style", "normal" ); mlt_properties_set( my_properties, "fgcolour", "0x000000ff" ); mlt_properties_set( my_properties, "bgcolour", "0x00000020" ); mlt_properties_set( my_properties, "olcolour", "0x00000000" ); mlt_properties_set( my_properties, "pad", "0" ); mlt_properties_set( my_properties, "halign", "left" ); mlt_properties_set( my_properties, "valign", "top" ); mlt_properties_set( my_properties, "outline", "0" ); mlt_properties_set_int( my_properties, "_filter_private", 1 ); filter->process = filter_process; } else { if( filter ) { mlt_filter_close( filter ); } if( text_filter ) { mlt_filter_close( text_filter ); } filter = NULL; } return filter; } mlt-6.20.0/src/modules/plus/filter_timer.yml000066400000000000000000000120331362234133600210270ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: timer title: Timer version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: Overlay a timer onto the video. The timer can count up or down. parameters: - identifier: format title: Format type: string description: > The time format of the overlaid timer text. values: - HH:MM:SS - HH:MM:SS.S - MM:SS - MM:SS.SS - SS - SS.S default: SS.SS readonly: no mutable: yes widget: combo - identifier: start title: Start type: string description: > The time that the timer will start counting up or down. The text will be frozen at 00:00:00.000 from the start of the filter until the start time has elapsed. Must be in the format HH:MM:SS.SSS default: 00:00:00.000 readonly: no mutable: yes widget: text - identifier: duration title: Duration type: string description: > The maximum elapsed duration of the timer after the start time has elapsed. The text will be frozen at the duration time after the duration has elapsed. Must be in the format HH:MM:SS.SSS default: 00:00:10.000 readonly: no mutable: yes widget: text - identifier: offset title: Offset type: string description: > An offset to be added to the timer value. When the direction is "down", the timer will count down to "offset" instead of 00:00:00.000. When the direction is up, the timer will count up starting from "offset". Must be in the format HH:MM:SS.SSS default: 00:00:00.000 readonly: no mutable: yes widget: text - identifier: direction title: Direction type: string description: > Whether the counter should count up from 00:00:00.000 or down from the duration time. values: - up - down default: up readonly: no mutable: yes widget: combo - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo mlt-6.20.0/src/modules/plus/interp.h000066400000000000000000000511401362234133600172730ustar00rootroot00000000000000//interp.c /* * Copyright (C) 2010 Marko Cebokli http://lea.hamradio.si/~s57uuu * This file is a part of the Frei0r plugin "c0rners" * * 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. */ /******************************************************************* * The remapping functions use a map array, which contains a pair * of floating values fo each pixel of the output image. These * represent the location in the input image, from where the value * of the given output pixel should be interpolated. * They are given in pixels of the input image. * If the output image is wo pixels wide, then the x coordinate * of the pixel in row r and column c is at 2*(r*wo+c) in the map * array, and y at 2*(r*wo+c)+1 * * The map array is usually computation intensive to generate, and * he purpose of the map array is to allow fast remapping of * several images (video) using the same map array. ******************************************************************/ //compile: gcc -c -O2 -Wall -std=c99 -fPIC interp.c -o interp.o // -std=c99 za rintf() // -fPIC da lahko linkas v .so (za frei0r) #include #include /* za debug printoute */ #include //#define TEST_XY_LIMITS //-------------------------------------------------------- //pointer to an interpolating function //parameters: // source image // source width // source height // X coordinate // Y coordinate // opacity // destination image // flag to overwrite alpha channel typedef int (*interpp)(unsigned char*, int, int, float, float, float, unsigned char*, int); //************************************** //HERE BEGIN THE INTERPOLATION FUNCTIONS //------------------------------------------------------ //za debugging - z izpisovanjem //interpolacija "najblizji sosed" (ni prava interpolacija) //za byte (char) vrednosti // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost int interpNNpr_b(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { //printf("u=%5.2f v=%5.2f ",x,y); printf("u=%5.3f v=%5.3f ",x/(w-1),y/(h-1)); //printf("U=%2d V=%2d ",(int)rintf(x),(int)rintf(y)); #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif *v=sl[(int)rintf(x)+(int)rintf(y)*w]; return 0; } //------------------------------------------------------ //interpolacija "najblizji sosed" (ni prava interpolacija) //za byte (char) vrednosti // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost int interpNN_b(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif *v=sl[(int)rintf(x)+(int)rintf(y)*w]; return 0; } //------------------------------------------------------ //interpolacija "najblizji sosed" (ni prava interpolacija) //za byte (char) vrednosti v packed color 32 bitnem formatu //little endian !! // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost int interpNN_b32(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif int p = (int) rintf(x) * 4 + (int) rintf(y) * 4 * w; float alpha_sl = (float) sl[p + 3] / 255.0f * o; float alpha_v = (float) v[3] / 255.0f; float alpha = alpha_sl + alpha_v - alpha_sl * alpha_v; v[3] = is_atop? sl[p + 3] : (255 * alpha); alpha = alpha_sl / alpha; v[0] = v[0] * (1.0f - alpha) + sl[p] * alpha; v[1] = v[1] * (1.0f - alpha) + sl[p + 1] * alpha; v[2] = v[2] * (1.0f - alpha) + sl[p + 2] * alpha; return 0; } //------------------------------------------------------ //bilinearna interpolacija //za byte (char) vrednosti // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost int interpBL_b(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int m,n,k,l; float a,b; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)floorf(x); n=(int)floorf(y); k=n*w+m; l=(n+1)*w+m; a=sl[k]+(sl[k+1]-sl[k])*(x-(float)m); b=sl[l]+(sl[l+1]-sl[l])*(x-(float)m); *v=a+(b-a)*(y-(float)n); return 0; } //------------------------------------------------------ //bilinearna interpolacija //za byte (char) vrednosti v packed color 32 bitnem formatu int interpBL_b32(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int m,n,k,l,n1,l1,k1; float a,b; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)floorf(x); if (m + 2 > w) m = w - 2; n=(int)floorf(y); if (n + 2 > h) n = h - 2; k=n*w+m; l=(n+1)*w+m; k1=4*(k+1); l1=4*(l+1); n1=4*((n+1)*w+m); l=4*l; k=4*k; a=sl[k+3]+(sl[k1+3]-sl[k+3])*(x-(float)m); b=sl[l+3]+(sl[l1+3]-sl[n1+3])*(x-(float)m); float alpha_sl = a+(b-a)*(y-(float)n); float alpha_v = (float) v[3] / 255.0f; if (is_atop) v[3] = alpha_sl; alpha_sl = alpha_sl / 255.0f * o; float alpha = alpha_sl + alpha_v - alpha_sl * alpha_v; if (!is_atop) v[3] = 255 * alpha; alpha = alpha_sl / alpha; a=sl[k]+(sl[k1]-sl[k])*(x-(float)m); b=sl[l]+(sl[l1]-sl[n1])*(x-(float)m); v[0]= v[0] * (1.0f - alpha) + (a+(b-a)*(y-(float)n)) * alpha; a=sl[k+1]+(sl[k1+1]-sl[k+1])*(x-(float)m); b=sl[l+1]+(sl[l1+1]-sl[n1+1])*(x-(float)m); v[1]= v[1] * (1.0f - alpha) + (a+(b-a)*(y-(float)n)) * alpha; a=sl[k+2]+(sl[k1+2]-sl[k+2])*(x-(float)m); b=sl[l+2]+(sl[l1+2]-sl[n1+2])*(x-(float)m); v[2]= v[2] * (1.0f - alpha) + (a+(b-a)*(y-(float)n)) * alpha; return 0; } //------------------------------------------------------ //bikubicna interpolacija "smooth" //za byte (char) vrednosti //kar Aitken-Neville formula iz Bronstajna // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost int interpBC_b(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,j,l,m,n; float k; float p[4],p1[4],p2[4],p3[4],p4[4]; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4; n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4; //njaprej po y (stiri stolpce) for (i=0;i<4;i++) { l=m+(i+n)*w; p1[i]=sl[l]; p2[i]=sl[l+1]; p3[i]=sl[l+2]; p4[i]=sl[l+3]; } for (j=1;j<4;j++) for (i=3;i>=j;i--) { k=(y-i-n)/j; p1[i]=p1[i]+k*(p1[i]-p1[i-1]); p2[i]=p2[i]+k*(p2[i]-p2[i-1]); p3[i]=p3[i]+k*(p3[i]-p3[i-1]); p4[i]=p4[i]+k*(p4[i]-p4[i-1]); } //zdaj pa po x p[0]=p1[3]; p[1]=p2[3]; p[2]=p3[3]; p[3]=p4[3]; for (j=1;j<4;j++) for (i=3;i>=j;i--) p[i]=p[i]+(x-i-m)/j*(p[i]-p[i-1]); if (p[3]<0.0) p[3]=0.0; //printf("p=%f ",p[3]); if (p[3]>256.0) p[3]=255.0; //printf("p=%f ",p[3]); *v=p[3]; return 0; } //------------------------------------------------------ //bikubicna interpolacija "smooth" //za byte (char) vrednosti v packed color 32 bitnem formatu int interpBC_b32(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,j,b,l,m,n; float k; float p[4],p1[4],p2[4],p3[4],p4[4]; float alpha = 1.0; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4; n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4; for (b=3;b>-1;b--) { //njaprej po y (stiri stolpce) for (i=0;i<4;i++) { l=m+(i+n)*w; p1[i]=sl[4*l+b]; p2[i]=sl[4*(l+1)+b]; p3[i]=sl[4*(l+2)+b]; p4[i]=sl[4*(l+3)+b]; } for (j=1;j<4;j++) for (i=3;i>=j;i--) { k=(y-i-n)/j; p1[i]=p1[i]+k*(p1[i]-p1[i-1]); p2[i]=p2[i]+k*(p2[i]-p2[i-1]); p3[i]=p3[i]+k*(p3[i]-p3[i-1]); p4[i]=p4[i]+k*(p4[i]-p4[i-1]); } //zdaj pa po x p[0]=p1[3]; p[1]=p2[3]; p[2]=p3[3]; p[3]=p4[3]; for (j=1;j<4;j++) for (i=3;i>=j;i--) p[i]=p[i]+(x-i-m)/j*(p[i]-p[i-1]); if (p[3] < 0.0f) p[3] = 0.0f; if (p[3] > 255.0f) p[3] = 255.0f; if (b == 3) { float alpha_sl = (float) p[3] / 255.0f * o; float alpha_v = (float) v[3] / 255.0f; alpha = alpha_sl + alpha_v - alpha_sl * alpha_v; v[3] = is_atop? p[3] : (255 * alpha); alpha = alpha_sl / alpha; } else { v[b] = v[b] * (1.0f - alpha) + p[3] * alpha; } } return 0; } //------------------------------------------------------ //bikubicna interpolacija "sharp" //za byte (char) vrednosti //Helmut Dersch polinom // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost //!!! ODKOD SUM??? (ze po eni rotaciji v interp_test !!) //!!! v defish tega suma ni??? int interpBC2_b(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,k,l,m,n; float pp,p[4],wx[4],wy[4],xx; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4; n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4; //najprej po y (stiri stolpce) xx=y-n; wy[0]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0; xx=xx-1.0; wy[1]=(1.25*xx-2.25)*xx*xx+1.0; xx=1.0-xx; wy[2]=(1.25*xx-2.25)*xx*xx+1.0; xx=xx+1.0; wy[3]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0; //se po x xx=x-m; wx[0]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0; xx=xx-1.0; wx[1]=(1.25*xx-2.25)*xx*xx+1.0; xx=1.0-xx; wx[2]=(1.25*xx-2.25)*xx*xx+1.0; xx=xx+1.0; wx[3]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0; k=n*w+m; for (i=0;i<4;i++) { p[i]=0.0; l=k+i; p[i]=wy[0]*sl[l]; l+=w; p[i]+=wy[1]*sl[l]; l+=w; p[i]+=wy[2]*sl[l]; l+=w; p[i]+=wy[3]*sl[l]; } pp=wx[0]*p[0]; pp+=wx[1]*p[1]; pp+=wx[2]*p[2]; pp+=wx[3]*p[3]; if (pp<0.0) pp=0.0; if (pp>256.0) pp=255.0; *v=pp; return 0; } //------------------------------------------------------ //bikubicna interpolacija "sharp" //za byte (char) vrednosti v packed color 32 bitnem formatu //!!! ODKOD SUM??? (ze po eni rotaciji v interp_test !!) //!!! v defish tega suma ni??? int interpBC2_b32(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int b,i,k,l,m,n,u; float pp,p[4],wx[4],wy[4],xx; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4; n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4; //najprej po y (stiri stolpce) xx=y-n; wy[0]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0; xx=xx-1.0; wy[1]=(1.25*xx-2.25)*xx*xx+1.0; xx=1.0-xx; wy[2]=(1.25*xx-2.25)*xx*xx+1.0; xx=xx+1.0; wy[3]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0; //se po x xx=x-m; wx[0]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0; xx=xx-1.0; wx[1]=(1.25*xx-2.25)*xx*xx+1.0; xx=1.0-xx; wx[2]=(1.25*xx-2.25)*xx*xx+1.0; xx=xx+1.0; wx[3]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0; k=4*(n*w+m); u=4*w; for (b=0;b<4;b++) { for (i=0;i<4;i++) { p[i]=0.0; l=k+4*i; p[i]=wy[0]*sl[l]; l+=u; p[i]+=wy[1]*sl[l]; l+=u; p[i]+=wy[2]*sl[l]; l+=u; p[i]+=wy[3]*sl[l]; } k++; pp=wx[0]*p[0]; pp+=wx[1]*p[1]; pp+=wx[2]*p[2]; pp+=wx[3]*p[3]; if (pp<0.0) pp=0.0; if (pp>256.0) pp=255.0; v[b]=pp; } return 0; } //------------------------------------------------------ //spline 4x4 interpolacija //za byte (char) vrednosti //Helmut Dersch polinom // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost int interpSP4_b(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,j,m,n; float pp,p[4],wx[4],wy[4],xx; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4; n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4; //najprej po y (stiri stolpce) xx=y-n; wy[0]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0); xx=xx-1.0; wy[1]=((xx-1.8)*xx-0.2)*xx+1.0; xx=1.0-xx; wy[2]=((xx-1.8)*xx-0.2)*xx+1.0; xx=xx+1.0; wy[3]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0); //se po x xx=x-m; wx[0]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0); xx=xx-1.0; wx[1]=((xx-1.8)*xx-0.2)*xx+1.0; xx=1.0-xx; wx[2]=((xx-1.8)*xx-0.2)*xx+1.0; xx=xx+1.0; wx[3]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0); for (i=0;i<4;i++) { p[i]=0.0; for (j=0;j<4;j++) { p[i]=p[i]+wy[j]*sl[(j+n)*w+i+m]; } } pp=0.0; for (i=0;i<4;i++) pp=pp+wx[i]*p[i]; if (pp<0.0) pp=0.0; if (pp>256.0) pp=255.0; *v=pp; return 0; } //------------------------------------------------------ //spline 4x4 interpolacija //za byte (char) vrednosti v packed color 32 bitnem formatu int interpSP4_b32(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,j,m,n,b; float pp,p[4],wx[4],wy[4],xx; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4; n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4; //najprej po y (stiri stolpce) xx=y-n; wy[0]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0); xx=xx-1.0; wy[1]=((xx-1.8)*xx-0.2)*xx+1.0; xx=1.0-xx; wy[2]=((xx-1.8)*xx-0.2)*xx+1.0; xx=xx+1.0; wy[3]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0); //se po x xx=x-m; wx[0]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0); xx=xx-1.0; wx[1]=((xx-1.8)*xx-0.2)*xx+1.0; xx=1.0-xx; wx[2]=((xx-1.8)*xx-0.2)*xx+1.0; xx=xx+1.0; wx[3]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0); for (b=0;b<4;b++) { for (i=0;i<4;i++) { p[i]=0.0; for (j=0;j<4;j++) { p[i]=p[i]+wy[j]*sl[4*((j+n)*w+i+m)+b]; } } pp=0.0; for (i=0;i<4;i++) pp=pp+wx[i]*p[i]; if (pp<0.0) pp=0.0; if (pp>256.0) pp=255.0; v[b]=pp; } return 0; } //------------------------------------------------------ //spline 6x6 interpolacija //za byte (char) vrednosti //Helmut Dersch polinom // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost //!!! PAZI, TOLE NE DELA CISTO PRAV ??? belina se siri //!!! zaenkrat sem dodal fudge factor... int interpSP6_b(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,j,m,n; float pp,p[6],wx[6],wy[6],xx; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-3; if (m<0) m=0; if ((m+7)>w) m=w-6; n=(int)ceilf(y)-3; if (n<0) n=0; if ((n+7)>h) n=h-6; //najprej po y (sest stolpcev) xx=y-n; wy[0]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0); xx=xx-1.0; wy[1]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0); xx=xx-1.0; wy[2]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0; xx=1.0-xx; wy[3]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0; xx=xx+1.0; wy[4]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0); xx=xx+1.0; wy[5]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0); //se po x xx=x-m; wx[0]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0); xx=xx-1.0; wx[1]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0); xx=xx-1.0; wx[2]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0; xx=1.0-xx; wx[3]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0; xx=xx+1.0; wx[4]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0); xx=xx+1.0; wx[5]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0); for (i=0;i<6;i++) { p[i]=0.0; for (j=0;j<6;j++) { p[i]=p[i]+wy[j]*sl[(j+n)*w+i+m]; } } pp=0.0; for (i=0;i<6;i++) pp=pp+wx[i]*p[i]; pp=0.947*pp; //fudge factor...!!! cca 0.947... if (pp<0.0) pp=0.0; if (pp>256.0) pp=255.0; *v=pp; return 0; } //------------------------------------------------------ //spline 6x6 interpolacija //za byte (char) vrednosti v packed color 32 bitnem formatu //!!! PAZI, TOLE NE DELA CISTO PRAV ??? belina se siri //!!! zaenkrat sem dodal fudge factor... int interpSP6_b32(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,b,j,m,n; float pp,p[6],wx[6],wy[6],xx; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-3; if (m<0) m=0; if ((m+7)>w) m=w-6; n=(int)ceilf(y)-3; if (n<0) n=0; if ((n+7)>h) n=h-6; //najprej po y (sest stolpcev) xx=y-n; wy[0]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0); xx=xx-1.0; wy[1]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0); xx=xx-1.0; wy[2]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0; xx=1.0-xx; wy[3]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0; xx=xx+1.0; wy[4]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0); xx=xx+1.0; wy[5]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0); //se po x xx=x-m; wx[0]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0); xx=xx-1.0; wx[1]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0); xx=xx-1.0; wx[2]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0; xx=1.0-xx; wx[3]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0; xx=xx+1.0; wx[4]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0); xx=xx+1.0; wx[5]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0); for (b=0;b<4;b++) { for (i=0;i<6;i++) { p[i]=0.0; for (j=0;j<6;j++) { p[i]=p[i]+wy[j]*sl[4*((j+n)*w+i+m)+b]; } } pp=0.0; for (i=0;i<6;i++) pp=pp+wx[i]*p[i]; pp=0.947*pp; //fudge factor...!!! cca 0.947... if (pp<0.0) pp=0.0; if (pp>256.0) pp=255.0; v[b]=pp; } return 0; } //------------------------------------------------------ //truncated sinc "lanczos" 16x16 interpolacija //za byte (char) vrednosti // *sl vhodni array (slika) // w,h dimenzija slike je wxh // x,y tocka, za katero izracuna interpolirano vrednost // o opacity // *v interpolirana vrednost int interpSC16_b(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,j,m,n; float pp,p[16],wx[16],wy[16],xx,xxx,x1; float PI=3.141592654; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-8; if (m<0) m=0; if ((m+17)>w) m=w-16; n=(int)ceilf(y)-8; if (n<0) n=0; if ((n+17)>h) n=h-16; //najprej po y xx=y-n; for (i=7;i>=0;i--) { x1=xx*PI; wy[7-i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125)); xxx=(float)(2*i+1)-xx; x1=xxx*PI; wy[8+i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125)); xx=xx-1.0; } //se po x xx=x-m; for (i=7;i>=0;i--) { x1=xx*PI; wx[7-i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125)); xxx=(float)(2*i+1)-xx; x1=xxx*PI; wx[8+i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125)); xx=xx-1.0; } for (i=0;i<16;i++) { p[i]=0.0; for (j=0;j<16;j++) { p[i]=p[i]+wy[j]*sl[(j+n)*w+i+m]; } } pp=0.0; for (i=0;i<16;i++) pp=pp+wx[i]*p[i]; if (pp<0.0) pp=0.0; if (pp>256.0) pp=255.0; *v=pp; return 0; } //------------------------------------------------------ //truncated sinc "lanczos" 16x16 interpolacija //za byte (char) vrednosti v packed color 32 bitnem formatu int interpSC16_b32(unsigned char *sl, int w, int h, float x, float y, float o, unsigned char *v, int is_atop) { int i,j,m,b,n; float pp,p[16],wx[16],wy[16],xx,xxx,x1; float PI=3.141592654; #ifdef TEST_XY_LIMITS if ((x<0)||(x>=w)||(y<0)||(y>=h)) return -1; #endif m=(int)ceilf(x)-8; if (m<0) m=0; if ((m+17)>w) m=w-16; n=(int)ceilf(y)-8; if (n<0) n=0; if ((n+17)>h) n=h-16; //najprej po y xx=y-n; for (i=7;i>=0;i--) { x1=xx*PI; wy[7-i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125)); xxx=(float)(2*i+1)-xx; x1=xxx*PI; wy[8+i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125)); xx=xx-1.0; } //se po x xx=x-m; for (i=7;i>=0;i--) { x1=xx*PI; wx[7-i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125)); xxx=(float)(2*i+1)-xx; x1=xxx*PI; wx[8+i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125)); xx=xx-1.0; } for (b=0;b<4;b++) { for (i=0;i<16;i++) { p[i]=0.0; for (j=0;j<16;j++) { p[i]=p[i]+wy[j]*sl[4*((j+n)*w+i+m)+b]; } } pp=0.0; for (i=0;i<16;i++) pp=pp+wx[i]*p[i]; if (pp<0.0) pp=0.0; if (pp>256.0) pp=255.0; v[b]=pp; } return 0; } mlt-6.20.0/src/modules/plus/producer_blipflash.c000066400000000000000000000212741362234133600216410ustar00rootroot00000000000000/* * producer_blipflash.c -- blip/flash generating producer * Copyright (C) 2013 Brian Matherly * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /** Fill an audio buffer with 1kHz "blip" samples. */ static void fill_blip( mlt_properties producer_properties, float* buffer, int frequency, int channels, int samples ) { int new_size = samples * channels * sizeof( float ); int old_size = 0; float* blip = mlt_properties_get_data( producer_properties, "_blip", &old_size ); if( !blip || new_size > old_size ) { blip = mlt_pool_alloc( new_size ); // Fill the blip buffer if ( blip != NULL ) { int s = 0; int c = 0; for( s = 0; s < samples; s++ ) { float f = 1000.0; float t = (float)s/(float)frequency; // Add 90 deg so the blip always starts at 1 for easy detection. float phase = M_PI / 2; float value = sin( 2*M_PI*f*t + phase ); for( c = 0; c < channels; c++ ) { float* sample_ptr = ((float*) blip) + (c * samples) + s; *sample_ptr = value; } } } // Cache the audio blip to save from regenerating it with every blip. mlt_properties_set_data( producer_properties, "_blip", blip, new_size, mlt_pool_release, NULL ); }; if( blip ) memcpy( buffer, blip, new_size ); } static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_blipflash", NULL ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); int size = *samples * *channels * sizeof( float ); double fps = mlt_producer_get_fps( producer ); int frames = mlt_frame_get_position( frame ) + mlt_properties_get_int( producer_properties, "offset" ); int seconds = frames / fps; // Correct the returns if necessary *format = mlt_audio_float; *frequency = *frequency <= 0 ? 48000 : *frequency; *channels = *channels <= 0 ? 2 : *channels; *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, frames ) : *samples; // Allocate the buffer *buffer = mlt_pool_alloc( size ); // Determine if this should be a blip or silence. frames = frames % lrint( fps ); seconds = seconds % mlt_properties_get_int( producer_properties, "period" ); if( seconds == 0 && frames == 0 ) { fill_blip( producer_properties, (float*)*buffer, *frequency, *channels, *samples ); } else { // Fill silence. memset( *buffer, 0, size ); } // Set the buffer for destruction mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); return 0; } /** Fill an image buffer with either white (flash) or black as requested. */ static void fill_image( mlt_properties producer_properties, char* color, uint8_t* buffer, mlt_image_format format, int width, int height ) { int new_size = mlt_image_format_size( format, width, height, NULL ); int old_size = 0; uint8_t* image = mlt_properties_get_data( producer_properties, color, &old_size ); if( !image || new_size > old_size ) { // Need to create a new cached image. image = mlt_pool_alloc( new_size ); if ( image != NULL ) { uint8_t r, g, b; uint8_t* p = image; if( !strcmp( color, "_flash" ) ) { r = g = b = 255; // White } else { r = g = b = 0; // Black } switch( format ) { default: case mlt_image_yuv422: { int uneven = width % 2; int count = ( width - uneven ) / 2 + 1; uint8_t y, u, v; RGB2YUV_601_SCALED( r, g, b, y, u, v ); int i = height + 1; while ( --i ) { int j = count; while ( --j ) { *p ++ = y; *p ++ = u; *p ++ = y; *p ++ = v; } if ( uneven ) { *p ++ = y; *p ++ = u; } } break; } case mlt_image_rgb24: { int i = width * height + 1; while ( --i ) { *p ++ = r; *p ++ = g; *p ++ = b; } break; } case mlt_image_rgb24a: { int i = width * height + 1; while ( --i ) { *p ++ = r; *p ++ = g; *p ++ = b; *p ++ = 255; // alpha } break; } } // Cache the image to save from regenerating it with every frame. mlt_properties_set_data( producer_properties, color, image, new_size, mlt_pool_release, NULL ); } } if( image ) memcpy( buffer, image, new_size ); } static int producer_get_image( mlt_frame frame, uint8_t** buffer, mlt_image_format* format, int* width, int* height, int writable ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_blipflash", NULL ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); int size = 0; double fps = mlt_producer_get_fps( producer ); int frames = mlt_frame_get_position( frame ); int seconds = frames / fps; mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); // Correct the returns if necessary if( *format != mlt_image_yuv422 && *format != mlt_image_rgb24 && *format != mlt_image_rgb24a ) *format = mlt_image_yuv422; if( *width <= 0 ) *width = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->width; if ( *height <= 0 ) *height = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->height; // Allocate the buffer size = mlt_image_format_size( *format, *width, *height, NULL ); *buffer = mlt_pool_alloc( size ); // Determine if this should be a flash or black. frames = frames % lrint( fps ); seconds = seconds % mlt_properties_get_int( producer_properties, "period" ); if( seconds == 0 && frames == 0 ) { fill_image( producer_properties, "_flash", *buffer, *format, *width, *height ); } else { fill_image( producer_properties, "_black", *buffer, *format, *width, *height ); } mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); // Create the alpha channel int alpha_size = *width * *height; uint8_t *alpha = mlt_pool_alloc( alpha_size ); if ( alpha ) memset( alpha, 255, alpha_size ); // Update the frame mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release ); mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) ); mlt_properties_set_int( properties, "progressive", 1 ); mlt_properties_set_int( properties, "meta.media.width", *width ); mlt_properties_set_int( properties, "meta.media.height", *height ); 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 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); // Save the producer to be used later mlt_properties_set_data( frame_properties, "_producer_blipflash", producer, 0, NULL, NULL ); // Update time code on the frame mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Configure callbacks mlt_frame_push_get_image( *frame, producer_get_image ); mlt_frame_push_audio( *frame, producer_get_audio ); } // Calculate the next time code mlt_producer_prepare_next( producer ); return 0; } static void producer_close( mlt_producer this ) { this->close = NULL; mlt_producer_close( this ); free( this ); } /** Initialize. */ mlt_producer producer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create a new producer object mlt_producer producer = mlt_producer_new( profile ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); // Initialize the producer if ( producer ) { mlt_properties_set_int( producer_properties, "period", 1 ); mlt_properties_set_int( producer_properties, "offset", 0 ); // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; } return producer; } mlt-6.20.0/src/modules/plus/producer_blipflash.yml000066400000000000000000000015521362234133600222150ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: blipflash title: Blip Flash version: 1 copyright: Brian Matherly creator: Brian Matherly license: LGPLv2.1 language: en tags: - Audio - Video description: > Generate periodic synchronized audio blips and video flashes. Blips are a 1kHz tone and last the duration of the flash frame. parameters: - identifier: period title: Flash Period type: integer description: > The period between flashes in seconds. default: 1 readonly: no mutable: yes widget: spinner - identifier: offset title: Audio Offset type: integer description: > The number of frames to offset the audio. A positive number results in audio earlier than video. An negative number results in audio later than video. default: 0 readonly: no mutable: yes widget: spinner mlt-6.20.0/src/modules/plus/producer_count.c000066400000000000000000000464651362234133600210360ustar00rootroot00000000000000/* * producer_count.c -- counting producer * Copyright (C) 2013 Brian Matherly * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include /* Private Constants */ #define MAX_TEXT_LEN 512 #define LINE_PIXEL_VALUE 0x00 #define RING_PIXEL_VALUE 0xff #define CLOCK_PIXEL_VALUE 0x50 #define FRAME_BACKGROUND_COLOR "0xd0d0d0ff" #define TEXT_BACKGROUND_COLOR "0x00000000" #define TEXT_FOREGROUND_COLOR "0x000000ff" // Ratio of graphic elements relative to image size #define LINE_WIDTH_RATIO 1 #define OUTER_RING_RATIO 90 #define INNER_RING_RATIO 80 #define TEXT_SIZE_RATIO 70 typedef struct { mlt_position position; int fps; int hours; int minutes; int seconds; int frames; char sep; // Either : or ; (for ndf) } time_info; static void get_time_info( mlt_producer producer, mlt_frame frame, time_info* info ) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_position position = mlt_frame_original_position( frame ); info->fps = ceil( mlt_producer_get_fps( producer ) ); char* direction = mlt_properties_get( producer_properties, "direction" ); if( !strcmp( direction, "down" ) ) { mlt_position length = mlt_properties_get_int( producer_properties, "length" ); info->position = length - 1 - position; } else { info->position = position; } char* tc_str = NULL; if( mlt_properties_get_int( producer_properties, "drop" ) ) { tc_str = mlt_properties_frames_to_time( producer_properties, info->position, mlt_time_smpte_df ); } else { tc_str = mlt_properties_frames_to_time( producer_properties, info->position, mlt_time_smpte_ndf ); } sscanf( tc_str, "%02d:%02d:%02d%c%d", &info->hours, &info->minutes, &info->seconds, &info->sep, &info->frames ); } static inline void mix_pixel( uint8_t* image, int width, int x, int y, int value, float mix ) { uint8_t* p = image + (( y * width ) + x ) * 4; if( mix != 1.0 ) { value = ((float)value * mix) + ((float)*p * (1.0 - mix)); } *p = value; p++; *p = value; p++; *p = value; } /** Fill an audio buffer with 1kHz samples. */ static void fill_beep( mlt_properties producer_properties, float* buffer, int frequency, int channels, int samples ) { int s = 0; int c = 0; for( s = 0; s < samples; s++ ) { float f = 1000.0; float t = (float)s/(float)frequency; float value = sin( 2*M_PI*f*t ); for( c = 0; c < channels; c++ ) { float* sample_ptr = buffer + (c * samples) + s; *sample_ptr = value; } } } static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_producer producer = (mlt_producer)mlt_frame_pop_audio( frame ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); char* sound = mlt_properties_get( producer_properties, "sound" ); double fps = mlt_producer_get_fps( producer ); mlt_position position = mlt_frame_original_position( frame ); int size = 0; int do_beep = 0; time_info info; if( fps == 0 ) fps = 25; // Correct the returns if necessary *format = mlt_audio_float; *frequency = *frequency <= 0 ? 48000 : *frequency; *channels = *channels <= 0 ? 2 : *channels; *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, position ) : *samples; // Allocate the buffer size = *samples * *channels * sizeof( float ); *buffer = mlt_pool_alloc( size ); mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); get_time_info( producer, frame, &info ); // Determine if this should be a tone or silence. if( strcmp( sound, "none") ) { if( !strcmp( sound, "2pop" ) ) { mlt_position out = mlt_properties_get_int( producer_properties, "out" ); mlt_position frames = out - position; if( frames == ( info.fps * 2 ) ) { do_beep = 1; } } else if( !strcmp( sound, "frame0" ) ) { if( info.frames == 0 ) { do_beep = 1; } } } if( do_beep ) { fill_beep( producer_properties, (float*)*buffer, *frequency, *channels, *samples ); } else { // Fill silence. memset( *buffer, 0, size ); } mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); // Set the buffer for destruction mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); return 0; } static mlt_frame get_background_frame( mlt_producer producer ) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_frame bg_frame = NULL; mlt_producer color_producer = mlt_properties_get_data( producer_properties, "_color_producer", NULL ); if( !color_producer ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); color_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "colour:" ); mlt_properties_set_data( producer_properties, "_color_producer", color_producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); mlt_properties color_properties = MLT_PRODUCER_PROPERTIES( color_producer ); mlt_properties_set( color_properties, "colour", FRAME_BACKGROUND_COLOR ); } if( color_producer ) { mlt_producer_seek( color_producer, 0 ); mlt_service_get_frame( MLT_PRODUCER_SERVICE( color_producer ), &bg_frame, 0 ); } return bg_frame; } static mlt_frame get_text_frame( mlt_producer producer, time_info* info ) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_producer text_producer = mlt_properties_get_data( producer_properties, "_text_producer", NULL ); mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); mlt_frame text_frame = NULL; if( !text_producer ) { text_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "qtext:" ); // Use pango if qtext is not available. if( !text_producer ) text_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "pango:" ); if( !text_producer ) mlt_log_warning( MLT_PRODUCER_SERVICE(producer), "QT or GTK modules required for count producer.\n" ); // Save the producer for future use. mlt_properties_set_data( producer_properties, "_text_producer", text_producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); // Calculate the font size. char font_size[MAX_TEXT_LEN]; snprintf( font_size, MAX_TEXT_LEN - 1, "%dpx", profile->height * TEXT_SIZE_RATIO / 100 ); // Configure the producer. mlt_properties text_properties = MLT_PRODUCER_PROPERTIES( text_producer ); mlt_properties_set( text_properties, "size", font_size ); mlt_properties_set( text_properties, "weight", "400" ); mlt_properties_set( text_properties, "fgcolour", TEXT_FOREGROUND_COLOR ); mlt_properties_set( text_properties, "bgcolour", TEXT_BACKGROUND_COLOR ); mlt_properties_set( text_properties, "pad", "0" ); mlt_properties_set( text_properties, "outline", "0" ); mlt_properties_set( text_properties, "align", "center" ); } if( text_producer ) { mlt_properties text_properties = MLT_PRODUCER_PROPERTIES( text_producer ); char* style = mlt_properties_get( producer_properties, "style" ); char text[MAX_TEXT_LEN] = ""; // Apply the time style if( !strcmp( style, "frames" ) ) { snprintf( text, MAX_TEXT_LEN - 1, MLT_POSITION_FMT, info->position ); } else if( !strcmp( style, "timecode" ) ) { snprintf( text, MAX_TEXT_LEN - 1, "%02d:%02d:%02d%c%0*d", info->hours, info->minutes, info->seconds, info->sep, ( info->fps > 999? 4 : info->fps > 99? 3 : 2 ), info->frames ); } else if( !strcmp( style, "clock" ) ) { snprintf( text, MAX_TEXT_LEN - 1, "%.2d:%.2d:%.2d", info->hours, info->minutes, info->seconds ); } else if( !strcmp( style, "seconds+1" ) ) { snprintf( text, MAX_TEXT_LEN - 1, "%d", info->seconds + 1 ); } else // seconds { snprintf( text, MAX_TEXT_LEN - 1, "%d", info->seconds ); } mlt_properties_set( text_properties, "text", text ); // Get the frame. mlt_service_get_frame( MLT_PRODUCER_SERVICE( text_producer ), &text_frame, 0 ); } return text_frame; } static void draw_ring( uint8_t* image, mlt_profile profile, int radius, int line_width ) { float sar = mlt_profile_sar( profile ); int x_center = profile->width / 2; int y_center = profile->height / 2; int max_radius = radius + line_width; int a = max_radius + 1; int b = 0; line_width += 1; // Compensate for aliasing. // Scan through each pixel in one quadrant of the circle. while( a-- ) { b = ( max_radius / sar ) + 1.0; while( b-- ) { // Use Pythagorean theorem to determine the distance from this pixel to the center. float a2 = a*a; float b2 = b*sar*b*sar; float c = sqrtf( a2 + b2 ); float distance = c - radius; if( distance > 0 && distance < line_width ) { // This pixel is within the ring. float mix = 1.0; if( distance < 1.0 ) { // Antialias the outside of the ring mix = distance; } else if( (float)line_width - distance < 1.0 ) { // Antialias the inside of the ring mix = (float)line_width - distance; } // Apply this value to all 4 quadrants of the circle. mix_pixel( image, profile->width, x_center + b, y_center - a, RING_PIXEL_VALUE, mix ); mix_pixel( image, profile->width, x_center - b, y_center - a, RING_PIXEL_VALUE, mix ); mix_pixel( image, profile->width, x_center + b, y_center + a, RING_PIXEL_VALUE, mix ); mix_pixel( image, profile->width, x_center - b, y_center + a, RING_PIXEL_VALUE, mix ); } } } } static void draw_cross( uint8_t* image, mlt_profile profile, int line_width ) { int x = 0; int y = 0; int i = 0; // Draw a horizontal line i = line_width; while( i-- ) { y = ( profile->height - line_width ) / 2 + i; x = profile->width - 1; while( x-- ) { mix_pixel( image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0 ); } } // Draw a vertical line line_width = lrint((float)line_width * mlt_profile_sar( profile )); i = line_width; while( i-- ) { x = ( profile->width - line_width ) / 2 + i; y = profile->height - 1; while( y-- ) { mix_pixel( image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0 ); } } } static void draw_clock( uint8_t* image, mlt_profile profile, int angle, int line_width ) { float sar = mlt_profile_sar( profile ); int q = 0; int x_center = profile->width / 2; int y_center = profile->height / 2; line_width += 1; // Compensate for aliasing. // Look at each quadrant of the frame to see what should be done. for( q = 1; q <= 4; q++ ) { int max_angle = q * 90; int x_sign = ( q == 1 || q == 2 ) ? 1 : -1; int y_sign = ( q == 1 || q == 4 ) ? 1 : -1; int x_start = x_center * x_sign; int y_start = y_center * y_sign; // Compensate for rounding error of even lengths // (there is no "middle" pixel so everything is offset). if( x_sign == 1 && profile->width % 2 == 0 ) x_start--; if( y_sign == -1 && profile->height % 2 == 0 ) y_start++; if( angle >= max_angle ) { // This quadrant is completely behind the clock hand. Fill it in. int dx = x_start + x_sign; while( dx ) { dx -= x_sign; int dy = y_start + y_sign; while( dy ) { dy -= y_sign; mix_pixel( image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0 ); } } } else if ( max_angle - angle < 90 ) { // This quadrant is partially filled // Calculate a point (vx,vy) that lies on the line created by the angle from 0,0. int vx = 0; int vy = y_start; float lv = 0; // Assume maximum y and calculate the corresponding x value // for a point at the other end of this line. if( x_sign * y_sign == 1 ) { vx = x_sign * sar * y_center / tan( ( max_angle - angle ) * M_PI / 180.0 ); } else { vx = x_sign * sar * y_center * tan( ( max_angle - angle ) * M_PI / 180.0 ); } // Calculate the length of the line defined by vx,vy lv = sqrtf((float)(vx*vx)*sar*sar + (float)vy*vy); // Scan through each pixel in the quadrant counting up/down to 0,0. int dx = x_start + x_sign; while( dx ) { dx -= x_sign; int dy = y_start + y_sign; while( dy ) { dy -= y_sign; // Calculate the cross product to determine which side of // the line this pixel lies on. int xp = vx * (vy - dy) - vy * (vx - dx); xp = xp * -1; // Easier to work with positive. Positive number means "behind" the line. if( xp > 0 ) { // This pixel is behind the clock hand and should be filled in. // Calculate the distance from the pixel to the line to determine // if it is part of the clock hand. float distance = (float)xp / lv; int val = CLOCK_PIXEL_VALUE; float mix = 1.0; if( distance < line_width ) { // This pixel makes up the clock hand. val = LINE_PIXEL_VALUE; if( distance < 1.0 ) { // Antialias the outside of the clock hand mix = distance; } else if( (float)line_width - distance < 1.0 ) { // Antialias the inside of the clock hand mix_pixel( image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0 ); mix = (float)line_width - distance; } } mix_pixel( image, profile->width, x_center + dx, y_center - dy, val, mix ); } } } } } } static void add_clock_to_frame( mlt_producer producer, mlt_frame frame, time_info* info ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); uint8_t* image = NULL; mlt_image_format format = mlt_image_rgb24a; int size = 0; int width = profile->width; int height = profile->height; int line_width = LINE_WIDTH_RATIO * (width > height ? height : width) / 100; int radius = (width > height ? height : width) / 2; char* direction = mlt_properties_get( producer_properties, "direction" ); int clock_angle = 0; mlt_frame_get_image( frame, &image, &format, &width, &height, 1 ); // Calculate the angle for the clock. int frames = info->frames; if( !strcmp( direction, "down" ) ) { frames = info->fps - info->frames - 1; } clock_angle = (frames + 1) * 360 / info->fps; draw_clock( image, profile, clock_angle, line_width ); draw_cross( image, profile, line_width ); draw_ring( image, profile, ( radius * OUTER_RING_RATIO ) / 100, line_width ); draw_ring( image, profile, ( radius * INNER_RING_RATIO ) / 100, line_width ); size = mlt_image_format_size( format, width, height, NULL ); mlt_frame_set_image( frame, image, size, mlt_pool_release ); } static void add_text_to_bg( mlt_producer producer, mlt_frame bg_frame, mlt_frame text_frame ) { mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_transition transition = mlt_properties_get_data( producer_properties, "_transition", NULL ); if( !transition ) { mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); transition = mlt_factory_transition( profile, "composite", NULL ); // Save the transition for future use. mlt_properties_set_data( producer_properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); // Configure the transition. mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition ); mlt_properties_set( transition_properties, "geometry", "0%/0%:100%x100%:100" ); mlt_properties_set( transition_properties, "halign", "center" ); mlt_properties_set( transition_properties, "valign", "middle" ); } if( transition && bg_frame && text_frame ) { // Apply the transition. mlt_transition_process( transition, bg_frame, text_frame ); } } static int producer_get_image( mlt_frame frame, uint8_t** image, mlt_image_format* format, int* width, int* height, int writable ) { mlt_producer producer = mlt_frame_pop_service( frame ); mlt_frame bg_frame = NULL; mlt_frame text_frame = NULL; int error = 1; int size = 0; char* background = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "background" ); time_info info; mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); get_time_info( producer, frame, &info ); bg_frame = get_background_frame( producer ); if( !strcmp( background, "clock" ) ) { add_clock_to_frame( producer, bg_frame, &info ); } text_frame = get_text_frame( producer, &info ); add_text_to_bg( producer, bg_frame, text_frame ); if( bg_frame ) { // Get the image from the background frame. error = mlt_frame_get_image( bg_frame, image, format, width, height, writable ); size = mlt_image_format_size( *format, *width, *height, NULL ); // Detach the image from the bg_frame so it is not released. mlt_frame_set_image( bg_frame, *image, size, NULL ); // Attach the image to the input frame. mlt_frame_set_image( frame, *image, size, mlt_pool_release ); mlt_frame_close( bg_frame ); } if( text_frame ) { mlt_frame_close( text_frame ); } mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); return error; } 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 ) ); mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ); if ( *frame != NULL ) { // Obtain properties of frame mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); // Update time code on the frame mlt_frame_set_position( *frame, mlt_producer_frame( producer ) ); mlt_properties_set_int( frame_properties, "progressive", 1 ); mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_profile_sar( profile ) ); mlt_properties_set_int( frame_properties, "meta.media.width", profile->width ); mlt_properties_set_int( frame_properties, "meta.media.height", profile->height ); // Configure callbacks mlt_frame_push_service( *frame, producer ); mlt_frame_push_get_image( *frame, producer_get_image ); mlt_frame_push_audio( *frame, producer ); mlt_frame_push_audio( *frame, producer_get_audio ); } // Calculate the next time code mlt_producer_prepare_next( producer ); return 0; } static void producer_close( mlt_producer this ) { this->close = NULL; mlt_producer_close( this ); free( this ); } /** Initialize. */ mlt_producer producer_count_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create a new producer object mlt_producer producer = mlt_producer_new( profile ); // Initialize the producer if ( producer ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties_set( properties, "direction", "down" ); mlt_properties_set( properties, "style", "seconds+1" ); mlt_properties_set( properties, "sound", "none" ); mlt_properties_set( properties, "background", "clock" ); mlt_properties_set( properties, "drop", "0" ); // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; } return producer; } mlt-6.20.0/src/modules/plus/producer_count.yml000066400000000000000000000037771362234133600214140ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: count title: Count version: 1 copyright: Brian Matherly creator: Brian Matherly license: LGPLv2.1 language: en tags: - Audio - Video description: > Generate frames with a counter and synchronized tone. The counter can go up or down. parameters: - identifier: direction title: Count Direction description: Whether to count up or down. type: string default: down values: - up - down mutable: yes widget: combo - identifier: style title: Counter Style description: | The style of the counter. * seconds - seconds counting up from or down to 0 * seconds+1 - seconds counting up from or down to 1 * frames - frames * timecode - timecode in the format HH:MM:SS:FF * clock - clock in the format HH:MM:SS type: string default: seconds+1 values: - seconds - seconds+1 - frames - timecode - clock mutable: yes widget: combo - identifier: sound title: Sound description: | The sound to be produced. * silent - No sound * 2pop - A 1kHz beep exactly two seconds before the out point * frame0 - A 1kHz beep at frame 0 of every second type: string default: silent values: - none - 2pop - frame0 mutable: yes widget: combo - identifier: background title: Background description: | The background style. * none - No background * clock - Film style clock animation type: string default: clock values: - none - clock mutable: yes widget: combo - identifier: drop title: Drop Frame description: | Use SMPTE style drop-frame counting for non-integer frame rates. The clock and timecode will advance two frames every minute if necessary to keep time with wall clock time mutable: yes type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/plus/transition_affine.c000066400000000000000000000604571362234133600215020ustar00rootroot00000000000000/* * transition_affine.c -- affine transformations * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "interp.h" static double alignment_parse( char* align ) { int ret = 0.0; if ( align == NULL ); else if ( isdigit( align[ 0 ] ) ) ret = atoi( align ); else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' ) ret = 1.0; else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' ) ret = 2.0; return ret; } static mlt_position repeat_position(mlt_properties properties, const char* name, mlt_position position, int length) { // Make mlt_properties parse and refresh animation. mlt_properties_anim_get_double(properties, name, position, length); mlt_animation animation = mlt_properties_get_animation(properties, name); if (animation) { // Apply repeat and mirror options. int anim_length = mlt_animation_get_length(animation); int repeat_off = mlt_properties_get_int(properties, "repeat_off"); if (!repeat_off && position >= anim_length && anim_length != 0) { int section = position / anim_length; int mirror_off = mlt_properties_get_int(properties, "mirror_off"); position -= section * anim_length; if (!mirror_off && section % 2 == 1) position = anim_length - position; } } return position; } static double anim_get_angle(mlt_properties properties, const char* name, mlt_position position, mlt_position length) { double result = 0.0; if (mlt_properties_get(properties, name)) { position = repeat_position(properties, name, position, length); result = mlt_properties_anim_get_double(properties, name, position, length); if (strchr(mlt_properties_get(properties, name), '%')) result *= 360; } return result; } /** Calculate real geometry. */ static void geometry_calculate( mlt_transition transition, const char *store, struct mlt_geometry_item_s *output, double position ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); 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 transition, const char *name, const char *store, int normalised_width, int normalised_height ) { // Get the properties of the transition mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); // Try to fetch it first mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL ); // Determine length and obtain cycle mlt_position length = mlt_transition_get_length( transition ); 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 transition, struct mlt_geometry_item_s *result, int nw, int nh, double position ) { // Structures for geometry mlt_geometry start = transition_parse_keys( transition, "geometry", "geometries", nw, nh ); // Do the calculation geometry_calculate( transition, "geometries", result, position ); return start; } typedef struct { double matrix[3][3]; } affine_t; static void affine_init( double affine[3][3] ) { affine[0][0] = 1; affine[0][1] = 0; affine[0][2] = 0; affine[1][0] = 0; affine[1][1] = 1; affine[1][2] = 0; affine[2][0] = 0; affine[2][1] = 0; affine[2][2] = 1; } // Multiply two this affine transform with that static void affine_multiply( double affine[3][3], double matrix[3][3] ) { double output[3][3]; int i; int j; for ( i = 0; i < 3; i ++ ) for ( j = 0; j < 3; j ++ ) output[i][j] = affine[i][0] * matrix[j][0] + affine[i][1] * matrix[j][1] + affine[i][2] * matrix[j][2]; affine[0][0] = output[0][0]; affine[0][1] = output[0][1]; affine[0][2] = output[0][2]; affine[1][0] = output[1][0]; affine[1][1] = output[1][1]; affine[1][2] = output[1][2]; affine[2][0] = output[2][0]; affine[2][1] = output[2][1]; affine[2][2] = output[2][2]; } // Rotate by a given angle static void affine_rotate_x( double affine[3][3], double angle ) { double matrix[3][3]; matrix[0][0] = cos( angle * M_PI / 180 ); matrix[0][1] = 0 - sin( angle * M_PI / 180 ); matrix[0][2] = 0; matrix[1][0] = sin( angle * M_PI / 180 ); matrix[1][1] = cos( angle * M_PI / 180 ); matrix[1][2] = 0; matrix[2][0] = 0; matrix[2][1] = 0; matrix[2][2] = 1; affine_multiply( affine, matrix ); } static void affine_rotate_y( double affine[3][3], double angle ) { double matrix[3][3]; matrix[0][0] = cos( angle * M_PI / 180 ); matrix[0][1] = 0; matrix[0][2] = 0 - sin( angle * M_PI / 180 ); matrix[1][0] = 0; matrix[1][1] = 1; matrix[1][2] = 0; matrix[2][0] = sin( angle * M_PI / 180 ); matrix[2][1] = 0; matrix[2][2] = cos( angle * M_PI / 180 ); affine_multiply( affine, matrix ); } static void affine_rotate_z( double affine[3][3], double angle ) { double matrix[3][3]; matrix[0][0] = 1; matrix[0][1] = 0; matrix[0][2] = 0; matrix[1][0] = 0; matrix[1][1] = cos( angle * M_PI / 180 ); matrix[1][2] = sin( angle * M_PI / 180 ); matrix[2][0] = 0; matrix[2][1] = - sin( angle * M_PI / 180 ); matrix[2][2] = cos( angle * M_PI / 180 ); affine_multiply( affine, matrix ); } static void affine_scale( double affine[3][3], double sx, double sy ) { double matrix[3][3]; matrix[0][0] = sx; matrix[0][1] = 0; matrix[0][2] = 0; matrix[1][0] = 0; matrix[1][1] = sy; matrix[1][2] = 0; matrix[2][0] = 0; matrix[2][1] = 0; matrix[2][2] = 1; affine_multiply( affine, matrix ); } // Shear by a given value static void affine_shear( double affine[3][3], double shear_x, double shear_y, double shear_z ) { double matrix[3][3]; matrix[0][0] = 1; matrix[0][1] = tan( shear_x * M_PI / 180 ); matrix[0][2] = 0; matrix[1][0] = tan( shear_y * M_PI / 180 ); matrix[1][1] = 1; matrix[1][2] = tan( shear_z * M_PI / 180 ); matrix[2][0] = 0; matrix[2][1] = 0; matrix[2][2] = 1; affine_multiply( affine, matrix ); } static void affine_offset( double affine[3][3], double x, double y ) { affine[0][2] += x; affine[1][2] += y; } // Obtain the mapped x coordinate of the input static inline double MapX( double affine[3][3], double x, double y ) { return affine[0][0] * x + affine[0][1] * y + affine[0][2]; } // Obtain the mapped y coordinate of the input static inline double MapY( double affine[3][3], double x, double y ) { return affine[1][0] * x + affine[1][1] * y + affine[1][2]; } static inline double MapZ( double affine[3][3], double x, double y ) { return affine[2][0] * x + affine[2][1] * y + affine[2][2]; } static void affine_max_output( double affine[3][3], double *w, double *h, double dz, double max_width, double max_height ) { int tlx = MapX( affine, -max_width, max_height ) / dz; int tly = MapY( affine, -max_width, max_height ) / dz; int trx = MapX( affine, max_width, max_height ) / dz; int try = MapY( affine, max_width, max_height ) / dz; int blx = MapX( affine, -max_width, -max_height ) / dz; int bly = MapY( affine, -max_width, -max_height ) / dz; int brx = MapX( affine, max_width, -max_height ) / dz; int bry = MapY( affine, max_width, -max_height ) / 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 = ( double )( max_x - min_x + 1 ) / max_width / 2.0; *h = ( double )( max_y - min_y + 1 ) / max_height / 2.0; } #define IN_RANGE( v, r ) ( v >= - r / 2 && v < r / 2 ) static inline void get_affine( affine_t *affine, mlt_transition transition, double position, int length, double scale_width, double scale_height ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); int keyed = mlt_properties_get_int( properties, "keyed" ); if ( keyed == 0 ) { double fix_rotate_x = anim_get_angle( properties, "fix_rotate_x", position, length ); double fix_rotate_y = anim_get_angle( properties, "fix_rotate_y", position, length ); double fix_rotate_z = anim_get_angle( properties, "fix_rotate_z", position, length ); double rotate_x = mlt_properties_get_double( properties, "rotate_x" ); double rotate_y = mlt_properties_get_double( properties, "rotate_y" ); double rotate_z = mlt_properties_get_double( properties, "rotate_z" ); double fix_shear_x = anim_get_angle( properties, "fix_shear_x", position, length ); double fix_shear_y = anim_get_angle( properties, "fix_shear_y", position, length ); double fix_shear_z = anim_get_angle( properties, "fix_shear_z", position, length ); double shear_x = mlt_properties_get_double( properties, "shear_x" ); double shear_y = mlt_properties_get_double( properties, "shear_y" ); double shear_z = mlt_properties_get_double( properties, "shear_z" ); double ox = mlt_properties_anim_get_double( properties, "ox", position, length ); double oy = mlt_properties_anim_get_double( properties, "oy", position, length ); 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 * scale_width, oy * scale_height ); } else { double rotate_x = anim_get_angle(properties, "rotate_x", position, length); double rotate_y = anim_get_angle(properties, "rotate_y", position, length); double rotate_z = anim_get_angle(properties, "rotate_z", position, length); double shear_x = anim_get_angle(properties, "shear_x", position, length); double shear_y = anim_get_angle(properties, "shear_y", position, length); double shear_z = anim_get_angle(properties, "shear_z", position, length); double o_x = mlt_properties_anim_get_double(properties, "ox", repeat_position(properties, "ox", position, length), length); double o_y = mlt_properties_anim_get_double(properties, "oy", repeat_position(properties, "oy", position, length), length); 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 ); affine_offset( affine->matrix, o_x * scale_width, o_y * scale_height ); } } struct sliced_desc { uint8_t *a_image, *b_image; interpp interp; affine_t affine; int a_width, a_height, b_width, b_height; double lower_x, lower_y; double dz, mix; double x_offset, y_offset; int b_alpha; double minima, xmax, ymax; }; static int sliced_proc( int id, int index, int jobs, void* cookie ) { (void) id; // unused struct sliced_desc ctx = *((struct sliced_desc*) cookie); int height_slice = (ctx.a_height + jobs / 2) / jobs; int starty = height_slice * index; double x, y; double dx, dy; int i, j; ctx.a_image += (index * height_slice) * (ctx.a_width * 4); for (i = 0, y = ctx.lower_y; i < ctx.a_height; i++, y++) { if (i >= starty && i < (starty + height_slice)) { for (j = 0, x = ctx.lower_x; j < ctx.a_width; j++, x++) { dx = MapX( ctx.affine.matrix, x, y ) / ctx.dz + ctx.x_offset; dy = MapY( ctx.affine.matrix, x, y ) / ctx.dz + ctx.y_offset; if (dx >= ctx.minima && dx <= ctx.xmax && dy >= ctx.minima && dy <= ctx.ymax) ctx.interp(ctx.b_image, ctx.b_width, ctx.b_height, dx, dy, ctx.mix, ctx.a_image, ctx.b_alpha); ctx.a_image += 4; } } } return 0; } /** 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 ); // Image, format, width, height and image for the b frame uint8_t *b_image = NULL; mlt_image_format b_format = mlt_image_rgb24a; int b_width = mlt_properties_get_int( b_props, "meta.media.width" ); int b_height = mlt_properties_get_int( b_props, "meta.media.height" ); double b_ar = mlt_frame_get_aspect_ratio( b_frame ); double b_dar = b_ar * b_width / b_height; // Assign the current position mlt_position position = mlt_transition_get_position( transition, a_frame ); int mirror = mlt_properties_get_position( properties, "mirror" ); int length = mlt_transition_get_length( transition ); if ( mlt_properties_get_int( properties, "always_active" ) ) { mlt_properties props = mlt_properties_get_data( b_props, "_producer", NULL ); mlt_position in = mlt_properties_get_int( props, "in" ); mlt_position out = mlt_properties_get_int( props, "out" ); length = out - in + 1; } // Obtain the normalised width and height from the a_frame mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) ); int normalised_width = profile->width; int normalised_height = profile->height; double consumer_ar = mlt_profile_sar( profile ); if ( mirror && position > length / 2 ) position = abs( position - length ); // Fetch the a frame image *format = mlt_image_rgb24a; int error = mlt_frame_get_image( a_frame, image, format, width, height, 1 ); if (error || !image) return error; // Calculate the region now double scale_width = mlt_profile_scale_width(profile, *width); double scale_height = mlt_profile_scale_height(profile, *height); mlt_rect result = {0, 0, normalised_width, normalised_height, 1.0}; mlt_service_lock( MLT_TRANSITION_SERVICE( transition ) ); if (mlt_properties_get(properties, "geometry")) { // Structures for geometry struct mlt_geometry_item_s geometry; composite_calculate( transition, &geometry, normalised_width, normalised_height, ( double )position ); result.x = geometry.x; result.y = geometry.y; result.w = geometry.w; result.h = geometry.h; result.o = geometry.mix / 100.0f; } else if (mlt_properties_get(properties, "rect")) { // Determine length and obtain cycle double cycle = mlt_properties_get_double( properties, "cycle" ); // Allow a repeat cycle if ( cycle >= 1 ) length = cycle; else if ( cycle > 0 ) length *= cycle; mlt_position anim_pos = repeat_position(properties, "rect", position, length); result = mlt_properties_anim_get_rect(properties, "rect", anim_pos, length); if (mlt_properties_get(properties, "rect") && strchr(mlt_properties_get(properties, "rect"), '%')) { result.x *= normalised_width; result.y *= normalised_height; result.w *= normalised_width; result.h *= normalised_height; } result.o = (result.o == DBL_MIN)? 1.0 : MIN(result.o, 1.0); } mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) ); result.x *= scale_width; result.y *= scale_height; result.w *= scale_width; result.h *= scale_height; double geometry_w = result.w; double geometry_h = result.h; if ( !mlt_properties_get_int( properties, "fill" ) ) { double geometry_dar = result.w * consumer_ar / result.h; if ( b_dar > geometry_dar ) { result.w = MIN( result.w, b_width * b_ar / consumer_ar ); result.h = result.w * consumer_ar / b_dar; } else { result.h = MIN( result.h, b_height ); result.w = result.h * b_dar / consumer_ar; } } // Fetch the b frame image if (mlt_properties_get_int(properties, "b_scaled") || mlt_properties_get_int(b_props, "always_scale")) { // Request b frame image size just what is needed. b_width = result.w; b_height = result.h; // Set the rescale interpolation to match the frame mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) ); } else if (scale_width != 1.0 || scale_height != 1.0) { // Scale request of b frame image to consumer scale maintaining its aspect ratio. b_height = *height; b_width = b_height * b_dar / b_ar; // Set the rescale interpolation to match the frame mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) ); mlt_properties_set_int( b_props, "distort", 1 ); } else { // Request full resolution of b frame image. mlt_properties_set_int( b_props, "rescale_width", b_width ); mlt_properties_set_int( b_props, "rescale_height", b_height ); // Suppress padding and aspect normalization. mlt_properties_set( b_props, "rescale.interp", "none" ); } // This is not a field-aware transform. mlt_properties_set_int( b_props, "consumer_deinterlace", 1 ); error = mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 ); if (error || !b_image) { // Remove potentially large image on the B frame. mlt_frame_set_image( b_frame, NULL, 0, NULL ); return error; } // Check that both images are of the correct format and process if ( *format == mlt_image_rgb24a && b_format == mlt_image_rgb24a ) { double sw, sh; // Get values from the transition double scale_x = mlt_properties_anim_get_double( properties, "scale_x", position, length ); double scale_y = mlt_properties_anim_get_double( properties, "scale_y", position, length ); int scale = mlt_properties_get_int( properties, "scale" ); double geom_scale_x = (double) b_width / result.w; double geom_scale_y = (double) b_height / result.h; struct sliced_desc desc = { .a_image = *image, .b_image = b_image, .interp = interpBL_b32, .a_width = *width, .a_height = *height, .b_width = b_width, .b_height = b_height, .lower_x = -(result.x + result.w / 2.0), // center .lower_y = -(result.y + result.h / 2.0), // middle .mix = result.o, .x_offset = (double) b_width / 2.0, .y_offset = (double) b_height / 2.0, .b_alpha = mlt_properties_get_int( properties, "b_alpha" ), // Affine boundaries .minima = 0, .xmax = b_width - 1, .ymax = b_height - 1 }; // Recalculate vars if alignment supplied. if ( mlt_properties_get( properties, "halign" ) || mlt_properties_get( properties, "valign" ) ) { double halign = alignment_parse( mlt_properties_get( properties, "halign" ) ); double valign = alignment_parse( mlt_properties_get( properties, "valign" ) ); desc.x_offset = halign * b_width / 2.0; desc.y_offset = valign * b_height / 2.0; desc.lower_x = -(result.x + geometry_w * halign / 2.0f); desc.lower_y = -(result.y + geometry_h * valign / 2.0f); } affine_init( desc.affine.matrix ); // Compute the affine transform get_affine( &desc.affine, transition, ( double )position, length, scale_width, scale_height ); desc.dz = MapZ( desc.affine.matrix, 0, 0 ); if ( (int) fabs( desc.dz * 1000 ) < 25 ) return 0; if (mlt_properties_get_int(properties, "invert_scale")) { scale_x = 1.0 / scale_x; scale_y = 1.0 / scale_y; } // Factor scaling into the transformation based on output resolution. if ( mlt_properties_get_int( properties, "distort" ) ) { scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x ); scale_y = geom_scale_y * ( scale_y == 0 ? 1 : scale_y ); } else { // Determine scale with respect to aspect ratio. double consumer_dar = consumer_ar * normalised_width / normalised_height; if ( b_dar > consumer_dar ) { scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x ); scale_y = geom_scale_x * ( scale_y == 0 ? 1 : scale_y ); scale_y *= b_ar / consumer_ar; } else { scale_x = geom_scale_y * ( scale_x == 0 ? 1 : scale_x ); scale_y = geom_scale_y * ( scale_y == 0 ? 1 : scale_y ); scale_x *= consumer_ar / b_ar; } } if ( scale ) { affine_max_output( desc.affine.matrix, &sw, &sh, desc.dz, *width, *height ); affine_scale( desc.affine.matrix, sw * MIN( geom_scale_x, geom_scale_y ), sh * MIN( geom_scale_x, geom_scale_y ) ); } else if ( scale_x != 0 && scale_y != 0 ) { affine_scale( desc.affine.matrix, scale_x, scale_y ); } char *interps = mlt_properties_get( a_props, "rescale.interp" ); // Copy in case string is changed. if ( interps ) interps = strdup( interps ); // Set the interpolation function if ( interps == NULL || strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 || strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 ) { desc.interp = interpNN_b32; // uses lrintf. Values should be >= -0.5 and < max + 0.5 desc.minima -= 0.5; desc.xmax += 0.49; desc.ymax += 0.49; } else if ( strcmp( interps, "bilinear" ) == 0 ) { desc.interp = interpBL_b32; // uses floorf. } else if ( strcmp( interps, "bicubic" ) == 0 || strcmp( interps, "hyper" ) == 0 || strcmp( interps, "sinc" ) == 0 || strcmp( interps, "lanczos" ) == 0 || strcmp( interps, "spline" ) == 0 ) { // TODO: lanczos 8x8 // TODO: spline 4x4 or 6x6 desc.interp = interpBC_b32; // uses ceilf. Values should be > -1 and <= max. desc.minima -= 1; } free( interps ); // Do the transform with interpolation int threads = mlt_properties_get_int(properties, "threads"); threads = CLAMP(threads, 0, mlt_slices_count_normal()); if (threads == 1) sliced_proc(0, 0, 1, &desc); else mlt_slices_run_normal(threads, sliced_proc, &desc); // Remove potentially large image on the B frame. mlt_frame_set_image( b_frame, NULL, 0, NULL ); } return 0; } /** Affine transition processing. */ static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_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 ), "distort", 0 ); mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "rect", "0%/0%:100%x100%:100%" ); // Inform apps and framework that this is a video only transition mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 ); mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "fill", 1 ); transition->process = transition_process; } return transition; } mlt-6.20.0/src/modules/plus/transition_affine.yml000066400000000000000000000203671362234133600220550ustar00rootroot00000000000000schema_version: 0.2 type: transition identifier: affine title: Transform version: 4 copyright: Meltytech, LLC creator: Charles Yates contributor: - Dan Dennedy license: LGPLv2.1 language: en tags: - Video parameters: - identifier: geometry title: Rectangle type: geometry description: This property is deprecated. Use rect instead. - identifier: distort title: Ignore aspect ratio description: > Determines whether the image aspect ratio will be distorted while scaling to completely fill the geometry rectangle. type: boolean default: 0 mutable: yes widget: checkbox - identifier: fill title: Upscale to fill description: > Determines whether the image will be scaled up to fill the rectangle or whether the size will be constrained to 100% of the profile resolution. type: boolean default: 1 mutable: yes widget: checkbox - identifier: repeat_off title: Disable looping description: > When animating properties with keyframes, whether to repeat the animation after it reaches the last key frame. type: boolean default: 0 mutable: yes widget: checkbox - identifier: mirror_off title: Disable ping-pong description: > When animating properties with keyframes and repeat_off=0, whether the animation alternates between reverses and forwards for each repetition. type: boolean default: 0 mutable: yes widget: checkbox - identifier: cycle title: Period description: > The duration to use when interpreting key frames for animation. If 0, the default, the transition length is used. If in range (0, 1), a percentage of transition length; otherwise, the number of frames. type: float default: 0 mutable: yes - identifier: keyed title: Key-framed description: > Whether rotate, shear, and offset are key-framed or not. This ("key-framed") refers to the legacy, deprecated, mlt_geometry-based property evaluation. Most of the properties support mlt_animation now. type: boolean default: 0 mutable: yes widget: checkbox - identifier: ox title: Horizontal offset type: float minimum: 0 default: 0 mutable: yes unit: pixels - identifier: oy title: Vertical offset type: float minimum: 0 default: 0 mutable: yes unit: pixels - identifier: rotate_x title: Rotate on X axis description: > Animate rotation around the X axis. If keyed=0, the amount to rotate per frame. type: float unit: degrees default: 0 mutable: yes - identifier: rotate_y title: Rotate on Y axis description: > Animate rotation around the Y axis. If keyed=0, the amount to rotate per frame. type: float unit: degrees default: 0 mutable: yes - identifier: rotate_z title: Rotate on Z axis description: > Animate rotation around the Z axis. If keyed=0, the amount to rotate per frame. type: float unit: degrees default: 0 mutable: yes - identifier: fix_rotate_x title: X axis rotation description: Fixed amount of rotation around the X axis. type: float unit: degrees default: 0 mutable: yes - identifier: fix_rotate_y title: Y axis rotation description: Fixed amount of rotation around the Y axis. type: float unit: degrees default: 0 mutable: yes - identifier: fix_rotate_z title: Z axis rotation description: Fixed amount of rotation around the Z axis. type: float unit: degrees default: 0 mutable: yes - identifier: shear_x title: Shear along X axis description: > Animate shear along the X axis. If keyed=0, the shear angle increment per frame. type: float unit: degrees default: 0 mutable: yes - identifier: shear_y title: Shear along Y axis description: > Animate shear along the Y axis. If keyed=0, the shear angle increment per frame. type: float unit: degrees default: 0 mutable: yes - identifier: shear_z title: Shear along Z axis description: > Animate shear along the Z axis. If keyed=0, the shear angle increment per frame. type: float unit: degrees default: 0 mutable: yes - identifier: fix_shear_x title: X axis shear description: Fixed amount of shear along the X axis. type: float unit: degrees default: 0 mutable: yes - identifier: fix_shear_y title: Y axis shear description: Fixed amount of shear along the Y axis. type: float unit: degrees default: 0 mutable: yes - identifier: fix_shear_z title: Z axis shear description: Fixed amount of shear along the Z axis. type: float unit: degrees default: 0 mutable: yes - identifier: mirror title: Ping-pong description: > When animating properties with key frames, whether the animation should behave with a ping-pong effect once over the duration of the transition. It will run in the forward direction over the first half the transition and in the reverse direction over the second half. type: boolean - identifier: scale title: Scale description: > Whether to automatic upscale B frame image to ensure the geometry area is filled. type: boolean default: 0 mutable: yes widget: checkbox - identifier: scale_x title: Horizontal scale description: A scale factor applied along the X axis. type: float default: 0 mutable: yes - identifier: scale_y title: Vertical scale description: A scale factor applied along the Y axis. type: float default: 0 mutable: yes - identifier: invert_scale title: Invert Scale description: > Whether to invert the scale_x and scale_y values. This is helpful to make animation interpolation sane because otherwise the scale values do not animate linearly. type: boolean default: 0 mutable: yes widget: checkbox - identifier: b_alpha title: Affect alpha channel description: > Whether to use the B frame's alpha channel in transformations for the output, The affine filter sets this to 1 by default. Basically, this tells the blend function to use the Porter-Duff atop mode instead of the default over. type: boolean default: 0 mutable: yes - identifier: fill title: Fill geometry description: > Determines whether the image will be scaled up to fill the geometry. Otherwise, if the B frame image fits within the geometry, it will not be scaled. If 0, and the B frame image exceeds the geometry, then it is scaled down to fit within the geometry. type: boolean default: 1 mutable: yes widget: checkbox - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - center - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo - identifier: threads title: Thread count description: > Use 0 to use the slice count, which defaults to the number of detected CPUs. Otherwise, set the number of threads to use up to the slice count. minimum: 0 default: 0 - identifier: rect title: Rectangle description: > This replaces the geometry property and specifies a specifies a rectangle for the size and position of the image. The format of this is "X/Y:WxH[:opacity]" and can be animated with key frames. Unlike the geometry property, if you use percentages you must use them for every field in the above format. For example, you cannot mix and match usage of absolute coordinates and percentages for size and opacity. type: rect default: "0%/0%:100%x100%:100%" readonly: no mutable: yes - identifier: b_scaled title: Do not use full resolution of B frame type: boolean default: 0 mutable: yes mlt-6.20.0/src/modules/plusgpl/000077500000000000000000000000001362234133600163235ustar00rootroot00000000000000mlt-6.20.0/src/modules/plusgpl/CMakeLists.txt000066400000000000000000000010021362234133600210540ustar00rootroot00000000000000if(GPL) file(GLOB mltplusgpl_src *.c) add_library(mltplusgpl MODULE ${mltplusgpl_src}) set(mltplusgpl_lib mlt m Threads::Threads) if(WIN32) list(APPEND mltplusgpl_lib ws2_32) elseif(UNIX) list(APPEND mltplusgpl_lib rt) endif() target_link_libraries(mltplusgpl ${mltplusgpl_lib}) install(TARGETS mltplusgpl LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/mltplusgpl) endif() mlt-6.20.0/src/modules/plusgpl/Makefile000066400000000000000000000015361362234133600177700ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lm -lpthread include ../../../config.mak TARGET = ../libmltplusgpl$(LIBSUF) OBJS = factory.o \ consumer_cbrts.o \ filter_burn.o \ filter_lumaliftgaingamma.o \ image.o \ utils.o \ filter_rotoscoping.o \ cJSON.o \ filter_telecide.o ifeq ($(targetos), MinGW) LDFLAGS += -lws2_32 else ifeq ($(targetos), Linux) LDFLAGS += -lrt 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)$(moduledir)" install -d $(DESTDIR)$(mltdatadir)/plusgpl install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/plusgpl" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/plusgpl/cJSON.c000066400000000000000000000442771362234133600174210ustar00rootroot00000000000000/* Copyright (c) 2009 Dave Gamble Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // cJSON // JSON parser in C. #include #include #include #include #include #include #include #include "cJSON.h" static int cJSON_strcasecmp(const char *s1,const char *s2) { if (!s1) return (s1==s2)?0:1; if (!s2) return 1; for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); } static void *(*cJSON_malloc)(size_t sz) = malloc; static void (*cJSON_free)(void *ptr) = free; static char* cJSON_strdup(const char* str) { size_t len; char* copy; len = strlen(str) + 1; if (!(copy = (char*)cJSON_malloc(len))) return 0; memcpy(copy,str,len); return copy; } void cJSON_InitHooks(cJSON_Hooks* hooks) { if (!hooks) { /* Reset hooks */ cJSON_malloc = malloc; cJSON_free = free; return; } cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; cJSON_free = (hooks->free_fn)?hooks->free_fn:free; } // Internal constructor. static cJSON *cJSON_New_Item() { cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); if (node) memset(node,0,sizeof(cJSON)); return node; } // Delete a cJSON structure. void cJSON_Delete(cJSON *c) { cJSON *next; while (c) { next=c->next; if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); if (c->string) cJSON_free(c->string); cJSON_free(c); c=next; } } // Parse the input text to generate a number, and populate the result into item. static const char *parse_number(cJSON *item,const char *num) { double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; // Could use sscanf for this? if (*num=='-') sign=-1,num++; // Has sign? if (*num=='0') num++; // is zero if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); // Number? if (*num=='.') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} // Fractional part? if (*num=='e' || *num=='E') // Exponent? { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; // With sign? while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); // Number? } n=sign*n*pow(10.0,(scale+subscale*signsubscale)); // number = +/- number.fraction * 10^+/- exponent item->valuedouble=n; item->valueint=(int)n; item->type=cJSON_Number; return num; } // Render the number nicely from the given item into a string. static char *print_number(cJSON *item) { char *str; double d=item->valuedouble; if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) { str=(char*)cJSON_malloc(21); // 2^64+1 can be represented in 21 chars. if (str) sprintf(str,"%d",item->valueint); } else { str=(char*)cJSON_malloc(64); // This is a nice tradeoff. if (str) { if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d); else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); else sprintf(str,"%f",d); } } return str; } // Parse the input text into an unescaped cstring, and populate item. static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; static const char *parse_string(cJSON *item,const char *str) { const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc; if (*str!='\"') return 0; // not a string! while (*ptr!='\"' && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++; // Skip escaped quotes. out=(char*)cJSON_malloc(len+1); // This is how long we need for the string, roughly. if (!out) return 0; ptr=str+1;ptr2=out; while (*ptr!='\"' && (unsigned char)*ptr>31) { if (*ptr!='\\') *ptr2++=*ptr++; else { ptr++; switch (*ptr) { case 'b': *ptr2++='\b'; break; case 'f': *ptr2++='\f'; break; case 'n': *ptr2++='\n'; break; case 'r': *ptr2++='\r'; break; case 't': *ptr2++='\t'; break; case 'u': // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY. sscanf(ptr+1,"%4x",&uc); // get the unicode char. len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len; switch (len) { case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 1: *--ptr2 =(uc | firstByteMark[len]); } ptr2+=len;ptr+=4; break; default: *ptr2++=*ptr; break; } ptr++; } } *ptr2=0; if (*ptr=='\"') ptr++; item->valuestring=out; item->type=cJSON_String; return ptr; } // Render the cstring provided to an escaped version that can be printed. static char *print_string_ptr(const char *str) { const char *ptr;char *ptr2,*out;int len=0; if (!str) return cJSON_strdup(""); ptr=str;while (*ptr && ++len) {if ((unsigned char)*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;} out=(char*)cJSON_malloc(len+3); if (!out) return 0; ptr2=out;ptr=str; *ptr2++='\"'; while (*ptr) { if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; else { *ptr2++='\\'; switch (*ptr++) { case '\\': *ptr2++='\\'; break; case '\"': *ptr2++='\"'; break; case '\b': *ptr2++='b'; break; case '\f': *ptr2++='f'; break; case '\n': *ptr2++='n'; break; case '\r': *ptr2++='r'; break; case '\t': *ptr2++='t'; break; default: ptr2--; break; // eviscerate with prejudice. } } } *ptr2++='\"';*ptr2++=0; return out; } // Invote print_string_ptr (which is useful) on an item. static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} // Predeclare these prototypes. static const char *parse_value(cJSON *item,const char *value); static char *print_value(cJSON *item,int depth,int fmt); static const char *parse_array(cJSON *item,const char *value); static char *print_array(cJSON *item,int depth,int fmt); static const char *parse_object(cJSON *item,const char *value); static char *print_object(cJSON *item,int depth,int fmt); // Utility to jump whitespace and cr/lf static const char *skip(const char *in) {while (in && (unsigned char)*in<=32) in++; return in;} // Parse an object - create a new root, and populate. cJSON *cJSON_Parse(const char *value) { cJSON *c=cJSON_New_Item(); if (!c) return 0; /* memory fail */ if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} return c; } // Render a cJSON item/entity/structure to text. char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} // Parser core - when encountering text, process appropriately. static const char *parse_value(cJSON *item,const char *value) { if (!value) return 0; // Fail on null. if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } if (*value=='\"') { return parse_string(item,value); } if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } if (*value=='[') { return parse_array(item,value); } if (*value=='{') { return parse_object(item,value); } return 0; // failure. } // Render a value to text. static char *print_value(cJSON *item,int depth,int fmt) { char *out=0; if (!item) return 0; switch ((item->type)&255) { case cJSON_NULL: out=cJSON_strdup("null"); break; case cJSON_False: out=cJSON_strdup("false");break; case cJSON_True: out=cJSON_strdup("true"); break; case cJSON_Number: out=print_number(item);break; case cJSON_String: out=print_string(item);break; case cJSON_Array: out=print_array(item,depth,fmt);break; case cJSON_Object: out=print_object(item,depth,fmt);break; } return out; } // Build an array from input text. static const char *parse_array(cJSON *item,const char *value) { cJSON *child; if (*value!='[') return 0; // not an array! item->type=cJSON_Array; value=skip(value+1); if (*value==']') return value+1; // empty array. item->child=child=cJSON_New_Item(); if (!item->child) return 0; // memory fail value=skip(parse_value(child,skip(value))); // skip any spacing, get the value. if (!value) return 0; while (*value==',') { cJSON *new_item; if (!(new_item=cJSON_New_Item())) return 0; // memory fail child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_value(child,skip(value+1))); if (!value) return 0; // memory fail } if (*value==']') return value+1; // end of array return 0; // malformed. } // Render an array to text static char *print_array(cJSON *item,int depth,int fmt) { char **entries; char *out=0,*ptr,*ret;int len=5; cJSON *child=item->child; int numentries=0,i=0,fail=0; // How many entries in the array? while (child) numentries++,child=child->next; // Allocate an array to hold the values for each entries=(char**)cJSON_malloc(numentries*sizeof(char*)); if (!entries) return 0; memset(entries,0,numentries*sizeof(char*)); // Retrieve all the results: child=item->child; while (child && !fail) { ret=print_value(child,depth+1,fmt); entries[i++]=ret; if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; child=child->next; } // If we didn't fail, try to malloc the output string if (!fail) out=cJSON_malloc(len); // If that fails, we fail. if (!out) fail=1; // Handle failure. if (fail) { for (i=0;itype=cJSON_Object; value=skip(value+1); if (*value=='}') return value+1; // empty array. item->child=child=cJSON_New_Item(); if (!item->child) return 0; value=skip(parse_string(child,skip(value))); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') return 0; // fail! value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. if (!value) return 0; while (*value==',') { cJSON *new_item; if (!(new_item=cJSON_New_Item())) return 0; // memory fail child->next=new_item;new_item->prev=child;child=new_item; value=skip(parse_string(child,skip(value+1))); if (!value) return 0; child->string=child->valuestring;child->valuestring=0; if (*value!=':') return 0; // fail! value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. if (!value) return 0; } if (*value=='}') return value+1; // end of array return 0; // malformed. } // Render an object to text. static char *print_object(cJSON *item,int depth,int fmt) { char **entries=0,**names=0; char *out=0,*ptr,*ret,*str;int len=7,i=0,j; cJSON *child=item->child; int numentries=0,fail=0; // Count the number of entries. while (child) numentries++,child=child->next; // Allocate space for the names and the objects entries=(char**)cJSON_malloc(numentries*sizeof(char*)); if (!entries) return 0; names=(char**)cJSON_malloc(numentries*sizeof(char*)); if (!names) {cJSON_free(entries);return 0;} memset(entries,0,sizeof(char*)*numentries); memset(names,0,sizeof(char*)*numentries); // Collect all the results into our arrays: child=item->child;depth++;if (fmt) len+=depth; while (child) { names[i]=str=print_string_ptr(child->string); entries[i++]=ret=print_value(child,depth,fmt); if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; child=child->next; } // Try to allocate the output string if (!fail) out=(char*)cJSON_malloc(len); if (!out) fail=1; // Handle failure if (fail) { for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} // Utility for array list handling. static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} // Utility for handling references. static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} // Add item to array/object. void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; if (c->prev) c->prev->next=c->next; if (c->next) c->next->prev=c->prev; if (c==array->child) array->child=c->next; c->prev=c->next=0; return c;} void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} // Replace array/object items with new ones. void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} // Create basic types: cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} // Create Arrays: cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} mlt-6.20.0/src/modules/plusgpl/cJSON.h000066400000000000000000000125151362234133600174140ustar00rootroot00000000000000/* Copyright (c) 2009 Dave Gamble Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef cJSON__h #define cJSON__h #ifdef __cplusplus extern "C" { #endif // cJSON Types: #define cJSON_False 0 #define cJSON_True 1 #define cJSON_NULL 2 #define cJSON_Number 3 #define cJSON_String 4 #define cJSON_Array 5 #define cJSON_Object 6 #define cJSON_IsReference 256 // The cJSON structure: typedef struct cJSON { struct cJSON *next,*prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem struct cJSON *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object. int type; // The type of the item, as above. char *valuestring; // The item's string, if type==cJSON_String int valueint; // The item's number, if type==cJSON_Number double valuedouble; // The item's number, if type==cJSON_Number char *string; // The item's name string, if this item is the child of, or is in the list of subitems of an object. } cJSON; typedef struct cJSON_Hooks { void *(*malloc_fn)(size_t sz); void (*free_fn)(void *ptr); } cJSON_Hooks; // Supply malloc, realloc and free functions to cJSON extern void cJSON_InitHooks(cJSON_Hooks* hooks); // Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. extern cJSON *cJSON_Parse(const char *value); // Render a cJSON entity to text for transfer/storage. Free the char* when finished. extern char *cJSON_Print(cJSON *item); // Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. extern char *cJSON_PrintUnformatted(cJSON *item); // Delete a cJSON entity and all subentities. extern void cJSON_Delete(cJSON *c); // Returns the number of items in an array (or object). extern int cJSON_GetArraySize(cJSON *array); // Retrieve item number "item" from array "array". Returns NULL if unsuccessful. extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); // Get item "string" from object. Case insensitive. extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); // These calls create a cJSON item of the appropriate type. extern cJSON *cJSON_CreateNull(); extern cJSON *cJSON_CreateTrue(); extern cJSON *cJSON_CreateFalse(); extern cJSON *cJSON_CreateBool(int b); extern cJSON *cJSON_CreateNumber(double num); extern cJSON *cJSON_CreateString(const char *string); extern cJSON *cJSON_CreateArray(); extern cJSON *cJSON_CreateObject(); // These utilities create an Array of count items. extern cJSON *cJSON_CreateIntArray(int *numbers,int count); extern cJSON *cJSON_CreateFloatArray(float *numbers,int count); extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count); extern cJSON *cJSON_CreateStringArray(const char **strings,int count); // Append item to the specified array/object. extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); // Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); // Remove/Detach items from Arrays/Objects. extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); extern void cJSON_DeleteItemFromArray(cJSON *array,int which); extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); // Update array items. extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) #ifdef __cplusplus } #endif #endif mlt-6.20.0/src/modules/plusgpl/consumer_cbrts.c000066400000000000000000001067731362234133600215350ustar00rootroot00000000000000/* * consumer_cbrts.c -- output constant bitrate MPEG-2 transport stream * * Copyright (C) 2010-2015 Broadcasting Center Europe S.A. http://www.bce.lu * an RTL Group Company http://www.rtlgroup.com * Author: Dan Dennedy * Some ideas and portions come from OpenCaster, Copyright (C) Lorenzo Pallara * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef _WIN32 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include // includes for socket IO #if (_POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE) && (_POSIX_TIMERS > 0) #if !(defined(__FreeBSD_kernel__) && defined(__GLIBC__)) #define CBRTS_BSD_SOCKETS 1 #include #include #include #include #include #endif #endif #include #include #define TSP_BYTES (188) #define MAX_PID (8192) #define SCR_HZ (27000000ULL) #define NULL_PID (0x1fff) #define PAT_PID (0) #define SDT_PID (0x11) #define PCR_SMOOTHING (12) #define PCR_PERIOD_MS (20) #define RTP_BYTES (12) #define UDP_MTU (RTP_BYTES + TSP_BYTES * 7) #define REMUX_BUFFER_MIN (10) #define REMUX_BUFFER_MAX (50) #define UDP_BUFFER_MINIMUM (100) #define UDP_BUFFER_DEFAULT (1000) #define RTP_VERSION (2) #define RTP_PAYLOAD (33) #define RTP_HZ (90000) #define PIDOF( packet ) ( ntohs( *( ( uint16_t* )( packet + 1 ) ) ) & 0x1fff ) #define HASPCR( packet ) ( (packet[3] & 0x20) && (packet[4] != 0) && (packet[5] & 0x10) ) #define CCOF( packet ) ( packet[3] & 0x0f ) #define ADAPTOF( packet ) ( ( packet[3] >> 4 ) & 0x03 ) typedef struct consumer_cbrts_s *consumer_cbrts; struct consumer_cbrts_s { struct mlt_consumer_s parent; mlt_consumer avformat; pthread_t thread; int joined; int running; mlt_position last_position; mlt_event event_registered; int fd; uint8_t *leftover_data[TSP_BYTES]; int leftover_size; mlt_deque tsp_packets; uint64_t previous_pcr; uint64_t previous_packet_count; uint64_t packet_count; int is_stuffing_set; int thread_running; uint8_t pcr_count; uint16_t pmt_pid; int is_si_sdt; int is_si_pat; int is_si_pmt; int dropped; uint8_t continuity_count[MAX_PID]; uint64_t output_counter; #ifdef CBRTS_BSD_SOCKETS struct addrinfo *addr; struct timespec timer; uint32_t nsec_per_packet; uint32_t femto_per_packet; uint64_t femto_counter; #endif int ( *write_tsp )( consumer_cbrts, const void *buf, size_t count ); uint8_t udp_packet[UDP_MTU]; size_t udp_bytes; size_t udp_packet_size; mlt_deque udp_packets; pthread_t output_thread; pthread_mutex_t udp_deque_mutex; pthread_cond_t udp_deque_cond; uint64_t muxrate; int udp_buffer_max; uint16_t rtp_sequence; uint32_t rtp_ssrc; uint32_t rtp_counter; }; typedef struct { int size; int period; int packet_count; uint16_t pid; uint8_t data[4096]; } ts_section; static uint8_t null_packet[ TSP_BYTES ]; /** 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 on_data_received( mlt_properties properties, mlt_consumer consumer, uint8_t *buf, int size ); mlt_consumer consumer_cbrts_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { consumer_cbrts self = calloc( sizeof( struct consumer_cbrts_s ), 1 ); if ( self && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { // Get the parent consumer object mlt_consumer parent = &self->parent; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); // Create child consumers self->avformat = mlt_factory_consumer( profile, "avformat", NULL ); parent->close = consumer_close; parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; self->joined = 1; self->tsp_packets = mlt_deque_init(); self->udp_packets = mlt_deque_init(); // Create the null packet memset( null_packet, 0xFF, TSP_BYTES ); null_packet[0] = 0x47; null_packet[1] = 0x1f; null_packet[2] = 0xff; null_packet[3] = 0x10; // Create the deque mutex and condition pthread_mutex_init( &self->udp_deque_mutex, NULL ); pthread_cond_init( &self->udp_deque_cond, NULL ); // Set consumer property defaults mlt_properties_set_int( properties, "real_time", -1 ); return parent; } free( self ); return NULL; } static ts_section* load_section( const char *filename ) { ts_section *section = NULL; int fd; if ( !filename ) return NULL; if ( ( fd = open( filename, O_RDONLY ) ) < 0 ) { mlt_log_error( NULL, "cbrts consumer failed to load section file %s\n", filename ); return NULL; } section = malloc( sizeof(*section) ); memset( section, 0xff, sizeof(*section) ); section->size = 0; if ( read( fd, section->data, 3 ) ) { // get the size uint16_t *p = (uint16_t*) §ion->data[1]; section->size = p[0]; section->size = ntohs( section->size ) & 0x0FFF; // read the data if ( section->size <= sizeof(section->data) - 3 ) { ssize_t has_read = 0; while ( has_read < section->size ) { ssize_t n = read( fd, section->data + 3 + has_read, section->size ); if ( n > 0 ) has_read += n; else break; } section->size += 3; } else { mlt_log_error( NULL, "Section too big - skipped.\n" ); } } close( fd ); return section; } static void load_sections( consumer_cbrts self, mlt_properties properties ) { int n = mlt_properties_count( properties ); // Store the sections with automatic cleanup // and make it easy to iterate over the data sections. mlt_properties si_properties = mlt_properties_get_data( properties, "si.properties", NULL ); if ( !si_properties ) { si_properties = mlt_properties_new(); mlt_properties_set_data( properties, "si.properties", si_properties, 0, (mlt_destructor) mlt_properties_close, NULL ); } while ( n-- ) { // Look for si..file=filename const char *name = mlt_properties_get_name( properties, n ); if ( strncmp( "si.", name, 3 ) == 0 && strncmp( ".file", name + strlen( name ) - 5, 5 ) == 0 ) { size_t len = strlen( name ); char *si_name = strdup( name + 3 ); // unbreak compilation on OpenBSD #ifdef si_pid # undef si_pid #endif char si_pid[len + 1]; si_name[len - 3 - 5] = 0; strcpy( si_pid, "si." ); strcat( si_pid, si_name ); strcat( si_pid, ".pid" ); // Look for si..pid= if ( mlt_properties_get( properties, si_pid ) ) { char *filename = mlt_properties_get_value( properties, n ); ts_section *section = load_section( filename ); if ( section ) { // Determine the periodicity of the section, if supplied char si_time[len + 1]; strcpy( si_time, "si." ); strcat( si_time, si_name ); strcat( si_time, ".time" ); int time = mlt_properties_get_int( properties, si_time ); if ( time == 0 ) time = 200; // Set flags if we are replacing PAT or SDT if ( strncasecmp( "pat", si_name, 3) == 0 ) self->is_si_pat = 1; else if ( strncasecmp( "pmt", si_name, 3) == 0 ) self->is_si_pmt = 1; else if ( strncasecmp( "sdt", si_name, 3) == 0 ) self->is_si_sdt = 1; // Calculate the period and get the PID section->period = ( self->muxrate * time ) / ( TSP_BYTES * 8 * 1000 ); // output one immediately section->packet_count = section->period - 1; mlt_log_verbose( NULL, "SI %s time=%d period=%d file=%s\n", si_name, time, section->period, filename ); section->pid = mlt_properties_get_int( properties, si_pid ); mlt_properties_set_data( si_properties, si_name, section, section->size, free, NULL ); } } free( si_name ); } } } static void write_section( consumer_cbrts self, ts_section *section ) { uint8_t *packet; const uint8_t *data_ptr = section->data; uint8_t *p; int size = section->size; int first, len; while ( size > 0 ) { first = ( section->data == data_ptr ); p = packet = malloc( TSP_BYTES ); *p++ = 0x47; *p = ( section->pid >> 8 ); if ( first ) *p |= 0x40; p++; *p++ = section->pid; *p++ = 0x10; // continuity count will be written later if ( first ) *p++ = 0; /* 0 offset */ len = TSP_BYTES - ( p - packet ); if ( len > size ) len = size; memcpy( p, data_ptr, len ); p += len; /* add known padding data */ len = TSP_BYTES - ( p - packet ); if ( len > 0 ) memset( p, 0xff, len ); mlt_deque_push_back( self->tsp_packets, packet ); self->packet_count++; data_ptr += len; size -= len; } } static void write_sections( consumer_cbrts self ) { mlt_properties properties = mlt_properties_get_data( MLT_CONSUMER_PROPERTIES( &self->parent ), "si.properties", NULL ); if ( properties ) { int n = mlt_properties_count( properties ); while ( n-- ) { ts_section *section = mlt_properties_get_data_at( properties, n, NULL ); if ( ++section->packet_count == section->period ) { section->packet_count = 0; write_section( self, section ); } } } } static uint64_t get_pcr( uint8_t *packet ) { uint64_t pcr = 0; pcr += (uint64_t) packet[6] << 25; pcr += packet[7] << 17; pcr += packet[8] << 9; pcr += packet[9] << 1; pcr += packet[10] >> 7; pcr *= 300; // convert 90KHz to 27MHz pcr += (packet[10] & 0x01) << 8; pcr += packet[11]; return pcr; } static void set_pcr( uint8_t *packet, uint64_t pcr ) { uint64_t pcr_base = pcr / 300; uint64_t pcr_ext = pcr % 300; packet[6] = pcr_base >> 25; packet[7] = pcr_base >> 17; packet[8] = pcr_base >> 9; packet[9] = pcr_base >> 1; packet[10] = (( pcr_base & 1 ) << 7) | 0x7E | (( pcr_ext & 0x100 ) >> 8); packet[11] = pcr_ext; } static uint64_t update_pcr( consumer_cbrts self, uint64_t muxrate, unsigned packets ) { return self->previous_pcr + packets * TSP_BYTES * 8 * SCR_HZ / muxrate; } static uint32_t get_rtp_timestamp( consumer_cbrts self ) { return self->rtp_counter++ * self->udp_packet_size * 8 * RTP_HZ / self->muxrate; } static double measure_bitrate( consumer_cbrts self, uint64_t pcr, int drop ) { double muxrate = 0; if ( self->is_stuffing_set || self->previous_pcr ) { muxrate = ( self->packet_count - self->previous_packet_count - drop ) * TSP_BYTES * 8; if ( pcr >= self->previous_pcr ) muxrate /= (double) ( pcr - self->previous_pcr ) / SCR_HZ; else muxrate /= ( ( (double) ( 1ULL << 33 ) - 1 ) * 300 - self->previous_pcr + pcr ) / SCR_HZ; mlt_log_debug( NULL, "measured TS bitrate %.1f bits/sec PCR %"PRIu64"\n", muxrate, pcr ); } return muxrate; } static int writen( consumer_cbrts self, const void *buf, size_t count ) { int result = 0; int written = 0; while ( written < count ) { if ( ( result = write( self->fd, buf + written, count - written ) ) < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to write: %s\n", strerror( errno ) ); break; } written += result; } return result; } static int sendn( consumer_cbrts self, const void *buf, size_t count ) { int result = 0; #ifdef CBRTS_BSD_SOCKETS int written = 0; while ( written < count ) { result = sendto(self->fd, buf + written, count - written, 0, self->addr->ai_addr, self->addr->ai_addrlen); if ( result < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to send: %s\n", strerror( errno ) ); exit( EXIT_FAILURE ); break; } written += result; } #endif return result; } static int write_udp( consumer_cbrts self, const void *buf, size_t count ) { int result = 0; #ifdef CBRTS_BSD_SOCKETS if ( !self->timer.tv_sec ) clock_gettime( CLOCK_MONOTONIC, &self->timer ); self->femto_counter += self->femto_per_packet; self->timer.tv_nsec += self->femto_counter / 1000000; self->femto_counter = self->femto_counter % 1000000; self->timer.tv_nsec += self->nsec_per_packet; self->timer.tv_sec += self->timer.tv_nsec / 1000000000; self->timer.tv_nsec = self->timer.tv_nsec % 1000000000; clock_nanosleep( CLOCK_MONOTONIC, TIMER_ABSTIME, &self->timer, NULL ); result = sendn( self, buf, count ); #endif return result; } // socket IO code static int create_socket( consumer_cbrts self ) { int result = -1; #ifdef CBRTS_BSD_SOCKETS struct addrinfo hints = {0}; mlt_properties properties = MLT_CONSUMER_PROPERTIES( &self->parent ); const char *hostname = mlt_properties_get( properties, "udp.address" ); const char *port = "1234"; if ( mlt_properties_get( properties, "udp.port" ) ) port = mlt_properties_get( properties, "udp.port" ); // Resolve the address string and port. hints.ai_socktype = SOCK_DGRAM; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_PASSIVE; result = getaddrinfo( hostname, port, &hints, &self->addr ); if ( result < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Error resolving UDP address and port: %s.\n", gai_strerror( result ) ); return result; } // Create the socket descriptor. struct addrinfo *addr = self->addr; for ( ; addr; addr = addr->ai_next ) { result = socket( addr->ai_addr->sa_family, SOCK_DGRAM, addr->ai_protocol ); if ( result != -1 ) { // success self->fd = result; result = 0; break; } } if ( result < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Error creating socket: %s.\n", strerror( errno ) ); freeaddrinfo( self->addr ); self->addr = NULL; return result; } // Set the reuse address socket option if not disabled (explicitly set 0). int reuse = mlt_properties_get_int( properties, "udp.reuse" ) || !mlt_properties_get( properties, "udp.reuse" ); if ( reuse ) { result = setsockopt( self->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) ); if ( result < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Error setting the reuse address socket option.\n" ); close( self->fd ); freeaddrinfo( self->addr ); self->addr = NULL; return result; } } // Set the socket buffer size if supplied. if ( mlt_properties_get( properties, "udp.sockbufsize" ) ) { int sockbufsize = mlt_properties_get_int( properties, "udp.sockbufsize" ); result = setsockopt( self->fd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, sizeof(sockbufsize) ); if ( result < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Error setting the socket buffer size.\n" ); close( self->fd ); freeaddrinfo( self->addr ); self->addr = NULL; return result; } } // Set the multicast TTL if supplied. if ( mlt_properties_get( properties, "udp.ttl" ) ) { int ttl = mlt_properties_get_int( properties, "udp.ttl" ); if ( addr->ai_addr->sa_family == AF_INET ) { result = setsockopt( self->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl) ); } else if ( addr->ai_addr->sa_family == AF_INET6 ) { result = setsockopt( self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl) ); } if ( result < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Error setting the multicast TTL.\n" ); close( self->fd ); freeaddrinfo( self->addr ); self->addr = NULL; return result; } } // Set the multicast interface if supplied. if ( mlt_properties_get( properties, "udp.interface" ) ) { const char *interface = mlt_properties_get( properties, "udp.interface" ); unsigned int iface = if_nametoindex( interface ); if ( iface ) { if ( addr->ai_addr->sa_family == AF_INET ) { struct ip_mreqn req = {{0}}; req.imr_ifindex = iface; result = setsockopt( self->fd, IPPROTO_IP, IP_MULTICAST_IF, &req, sizeof(req) ); } else if ( addr->ai_addr->sa_family == AF_INET6 ) { result = setsockopt( self->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface) ); } if ( result < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Error setting the multicast interface.\n" ); close( self->fd ); freeaddrinfo( self->addr ); self->addr = NULL; return result; } } else { mlt_log_warning( MLT_CONSUMER_SERVICE(&self->parent), "The network interface \"%s\" was not found.\n", interface ); } } #endif return result; } static void *output_thread( void *arg ) { consumer_cbrts self = arg; int result = 0; while ( self->thread_running ) { pthread_mutex_lock( &self->udp_deque_mutex ); while ( self->thread_running && mlt_deque_count( self->udp_packets ) < 1 ) pthread_cond_wait( &self->udp_deque_cond, &self->udp_deque_mutex ); pthread_mutex_unlock( &self->udp_deque_mutex ); // Dequeue the UDP packets and write them. int i = mlt_deque_count( self->udp_packets ); mlt_log_debug( MLT_CONSUMER_SERVICE(&self->parent), "%s: count %d\n", __FUNCTION__, i ); while ( self->thread_running && i-- && result >= 0 ) { pthread_mutex_lock( &self->udp_deque_mutex ); uint8_t *packet = mlt_deque_pop_front( self->udp_packets ); pthread_cond_broadcast( &self->udp_deque_cond ); pthread_mutex_unlock( &self->udp_deque_mutex ); size_t size = self->rtp_ssrc ? RTP_BYTES + self->udp_packet_size : self->udp_packet_size; result = write_udp( self, packet, size ); free( packet ); } } return NULL; } static int enqueue_udp( consumer_cbrts self, const void *buf, size_t count ) { // Append TSP to the UDP packet. memcpy( &self->udp_packet[self->udp_bytes], buf, count ); self->udp_bytes = ( self->udp_bytes + count ) % self->udp_packet_size; // Send the UDP packet. if ( !self->udp_bytes ) { size_t offset = self->rtp_ssrc ? RTP_BYTES : 0; // Duplicate the packet. uint8_t *packet = malloc( self->udp_packet_size + offset ); memcpy( packet + offset, self->udp_packet, self->udp_packet_size ); // Add the RTP header. if ( self->rtp_ssrc ) { // Padding, extension, and CSRC count are all 0. packet[0] = RTP_VERSION << 6; // Marker bit is 0. packet[1] = RTP_PAYLOAD & 0x7f; packet[2] = (self->rtp_sequence >> 8) & 0xff; packet[3] = (self->rtp_sequence >> 0) & 0xff; // Timestamp in next 4 bytes. uint32_t timestamp = get_rtp_timestamp( self ); packet[4] = (timestamp >> 24) & 0xff; packet[5] = (timestamp >> 16) & 0xff; packet[6] = (timestamp >> 8) & 0xff; packet[7] = (timestamp >> 0) & 0xff; // SSRC in next 4 bytes. packet[8] = (self->rtp_ssrc >> 24) & 0xff; packet[9] = (self->rtp_ssrc >> 16) & 0xff; packet[10] = (self->rtp_ssrc >> 8) & 0xff; packet[11] = (self->rtp_ssrc >> 0) & 0xff; self->rtp_sequence++; } // Wait for room in the fifo. pthread_mutex_lock( &self->udp_deque_mutex ); while ( self->thread_running && mlt_deque_count( self->udp_packets ) >= self->udp_buffer_max ) pthread_cond_wait( &self->udp_deque_cond, &self->udp_deque_mutex ); // Add the packet to the fifo. mlt_deque_push_back( self->udp_packets, packet ); pthread_cond_broadcast( &self->udp_deque_cond ); pthread_mutex_unlock( &self->udp_deque_mutex ); } return 0; } static int insert_pcr( consumer_cbrts self, uint16_t pid, uint8_t cc, uint64_t pcr ) { uint8_t packet[ TSP_BYTES ]; uint8_t *p = packet; *p++ = 0x47; *p++ = pid >> 8; *p++ = pid; *p++ = 0x20 | cc; // Adaptation only // Continuity Count field does not increment (see 13818-1 section 2.4.3.3) *p++ = TSP_BYTES - 5; // Adaptation Field Length *p++ = 0x10; // Adaptation flags: PCR present set_pcr( packet, pcr ); p += 6; // 6 pcr bytes memset( p, 0xff, TSP_BYTES - ( p - packet ) ); // stuffing return self->write_tsp( self, packet, TSP_BYTES ); } static int output_cbr( consumer_cbrts self, uint64_t input_rate, uint64_t output_rate, uint64_t *pcr ) { int n = mlt_deque_count( self->tsp_packets ); unsigned output_packets = 0; unsigned packets_since_pcr = 0; int result = 0; int dropped = 0; int warned = 0; uint16_t pcr_pid = 0; uint8_t cc = 0xff; float ms_since_pcr; float ms_to_end; uint64_t input_counter = 0; uint64_t last_input_counter; mlt_log_debug( NULL, "%s: n %i output_counter %"PRIu64" input_rate %"PRIu64"\n", __FUNCTION__, n, self->output_counter, input_rate ); while ( self->thread_running && n-- && result >= 0 ) { uint8_t *packet = mlt_deque_pop_front( self->tsp_packets ); uint16_t pid = PIDOF( packet ); // Check for overflow if ( input_rate > output_rate && !HASPCR( packet ) && pid != SDT_PID && pid != PAT_PID && pid != self->pmt_pid ) { if ( !warned ) { mlt_log_warning( MLT_CONSUMER_SERVICE( &self->parent ), "muxrate too low %"PRIu64" > %"PRIu64"\n", input_rate, output_rate ); warned = 1; } // Skip this packet free( packet ); // Compute new input_rate based on dropped count input_rate = measure_bitrate( self, *pcr, ++dropped ); continue; } if ( HASPCR( packet ) ) { pcr_pid = pid; set_pcr( packet, update_pcr( self, output_rate, output_packets ) ); packets_since_pcr = 0; } // Rewrite the continuity counter if not only adaptation field if ( ADAPTOF( packet ) != 2 ) { packet[3] = ( packet[3] & 0xf0 ) | self->continuity_count[ pid ]; self->continuity_count[ pid ] = ( self->continuity_count[ pid ] + 1 ) & 0xf; } if ( pcr_pid && pid == pcr_pid ) cc = CCOF( packet ); result = self->write_tsp( self, packet, TSP_BYTES ); free( packet ); if ( result < 0 ) break; output_packets++; packets_since_pcr++; self->output_counter += TSP_BYTES * 8 * output_rate; input_counter += TSP_BYTES * 8 * input_rate; // See if we need to output a dummy packet with PCR ms_since_pcr = (float) ( packets_since_pcr + 1 ) * 8 * TSP_BYTES * 1000 / output_rate; ms_to_end = (float) n * 8 * TSP_BYTES * 1000 / input_rate; if ( pcr_pid && ms_since_pcr >= PCR_PERIOD_MS && ms_to_end > PCR_PERIOD_MS / 2.0 ) { uint64_t new_pcr = update_pcr( self, output_rate, output_packets ); if ( ms_since_pcr > 40 ) mlt_log_warning( NULL, "exceeded PCR interval %.2f ms queued %.2f ms\n", ms_since_pcr, ms_to_end ); if ( ( result = insert_pcr( self, pcr_pid, cc, new_pcr ) ) < 0 ) break; packets_since_pcr = 0; output_packets++; input_counter += TSP_BYTES * 8 * input_rate; } // Output null packets as needed while ( self->thread_running && input_counter + ( TSP_BYTES * 8 * input_rate ) <= self->output_counter ) { // See if we need to output a dummy packet with PCR ms_since_pcr = (float) ( packets_since_pcr + 1 ) * 8 * TSP_BYTES * 1000 / output_rate; ms_to_end = (float) n * 8 * TSP_BYTES * 1000 / input_rate; if ( pcr_pid && ms_since_pcr >= PCR_PERIOD_MS && ms_to_end > PCR_PERIOD_MS / 2.0 ) { uint64_t new_pcr = update_pcr( self, output_rate, output_packets ); if ( ms_since_pcr > 40 ) mlt_log_warning( NULL, "exceeded PCR interval %.2f ms queued %.2f ms\n", ms_since_pcr, ms_to_end ); if ( ( result = insert_pcr( self, pcr_pid, cc, new_pcr ) ) < 0 ) break; packets_since_pcr = 0; } else { // Otherwise output a null packet if ( ( result = self->write_tsp( self, null_packet, TSP_BYTES ) ) < 0 ) break; packets_since_pcr++; } output_packets++; // Increment input last_input_counter = input_counter; input_counter += TSP_BYTES * 8 * input_rate; // Handle wrapping if ( last_input_counter > input_counter ) { last_input_counter -= self->output_counter; self->output_counter = 0; input_counter = last_input_counter + TSP_BYTES * 8 * input_rate; } } } // Reset counters leaving a residual output count if ( input_counter < self->output_counter ) self->output_counter -= input_counter; else self->output_counter = 0; // Warn if the PCR interval is too large or small ms_since_pcr = (float) packets_since_pcr * 8 * TSP_BYTES * 1000 / output_rate; if ( ms_since_pcr > 40 ) mlt_log_warning( NULL, "exceeded PCR interval %.2f ms\n", ms_since_pcr ); else if ( ms_since_pcr < PCR_PERIOD_MS / 2.0 ) mlt_log_debug( NULL, "PCR interval too short %.2f ms\n", ms_since_pcr ); // Update the current PCR based on number of packets output *pcr = update_pcr( self, output_rate, output_packets ); return result; } static void get_pmt_pid( consumer_cbrts self, uint8_t *packet ) { // Skip 5 bytes of TSP header + 8 bytes of section header + 2 bytes of service ID uint16_t *p = ( uint16_t* )( packet + 5 + 8 + 2 ); self->pmt_pid = ntohs( p[0] ) & 0x1fff; mlt_log_debug(NULL, "PMT PID 0x%04x\n", self->pmt_pid ); return; } static int remux_packet( consumer_cbrts self, uint8_t *packet ) { mlt_service service = MLT_CONSUMER_SERVICE( &self->parent ); uint16_t pid = PIDOF( packet ); int result = 0; write_sections( self ); // Sanity checks if ( packet[0] != 0x47 ) { mlt_log_panic( service, "NOT SYNC BYTE 0x%02x\n", packet[0] ); exit(1); } if ( pid == NULL_PID ) { mlt_log_panic( service, "NULL PACKET\n" ); exit(1); } // Measure the bitrate between consecutive PCRs if ( HASPCR( packet ) ) { if ( self->pcr_count++ % PCR_SMOOTHING == 0 ) { uint64_t pcr = get_pcr( packet ); double input_rate = measure_bitrate( self, pcr, 0 ); if ( input_rate > 0 ) { self->is_stuffing_set = 1; if ( input_rate > 1.0 ) { result = output_cbr( self, input_rate, self->muxrate, &pcr ); set_pcr( packet, pcr ); } } self->previous_pcr = pcr; self->previous_packet_count = self->packet_count; } } mlt_deque_push_back( self->tsp_packets, packet ); self->packet_count++; return result; } static void start_output_thread( consumer_cbrts self ) { int rtprio = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( &self->parent ), "udp.rtprio" ); self->thread_running = 1; if ( rtprio > 0 ) { // Use realtime priority class struct sched_param priority; pthread_attr_t thread_attributes; pthread_attr_init( &thread_attributes ); priority.sched_priority = rtprio; pthread_attr_setschedpolicy( &thread_attributes, SCHED_FIFO ); 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( &self->output_thread, &thread_attributes, output_thread, self ) < 0 ) { mlt_log_info( MLT_CONSUMER_SERVICE( &self->parent ), "failed to initialize output thread with realtime priority\n" ); pthread_create( &self->output_thread, &thread_attributes, output_thread, self ); } pthread_attr_destroy( &thread_attributes ); } else { // Use normal priority class pthread_create( &self->output_thread, NULL, output_thread, self ); } } static void stop_output_thread( consumer_cbrts self ) { self->thread_running = 0; // Broadcast to the condition in case it's waiting. pthread_mutex_lock( &self->udp_deque_mutex ); pthread_cond_broadcast( &self->udp_deque_cond ); pthread_mutex_unlock( &self->udp_deque_mutex ); // Join the thread. pthread_join( self->output_thread, NULL ); // Release the buffered packets. pthread_mutex_lock( &self->udp_deque_mutex ); int n = mlt_deque_count( self->udp_packets ); while ( n-- ) free( mlt_deque_pop_back( self->udp_packets ) ); pthread_mutex_unlock( &self->udp_deque_mutex ); } static inline int filter_packet( consumer_cbrts self, uint8_t *packet ) { uint16_t pid = PIDOF( packet ); // We are going to keep the existing PMT; replace all other signaling sections. int result = ( pid == NULL_PID ) || ( pid == PAT_PID && self->is_si_pat ) || ( pid == self->pmt_pid && self->is_si_pmt ) || ( pid == SDT_PID && self->is_si_sdt ); // Get the PMT PID from the PAT if ( pid == PAT_PID && !self->pmt_pid ) { get_pmt_pid( self, packet ); if ( self->is_si_pmt ) result = 1; } return result; } static void filter_remux_or_write_packet( consumer_cbrts self, uint8_t *packet ) { int remux = !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( &self->parent ), "noremux" ); // Filter out packets if ( remux ) { if ( !filter_packet( self, packet ) ) remux_packet( self, packet ); else free( packet ); } else { self->write_tsp( self, packet, TSP_BYTES ); free( packet ); } } static void on_data_received( mlt_properties properties, mlt_consumer consumer, uint8_t *buf, int size ) { if ( size > 0 ) { consumer_cbrts self = (consumer_cbrts) consumer->child; // Sanity check if ( self->leftover_size == 0 && buf[0] != 0x47 ) { mlt_log_verbose(MLT_CONSUMER_SERVICE( consumer ), "NOT SYNC BYTE 0x%02x\n", buf[0] ); while ( size && buf[0] != 0x47 ) { buf++; size--; } if ( size <= 0 ) exit(1); } // Enqueue the packets int num_packets = ( self->leftover_size + size ) / TSP_BYTES; int remaining = ( self->leftover_size + size ) % TSP_BYTES; uint8_t *packet = NULL; int i; // mlt_log_verbose( MLT_CONSUMER_SERVICE(consumer), "%s: packets %d remaining %i\n", __FUNCTION__, num_packets, self->leftover_size ); if ( self->leftover_size ) { packet = malloc( TSP_BYTES ); memcpy( packet, self->leftover_data, self->leftover_size ); memcpy( packet + self->leftover_size, buf, TSP_BYTES - self->leftover_size ); buf += TSP_BYTES - self->leftover_size; --num_packets; filter_remux_or_write_packet( self, packet ); } for ( i = 0; i < num_packets; i++, buf += TSP_BYTES ) { packet = malloc( TSP_BYTES ); memcpy( packet, buf, TSP_BYTES ); filter_remux_or_write_packet( self, packet ); } self->leftover_size = remaining; memcpy( self->leftover_data, buf, self->leftover_size ); if ( !self->thread_running ) start_output_thread( self ); mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: %p 0x%x (%d)\n", __FUNCTION__, buf, *buf, size % TSP_BYTES ); // Do direct output // result = self->write_tsp( self, buf, size ); } } static int consumer_start( mlt_consumer parent ) { consumer_cbrts self = parent->child; if ( !self->running ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); mlt_properties avformat = MLT_CONSUMER_PROPERTIES(self->avformat); // Cleanup after a possible abort consumer_stop( parent ); // Pass properties down mlt_properties_pass( avformat, properties, "" ); mlt_properties_set_data( avformat, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL ); mlt_properties_set_data( avformat, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL ); mlt_properties_set_int( avformat, "put_mode", 1 ); mlt_properties_set_int( avformat, "real_time", -1 ); mlt_properties_set_int( avformat, "buffer", 2 ); mlt_properties_set_int( avformat, "terminate_on_pause", 0 ); mlt_properties_set_int( avformat, "muxrate", 1 ); mlt_properties_set_int( avformat, "redirect", 1 ); mlt_properties_set( avformat, "f", "mpegts" ); self->dropped = 0; self->fd = STDOUT_FILENO; self->write_tsp = writen; self->muxrate = mlt_properties_get_int64( MLT_CONSUMER_PROPERTIES(&self->parent), "muxrate" ); if ( mlt_properties_get( properties, "udp.address" ) ) { if ( create_socket( self ) >= 0 ) { int is_rtp = 1; if ( mlt_properties_get( properties, "udp.rtp" ) ) is_rtp = !!mlt_properties_get_int( properties, "udp.rtp" ); if ( is_rtp ) { self->rtp_ssrc = mlt_properties_get_int( properties, "udp.rtp_ssrc" ); while ( !self->rtp_ssrc ) self->rtp_ssrc = (uint32_t) rand(); self->rtp_counter = (uint32_t) rand(); } self->udp_packet_size = mlt_properties_get_int( properties, "udp.nb_tsp" ) * TSP_BYTES; if ( self->udp_packet_size <= 0 || self->udp_packet_size > UDP_MTU ) self->udp_packet_size = 7 * TSP_BYTES; #ifdef CBRTS_BSD_SOCKETS self->nsec_per_packet = 1000000000UL * self->udp_packet_size * 8 / self->muxrate; self->femto_per_packet = 1000000000000000ULL * self->udp_packet_size * 8 / self->muxrate - self->nsec_per_packet * 1000000; #endif self->udp_buffer_max = mlt_properties_get_int( properties, "udp.buffer" ); if ( self->udp_buffer_max < UDP_BUFFER_MINIMUM ) self->udp_buffer_max = UDP_BUFFER_DEFAULT; self->write_tsp = enqueue_udp; } } // Load the DVB PSI/SI sections load_sections( self, properties ); // Start the FFmpeg consumer and our thread mlt_consumer_start( self->avformat ); pthread_create( &self->thread, NULL, consumer_thread, self ); self->running = 1; self->joined = 0; } return 0; } static int consumer_stop( mlt_consumer parent ) { // Get the actual object consumer_cbrts self = parent->child; if ( !self->joined ) { 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 threads and clean up self->running = 0; #ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); self->joined = 1; if ( self->avformat ) mlt_consumer_stop( self->avformat ); stop_output_thread( self ); if ( self->fd > 1 ) close( self->fd ); if ( app_locked && lock ) lock( ); } return 0; } static int consumer_is_stopped( mlt_consumer parent ) { consumer_cbrts self = parent->child; return !self->running; } static void *consumer_thread( void *arg ) { // Identify the arg consumer_cbrts self = arg; // Get the consumer and producer mlt_consumer consumer = &self->parent; // internal initialization mlt_frame frame = NULL; int last_position = -1; // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame( consumer ); // Ensure that we have a frame if ( self->running && frame ) { if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "rendered" ) != 1 ) { mlt_frame_close( frame ); mlt_log_warning( MLT_CONSUMER_SERVICE(consumer), "dropped frame %d\n", ++self->dropped ); continue; } // Get the speed of the frame double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ); // Optimisation to reduce latency if ( speed == 1.0 ) { if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) mlt_consumer_purge( self->avformat ); last_position = mlt_frame_get_position( frame ); } else { //mlt_consumer_purge( this->play ); last_position = -1; } mlt_consumer_put_frame( self->avformat, frame ); // Setup event listener as a callback from consumer avformat if ( !self->event_registered ) self->event_registered = mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->avformat ), consumer, "avformat-write", (mlt_listener) on_data_received ); } else { if ( frame ) mlt_frame_close( frame ); mlt_consumer_put_frame( self->avformat, NULL ); self->running = 0; } } return NULL; } /** Callback to allow override of the close method. */ static void consumer_close( mlt_consumer parent ) { // Get the actual object consumer_cbrts self = parent->child; // Stop the consumer mlt_consumer_stop( parent ); // Close the child consumers mlt_consumer_close( self->avformat ); // Now clean up the rest mlt_deque_close( self->tsp_packets ); mlt_deque_close( self->udp_packets ); mlt_consumer_close( parent ); // Finally clean up this free( self ); } mlt-6.20.0/src/modules/plusgpl/consumer_cbrts.yml000066400000000000000000000071131362234133600221000ustar00rootroot00000000000000schema_version: 0.3 type: consumer identifier: cbrts title: CBR MPEG2-TS version: 2 copyright: Copyright (C) 2010-2015 Broadcasting Center Europe S.A. http://www.bce.lu license: GPLv2 language: en creator: Dan Dennedy tags: - Audio - Video description: Constant bit-rate MPEG-2 Transport Stream notes: | All properties, except some key operational properties such as real_time and terminate_on_pause, set on the this consumer are passed onto an encapsulated avformat consumer - no special prefix required. While some avformat properties can accept a "k" suffix, this consumer requires "muxrate" but does not understand the "k" suffix; so, specify the value in bytes per second. The stream is always output to STDOUT at this time. You can rewrite and insert table sections into the transport stream. If you choose to rewrite the PMT sections, then you need to know how libavformat sets the PIDs on the elementary streams. Currently, the video stream is 256 (0x100) and audio streams start at 257, incrementing from there. There are conventions for property names to pass the .sec files to the consumer. The conventions are: si.
.file= si.
.pid= si.
.time=
is really anything, but typically: pat, sdt, nit, eit, etc. "pat," "pmt," and "sdt" are special such that when supplied, they cause libavformat's corresponding sections to be filtered out and replaced with yours. You should always use PID 16 for NIT, 17 for SDT, and of course, 0 for PAT; PMT may be anything, but libavformat uses 4095 (0xfff). The time property indicates the frequency to insert the section - every N milliseconds. parameters: - identifier: muxrate type: integer unit: bytes/second - identifier: udp.rtprio title: Real-time priority description: > When set to a valid value, this makes the network output thread run with a real-time policy and priority where 1 is lowest and 99 is highest. type: integer minimum: 1 maximum: 99 - identifier: udp.address title: UDP address description: > If an IP address is provided, the stream is sent over UDP instead of STDOUT. type: string - identifier: udp.port title: UDP port type: integer minimum: 0 default: 1234 - identifier: udp.ttl title: Multicast TTL description: > The multicast time-to-live value controls how many routing hops the multicast will survive. type: integer minimum: 0 maximum: 255 - identifier: udp.reuse title: Reuse socket address description: > When not supplied, the socket is opened with the reuse address option. Set this to 0 to disable that. type: boolean default: 1 - identifier: udp.sockbufsize title: Socket buffer size type: integer minimum: 1 unit: bytes - identifier: udp.nb_tsp title: TS packets per UDP packet type: integer minimum: 0 maximum: 7 default: 7 - identifier: udp.buffer title: Max buffer IP packets type: integer minimum: 100 default: 1000 - identifier: udp.rtp title: Use RTP type: boolean default: 1 - identifier: udp.rtp_ssrc title: RTP synchronization source type: integer description: The default is a random number, but you can override it. - identifier: udp.interface title: Multicast interface name description: > Normally the multicast interface is selected by the IP routing table configured on the system, but this might be more convenient. It takes a name like "eth0". type: string mlt-6.20.0/src/modules/plusgpl/factory.c000066400000000000000000000050351362234133600201410ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2008-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include extern mlt_consumer consumer_cbrts_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_burn_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_lumaliftgaingamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_telecide_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/plusgpl/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "cbrts", consumer_cbrts_init ); MLT_REGISTER( filter_type, "BurningTV", filter_burn_init ); MLT_REGISTER( filter_type, "burningtv", filter_burn_init ); MLT_REGISTER( filter_type, "lumaliftgaingamma", filter_lumaliftgaingamma_init ); MLT_REGISTER( filter_type, "rotoscoping", filter_rotoscoping_init ); MLT_REGISTER( filter_type, "telecide", filter_telecide_init ); MLT_REGISTER_METADATA( consumer_type, "cbrts", metadata, "consumer_cbrts.yml" ); MLT_REGISTER_METADATA( filter_type, "BurningTV", metadata, "filter_burningtv.yml" ); MLT_REGISTER_METADATA( filter_type, "burningtv", metadata, "filter_burningtv.yml" ); MLT_REGISTER_METADATA( filter_type, "lumaliftgaingamma", metadata, "filter_lumaliftgaingamma.yml" ); MLT_REGISTER_METADATA( filter_type, "rotoscoping", metadata, "filter_rotoscoping.yml" ); } mlt-6.20.0/src/modules/plusgpl/filter_burn.c000066400000000000000000000142131362234133600210030ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "utils.h" #define MaxColor 120 #define Decay 15 #define MAGIC_THRESHOLD "50" static RGB32 palette[256]; static void makePalette(void) { int i, r, g, b; uint8_t *p = (uint8_t*) palette; for(i=0; i> 8)) | c; i++; } i += 2; } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter mlt_frame_push_service( frame, filter ); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "foreground", "0" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "threshold", MAGIC_THRESHOLD ); } if (!palette[128]) { makePalette(); } return filter; } mlt-6.20.0/src/modules/plusgpl/filter_burningtv.yml000066400000000000000000000003631362234133600224330ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: burningtv title: BurningTV version: 1 copyright: FUKUCHI Kentaro, Stephane Fillod creator: FUKUCHI Kentaro, Stephane Fillod contributor: - Jan Sorensen license: GPLv2 language: en tags: - Video mlt-6.20.0/src/modules/plusgpl/filter_lumaliftgaingamma.c000066400000000000000000000104401362234133600235120ustar00rootroot00000000000000/* * filter_lumaliftgaingamma.c -- Lift Gain Gamma filter for luma correction * Copyright (C) 2014 Janne Liljeblad * Author: Janne Liljeblad * * This filter is a port from Gimp and is distributed under a compatible license. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include static double clamp( double value, double low, double high) { if (value < low) value = low; if (value > high) value = high; return value; } static double lut_value( double value, double lift, double gain, double gamma ) { double nvalue; double power; value = clamp( value + lift, 0, 1 ); if( gain < 0.0) value = value * (1.0 + gain); else value = value + ((1.0 - value) * gain); if(gamma < 0.0) { if (value > 0.5) nvalue = 1.0 - value; else nvalue = value; if (nvalue < 0.0) nvalue = 0.0; nvalue = 0.5 * pow (nvalue * 2.0 , (double) (1.0 + gamma)); if (value > 0.5) value = 1.0 - nvalue; else value = nvalue; } else { if (value > 0.5) nvalue = 1.0 - value; else nvalue = value; if (nvalue < 0.0) nvalue = 0.0; power = (gamma == 1.0) ? 127 : 1.0 / (1.0 - gamma); nvalue = 0.5 * pow (2.0 * nvalue, power); if (value > 0.5) value = 1.0 - nvalue; else value = nvalue; } return value; } static void fill_lgg_lut(int lgg_lut[], double lift, double gain, double gamma) { int i; double val; for( i = 0; i < 256; i++ ) { val = (double) i / 255.0; lgg_lut[ i ] = (int) (lut_value( val, lift, gain, gamma ) * 255.0); } } /** Do image filtering. */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { // Get the image mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); *format = mlt_image_rgb24; int error = mlt_frame_get_image( frame, image, format, width, height, 0 ); // Only process if we have no error and a valid colour space if ( error == 0 ) { // Get values and force accepted ranges double lift = mlt_properties_anim_get_double( properties, "lift", position, length ); double gain = mlt_properties_anim_get_double( properties, "gain", position, length ); double gamma = mlt_properties_anim_get_double( properties, "gamma", position, length ); lift = clamp( lift, -0.5, 0.5 ); gain = clamp( gain, -0.5, 0.5 ); gamma = clamp( gamma, -1.0, 1.0 ); // Build lut int lgg_lut[256]; fill_lgg_lut( lgg_lut, lift, gain, gamma); // Filter int i = *width * *height + 1; uint8_t *p = *image; uint8_t *r = *image; while ( --i ) { *p ++ = lgg_lut[ *r ++ ]; *p ++ = lgg_lut[ *r ++ ]; *p ++ = lgg_lut[ *r ++ ]; } } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { // Push the frame filter mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ mlt_filter filter_lumaliftgaingamma_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = filter_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "lift", "0" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "gain", "0" ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "gamma", "0" ); } return filter; } mlt-6.20.0/src/modules/plusgpl/filter_lumaliftgaingamma.yml000066400000000000000000000016241362234133600240750ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: lumaliftgaingamma title: LumaLiftGainGamma version: 1 copyright: Janne Liljeblad creator: Janne Liljeblad license: GPL language: en tags: - Video description: > Filter can be used to apply lift, gain and gamma correction to luma values of image. parameters: - identifier: lift title: Lift type: float minimum: -0.5 maximum: 0.5 default: 0 description: > Adds a value computed using parameter value to color channel values. - identifier: gain title: Gain type: float minimum: -0.5 maximum: 0.5 default: 0 description: > Multiplies color channel values by value computed using parameter value. - identifier: gamma title: Gamma type: float minimum: -1.0 maximum: 1.0 default: 0 description: > Applies a gamma correction to all color channel values. mlt-6.20.0/src/modules/plusgpl/filter_rotoscoping.c000066400000000000000000000512471362234133600224130ustar00rootroot00000000000000/* * rotoscoping.c -- keyframable vector based rotoscoping * Copyright (C) 2011 Till Theato * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "cJSON.h" #include #include #include #include #define SQR( x ) ( x ) * ( x ) /** x, y tuple with double precision */ typedef struct PointF { double x; double y; } PointF; typedef struct BPointF { struct PointF h1; struct PointF p; struct PointF h2; } BPointF; enum MODES { MODE_RGB, MODE_ALPHA, MODE_LUMA }; const char *MODESTR[3] = { "rgb", "alpha", "luma" }; enum ALPHAOPERATIONS { ALPHA_CLEAR, ALPHA_MAX, ALPHA_MIN, ALPHA_ADD, ALPHA_SUB }; const char *ALPHAOPERATIONSTR[5] = { "clear", "max", "min", "add", "sub" }; /** Returns the index of \param string in \param stringList. * Useful for assigning string parameters to enums. */ static int stringValue( const char *string, const char **stringList, int max ) { int i; for ( i = 0; i < max; i++ ) if ( strcmp( stringList[i], string ) == 0 ) return i; return 0; } /** Sets "spline_is_dirty" to 1 if property "spline" was changed. * We then know when to parse the json stored in "spline" */ static void rotoPropertyChanged( mlt_service owner, mlt_filter this, char *name ) { if ( !strcmp( name, "spline" ) ) mlt_properties_set_int( MLT_FILTER_PROPERTIES( this ), "_spline_is_dirty", 1 ); } /** Linear interp */ static inline void lerp( const PointF *a, const PointF *b, PointF *result, double t ) { result->x = a->x + ( b->x - a->x ) * t; result->y = a->y + ( b->y - a->y ) * t; } /** Linear interp. with t = 0.5 * Speed gain? */ static inline void lerpHalf( const PointF *a, const PointF *b, PointF *result ) { result->x = ( a->x + b->x ) * .5; result->y = ( a->y + b->y ) * .5; } /** Helper for using qsort with an array of integers. */ int ncompare( const void *a, const void *b ) { return *(const int*)a - *(const int*)b; } /** Turns a json array with two children into a point (x, y tuple). */ static void jsonGetPoint( cJSON *json, PointF *point ) { if ( cJSON_GetArraySize( json ) == 2 ) { point->x = json->child->valuedouble; point->y = json->child->next->valuedouble; } } /** * Turns the array of json elements into an array of Bézier points. * \param array cJSON array. values have to be Bézier points: handle 1, point , handl2 * ( [ [ [h1x, h1y], [px, py], [h2x, h2y] ], ... ] ) * \param points pointer to array of points. Will be allocated and filled with the points in \param array * \return number of points */ static int json2BCurves( cJSON *array, BPointF **points ) { int count = cJSON_GetArraySize( array ); cJSON *child = array->child; *points = mlt_pool_alloc( count * sizeof( BPointF ) ); int i = 0; do { if ( child && cJSON_GetArraySize( child ) == 3 ) { jsonGetPoint( child->child , &(*points)[i].h1 ); jsonGetPoint( child->child->next, &(*points)[i].p ); jsonGetPoint( child->child->next->next, &(*points)[i].h2 ); i++; } } while ( child && ( child = child->next ) ); if ( i < count ) *points = mlt_pool_realloc( *points, i * sizeof( BPointF ) ); return i; } /** Blurs \param src horizontally. \See function blur. */ static void blurHorizontal( uint8_t *src, uint8_t *dst, int width, int height, int radius) { int x, y, kx, yOff, total, amount, amountInit; amountInit = radius * 2 + 1; for (y = 0; y < height; ++y) { total = 0; yOff = y * width; // Process entire window for first pixel int size = MIN(radius + 1, width); for ( kx = 0; kx < size; ++kx ) total += src[yOff + kx]; dst[yOff] = total / ( radius + 1 ); // Subsequent pixels just update window total for ( x = 1; x < width; ++x ) { amount = amountInit; // Subtract pixel leaving window if ( x - radius - 1 >= 0 ) total -= src[yOff + x - radius - 1]; else amount -= radius - x; // Add pixel entering window if ( x + radius < width ) total += src[yOff + x + radius]; else amount -= radius - width + x; dst[yOff + x] = total / amount; } } } /** Blurs \param src vertically. \See function blur. */ static void blurVertical( uint8_t *src, uint8_t *dst, int width, int height, int radius) { int x, y, ky, total, amount, amountInit; amountInit = radius * 2 + 1; for (x = 0; x < width; ++x) { total = 0; int size = MIN(radius + 1, height); for ( ky = 0; ky < size; ++ky ) total += src[x + ky * width]; dst[x] = total / ( radius + 1 ); for ( y = 1; y < height; ++y ) { amount = amountInit; if ( y - radius - 1 >= 0 ) total -= src[( y - radius - 1 ) * width + x]; else amount -= radius - y; if ( y + radius < height ) total += src[( y + radius ) * width + x]; else amount -= radius - height + y; dst[y * width + x] = total / amount; } } } /** * Blurs the \param map using a simple "average" blur. * \param map Will be blurred; 1bpp * \param width x dimension of channel stored in \param map * \param height y dimension of channel stored in \param map * \param radius blur radius * \param passes blur passes */ static void blur( uint8_t *map, int width, int height, int radius, int passes ) { uint8_t *src = mlt_pool_alloc( width * height ); uint8_t *tmp = mlt_pool_alloc( width * height ); int i; for ( i = 0; i < passes; ++i ) { memcpy( src, map, width * height ); blurHorizontal( src, tmp, width, height, radius ); blurVertical( tmp, map, width, height, radius ); } mlt_pool_release(src); mlt_pool_release(tmp); } /** * Determines which points are located in the polygon and sets their value in \param map to \param value * \param vertices points defining the polygon * \param count number of vertices * \param with x range * \param height y range * \param value value identifying points in the polygon * \param map array of integers of the dimension width * height. * The map entries belonging to the points in the polygon will be set to \param set * 255 the others to !set * 255. */ static void fillMap( PointF *vertices, int count, int width, int height, int invert, uint8_t *map ) { int nodes, nodeX[1024], pixelY, i, j, value; value = !invert * 255; memset( map, invert * 255, width * height ); // Loop through the rows of the image for ( pixelY = 0; pixelY < height; pixelY++ ) { /* * Build a list of nodes. * nodes are located at the borders of the polygon * and therefore indicate a move from in to out or vice versa */ nodes = 0; for ( i = 0, j = count - 1; i < count; j = i++ ) if ( (vertices[i].y > (double)pixelY) != (vertices[j].y > (double)pixelY) ) nodeX[nodes++] = (int)(vertices[i].x + (pixelY - vertices[i].y) / (vertices[j].y - vertices[i].y) * (vertices[j].x - vertices[i].x) ); qsort( nodeX, nodes, sizeof( int ), ncompare ); // Set map values for points between the node pairs to 1 for ( i = 0; i < nodes; i += 2 ) { if ( nodeX[i] >= width ) break; if ( nodeX[i+1] > 0 ) { nodeX[i] = MAX( 0, nodeX[i] ); nodeX[i+1] = MIN( nodeX[i+1], width ); memset( map + width * pixelY + nodeX[i], value, nodeX[i+1] - nodeX[i] ); } } } } /** Determines the point in the middle of the Bézier curve (t = 0.5) defined by \param p1 and \param p2 * using De Casteljau's algorithm. */ static void deCasteljau( BPointF *p1, BPointF *p2, BPointF *mid ) { struct PointF ab, bc, cd; lerpHalf( &(p1->p), &(p1->h2), &ab ); lerpHalf( &(p1->h2), &(p2->h1), &bc ); lerpHalf( &(p2->h1), &(p2->p), &cd ); lerpHalf( &ab, &bc, &(mid->h1) ); // mid->h1 = abbc lerpHalf( &bc, &cd, &(mid->h2) ); // mid->h2 = bccd lerpHalf( &(mid->h1), &(mid->h2), &(mid->p) ); p1->h2 = ab; p2->h1 = cd; } /** * Calculates points for the cubic Bézier curve defined by \param p1 and \param p2. * Points are calculated until the squared distanced between neighbour points is smaller than 2. * \param points Pointer to list of points. Will be allocated and filled with calculated points. * \param count Number of calculated points in \param points * \param size Allocated size of \param points (in elements not in bytes) */ static void curvePoints( BPointF p1, BPointF p2, PointF **points, int *count, int *size ) { double errorSqr = SQR( p1.p.x - p2.p.x ) + SQR( p1.p.y - p2.p.y ); if ( *count + 1 >= *size ) { *size += (int)sqrt( errorSqr / 2 ) + 1; *points = mlt_pool_realloc( *points, *size * sizeof ( struct PointF ) ); } (*points)[(*count)++] = p1.p; if ( errorSqr <= 2 ) return; BPointF mid; deCasteljau( &p1, &p2, &mid ); curvePoints( p1, mid, points, count, size ); curvePoints( mid, p2, points, count, size ); (*points)[*(count)++] = p2.p; } /** Do it :-). */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_properties unique = mlt_frame_pop_service( frame ); int mode = mlt_properties_get_int( unique, "mode" ); // Get the image if ( mode == MODE_RGB ) *format = mlt_image_rgb24; int error = mlt_frame_get_image( frame, image, format, width, height, writable ); // Only process if we have no error and a valid colour space if ( !error ) { BPointF *bpoints; struct PointF *points; int bcount, length, count, size, i, j; bpoints = mlt_properties_get_data( unique, "points", &length ); bcount = length / sizeof( BPointF ); for ( i = 0; i < bcount; i++ ) { // map to image dimensions bpoints[i].h1.x *= *width; bpoints[i].p.x *= *width; bpoints[i].h2.x *= *width; bpoints[i].h1.y *= *height; bpoints[i].p.y *= *height; bpoints[i].h2.y *= *height; } count = 0; size = 1; points = mlt_pool_alloc( size * sizeof( struct PointF ) ); for ( i = 0; i < bcount; i++ ) { j = (i + 1) % bcount; curvePoints( bpoints[i], bpoints[j], &points, &count, &size ); } if ( count ) { length = *width * *height; uint8_t *map = mlt_pool_alloc( length ); int invert = mlt_properties_get_int( unique, "invert" ); fillMap( points, count, *width, *height, invert, map ); int feather = mlt_properties_get_int( unique, "feather" ); if ( feather && mode != MODE_RGB ) blur( map, *width, *height, feather, mlt_properties_get_int( unique, "feather_passes" ) ); int bpp; size = mlt_image_format_size( *format, *width, *height - 1, &bpp ); // mlt_image_format_size increments height! uint8_t *p = *image; uint8_t *q = *image + size; i = 0; uint8_t *alpha; switch ( mode ) { case MODE_RGB: // *format == mlt_image_rgb24 while ( p != q ) { if ( !map[i++] ) p[0] = p[1] = p[2] = 0; p += 3; } break; case MODE_LUMA: switch ( *format ) { case mlt_image_rgb24: case mlt_image_rgb24a: case mlt_image_opengl: while ( p != q ) { p[0] = p[1] = p[2] = map[i++]; p += bpp; } break; case mlt_image_yuv422: while ( p != q ) { p[0] = map[i++]; p[1] = 128; p += 2; } break; case mlt_image_yuv420p: memcpy( p, map, length ); memset( p + length, 128, length / 2 ); break; default: break; } break; case MODE_ALPHA: switch ( *format ) { case mlt_image_rgb24a: case mlt_image_opengl: switch ( mlt_properties_get_int( unique, "alpha_operation" ) ) { case ALPHA_CLEAR: while ( p != q ) { p[3] = map[i++]; p += 4; } break; case ALPHA_MAX: while ( p != q ) { p[3] = MAX( p[3], map[i] ); p += 4; i++; } break; case ALPHA_MIN: while ( p != q ) { p[3] = MIN( p[3], map[i] ); p += 4; i++; } break; case ALPHA_ADD: while ( p != q ) { p[3] = MIN( p[3] + map[i], 255 ); p += 4; i++; } break; case ALPHA_SUB: while ( p != q ) { p[3] = MAX( p[3] - map[i], 0 ); p += 4; i++; } break; } break; default: alpha = mlt_frame_get_alpha_mask( frame ); switch ( mlt_properties_get_int( unique, "alpha_operation" ) ) { case ALPHA_CLEAR: memcpy( alpha, map, length ); break; case ALPHA_MAX: for ( ; i < length; i++, alpha++ ) *alpha = MAX( map[i], *alpha ); break; case ALPHA_MIN: for ( ; i < length; i++, alpha++ ) *alpha = MIN( map[i], *alpha ); break; case ALPHA_ADD: for ( ; i < length; i++, alpha++ ) *alpha = MIN( *alpha + map[i], 255 ); break; case ALPHA_SUB: for ( ; i < length; i++, alpha++ ) *alpha = MAX( *alpha - map[i], 0 ); break; } break; } break; } mlt_pool_release( map ); } mlt_pool_release( points ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); int splineIsDirty = mlt_properties_get_int( properties, "_spline_is_dirty" ); char *modeStr = mlt_properties_get( properties, "mode" ); cJSON *root = mlt_properties_get_data( properties, "_spline_parsed", NULL ); if ( splineIsDirty || root == NULL ) { // we need to (re-)parse char *spline = mlt_properties_get( properties, "spline" ); root = cJSON_Parse( spline ); mlt_properties_set_data( properties, "_spline_parsed", root, 0, (mlt_destructor)cJSON_Delete, NULL ); mlt_properties_set_int( properties, "_spline_is_dirty", 0 ); } if ( root == NULL ) return frame; BPointF *points; int count, i; if ( root->type == cJSON_Array ) { /* * constant */ count = json2BCurves( root, &points ); } else if ( root->type == cJSON_Object ) { /* * keyframes */ mlt_position time, pos1, pos2; time = mlt_frame_get_position( frame ); cJSON *keyframe = root->child; cJSON *keyframeOld = keyframe; if ( !keyframe ) return frame; while ( atoi( keyframe->string ) < time && keyframe->next ) { keyframeOld = keyframe; keyframe = keyframe->next; } pos1 = atoi( keyframeOld->string ); pos2 = atoi( keyframe->string ); if ( pos1 >= pos2 || time >= pos2 ) { // keyframes in wrong order or before first / after last keyframe count = json2BCurves( keyframe, &points ); } else { /* * pos1 < time < pos2 */ BPointF *p1, *p2; int c1 = json2BCurves( keyframeOld, &p1 ); int c2 = json2BCurves( keyframe, &p2 ); // range 0-1 double position = ( time - pos1 ) / (double)( pos2 - pos1 ); count = MIN( c1, c2 ); // additional points are ignored points = mlt_pool_alloc( count * sizeof( BPointF ) ); for ( i = 0; i < count; i++ ) { lerp( &(p1[i].h1), &(p2[i].h1), &(points[i].h1), position ); lerp( &(p1[i].p), &(p2[i].p), &(points[i].p), position ); lerp( &(p1[i].h2), &(p2[i].h2), &(points[i].h2), position ); } mlt_pool_release( p1 ); mlt_pool_release( p2 ); } } else { return frame; } mlt_properties unique = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( filter ) ); mlt_properties_set_data( unique, "points", points, count * sizeof( BPointF ), (mlt_destructor)mlt_pool_release, NULL ); mlt_properties_set_int( unique, "mode", stringValue( modeStr, MODESTR, 3 ) ); mlt_properties_set_int( unique, "alpha_operation", stringValue( mlt_properties_get( properties, "alpha_operation" ), ALPHAOPERATIONSTR, 5 ) ); mlt_properties_set_int( unique, "invert", mlt_properties_get_int( properties, "invert" ) ); mlt_properties_set_int( unique, "feather", mlt_properties_get_int( properties, "feather" ) ); mlt_properties_set_int( unique, "feather_passes", mlt_properties_get_int( properties, "feather_passes" ) ); mlt_frame_push_service( frame, unique ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if ( filter ) { filter->process = filter_process; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set( properties, "mode", "alpha" ); mlt_properties_set( properties, "alpha_operation", "clear" ); mlt_properties_set_int( properties, "invert", 0 ); mlt_properties_set_int( properties, "feather", 0 ); mlt_properties_set_int( properties, "feather_passes", 1 ); if ( arg ) mlt_properties_set( properties, "spline", arg ); mlt_events_listen( properties, filter, "property-changed", (mlt_listener)rotoPropertyChanged ); } return filter; } mlt-6.20.0/src/modules/plusgpl/filter_rotoscoping.yml000066400000000000000000000051421362234133600227630ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: rotoscoping title: Rotoscoping copyright: Copyright (C) 2011 Till Theato version: 0.3 license: GPL language: en url: none creator: Till Theato tags: - Video description: Keyframable vector based rotoscoping bugs: - in some cases top most row in polygon is assigned to outside parameters: - identifier: mode title: Mode type: string description: How to visualize the area described by the spline readonly: no required: no default: alpha mutable: yes widget: dropdown values: - alpha - luma - rgb - identifier: alpha_operation title: Alpha Operation type: string description: | How to proceed with the current alpha mask (only if mode = alpha). clear = existing alpha mask is overwritten max = maximum: existing alpha mask, mask generated by this filter min = minimum: existing alpha mask, mask generated by this filter add = existing alpha mask + generated mask sub = existing alpha mask - generated mask readonly: no required: no default: clear mutable: yes widget: dropdown values: - clear - max - min - add - sub - identifier: invert title: Invert type: integer description: use area inside of spline (0) or the outside (1) readonly: no required: no minimum: 0 maximum: 1 default: 0 mutable: yes widget: checkbox - identifier: feather title: Feather type: integer description: amount of feathering (radius of "average" blur applied on mask) readonly: no required: no minimum: 0 maximum: 1000 # no real limit default: 0 mutable: yes widget: spinner - identifier: feather_passes title: Feathering passes type: integer description: number of blur (feathering) passes readonly: no required: no minimum: 1 maximum: 1000 # no real limit default: 1 mutable: yes widget: spinner - identifier: spline title: Spline type: string description: > The spline, a list of cubic Bézier curves, is described using JSON. The most basic parts are the coordinate tuples: [x, y]; x,y will be mapped from the range 0-1 to the image dimensions. Next layer are the Bézier points: [handle 1, point, handle 2] with handle 1, point, handle 2 being coordinate tuples The spline is a list of Bézier points. Optionally keyframes can be defined as a object of frame values, relative to the filter's in point, assigned to splines. readonly: no required: yes mutable: yes mlt-6.20.0/src/modules/plusgpl/filter_telecide.c000066400000000000000000001043141362234133600216150ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #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 *dstp, *finalp; 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. 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;) { index = (y/BLKSIZE) * cx->xblocks + x/BLKSIZE_TIMES2; // Test combination with current frame. tmp1 = ((long)currbot0[x] + (long)currbot2[x]); diff = labs((((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 = labs((((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 ( 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" ); } // 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_filter_get_position( filter, frame ); uint8_t *image_copy = mlt_pool_alloc( image_size ); memcpy( image_copy, *image, image_size ); char key[20]; sprintf( key, MLT_POSITION_FMT, 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, MLT_POSITION_FMT, 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->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 * ( 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 * ( 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_frame_set_image( frame, final, image_size, mlt_pool_release ); 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, MLT_POSITION_FMT, 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 " MLT_POSITION_FMT "\n", __FUNCTION__, *width * *height * 3/2, mlt_frame_get_position(frame) ); } return error; } /** Process the frame object. */ static mlt_frame process( mlt_filter filter, mlt_frame frame ) { // Push the filter on to the stack mlt_frame_push_service( frame, filter ); // 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 filter = mlt_filter_new( ); if ( filter != NULL ) { filter->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( filter ); 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 filter; } mlt-6.20.0/src/modules/plusgpl/gpl000066400000000000000000000000001362234133600170160ustar00rootroot00000000000000mlt-6.20.0/src/modules/plusgpl/image.c000066400000000000000000000136051362234133600175560ustar00rootroot00000000000000/* * 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 __APPLE__ 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; } mlt-6.20.0/src/modules/plusgpl/utils.h000066400000000000000000000037351362234133600176440ustar00rootroot00000000000000/* * 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 __APPLE__ 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__ */ mlt-6.20.0/src/modules/qt/000077500000000000000000000000001362234133600152615ustar00rootroot00000000000000mlt-6.20.0/src/modules/qt/CMakeLists.txt000066400000000000000000000035541362234133600200300ustar00rootroot00000000000000if(GPL) find_package(Qt5 COMPONENTS Core Gui Xml Svg Widgets) if(Qt5_FOUND) set(mltqt_src factory.c producer_qimage.c producer_kdenlivetitle.c common.cpp graph.cpp qimage_wrapper.cpp kdenlivetitle_wrapper.cpp filter_audiowaveform.cpp filter_qtext.cpp filter_qtblend.cpp producer_qtext.cpp transition_qtblend.cpp consumer_qglsl.cpp) set(mltqt_lib mlt++ mlt m Threads::Threads Qt5::Core Qt5::Gui Qt5::Xml Qt5::Svg Qt5::Widgets) set(mltqt_def USE_QT_OPENGL) if(GPL3) list(APPEND mltqt_src transition_vqm.cpp) list(APPEND mltqt_def GPL3) endif() find_package(FFTW QUIET) if(TARGET FFTW3::fftw3) add_library(fftw ALIAS FFTW3::fftw3) else() pkg_check_modules(fftw3 IMPORTED_TARGET GLOBAL fftw3) if(TARGET PkgConfig::fftw3) add_library(fftw ALIAS PkgConfig::fftw3) endif() endif() if(TARGET fftw) list(APPEND mltqt_src filter_lightshow.cpp filter_audiospectrum.cpp) list(APPEND mltqt_lib fftw) list(APPEND mltqt_def USE_FFTW) endif() pkg_check_modules(libexif IMPORTED_TARGET libexif) if(TARGET PkgConfig::libexif) list(APPEND mltqt_lib PkgConfig::libexif) list(APPEND mltqt_def USE_EXIF) endif() add_library(mltqt MODULE ${mltqt_src}) target_link_libraries(mltqt ${mltqt_lib}) target_compile_definitions(mltqt PRIVATE ${mltqt_def}) set_target_properties(mltqt PROPERTIES CXX_STANDARD 11) install(TARGETS mltqt LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/qt) endif() endif() mlt-6.20.0/src/modules/qt/Makefile000066400000000000000000000030061362234133600167200ustar00rootroot00000000000000include ../../../config.mak include config.mak CFLAGS := -I../.. $(CFLAGS) LDFLAGS := -L../../framework -lmlt -lpthread -lm -L../../mlt++ -lmlt++ $(LDFLAGS) TARGET = ../libmltqt$(LIBSUF) OBJS = factory.o producer_qimage.o producer_kdenlivetitle.o CPPOBJS = common.o \ graph.o \ filter_audiowaveform.o \ filter_qtext.o \ qimage_wrapper.o \ kdenlivetitle_wrapper.o \ producer_qtext.o \ transition_qtblend.o \ filter_qtblend.o ifdef USE_QT_OPENGL CPPOBJS += consumer_qglsl.o CFLAGS += -DUSE_QT_OPENGL endif ifdef GPL3 CPPOBJS += transition_vqm.o CFLAGS += -DGPL3 endif ifdef USE_FFTW CPPOBJS += filter_lightshow.o \ filter_audiospectrum.o CFLAGS += -DUSE_FFTW endif ifneq ($(targetos), Darwin) ifneq ($(targetos), MinGW) LDFLAGS += -lX11 endif endif CXXFLAGS := $(QTCXXFLAGS) $(CXXFLAGS) $(CFLAGS) $(EXIFCXXFLAGS) $(KDECXXFLAGS) -Wno-deprecated ifdef USE_CPP11 ifneq (, $(shell $(CXX) --version | grep -is -e g++ -e clang)) CXXFLAGS += -std=c++11 endif endif LDFLAGS += $(QTLIBS) $(EXIFLIBS) $(KDELIBS) SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) all: $(TARGET) $(TARGET): $(OBJS) $(CPPOBJS) $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) depend: $(SRCS) $(CXX) -MM $(CXXFLAGS) $^ 1>.depend distclean: clean rm -f .depend config.h config.mak clean: rm -f $(OBJS) $(TARGET) $(CPPOBJS) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/qt" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/qt" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/qt/common.cpp000066400000000000000000000103541362234133600172600ustar00rootroot00000000000000/* * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) #include #include #endif bool createQApplicationIfNeeded(mlt_service service) { if (!qApp) { #if defined(Q_OS_WIN) && defined(NODEPLOY) QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR"))+QStringLiteral("/bin")); QCoreApplication::addLibraryPath(QString(mlt_environment("MLT_APPDIR"))+QStringLiteral("/plugins")); #endif #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) if (getenv("DISPLAY") == 0) { mlt_log_error(service, "The MLT Qt module requires a X11 environment.\n" "Please either run melt from an X session or use a fake X server like xvfb:\n" "xvfb-run -a melt (...)\n" ); return false; } #endif if (!mlt_properties_get(mlt_global_properties(), "qt_argv")) mlt_properties_set(mlt_global_properties(), "qt_argv", "MLT"); static int argc = 1; static char* argv[] = { mlt_properties_get(mlt_global_properties(), "qt_argv") }; new QApplication(argc, argv); const char *localename = mlt_properties_get_lcnumeric(MLT_SERVICE_PROPERTIES(service)); QLocale::setDefault(QLocale(localename)); } return true; } void convert_qimage_to_mlt_rgba( QImage* qImg, uint8_t* mImg, int width, int height ) { #if QT_VERSION >= 0x050200 // QImage::Format_RGBA8888 was added in Qt5.2 // Nothing to do in this case because the image was modified directly. // Destination pointer must be the same pointer that was provided to // convert_mlt_to_qimage_rgba() Q_ASSERT(mImg == qImg->constBits()); #else int y = height + 1; while (--y) { QRgb* src = (QRgb*)qImg->scanLine(height - y); int x = width + 1; while (--x) { *mImg++ = qRed(*src); *mImg++ = qGreen(*src); *mImg++ = qBlue(*src); *mImg++ = qAlpha(*src); src++; } } #endif } void convert_mlt_to_qimage_rgba( uint8_t* mImg, QImage* qImg, int width, int height ) { #if QT_VERSION >= 0x050200 // QImage::Format_RGBA8888 was added in Qt5.2 // Initialize the QImage with the MLT image because the data formats match. *qImg = QImage( mImg, width, height, QImage::Format_RGBA8888 ); #else *qImg = QImage( width, height, QImage::Format_ARGB32 ); int y = height + 1; while (--y) { QRgb *dst = (QRgb*)qImg->scanLine(height - y); int x = width + 1; while (--x) { *dst++ = qRgba(mImg[0], mImg[1], mImg[2], mImg[3]); mImg += 4; } } #endif } int create_image( mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable ) { int error = 0; mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); *image_format = mlt_image_rgb24a; // Use the width and height suggested by the rescale filter. if( mlt_properties_get_int( frame_properties, "rescale_width" ) > 0 ) *width = mlt_properties_get_int( frame_properties, "rescale_width" ); if( mlt_properties_get_int( frame_properties, "rescale_height" ) > 0 ) *height = mlt_properties_get_int( frame_properties, "rescale_height" ); // If no size is requested, use native size. if( *width <=0 ) *width = mlt_properties_get_int( frame_properties, "meta.media.width" ); if( *height <=0 ) *height = mlt_properties_get_int( frame_properties, "meta.media.height" ); int size = mlt_image_format_size( *image_format, *width, *height, NULL ); *image = static_cast( mlt_pool_alloc( size ) ); memset( *image, 0, size ); // Transparent mlt_frame_set_image( frame, *image, size, mlt_pool_release ); return error; } mlt-6.20.0/src/modules/qt/common.h000066400000000000000000000023471362234133600167300ustar00rootroot00000000000000/* * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_H #define COMMON_H #include class QImage; bool createQApplicationIfNeeded(mlt_service service); void convert_qimage_to_mlt_rgba( QImage* qImg, uint8_t* mImg, int width, int height ); void convert_mlt_to_qimage_rgba( uint8_t* mImg, QImage* qImg, int width, int height ); int create_image( mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable ); #endif // COMMON_H mlt-6.20.0/src/modules/qt/configure000077500000000000000000000156161362234133600172010ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF Qt options: --qt-libdir - Location of Qt lib directory [/usr/lib/qt4] --qt-includedir - Location of Qt include directory [/usr/include/qt4] --kde-libdir - Location of KDE lib directory [/usr/lib] --kde-includedir - Location of KDE include directory [/usr/include/kde] --exif-libdir - Location of libexif lib directory [/usr/lib] --exif-includedir - Location of libexif include directory [/usr/include/libexif] --without-kde - Don't link to KDE libraries EOF else targetos=$(uname -s) case $targetos in MINGW32*) export LIBSUF=.dll ;; Darwin) export LIBSUF=.dylib ;; Linux|FreeBSD|NetBSD) export LIBSUF=.so ;; *) ;; esac qt_includedir= qt_libdir= if [ "$QTDIR" != "" ] then qt_includedir="$QTDIR/include" qt_libdir="$QTDIR/lib" qt_version=$($QTDIR/bin/qmake -query QT_VERSION) else qt_version=$(qmake -query QT_VERSION) fi export without_kde= export without_opengl= for i in "$@" do case $i in --qt-libdir=* ) qt_libdir="${i#--qt-libdir=}" ;; --qt-includedir=* ) qt_includedir="${i#--qt-includedir=}" ;; --kde-libdir=* ) kde_libdir="${i#--kde-libdir=}" ;; --kde-includedir=* ) kde_includedir="${i#--kde-includedir=}" ;; --exif-libdir=* ) exif_libdir="${i#--exif-libdir=}" ;; --exif-includedir=* ) exif_includedir="${i#--exif-includedir=}" ;; --without-kde ) without_kde="true" ;; esac done echo > config.mak if $(echo $qt_version | awk '{split($0, a, "."); if (a[1] >= 5 && a[2] > 6) exit 0; else exit 1;}') then echo "USE_CPP11=1" >> config.mak fi pkg-config --exists 'libexif' if [ $? -eq 0 ] then echo "- Libexif found, enabling auto rotate" echo "USE_EXIF=1" >> config.mak echo EXIFCXXFLAGS=$(pkg-config --cflags libexif ) >> config.mak echo EXIFCXXFLAGS += -DUSE_EXIF >> config.mak echo EXIFLIBS=$(pkg-config --libs libexif) >> config.mak elif [ -d "$exif_libdir" -a -d "$exif_includedir" ] then # test if we have a libexif if [ -f "$exif_libdir/exif-data.h" ] then echo "- Libexif found, enabling auto rotate" echo "USE_EXIF=1" >> config.mak echo EXIFCXXFLAGS=-I$exif_includedir >> config.mak echo EXIFCXXFLAGS += -DUSE_EXIF >> config.mak echo EXIFLIBS=-L$exif_libdir lexif >> config.mak else echo "- Libexif not found, disabling exif features (auto rotate)" fi else echo "- Libexif not found, disabling exif features (auto rotate)" fi if [ -d "$qt_libdir" -a -d "$qt_includedir" ] then # test if we have a Qt5 or Qt4 if [ -f "$qt_libdir/libQt5Core.so" ] || [ -d "$qt_libdir/QtWidgets.framework" ] || [ -f "$qt_libdir/libQt5Core.a" ] then echo "- Qt version 5.x detected" # TODO re-enable KDE support when KDE Frameworks 5 widely available without_kde=true elif [ -f "$qt_libdir/libQtCore.so" ] || [ -d "$qt_libdir/QtCore.framework" ] || [ -f "$qt_libdir/libQtCore4.a" ] then echo "- Qt version 4.x detected" else echo "- Qt not found: disabling" touch ../disable-qt exit 0 fi echo "- Include directory: " $qt_includedir # Qt5 on Linux, BSD, or Windows if [ -f "$qt_libdir/libQt5Core.so" ] || [ -f "$qt_libdir/libQt5Core.a" ] then echo QTCXXFLAGS=-I$qt_includedir -I$qt_includedir/QtCore -I$qt_includedir/QtGui -I$qt_includedir/QtXml -I$qt_includedir/QtSvg -I$qt_includedir/QtWidgets >> config.mak echo QTLIBS=-Wl,-rpath-link,"$qt_libdir" -L"$qt_libdir" -lQt5Core -lQt5Gui -lQt5Xml -lQt5Svg -lQt5Widgets >> config.mak # Qt5 on OS X elif [ -d "$qt_libdir/QtWidgets.framework" ] then echo QTCXXFLAGS=-I$qt_includedir -F$qt_libdir \ -I$qt_includedir/QtCore -I$qt_libdir/QtCore.framework/Headers \ -I$qt_includedir/QtGui -I$qt_libdir/QtGui.framework/Headers \ -I$qt_includedir/QtXml -I$qt_libdir/QtXml.framework/Headers \ -I$qt_includedir/QtSvg -I$qt_libdir/QtSvg.framework/Headers \ -I$qt_includedir/QtWidgets -I$qt_libdir/QtWidgets.framework/Headers \ >> config.mak echo QTLIBS=-F"$qt_libdir" -framework QtCore -framework QtGui -framework \ QtXml -framework QtSvg -framework QtWidgets >> config.mak # Qt4 on OS X elif [ -d "$qt_libdir/QtGui.framework" ] then echo QTCXXFLAGS=$(pkg-config --cflags QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak echo QTLIBS=$(pkg-config --libs QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak # Qt4 on Windows elif [ -f "$qt_libdir/libQtCore4.a" ] then echo QTCXXFLAGS=-I$qt_includedir -I$qt_includedir/QtCore -I$qt_includedir/QtGui -I$qt_includedir/QtXml -I$qt_includedir/QtSvg -I$qt_includedir/QtOpenGL >> config.mak echo QTLIBS=-Wl,-enable-auto-import -L$qt_libdir -lQtCore4 -lQtGui4 -lQtXml4 -lQtSvg4 -lQtOpenGL4 >> config.mak # Qt4 on Linux or BSD else echo QTCXXFLAGS=-I$qt_includedir -I$qt_includedir/QtCore -I$qt_includedir/QtGui -I$qt_includedir/QtXml -I$qt_includedir/QtSvg -I$qt_includedir/QtOpenGL >> config.mak echo QTLIBS=-L$qt_libdir -lQtCore -lQtGui -lQtXml -lQtSvg -lQtOpenGL >> config.mak fi else pkg-config --exists 'Qt5Gui' if [ $? -eq 0 ] then echo "- Qt version 5.x detected" without_kde=true echo QTCXXFLAGS=$(pkg-config --cflags Qt5Core Qt5Gui Qt5Xml Qt5Svg Qt5Widgets) >> config.mak echo QTLIBS=$(pkg-config --libs Qt5Core Qt5Gui Qt5Xml Qt5Svg Qt5Widgets) >> config.mak else pkg-config --exists 'QtGui >= 4' if [ $? -eq 0 ] then echo "- Qt version 4.x detected" echo QTCXXFLAGS=$(pkg-config --cflags QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak echo QTLIBS=$(pkg-config --libs QtCore QtGui QtXml QtSvg QtOpenGL) >> config.mak else echo "- Qt not found - disabling" touch ../disable-qt exit 0 fi fi fi if [ "$without_opengl" = "" ] then echo "USE_QT_OPENGL=1" >> config.mak fi if [ "$without_kde" = "" ] then kde4-config if [ $? -eq 0 ] then # test if we have KDE4, required on some systems to get Qt extra formats (xcf, ...) if [ "$kde_includedir" = "" ] then kde_includedir=`kde4-config --install include` fi if [ "$kde_libdir" = "" ] then kde_libdir=`kde4-config --install lib` fi if [ -d "$kde_includedir" ] && [ -d "$kde_libdir" ] then echo "- KDE version 4.x detected, will enable extra image formats" echo "USE_KDE4=1" >> config.mak echo KDECXXFLAGS=-I$kde_includedir >> config.mak echo KDECXXFLAGS += -DUSE_KDE4 >> config.mak # the -L with kde4/devel is for Fedora echo KDELIBS=-L$kde_libdir -L${kde_libdir}/kde4/devel -lkdecore >> config.mak fi fi fi if pkg-config fftw3 then FFTWPC=fftw3 elif pkg-config fftw then FFTWPC=fftw fi if [ "$FFTWPC" != "" ] then echo "- fftw found, enabling lightshow" echo "USE_FFTW=1" >> config.mak echo FFTWCXXFLAGS=$(pkg-config --cflags $FFTWPC ) >> config.mak echo FFTWCXXFLAGS += -DUSE_FFTW >> config.mak echo FFTWLIBS=$(pkg-config --libs $FFTWPC ) >> config.mak else echo "- fftw not found: disabling lightshow filter" fi [ "$gpl3" = "true" ] && echo GPL3=1 >> config.mak exit 0 fi mlt-6.20.0/src/modules/qt/consumer_qglsl.cpp000066400000000000000000000137071362234133600210320ustar00rootroot00000000000000/* * consumer_qglsl.cpp * Copyright (C) 2012-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #include #include #include class GLWidget : public QGLWidget { private: QGLWidget *renderContext; bool isInitialized; QMutex mutex; QWaitCondition condition; public: GLWidget() #ifdef Q_OS_MAC : QGLWidget() #else : QGLWidget(0, 0, Qt::SplashScreen) #endif , renderContext(0) , isInitialized(false) { resize(0, 0); show(); } ~GLWidget() { delete renderContext; } bool createRenderContext() { if (!isInitialized) { mutex.lock(); condition.wait(&mutex); mutex.unlock(); } if (!renderContext) { renderContext = new QGLWidget(0, this, Qt::SplashScreen); renderContext->resize(0, 0); renderContext->makeCurrent(); } return renderContext->isValid(); } protected: void initializeGL() { condition.wakeAll(); isInitialized = true; } }; #else // Qt 5 #include #include #include typedef void* ( *thread_function_t )( void* ); class RenderThread : public QThread { public: RenderThread(thread_function_t function, void *data) : QThread(0) , m_function(function) , m_data(data) { m_context = new QOpenGLContext; m_context->create(); m_context->moveToThread(this); m_surface = new QOffscreenSurface(); m_surface->create(); } ~RenderThread() { m_surface->destroy(); delete m_surface; } protected: void run() { Q_ASSERT(m_context->isValid()); m_context->makeCurrent(m_surface); m_function(m_data); m_context->doneCurrent(); delete m_context; } private: thread_function_t m_function; void* m_data; QOpenGLContext* m_context; QOffscreenSurface* m_surface; }; static void onThreadCreate(mlt_properties owner, mlt_consumer self, RenderThread** thread, int* priority, thread_function_t function, void* data ) { Q_UNUSED(owner) Q_UNUSED(priority) (*thread) = new RenderThread(function, data); (*thread)->start(); } static void onThreadJoin(mlt_properties owner, mlt_consumer self, RenderThread* thread) { Q_UNUSED(owner) Q_UNUSED(self) if (thread) { thread->quit(); thread->wait(); qApp->processEvents(); delete thread; } } #endif // Qt 5 static void onThreadStarted(mlt_properties owner, mlt_consumer consumer) { mlt_service service = MLT_CONSUMER_SERVICE(consumer); mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_filter filter = (mlt_filter) mlt_properties_get_data(properties, "glslManager", NULL); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); mlt_log_debug(service, "%s\n", __FUNCTION__); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) GLWidget *widget = (GLWidget*) mlt_properties_get_data(properties, "GLWidget", NULL); if (widget->createRenderContext()) { #else { #endif mlt_events_fire(filter_properties, "init glsl", NULL); if (!mlt_properties_get_int(filter_properties, "glsl_supported")) { mlt_log_fatal(service, "OpenGL Shading Language rendering is not supported on this machine.\n" ); mlt_events_fire(properties, "consumer-fatal-error", NULL); } } } static void onThreadStopped(mlt_properties owner, mlt_consumer consumer) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_filter filter = (mlt_filter) mlt_properties_get_data(properties, "glslManager", NULL); mlt_events_fire(MLT_FILTER_PROPERTIES(filter), "close glsl", NULL); } static void onCleanup(mlt_properties owner, mlt_consumer consumer) { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) GLWidget* widget = (GLWidget*) mlt_properties_get_data( MLT_CONSUMER_PROPERTIES(consumer), "GLWidget", NULL); delete widget; mlt_properties_set_data(MLT_CONSUMER_PROPERTIES(consumer), "GLWidget", NULL, 0, NULL, NULL); qApp->processEvents(); #endif } extern "C" { mlt_consumer consumer_qglsl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_consumer consumer = mlt_factory_consumer(profile, "multi", arg); if (consumer) { mlt_filter filter = mlt_factory_filter(profile, "glsl.manager", 0); if (filter) { mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer); mlt_properties_set_data(properties, "glslManager", filter, 0, (mlt_destructor) mlt_filter_close, NULL); mlt_events_register( properties, "consumer-cleanup", NULL ); mlt_events_listen(properties, consumer, "consumer-thread-started", (mlt_listener) onThreadStarted); mlt_events_listen(properties, consumer, "consumer-thread-stopped", (mlt_listener) onThreadStopped); mlt_events_listen(properties, consumer, "consumer-cleanup", (mlt_listener) onCleanup); if (!createQApplicationIfNeeded(MLT_CONSUMER_SERVICE(consumer))) { mlt_filter_close(filter); mlt_consumer_close(consumer); return NULL; } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) mlt_properties_set_data(properties, "GLWidget", new GLWidget, 0, NULL, NULL); #else mlt_events_listen(properties, consumer, "consumer-thread-create", (mlt_listener) onThreadCreate); mlt_events_listen(properties, consumer, "consumer-thread-join", (mlt_listener) onThreadJoin); #endif qApp->processEvents(); return consumer; } mlt_consumer_close(consumer); } return NULL; } } mlt-6.20.0/src/modules/qt/factory.c000066400000000000000000000101641362234133600170760ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #ifdef USE_QT_OPENGL extern mlt_consumer consumer_qglsl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #endif extern mlt_filter filter_audiowaveform_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_qtext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_qtext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_kdenlivetitle_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ); extern mlt_transition transition_qtblend_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ); extern mlt_filter filter_qtblend_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #ifdef USE_FFTW extern mlt_filter filter_audiospectrum_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_lightshow_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); #endif static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/qt/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { #ifdef USE_QT_OPENGL MLT_REGISTER( consumer_type, "qglsl", consumer_qglsl_init ); #endif MLT_REGISTER( filter_type, "audiowaveform", filter_audiowaveform_init ); MLT_REGISTER( filter_type, "qtext", filter_qtext_init ); MLT_REGISTER( producer_type, "qimage", producer_qimage_init ); MLT_REGISTER( producer_type, "qtext", producer_qtext_init ); MLT_REGISTER( producer_type, "kdenlivetitle", producer_kdenlivetitle_init ); MLT_REGISTER( transition_type, "qtblend", transition_qtblend_init ); MLT_REGISTER( filter_type, "qtblend", filter_qtblend_init ); MLT_REGISTER_METADATA( transition_type, "qtblend", metadata, "transition_qtblend.yml" ); MLT_REGISTER_METADATA( filter_type, "qtblend", metadata, "filter_qtblend.yml" ); #ifdef USE_FFTW MLT_REGISTER( filter_type, "audiospectrum", filter_audiospectrum_init ); MLT_REGISTER( filter_type, "lightshow", filter_lightshow_init ); #endif MLT_REGISTER_METADATA( filter_type, "audiowaveform", metadata, "filter_audiowaveform.yml" ); MLT_REGISTER_METADATA( filter_type, "qtext", metadata, "filter_qtext.yml" ); #ifdef USE_FFTW MLT_REGISTER_METADATA( filter_type, "lightshow", metadata, "filter_lightshow.yml" ); MLT_REGISTER_METADATA( filter_type, "audiospectrum", metadata, "filter_audiospectrum.yml" ); #endif MLT_REGISTER_METADATA( producer_type, "qimage", metadata, "producer_qimage.yml" ); MLT_REGISTER_METADATA( producer_type, "qtext", metadata, "producer_qtext.yml" ); MLT_REGISTER_METADATA( producer_type, "kdenlivetitle", metadata, "producer_kdenlivetitle.yml" ); #ifdef GPL3 MLT_REGISTER( transition_type, "vqm", transition_vqm_init ); MLT_REGISTER_METADATA( transition_type, "vqm", metadata, "transition_vqm.yml" ); #endif } mlt-6.20.0/src/modules/qt/filter_audiospectrum.cpp000066400000000000000000000305261362234133600222240ustar00rootroot00000000000000/* * filter_audiospectrum.cpp -- audio spectrum visualization filter * Copyright (c) 2015-2020 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "graph.h" #include #include #include // memset #include #include #include #include //pow // Private Types typedef struct { mlt_filter fft; char* fft_prop_name; int preprocess_warned; } private_data; static int filter_get_audio( mlt_frame frame, void** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_filter filter = (mlt_filter)mlt_frame_pop_audio( frame ); mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; // Create the FFT filter the first time. if( !pdata->fft ) { mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); pdata->fft = mlt_factory_filter( profile, "fft", NULL ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( pdata->fft ), "window_size", mlt_properties_get_int( filter_properties, "window_size" ) ); if( !pdata->fft ) { mlt_log_warning( MLT_FILTER_SERVICE(filter), "Unable to create FFT.\n" ); return 1; } } mlt_properties fft_properties = MLT_FILTER_PROPERTIES( pdata->fft ); // The service must stay locked while using the private data mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // Perform FFT processing on the frame mlt_filter_process( pdata->fft, frame ); mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); float* bins = (float*)mlt_properties_get_data( fft_properties, "bins", NULL ); if( bins ) { double window_level = mlt_properties_get_double( fft_properties, "window_level" ); int bin_count = mlt_properties_get_int( fft_properties, "bin_count" ); size_t bins_size = bin_count * sizeof(float); float* save_bins = (float*)mlt_pool_alloc( bins_size ); if( window_level == 1.0 ) { memcpy( save_bins, bins, bins_size ); } else { memset( save_bins, 0, bins_size ); } // Save the bin data as a property on the frame to be used in get_image() mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), pdata->fft_prop_name, save_bins, bins_size, mlt_pool_release, NULL ); } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); return 0; } static void convert_fft_to_spectrum( mlt_filter filter, mlt_frame frame, int spect_bands, float* spectrum ) { private_data* pdata = (private_data*)filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties fft_properties = MLT_FILTER_PROPERTIES( pdata->fft ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); double low_freq = mlt_properties_get_int( filter_properties, "frequency_low" ); double hi_freq = mlt_properties_get_int( filter_properties, "frequency_high" ); int bin_count = mlt_properties_get_int( fft_properties, "bin_count" ); double bin_width = mlt_properties_get_double( fft_properties, "bin_width" ); float* bins = (float*)mlt_properties_get_data( frame_properties, pdata->fft_prop_name, NULL ); double threshold = mlt_properties_get_int( filter_properties, "threshold" ); int reverse = mlt_properties_get_int( filter_properties, "reverse" ); // Map the linear fft bin frequencies to a log scale spectrum. double band_freq_factor = pow( hi_freq / low_freq, 1.0 / (double)spect_bands ); double band_freq_low = low_freq; double band_freq_hi = band_freq_low * band_freq_factor; int bin_index = 0; int spect_index = 0; double bin_freq = 0; // Skip bins that occur before the low frequency of the spectrum while( bin_freq < band_freq_low ) { bin_freq += bin_width; bin_index ++; } for( spect_index = 0; spect_index < spect_bands && bin_index < bin_count; spect_index++ ) { float mag = 0.0; if( bin_freq > band_freq_hi ) { // There is no bin for this point. Interpolate between the two closest. if( bin_index == 0 ) { mag = bins[bin_index]; } else { double y0 = bins[bin_index - 1]; double y1 = bins[bin_index]; double spect_center = band_freq_low + (band_freq_hi - band_freq_low) / 2; double prev_freq = bin_freq - bin_width; double t = bin_width / ( spect_center - prev_freq ); mag = y0 + ( y1 - y0 ) * t; } } else { // Find the bin frequency with the greatest magnitude in the range for // this spectrum point. while( bin_freq < band_freq_hi && bin_index < bin_count ) { if( mag < bins[bin_index] ) { mag = bins[bin_index]; } bin_freq += bin_width; bin_index ++; } } // Scale the magnitude to the range 0.0-1.0 based on dB double dB = mag > 0.0 ? 20 * log10( mag ) : -1000.0; double spect_val = 0; if( dB >= threshold ) { spect_val = 1.0 - (dB / threshold); } if( reverse ) { spectrum[spect_bands - spect_index - 1] = spect_val; } else { spectrum[spect_index] = spect_val; } // Calculate the next spectrum point frequency range. band_freq_low = band_freq_hi; band_freq_hi = band_freq_hi * band_freq_factor; } } static void draw_spectrum( mlt_filter filter, mlt_frame frame, QImage* qimg, int width, int height ) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_rect rect = mlt_properties_anim_get_rect( filter_properties, "rect", position, length ); if ( strchr( mlt_properties_get( filter_properties, "rect" ), '%' ) ) { rect.x *= qimg->width(); rect.w *= qimg->width(); rect.y *= qimg->height(); rect.h *= qimg->height(); } double scale = mlt_profile_scale_width(profile, width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, height); rect.y *= scale; rect.h *= scale; char* graph_type = mlt_properties_get( filter_properties, "type" ); int mirror = mlt_properties_get_int( filter_properties, "mirror" ); int fill = mlt_properties_get_int( filter_properties, "fill" ); double tension = mlt_properties_get_double( filter_properties, "tension" ); QRectF r( rect.x, rect.y, rect.w, rect.h ); QPainter p( qimg ); if( mirror ) { // Draw two half rectangle instead of one full rectangle. r.setHeight( r.height() / 2.0 ); } setup_graph_painter( p, r, filter_properties ); setup_graph_pen( p, r, filter_properties, scale ); int bands = mlt_properties_get_int( filter_properties, "bands" ); if ( bands == 0 ) { // "0" means match rectangle width bands = r.width(); } float* spectrum = (float*)mlt_pool_alloc( bands * sizeof(float) ); convert_fft_to_spectrum( filter, frame, bands, spectrum ); if( graph_type && graph_type[0] == 'b' ) { paint_bar_graph( p, r, bands, spectrum ); } else { paint_line_graph( p, r, bands, spectrum, tension, fill ); } if( mirror ) { // Second rectangle is mirrored. p.translate( 0, r.y() * 2 + r.height() * 2 ); p.scale( 1, -1 ); if( graph_type && graph_type[0] == 'b' ) { paint_bar_graph( p, r, bands, spectrum ); } else { paint_line_graph( p, r, bands, spectrum, tension, fill ); } } mlt_pool_release( spectrum ); p.end(); } /** Get the image. */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame ); private_data* pdata = (private_data*)filter->child; mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); if( mlt_properties_get_data( frame_properties, pdata->fft_prop_name, NULL ) ) { // Get the current image *format = mlt_image_rgb24a; error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Draw the spectrum if( !error ) { QImage qimg( *width, *height, QImage::Format_ARGB32 ); convert_mlt_to_qimage_rgba( *image, &qimg, *width, *height ); draw_spectrum( filter, frame, &qimg, *width, *height ); convert_qimage_to_mlt_rgba( &qimg, *image, *width, *height ); } } else { if ( pdata->preprocess_warned++ == 2 ) { // This filter depends on the consumer processing the audio before // the video. mlt_log_warning( MLT_FILTER_SERVICE(filter), "Audio not preprocessed.\n" ); } mlt_frame_get_image( frame, image, format, width, height, writable ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { if( mlt_frame_is_test_card( frame ) ) { // The producer does not generate video. This filter will create an // image on the producer's behalf. mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); mlt_properties_set_int( frame_properties, "progressive", 1 ); mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_profile_sar( profile ) ); mlt_properties_set_int( frame_properties, "meta.media.width", profile->width ); mlt_properties_set_int( frame_properties, "meta.media.height", profile->height ); // Tell the framework that there really is an image. mlt_properties_set_int( frame_properties, "test_image", 0 ); // Push a callback to create the image. mlt_frame_push_get_image( frame, create_image ); } mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, (void*)filter_get_audio ); mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } static void filter_close( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if ( pdata ) { mlt_filter_close( pdata->fft ); free( pdata->fft_prop_name ); free( pdata ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_audiospectrum_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); if ( filter && pdata && createQApplicationIfNeeded( MLT_FILTER_SERVICE(filter) ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set_int( properties, "_filter_private", 1 ); mlt_properties_set_int( properties, "frequency_low", 20 ); mlt_properties_set_int( properties, "frequency_high", 20000 ); mlt_properties_set( properties, "type", "line" ); mlt_properties_set( properties, "bgcolor", "0x00000000" ); mlt_properties_set( properties, "color.1", "0xffffffff" ); mlt_properties_set( properties, "rect", "0% 0% 100% 100%" ); mlt_properties_set( properties, "thickness", "0" ); mlt_properties_set( properties, "fill", "0" ); mlt_properties_set( properties, "mirror", "0" ); mlt_properties_set( properties, "reverse", "0" ); mlt_properties_set( properties, "tension", "0.4" ); mlt_properties_set( properties, "angle", "0" ); mlt_properties_set( properties, "gorient", "v" ); mlt_properties_set_int( properties, "bands", 31 ); mlt_properties_set_double( properties, "threshold", -60.0 ); mlt_properties_set_int( properties, "window_size", 8192 ); // Create a unique ID for storing data on the frame pdata->fft_prop_name = (char*)calloc( 1, 20 ); snprintf( pdata->fft_prop_name, 20, "fft.%p", filter ); pdata->fft_prop_name[20 - 1] = '\0'; pdata->fft = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter audio spectrum failed\n" ); if( filter ) { mlt_filter_close( filter ); } if( pdata ) { free( pdata ); } filter = NULL; } return filter; } } mlt-6.20.0/src/modules/qt/filter_audiospectrum.yml000066400000000000000000000132041362234133600222350ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: audiospectrum title: Audio Spectrum Filter version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that draws an audio spectrum on the image. parameters: - identifier: type title: Graph type description: The type of graph to display the spectrum. type: string default: line values: - line - bar readonly: no mutable: yes widget: combo - identifier: bgcolor title: Background Color type: color description: | The background color to be applied to the entire frame. The default color is transparent. A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: color.* title: Foreground color type: color description: | The color of the waveform. Multiple colors can be specified with incrementing suffixes to cause the waveform to be drawn in a gradient. color.1 is the top of the waveform. Subsequent colors will produce a gradient toward the bottom. By default, the filter has one color defined: color.1=0xffffffff" This results in a white waveform. To create a gradient, define more colors: color.2=green color.3=0x77777777 A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: thickness title: Line Thickness type: integer description: > The thickness of the line used to draw the waveform for line graph. The thickness of the bar for bar graph. readonly: no default: 0 minimum: 0 maximum: 20 mutable: yes widget: spinner unit: pixels - identifier: angle title: Angle type: float description: > The rotation angle to be applied to the waveform. readonly: no default: 0 minimum: 0 maximum: 360 mutable: yes widget: spinner - identifier: rect title: Rectangle description: > Defines the rectangle that the waveform(s) should be drawn in. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 100% 100%" readonly: no mutable: yes - identifier: fill title: Fill description: > Whether the area under the waveform should be filled in. Only applies to line graph type. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: mirror title: Mirror description: > Mirror the spectrum about the center of the rectangle. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: reverse title: Reverse description: > Draw the points starting with the highest frequency first. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: tension title: Line Tension description: > Affects the amount of curve in the line interpolating between points. 0.0 = a straight line between points. 1.0 = very curved lines between points. values < 0 and > 1 will cause loops in the lines. Only applies to line graph type. type: float default: 0.4 readonly: no mutable: yes - identifier: gorient title: Gradient Orientation description: Direction of the color gradient. type: string default: vertical values: - vertical - horizontal readonly: no mutable: yes widget: combo - identifier: bands title: Points type: integer description: > The number of bands to draw in the spectrum. Each band shows up as a data point in the graph. mutable: yes readonly: no default: 31 - identifier: frequency_low title: Low Frequency type: integer description: > The low end of the frequency range to be used for the graph. motion. mutable: yes readonly: no default: 20 unit: Hz - identifier: frequency_high title: High Frequency type: integer description: > The high end of the frequency range to be used for the graph. motion. mutable: yes readonly: no default: 20000 unit: Hz - identifier: threshold title: Level Threshold type: float description: > The minimum amplitude of sound that must occur within the frequency range to cause the value to be applied. motion. mutable: yes readonly: no default: -30 minimum: -100 maximum: 0 unit: dB - identifier: window_size title: Window Size type: integer description: > The number of samples that the FFT will be performed on. If window_size is less than the number of samples in a frame, extra samples will be ignored. If window_size is more than the number of samples in a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 2048 mlt-6.20.0/src/modules/qt/filter_audiowaveform.cpp000066400000000000000000000342531362234133600222110ustar00rootroot00000000000000/* * filter_audiowaveform.cpp -- audio waveform visualization filter * Copyright (c) 2015-2020 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include "graph.h" #include #include #include #include #include static const qreal MAX_S16_AMPLITUDE = 32768.0; // Private Types typedef struct { char *buffer_prop_name; int reset_window; int16_t* window_buffer; int window_samples; int window_frequency; int window_channels; } private_data; typedef struct { int16_t* buffer; int samples; int channels; } save_buffer; static save_buffer* create_save_buffer( int samples, int channels, int16_t* buffer ) { save_buffer* ret = (save_buffer*)calloc( 1, sizeof(save_buffer) ); int buffer_size = samples * channels * sizeof(int16_t); ret->samples = samples; ret->channels = channels; ret->buffer = (int16_t*)calloc( 1, buffer_size ); memcpy( ret->buffer, buffer, buffer_size ); return ret; } static void destory_save_buffer( void* ptr ) { if( !ptr ) { mlt_log_error( NULL, "Invalid save_buffer ptr.\n" ); return; } save_buffer* buff = (save_buffer*)ptr; free( buff->buffer ); free( buff ); } static void property_changed( mlt_service owner, mlt_filter filter, char *name ) { if ( !strcmp( name, "window" ) ) { private_data* pdata = (private_data*)filter->child; pdata->reset_window = 1; } } static int filter_get_audio( mlt_frame frame, void** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { int error = 0; mlt_filter filter = (mlt_filter)mlt_frame_pop_audio( frame ); private_data* pdata = (private_data*)filter->child; if( *format != mlt_audio_s16 && *format != mlt_audio_float ) { *format = mlt_audio_float; } error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); if( error ) { return error; } if( *frequency != pdata->window_frequency || *channels != pdata->window_channels ) { pdata->reset_window = true; } if( pdata->reset_window ) { mlt_log_info( MLT_FILTER_SERVICE(filter), "Reset window buffer: %d.\n", mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "window" ) ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); double fps = mlt_profile_fps( profile ); int frame_samples = mlt_sample_calculator( fps, *frequency, mlt_frame_get_position( frame ) ); int window_ms = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "window" ); pdata->window_frequency = *frequency; pdata->window_channels = *channels; pdata->window_samples = window_ms * *frequency / 1000; if( pdata->window_samples < frame_samples ) { pdata->window_samples = frame_samples; } free( pdata->window_buffer ); pdata->window_buffer = (int16_t*)calloc( 1, pdata->window_samples * pdata->window_channels * sizeof(int16_t) ); pdata->reset_window = 0; } int new_sample_count = *samples; if( new_sample_count > pdata->window_samples ) { new_sample_count = pdata->window_samples; } int old_sample_count = pdata->window_samples - new_sample_count; int window_buff_bytes = pdata->window_samples * pdata->window_channels * sizeof(int16_t); int new_sample_bytes = new_sample_count * pdata->window_channels * sizeof(int16_t); int old_sample_bytes = old_sample_count * pdata->window_channels * sizeof(int16_t); // Move the old samples ahead in the window buffer to make room for new samples. if( new_sample_bytes < window_buff_bytes ) { char* old_sample_src = (char*)pdata->window_buffer + new_sample_bytes; char* old_sample_dst = (char*)pdata->window_buffer; memmove( old_sample_dst, old_sample_src, old_sample_bytes ); } // Copy the new samples to the back of the window buffer. if( *format == mlt_audio_s16 ) { char* new_sample_src = (char*)*buffer; char* new_sample_dst = (char*)pdata->window_buffer + old_sample_bytes; memcpy( new_sample_dst, new_sample_src, new_sample_bytes ); } else // mlt_audio_float { for( int c = 0; c < pdata->window_channels; c++ ) { float* src = (float*)*buffer + (*samples * c); int16_t* dst = pdata->window_buffer + (old_sample_count * pdata->window_channels) + c; for( int s = 0; s < new_sample_count; s++ ) { *dst = *src * MAX_S16_AMPLITUDE; src++; dst += pdata->window_channels; } } } // Copy the window buffer and pass it along with the frame. save_buffer* out = create_save_buffer( pdata->window_samples, pdata->window_channels, pdata->window_buffer ); mlt_properties_set_data( MLT_FRAME_PROPERTIES(frame), pdata->buffer_prop_name, out, sizeof(save_buffer), destory_save_buffer, NULL ); return 0; } static void paint_waveform( QPainter& p, QRectF& rect, int16_t* audio, int samples, int channels, int fill ) { int width = rect.width(); const int16_t* q = audio; qreal half_height = rect.height() / 2.0; qreal center_y = rect.y() + half_height; if( samples < width ) { // For each x position on the waveform, find the sample value that // applies to that position and draw a point at that location. QPoint point(0, *q * half_height / MAX_S16_AMPLITUDE + center_y); QPoint lastPoint = point; int lastSample = 0; for ( int x = 0; x < width; x++ ) { int sample = ( x * samples ) / width; if ( sample != lastSample ) { lastSample = sample; q += channels; } lastPoint.setX( x + rect.x() ); lastPoint.setY( point.y() ); point.setX( x + rect.x() ); point.setY( *q * half_height / MAX_S16_AMPLITUDE + center_y ); if ( fill ) { // Draw the line all the way to 0 to "fill" it in. if ( ( point.y() > center_y && lastPoint.y() > center_y ) || ( point.y() < center_y && lastPoint.y() < center_y ) ) { lastPoint.setY( center_y ); } } if ( point.y() == lastPoint.y() ) { p.drawPoint( point ); } else { p.drawLine( lastPoint, point ); } } } else { // For each x position on the waveform, find the min and max sample // values that apply to that position. Draw a vertical line from the // min value to the max value. QPoint high; QPoint low; qreal max = *q; qreal min = *q; int lastX = 0; for ( int s = 0; s <= samples; s++ ) { int x = ( s * width ) / samples; if ( x != lastX ) { // The min and max have been determined for the previous x // So draw the line if ( fill ) { // Draw the line all the way to 0 to "fill" it in. if ( max > 0 && min > 0 ) { min = 0; } else if ( min < 0 && max < 0 ) { max = 0; } } high.setX( lastX + rect.x() ); high.setY( max * half_height / MAX_S16_AMPLITUDE + center_y ); low.setX( lastX + rect.x() ); low.setY( min * half_height / MAX_S16_AMPLITUDE + center_y ); if ( high.y() == low.y() ) { p.drawPoint( high ); } else { p.drawLine( low, high ); } lastX = x; // Swap max and min so that the next line picks up where // this one left off. int tmp = max; max = min; min = tmp; } if ( *q > max ) max = *q; if ( *q < min ) min = *q; q += channels; } } } static void draw_waveforms( mlt_filter filter, mlt_frame frame, QImage* qimg, int16_t* audio, int channels, int samples, int width, int height ) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); int show_channel = mlt_properties_get_int( filter_properties, "show_channel" ); int fill = mlt_properties_get_int( filter_properties, "fill" ); mlt_rect rect = mlt_properties_anim_get_rect( filter_properties, "rect", position, length ); if ( strchr( mlt_properties_get( filter_properties, "rect" ), '%' ) ) { rect.x *= qimg->width(); rect.w *= qimg->width(); rect.y *= qimg->height(); rect.h *= qimg->height(); } double scale = mlt_profile_scale_width(profile, width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, height); rect.y *= scale; rect.h *= scale; QRectF r( rect.x, rect.y, rect.w, rect.h ); QPainter p( qimg ); setup_graph_painter( p, r, filter_properties ); if ( show_channel == -1 ) // Combine all channels { if( channels > 1 ) { int16_t* in = audio; int16_t* out = audio; for ( int s = 0; s < samples; s++ ) { double acc = 0.0; for ( int c = 0; c < channels; c++ ) { acc += *in++; } *out++ = acc / channels; } channels = 1; } show_channel = 1; } if ( show_channel == 0 ) // Show all channels { QRectF c_rect = r; qreal c_height = r.height() / channels; for ( int c = 0; c < channels; c++ ) { // Divide the rectangle into smaller rectangles for each channel. c_rect.setY( r.y() + c_height * c ); c_rect.setHeight( c_height ); setup_graph_pen( p, c_rect, filter_properties, scale ); paint_waveform( p, c_rect, audio + c, samples, channels, fill ); } } else if ( show_channel > 0 ) { // Show one specific channel if ( show_channel > channels ) { // Sanity show_channel = 1; } setup_graph_pen( p, r, filter_properties, scale ); paint_waveform( p, r, audio + show_channel - 1, samples, channels, fill ); } p.end(); } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable ) { int error = 0; mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame ); private_data* pdata = (private_data*)filter->child; save_buffer* audio = (save_buffer*)mlt_properties_get_data( frame_properties, pdata->buffer_prop_name, NULL ); if( audio ) { // Get the current image *image_format = mlt_image_rgb24a; error = mlt_frame_get_image( frame, image, image_format, width, height, writable ); // Draw the waveforms if( !error ) { QImage qimg( *width, *height, QImage::Format_ARGB32 ); convert_mlt_to_qimage_rgba( *image, &qimg, *width, *height ); draw_waveforms( filter, frame, &qimg, audio->buffer, audio->channels, audio->samples, *width, *height ); convert_qimage_to_mlt_rgba( &qimg, *image, *width, *height ); } } else { // This filter depends on the consumer processing the audio before // the video. mlt_log_warning( MLT_FILTER_SERVICE(filter), "Audio not preprocessed.\n" ); return mlt_frame_get_image( frame, image, image_format, width, height, writable ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); if( mlt_frame_is_test_card( frame ) ) { // The producer does not generate video. This filter will create an // image on the producer's behalf. mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( mlt_frame_get_original_producer( frame ) ) ); mlt_properties_set_int( frame_properties, "progressive", 1 ); mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_profile_sar( profile ) ); mlt_properties_set_int( frame_properties, "meta.media.width", profile->width ); mlt_properties_set_int( frame_properties, "meta.media.height", profile->height ); // Tell the framework that there really is an image. mlt_properties_set_int( frame_properties, "test_image", 0 ); // Push a callback to create the image. mlt_frame_push_get_image( frame, create_image ); } mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, (void*)filter_get_audio ); mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } static void filter_close( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if ( pdata ) { free( pdata->window_buffer ); free( pdata->buffer_prop_name ); free( pdata ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_audiowaveform_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); if( filter && pdata ) { if ( !createQApplicationIfNeeded( MLT_FILTER_SERVICE(filter) ) ) { mlt_filter_close( filter ); return NULL; } mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set( filter_properties, "bgcolor", "0x00000000" ); mlt_properties_set( filter_properties, "color.1", "0xffffffff" ); mlt_properties_set( filter_properties, "thickness", "0" ); mlt_properties_set( filter_properties, "show_channel", "0" ); mlt_properties_set( filter_properties, "angle", "0" ); mlt_properties_set( filter_properties, "rect", "0 0 100% 100%" ); mlt_properties_set( filter_properties, "fill", "0" ); mlt_properties_set( filter_properties, "gorient", "v" ); mlt_properties_set_int( filter_properties, "window", 0 ); pdata->reset_window = 1; // Create a unique ID for storing data on the frame pdata->buffer_prop_name = (char*)calloc( 1, 20 ); snprintf( pdata->buffer_prop_name, 20, "audiowave.%p", filter ); pdata->buffer_prop_name[20 - 1] = '\0'; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; mlt_events_listen( filter_properties, filter, "property-changed", (mlt_listener)property_changed ); } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Failed to initialize\n" ); if( filter ) { mlt_filter_close( filter ); } if( pdata ) { free( pdata ); } filter = NULL; } return filter; } } mlt-6.20.0/src/modules/qt/filter_audiowaveform.yml000066400000000000000000000074561362234133600222350ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: audiowaveform title: Audio Waveform Filter version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that draws an audio waveform on the image. parameters: - identifier: bgcolor title: Background Color type: color description: | The background color to be applied to the entire frame. The default color is transparent. A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: color.* title: Foreground color type: color description: | The color of the waveform. Multiple colors can be specified with incrementing suffixes to cause the waveform to be drawn in a gradient. color.1 is the top of the waveform. Subsequent colors will produce a gradient toward the bottom. By default, the filter has one color defined: color.1=0xffffffff" This results in a white waveform. To create a gradient, define more colors: color.2=green color.3=0x77777777 A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: thickness title: Line Thickness type: integer description: > The thickness of the line used to draw the waveform. readonly: no default: 0 minimum: 0 maximum: 20 mutable: yes widget: spinner unit: pixels - identifier: show_channel title: Audio Channel type: integer description: > The audio channel to draw. "0" indicates that all channels should be drawn. "-1" indicates that all channels will be added together in a single waveform. readonly: no default: 0 minimum: 0 maximum: 20 mutable: yes widget: spinner - identifier: angle title: Angle type: float description: > The rotation angle to be applied to the waveform. readonly: no default: 0 minimum: 0 maximum: 360 mutable: yes widget: spinner - identifier: rect title: Rectangle description: > Defines the rectangle that the waveform(s) should be drawn in. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 100% 100%" readonly: no mutable: yes - identifier: fill title: Fill description: Whether the area under the waveform should be filled in. type: boolean default: 0 readonly: no mutable: yes widget: checkbox - identifier: gorient title: Gradient Orientation description: Direction of the color gradient. type: string default: vertical values: - vertical - horizontal readonly: no mutable: yes widget: combo - identifier: window title: Window type: integer description: > The duration of the audio (in ms) to be drawn in the waveform. If the window is less than the duration of a frame, the duration of a frame will be used. If the window is more than the duration of a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 0 mlt-6.20.0/src/modules/qt/filter_lightshow.cpp000066400000000000000000000252771362234133600213570ustar00rootroot00000000000000/* * filter_lightshow.cpp -- animate color to the audio * Copyright (C) 2015-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include // calloc(), free() #include // sin() #include #include // Private Constants static const double PI = 3.14159265358979323846; // Private Types typedef struct { mlt_filter fft; char* mag_prop_name; int rel_pos; int preprocess_warned; } private_data; static int filter_get_audio( mlt_frame frame, void** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples ) { mlt_filter filter = (mlt_filter)mlt_frame_pop_audio( frame ); mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; // Create the FFT filter the first time. if( !pdata->fft ) { mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); pdata->fft = mlt_factory_filter( profile, "fft", NULL ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( pdata->fft ), "window_size", mlt_properties_get_int( filter_properties, "window_size" ) ); if( !pdata->fft ) { mlt_log_warning( MLT_FILTER_SERVICE(filter), "Unable to create FFT.\n" ); return 1; } } mlt_properties fft_properties = MLT_FILTER_PROPERTIES( pdata->fft ); double low_freq = mlt_properties_get_int( filter_properties, "frequency_low" ); double hi_freq = mlt_properties_get_int( filter_properties, "frequency_high" ); double threshold = mlt_properties_get_int( filter_properties, "threshold" ); double osc = mlt_properties_get_int( filter_properties, "osc" ); float peak = 0; // The service must stay locked while using the private data mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // Perform FFT processing on the frame mlt_filter_process( pdata->fft, frame ); mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); float* bins = (float*)mlt_properties_get_data( fft_properties, "bins", NULL ); double window_level = mlt_properties_get_double( fft_properties, "window_level" ); if( bins && window_level == 1.0 ) { // Find the peak FFT magnitude in the configured range of frequencies int bin_count = mlt_properties_get_int( fft_properties, "bin_count" ); double bin_width = mlt_properties_get_double( fft_properties, "bin_width" ); int bin = 0; for( bin = 0; bin < bin_count; bin++ ) { double F = bin_width * (double)bin; if( F >= low_freq && F <= hi_freq ) { if( bins[bin] > peak ) { peak = bins[bin]; } } } } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); // Scale the magnitude to dB and apply oscillation double dB = peak > 0.0 ? 20 * log10( peak ) : -1000.0; double mag = 0.0; if( dB >= threshold ) { // Scale to range 0.0-1.0 mag = 1 - (dB / threshold); if( osc != 0 ) { // Apply the oscillation double fps = mlt_profile_fps( mlt_service_profile( MLT_FILTER_SERVICE(filter) ) ); double t = pdata->rel_pos / fps; mag = mag * sin( 2 * PI * osc * t ); } pdata->rel_pos++; } else { pdata->rel_pos = 1; mag = 0; } // Save the magnitude as a property on the frame to be used in get_image() mlt_properties_set_double( MLT_FRAME_PROPERTIES(frame), pdata->mag_prop_name, mag ); return 0; } static void setup_pen( QPainter& p, QRect& rect, mlt_properties filter_properties ) { QVector colors; bool color_found = true; // Find user specified colors for the gradient while( color_found ) { QString prop_name = QString("color.") + QString::number(colors.size() + 1); if( mlt_properties_get(filter_properties, prop_name.toUtf8().constData() ) ) { mlt_color mcolor = mlt_properties_get_color( filter_properties, prop_name.toUtf8().constData() ); colors.append( QColor( mcolor.r, mcolor.g, mcolor.b, mcolor.a ) ); } else { color_found = false; } } if( !colors.size() ) { // No color specified. Just use white. p.setBrush( Qt::white ); } else if( colors.size() == 1 ) { // Only use one color p.setBrush( colors[0] ); } else { // Use Gradient qreal sx = 1.0; qreal sy = 1.0; qreal dx = rect.x(); qreal dy = rect.y(); qreal radius = rect.width() / 2; if ( rect.width() > rect.height() ) { radius = rect.height() / 2; sx = (qreal)rect.width() / (qreal)rect.height(); } else if ( rect.height() > rect.width() ) { radius = rect.width() / 2; sy = (qreal)rect.height() / (qreal)rect.width(); } QPointF center( radius, radius ); QRadialGradient gradient( center, radius ); qreal step = 1.0 / ( colors.size() - 1 ); for( int i = 0; i < colors.size(); i++ ) { gradient.setColorAt( (qreal)i * step, colors[i] ); } QBrush brush( gradient ); QTransform transform( sx, 0.0, 0.0, 0.0, sy, 0.0, dx, dy, 1.0 ); brush.setTransform( transform ); p.setBrush( brush ); } p.setPen( QColor(0,0,0,0) ); // Clear pen } static void draw_light( mlt_properties filter_properties, QImage* qimg, mlt_rect* rect, double mag ) { QPainter p( qimg ); QRect r( rect->x, rect->y, rect->w, rect->h ); p.setRenderHint( QPainter::Antialiasing ); // Output transparency = input transparency p.setCompositionMode(QPainter::CompositionMode_SourceAtop); setup_pen( p, r, filter_properties ); p.setOpacity( mag ); p.drawRect( r ); p.end(); } /** Get the image. */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame ); private_data* pdata = (private_data*)filter->child; mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); if( mlt_properties_get( frame_properties, pdata->mag_prop_name ) ) { double mag = mlt_properties_get_double( frame_properties, pdata->mag_prop_name ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_rect rect = mlt_properties_anim_get_rect( filter_properties, "rect", position, length ); // Get the current image *format = mlt_image_rgb24a; error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( strchr( mlt_properties_get( filter_properties, "rect" ), '%' ) ) { rect.x *= *width; rect.w *= *width; rect.y *= *height; rect.h *= *height; } else { mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double scale = mlt_profile_scale_width(profile, *width); rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); rect.y *= scale; rect.h *= scale; } // Draw the light if( !error ) { QImage qimg( *width, *height, QImage::Format_ARGB32 ); convert_mlt_to_qimage_rgba( *image, &qimg, *width, *height ); draw_light( filter_properties, &qimg, &rect, mag ); convert_qimage_to_mlt_rgba( &qimg, *image, *width, *height ); } } else { if ( pdata->preprocess_warned++ == 2 ) { // This filter depends on the consumer processing the audio before // the video. mlt_log_warning( MLT_FILTER_SERVICE(filter), "Audio not preprocessed.\n" ); } mlt_frame_get_image( frame, image, format, width, height, writable ); } return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { if( mlt_frame_is_test_card( frame ) ) { // The producer does not generate video. This filter will create an // image on the producer's behalf. mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); mlt_properties_set_int( frame_properties, "progressive", 1 ); mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_profile_sar( profile ) ); mlt_properties_set_int( frame_properties, "meta.media.width", profile->width ); mlt_properties_set_int( frame_properties, "meta.media.height", profile->height ); // Tell the framework that there really is an image. mlt_properties_set_int( frame_properties, "test_image", 0 ); // Push a callback to create the image. mlt_frame_push_get_image( frame, create_image ); } mlt_frame_push_audio( frame, filter ); mlt_frame_push_audio( frame, (void*)filter_get_audio ); mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } static void filter_close( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if ( pdata ) { mlt_filter_close( pdata->fft ); free( pdata->mag_prop_name ); free( pdata ); } filter->child = NULL; filter->close = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } /** Constructor for the filter. */ extern "C" { mlt_filter filter_lightshow_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); if ( filter && pdata && createQApplicationIfNeeded( MLT_FILTER_SERVICE(filter) ) ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set_int( properties, "_filter_private", 1 ); mlt_properties_set_int( properties, "frequency_low", 20 ); mlt_properties_set_int( properties, "frequency_high", 20000 ); mlt_properties_set_double( properties, "threshold", -30.0 ); mlt_properties_set_double( properties, "osc", 5.0 ); mlt_properties_set( properties, "color.1", "0xffffffff" ); mlt_properties_set( properties, "rect", "0% 0% 100% 100%" ); mlt_properties_set_int( properties, "window_size", 2048 ); // Create a unique ID for storing data on the frame pdata->mag_prop_name = (char*)calloc( 1, 20 ); snprintf( pdata->mag_prop_name, 20, "fft_mag.%p", filter ); pdata->mag_prop_name[20 - 1] = '\0'; pdata->fft = 0; filter->close = filter_close; filter->process = filter_process; filter->child = pdata; } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter lightshow failed\n" ); if( filter ) { mlt_filter_close( filter ); } if( pdata ) { free( pdata ); } filter = NULL; } return filter; } } mlt-6.20.0/src/modules/qt/filter_lightshow.yml000066400000000000000000000063511362234133600213660ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: lightshow title: Light Show version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: > An audio visualization filter that colors the image proportional to the magnitude of the audio spectrum. parameters: - identifier: frequency_low title: Low Frequency type: integer description: > The low end of the frequency range to be used to influence the image motion. mutable: yes readonly: no default: 20 unit: Hz - identifier: frequency_high title: High Frequency type: integer description: > The high end of the frequency range to be used to influence the image motion. mutable: yes readonly: no default: 20000 unit: Hz - identifier: threshold title: Level Threshold type: float description: > The minimum amplitude of sound that must occur within the frequency range to cause the image to move. motion. mutable: yes readonly: no default: -30 minimum: -100 maximum: 0 unit: dB - identifier: osc title: Oscillation type: float description: > Oscillation can be useful to make the image move back and forth during long periods of sound. A value of 0 specifies no oscillation. mutable: yes readonly: no default: 5 minimum: 0 unit: Hz - identifier: color.* title: Light Color type: color description: | The color of the light. Multiple colors can be specified with incrementing suffixes to cause the waveform to be drawn in a circular gradient. color.1 is the inside of the circle. Subsequent colors will produce a gradient toward the outside. By default, the filter has one color defined: color.1=0xffffffff" This results in the image being lightened. To create a gradient, define more colors: color.2=green color.3=0x77777777 color.4=0x00000000 A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: rect title: Rectangle description: > Defines the rectangle that the color should be drawn in. Format is: "X Y W H". X, Y, W, H are assumed to be pixel units unless they have the suffix '%'. type: rect default: "0 0 100% 100%" readonly: no mutable: yes - identifier: window_size title: Window Size type: integer description: > The number of samples that the FFT will be performed on. If window_size is less than the number of samples in a frame, extra samples will be ignored. If window_size is more than the number of samples in a frame, samples will be buffered from previous frames to fill the window. The buffering is performed as a sliding window so that the most recent samples are always transformed. mutable: no readonly: no default: 2048 mlt-6.20.0/src/modules/qt/filter_qtblend.cpp000066400000000000000000000164561362234133600207770ustar00rootroot00000000000000/* * filter_lightshow.cpp -- animate color to the audio * Copyright (C) 2015 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include // calloc(), free() #include // sin() #include // strchr() #include #include #include /** Get the image. */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; // Get the filter mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); // Get the properties mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); bool hasAlpha = false; // Only process if we have no error and a valid colour space mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); double output_ar = mlt_profile_sar( profile ); // Special case - aspect_ratio = 0 if ( mlt_frame_get_aspect_ratio( frame ) == 0 ) mlt_frame_set_aspect_ratio( frame, output_ar ); // Check transform QTransform transform; int normalised_width = profile->width; int normalised_height = profile->height; double consumer_ar = mlt_profile_sar( profile ); // Destination rect mlt_rect rect = {0, 0, normalised_width * mlt_profile_scale_width(profile, *width), normalised_height * mlt_profile_scale_height(profile, *height), 1.0}; int b_width = mlt_properties_get_int( frame_properties, "meta.media.width" ); int b_height = mlt_properties_get_int( frame_properties, "meta.media.height" ); if ( b_height == 0 ) { b_width = normalised_width; b_height = normalised_height; } double b_ar = mlt_frame_get_aspect_ratio( frame ); double b_dar = b_ar * b_width / b_height; double opacity = 1.0; bool consumerScaling = false; if ( mlt_properties_get( properties, "rect" ) ) { rect = mlt_properties_anim_get_rect( properties, "rect", position, length ); if (mlt_properties_get(properties, "rect") && ::strchr(mlt_properties_get(properties, "rect"), '%')) { rect.x *= normalised_width; rect.y *= normalised_height; rect.w *= normalised_width; rect.h *= normalised_height; } double scale = mlt_profile_scale_width(profile, *width); if ( scale != 1.0 ) { consumerScaling = true; } rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); if ( !consumerScaling && scale != 1.0 ) { consumerScaling = true; } rect.y *= scale; rect.h *= scale; transform.translate(rect.x, rect.y); opacity = rect.o; hasAlpha = true; } if ( mlt_properties_get( properties, "rotation" ) ) { double angle = mlt_properties_anim_get_double( properties, "rotation", position, length ); if (angle != 0.0) { if ( mlt_properties_get_int( properties, "rotate_center" ) ) { transform.translate( rect.w / 2.0, rect.h / 2.0 ); transform.rotate( angle ); transform.translate( -rect.w / 2.0, -rect.h / 2.0 ); } else { // old style rotation (from top left corner) to keep compatibility transform.rotate( angle ); } hasAlpha = true; } } if ( !hasAlpha && ( mlt_properties_get_int( properties, "compositing" ) != 0 || b_width < *width || b_height < *height || b_width < normalised_width || b_height < normalised_height ) ) { hasAlpha = true; } if ( !hasAlpha ) { uint8_t *src_image = NULL; error = mlt_frame_get_image( frame, &src_image, format, &b_width, &b_height, 0 ); if ( *format == mlt_image_rgb24a || mlt_frame_get_alpha( frame ) ) { hasAlpha = true; } else { // Prepare output image *image = src_image; *width = b_width; *height = b_height; return 0; } } // fetch image *format = mlt_image_rgb24a; uint8_t *src_image = NULL; // Adjust if consumer is scaling if ( consumerScaling ) { // Scale request of b frame image to consumer scale maintaining its aspect ratio. b_height = *height; b_width = b_height * b_dar / b_ar; } error = mlt_frame_get_image( frame, &src_image, format, &b_width, &b_height, 0 ); // Put source buffer into QImage QImage sourceImage; convert_mlt_to_qimage_rgba( src_image, &sourceImage, b_width, b_height ); int image_size = mlt_image_format_size( *format, *width, *height, NULL ); // resize to rect if ( mlt_properties_get_int( properties, "distort" ) ) { transform.scale( rect.w / b_width, rect.h / b_height ); } else { // Determine scale with respect to aspect ratio. double geometry_dar = rect.w * consumer_ar / rect.h; double scale; if ( b_dar > geometry_dar ) { scale = rect.w / b_width; } else { scale = rect.h / b_height * b_ar; } // Center image in rect transform.translate( ( rect.w - ( b_width * scale ) ) / 2.0, ( rect.h - ( b_height * scale ) ) / 2.0 ); transform.scale( scale, scale ); } uint8_t *dest_image = NULL; dest_image = (uint8_t *) mlt_pool_alloc( image_size ); QImage destImage; convert_mlt_to_qimage_rgba( dest_image, &destImage, *width, *height ); destImage.fill( mlt_properties_get_int( properties, "background_color" ) ); QPainter painter( &destImage ); painter.setCompositionMode( ( QPainter::CompositionMode ) mlt_properties_get_int( properties, "compositing" ) ); painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform ); painter.setTransform(transform); painter.setOpacity(opacity); // Composite top frame painter.drawImage(0, 0, sourceImage); // finish Qt drawing painter.end(); convert_qimage_to_mlt_rgba( &destImage, dest_image, *width, *height ); *image = dest_image; mlt_frame_set_image( frame, *image, *width * *height * 4, mlt_pool_release ); return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ extern "C" { mlt_filter filter_qtblend_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); if ( filter && createQApplicationIfNeeded( MLT_FILTER_SERVICE(filter) ) ) { filter->process = filter_process; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); mlt_properties_set_int( properties, "rotate_center", 0 ); } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter qtblend failed\n" ); if( filter ) { mlt_filter_close( filter ); } filter = NULL; } return filter; } } mlt-6.20.0/src/modules/qt/filter_qtblend.yml000066400000000000000000000026611362234133600210070ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: qtblend title: Composite and transform version: 2 copyright: Meltytech, LLC creator: Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video description: > A filter allowing compositing and transform. parameters: - identifier: rect title: Rectangle type: rect description: > Keyframable rectangle specification. mutable: yes - identifier: compositing title: Composition mode description: > Defines which composition operation will be performed (see QPainter CompositionMode for doc). type: integer default: 0 minimum: 0 maximum: 40 mutable: yes widget: spinner - identifier: rotation title: Rotation angle description: > Angle for rotation. type: float default: 1 minimum: 0 maximum: 360 mutable: yes widget: spinner unit: degrees - identifier: rotate_center title: Rotate from center type: integer description: > Process the rotation from center if set, otherwise from top left corner minimum: 0 maximum: 1 mutable: yes widget: checkbox - identifier: background_color title: Background color type: integer description: > An integer formed like 0xaabbggrr that will be used as background color for the compositing operation. Defaults to 0 (fully transparent) default: 0 mutable: yes widget: color mlt-6.20.0/src/modules/qt/filter_qtext.cpp000066400000000000000000000247611362234133600205110ustar00rootroot00000000000000/* * filter_qtext.cpp -- text overlay filter * Copyright (c) 2018-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include static QRectF get_text_path( QPainterPath* qpath, mlt_properties filter_properties, const char* text, double scale ) { int outline = mlt_properties_get_int( filter_properties, "outline" ) * scale; char halign = mlt_properties_get( filter_properties, "halign" )[0]; char style = mlt_properties_get( filter_properties, "style" )[0]; int pad = mlt_properties_get_int( filter_properties, "pad" ) * scale; int offset = pad + ( outline / 2 ); int width = 0; int height = 0; qpath->setFillRule( Qt::WindingFill ); // Get the strings to display QString s = QString::fromUtf8(text); QStringList lines = s.split( "\n" ); // Configure the font QFont font; font.setPixelSize( mlt_properties_get_int( filter_properties, "size" ) * scale ); font.setFamily( mlt_properties_get( filter_properties, "family" ) ); font.setWeight( ( mlt_properties_get_int( filter_properties, "weight" ) / 10 ) -1 ); switch( style ) { case 'i': case 'I': font.setStyle( QFont::StyleItalic ); break; } QFontMetrics fm( font ); // Determine the text rectangle size height = fm.lineSpacing() * lines.size(); for( int i = 0; i < lines.size(); ++i ) { const QString line = lines[i]; int line_width = fm.width(line); int bearing = (line.size() > 0) ? fm.leftBearing(line.at(0)) : 0; if (bearing < 0) line_width -= bearing; bearing = (line.size() > 0) ? fm.rightBearing(line.at(line.size() - 1)) : 0; if (bearing < 0) line_width -= bearing; if (line_width > width) width = line_width; } // Lay out the text in the path int x = 0; int y = fm.ascent() + offset; for( int i = 0; i < lines.size(); ++i ) { QString line = lines.at(i); x = offset; int line_width = fm.width(line); int bearing = (line.size() > 0)? fm.leftBearing(line.at(0)) : 0; if (bearing < 0) { line_width -= bearing; x -= bearing; } bearing = (line.size() > 0)? fm.rightBearing(line.at(line.size() - 1)) : 0; if (bearing < 0) line_width -= bearing; switch( halign ) { default: case 'l': case 'L': break; case 'c': case 'C': x += ( width - line_width ) / 2; break; case 'r': case 'R': x += width - line_width; break; } qpath->addText( x, y, font, line ); y += fm.lineSpacing(); } // Account for outline and pad width += offset * 2; height += offset * 2; // Sanity check if( width == 0 ) width = 1; height += 2; // I found some fonts whose descenders get cut off. return QRectF( 0, 0, width, height ); } static QColor get_qcolor( mlt_properties filter_properties, const char* name ) { mlt_color color = mlt_properties_get_color( filter_properties, name ); return QColor( color.r, color.g, color.b, color.a ); } static QPen get_qpen( mlt_properties filter_properties ) { QColor color; int outline = mlt_properties_get_int( filter_properties, "outline" ); QPen pen; pen.setWidth( outline ); if( outline ) { color = get_qcolor( filter_properties, "olcolour" ); } else { color = get_qcolor( filter_properties, "bgcolour" ); } pen.setColor( color ); return pen; } static QBrush get_qbrush( mlt_properties filter_properties ) { QColor color = get_qcolor( filter_properties, "fgcolour" ); return QBrush ( color ); } static void transform_painter( QPainter* painter, mlt_rect frame_rect, QRectF path_rect, mlt_properties filter_properties, mlt_profile profile ) { qreal sx = 1.0; qreal sy = mlt_profile_sar( profile ); qreal path_width = path_rect.width() * sx; if( path_width > frame_rect.w ) { sx *= frame_rect.w / path_width; sy *= frame_rect.w / path_width; } qreal path_height = path_rect.height() * sy; if( path_height > frame_rect.h ) { sx *= frame_rect.h / path_height; sy *= frame_rect.h / path_height; } qreal dx = frame_rect.x; qreal dy = frame_rect.y; char halign = mlt_properties_get( filter_properties, "halign" )[0]; switch( halign ) { default: case 'l': case 'L': break; case 'c': case 'C': dx += ( frame_rect.w - ( sx * path_rect.width() ) ) / 2; break; case 'r': case 'R': dx += frame_rect.w - ( sx * path_rect.width() ); break; } char valign = mlt_properties_get( filter_properties, "valign" )[0]; switch( valign ) { default: case 't': case 'T': break; case 'm': case 'M': dy += ( frame_rect.h - ( sy * path_rect.height() ) ) / 2; break; case 'b': case 'B': dy += frame_rect.h - ( sy * path_rect.height() ); break; } QTransform transform; transform.translate( dx, dy ); transform.scale( sx, sy ); painter->setTransform( transform ); } static void paint_background( QPainter* painter, QRectF path_rect, mlt_properties filter_properties ) { QColor bg_color = get_qcolor( filter_properties, "bgcolour" ); painter->fillRect( path_rect, bg_color ); } static void paint_text( QPainter* painter, QPainterPath* qpath, mlt_properties filter_properties ) { QPen pen = get_qpen( filter_properties ); painter->setPen( pen ); QBrush brush = get_qbrush( filter_properties ); painter->setBrush( brush ); painter->drawPath( *qpath ); } static mlt_properties get_filter_properties( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = mlt_frame_get_unique_properties( frame, MLT_FILTER_SERVICE(filter) ); if ( !properties ) properties = MLT_FILTER_PROPERTIES(filter); return properties; } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *image_format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame ); char* argument = (char*)mlt_frame_pop_service( frame ); mlt_properties filter_properties = get_filter_properties( filter, frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); QString geom_str = QString::fromLatin1( mlt_properties_get( filter_properties, "geometry" ) ); if( geom_str.isEmpty() ) { free( argument ); mlt_log_warning( MLT_FILTER_SERVICE(filter), "geometry property not set\n" ); return mlt_frame_get_image( frame, image, image_format, width, height, writable ); } mlt_rect rect = mlt_properties_anim_get_rect( filter_properties, "geometry", position, length ); // Get the current image *image_format = mlt_image_rgb24a; mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "resize_alpha", 255 ); error = mlt_frame_get_image( frame, image, image_format, width, height, writable ); if( !error ) { double scale = mlt_profile_scale_width(profile, *width); if ( geom_str.contains('%') ) { rect.x *= *width; rect.w *= *width; rect.y *= *height; rect.h *= *height; } else { double scale_height = mlt_profile_scale_height(profile, *height); rect.x *= scale; rect.y *= scale_height; rect.w *= scale; rect.h *= scale_height; } QImage qimg( *width, *height, QImage::Format_ARGB32 ); convert_mlt_to_qimage_rgba( *image, &qimg, *width, *height ); QPainterPath text_path; QRectF path_rect = get_text_path( &text_path, filter_properties, argument, scale ); QPainter painter( &qimg ); painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); transform_painter( &painter, rect, path_rect, filter_properties, profile ); paint_background( &painter, path_rect, filter_properties ); paint_text( &painter, &text_path, filter_properties ); painter.end(); convert_qimage_to_mlt_rgba( &qimg, *image, *width, *height ); } free( argument ); return error; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties properties = get_filter_properties( filter, frame ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); char* argument = mlt_properties_anim_get( properties, "argument", position, length ); if ( !argument || !strcmp( "", argument ) ) return frame; // Save the text to be used by get_image() to support parallel processing // when this filter is encapsulated by other filters. mlt_frame_push_service( frame, strdup( argument ) ); // Push the filter on to the stack mlt_frame_push_service( frame, filter ); // Push the get_image on to the stack mlt_frame_push_get_image( frame, filter_get_image ); return frame; } /** Constructor for the filter. */ extern "C" { mlt_filter filter_qtext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); if( !filter ) return NULL; if ( !createQApplicationIfNeeded( MLT_FILTER_SERVICE(filter) ) ) { mlt_filter_close( filter ); return NULL; } filter->process = filter_process; mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); // Assign default values mlt_properties_set( filter_properties, "argument", arg ? arg: "text" ); mlt_properties_set( filter_properties, "geometry", "0%/0%:100%x100%:100%" ); mlt_properties_set( filter_properties, "family", "Sans" ); mlt_properties_set( filter_properties, "size", "48" ); mlt_properties_set( filter_properties, "weight", "400" ); mlt_properties_set( filter_properties, "style", "normal" ); mlt_properties_set( filter_properties, "fgcolour", "0x000000ff" ); mlt_properties_set( filter_properties, "bgcolour", "0x00000020" ); mlt_properties_set( filter_properties, "olcolour", "0x00000000" ); mlt_properties_set( filter_properties, "pad", "0" ); mlt_properties_set( filter_properties, "halign", "left" ); mlt_properties_set( filter_properties, "valign", "top" ); mlt_properties_set( filter_properties, "outline", "0" ); mlt_properties_set_int( filter_properties, "_filter_private", 1 ); return filter; } } mlt-6.20.0/src/modules/qt/filter_qtext.yml000066400000000000000000000070001362234133600205130ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: qtext title: QText version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: Overlay text onto the video parameters: - identifier: argument title: Text type: string description: | The text to overlay. required: yes readonly: no default: text widget: text - identifier: geometry title: Geometry type: rect description: A set of X/Y coordinates by which to adjust the text. default: 0%/0%:100%x100%:100 - identifier: family title: Font family type: string description: > The typeface of the font. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner unit: pixels - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x000000ff readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. default: 0x00000020 readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner unit: pixels - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner unit: pixels - identifier: halign title: Horizontal alignment description: > Set the horizontal alignment within the geometry rectangle. type: string default: left values: - left - centre - right mutable: yes widget: combo - identifier: valign title: Vertical alignment description: > Set the vertical alignment within the geometry rectangle. type: string default: top values: - top - middle - bottom mutable: yes widget: combo mlt-6.20.0/src/modules/qt/gpl000066400000000000000000000000001362234133600157540ustar00rootroot00000000000000mlt-6.20.0/src/modules/qt/graph.cpp000066400000000000000000000146671362234133600171040ustar00rootroot00000000000000/* * Copyright (c) 2015-2020 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "graph.h" #include #include /* * Apply the "bgcolor" and "angle" parameters to the QPainter */ void setup_graph_painter( QPainter& p, QRectF& r, mlt_properties filter_properties ) { mlt_color bg_color = mlt_properties_get_color( filter_properties, "bgcolor" ); double angle = mlt_properties_get_double( filter_properties, "angle" ); p.setRenderHint(QPainter::Antialiasing); // Fill background if ( bg_color.r || bg_color.g || bg_color.g || bg_color.a ) { QColor qbgcolor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ); p.fillRect( 0, 0, p.device()->width(), p.device()->height(), qbgcolor ); } // Apply rotation if ( angle ) { p.translate( r.x() + r.width() / 2, r.y() + r.height() / 2 ); p.rotate( angle ); p.translate( -(r.x() + r.width() / 2), -(r.y() + r.height() / 2) ); } } /* * Apply the "thickness", "gorient" and "color.x" parameters to the QPainter */ void setup_graph_pen(QPainter& p, QRectF& r, mlt_properties filter_properties , double scale) { int thickness = mlt_properties_get_int( filter_properties, "thickness" ) * scale; QString gorient = mlt_properties_get( filter_properties, "gorient" ); QVector colors; bool color_found = true; QPen pen; pen.setWidth( qAbs(thickness) ); // Find user specified colors for the gradient while( color_found ) { QString prop_name = QString("color.") + QString::number(colors.size() + 1); if( mlt_properties_get(filter_properties, prop_name.toUtf8().constData() ) ) { mlt_color mcolor = mlt_properties_get_color( filter_properties, prop_name.toUtf8().constData() ); colors.append( QColor( mcolor.r, mcolor.g, mcolor.b, mcolor.a ) ); } else { color_found = false; } } if( !colors.size() ) { // No color specified. Just use white. pen.setBrush(Qt::white); } else if ( colors.size() == 1 ) { // Only use one color pen.setBrush(colors[0]); } else { QLinearGradient gradient; if( gorient.startsWith("h", Qt::CaseInsensitive) ) { gradient.setStart ( r.x(), r.y() ); gradient.setFinalStop ( r.x() + r.width(), r.y() ); } else { // Vertical gradient.setStart ( r.x(), r.y() ); gradient.setFinalStop ( r.x(), r.y() + r.height() ); } qreal step = 1.0 / ( colors.size() - 1 ); for( int i = 0; i < colors.size(); i++ ) { gradient.setColorAt( (qreal)i * step, colors[i] ); } pen.setBrush(gradient); } p.setPen(pen); } static inline void fix_point( QPointF& point, QRectF& rect ) { if( point.x() < rect.x() ) { point.setX( rect.x() ); } else if ( point.x() > rect.x() + rect.width() ) { point.setX( rect.x() + rect.width() ); } if( point.y() < rect.y() ) { point.setY( rect.y() ); } else if ( point.y() > rect.y() + rect.height() ) { point.setY( rect.y() + rect.height() ); } } void paint_line_graph( QPainter& p, QRectF& rect, int points, float* values, double tension, int fill ) { double width = rect.width(); double height = rect.height(); double pixelsPerPoint = width / (double)(points - 1); // Calculate cubic control points QVector controlPoints( (points - 1) * 2 ); int cpi = 0; // First control point is equal to first point controlPoints[cpi++] = QPointF( rect.x(), rect.y() + height - values[0] * height ); // Calculate control points between data points // Based on ideas from: // http://scaledinnovation.com/analytics/splines/aboutSplines.html for( int i = 0; i < (points - 2); i++ ) { double x0 = rect.x() + (double)i * pixelsPerPoint; double x1 = rect.x() + (double)(i + 1) * pixelsPerPoint; double x2 = rect.x() + (double)(i + 2) * pixelsPerPoint; double y0 = rect.y() + height - values[i] * height; double y1 = rect.y() + height - values[i + 1] * height; double y2 = rect.y() + height - values[i + 2] * height; double d01 = sqrt( pow( x1 - x0, 2 ) + pow( y1 - y0, 2) ); double d12 = sqrt( pow( x2 - x1, 2 ) + pow( y2 - y1, 2) ); double fa = tension * d01 / ( d01 + d12 ); double fb = tension * d12 / ( d01 + d12 ); double p1x = x1 - fa * ( x2 - x0 ); double p1y = y1 - fa * ( y2 - y0 ); QPointF c1( p1x, p1y ); fix_point( c1, rect ); double p2x = x1 + fb * ( x2 - x0 ); double p2y = y1 + fb * ( y2 - y0 ); QPointF c2( p2x, p2y ); fix_point( c2, rect ); controlPoints[cpi++] = c1; controlPoints[cpi++] = c2; } // Last control point is equal to last point controlPoints[cpi++] = QPointF( rect.x() + width, rect.y() + height - values[points - 1] * height ); // Draw a sequence of bezier curves to interpolate between points. QPainterPath curvePath; // Begin at the first data point. curvePath.moveTo( rect.x(), rect.y() + height - values[0] * height ); cpi = 0; for( int i = 1; i < points; i++ ) { QPointF c1 = controlPoints[cpi++]; QPointF c2 = controlPoints[cpi++]; double endX = rect.x() + (double)i * pixelsPerPoint; double endY = rect.y() + height - values[i] * height; QPointF end( endX, endY ); curvePath.cubicTo( c1, c2, end ); } if( fill ) { curvePath.lineTo( rect.x() + width, rect.y() + height ); curvePath.lineTo( rect.x(), rect.y() + height ); curvePath.closeSubpath(); p.fillPath( curvePath, p.pen().brush() ); } else { p.drawPath( curvePath ); } } void paint_bar_graph( QPainter& p, QRectF& rect, int points, float* values ) { double width = rect.width(); double height = rect.height(); double pixelsPerPoint = width / (double)points; QPointF bottomPoint( rect.x() + pixelsPerPoint / 2, rect.y() + height ); QPointF topPoint( rect.x() + pixelsPerPoint / 2, rect.y() ); for( int i = 0; i < points; i++ ) { topPoint.setY( rect.y() + height - values[i] * height ); p.drawLine( bottomPoint, topPoint ); bottomPoint.setX( bottomPoint.x() + pixelsPerPoint ); topPoint.setX( bottomPoint.x() ); } } mlt-6.20.0/src/modules/qt/graph.h000066400000000000000000000024671362234133600165440ustar00rootroot00000000000000/* * Copyright (c) 2015-2020 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GRAPH_H #define GRAPH_H #include #include #include void setup_graph_painter( QPainter& p, QRectF& rect, mlt_properties filter_properties ); void setup_graph_pen( QPainter& p, QRectF& rect, mlt_properties filter_properties, double scale ); void paint_line_graph( QPainter& p, QRectF& rect, int points, float* values, double tension, int fill ); void paint_bar_graph( QPainter& p, QRectF& rect, int points, float* values ); #endif // GRAPH_H mlt-6.20.0/src/modules/qt/kdenlivetitle_wrapper.cpp000077500000000000000000001065531362234133600224050ustar00rootroot00000000000000/* * kdenlivetitle_wrapper.cpp -- kdenlivetitle wrapper * Copyright (c) 2009 Marco Gittler * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "kdenlivetitle_wrapper.h" #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x040600 #include #include #include #endif Q_DECLARE_METATYPE(QTextCursor); // Private Constants static const double PI = 3.14159265358979323846; class ImageItem: public QGraphicsItem { public: ImageItem(QImage img) { m_img = img; } virtual QRectF boundingRect() const { return QRectF(0, 0, m_img.width(), m_img.height()); } virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget* ) { painter->setRenderHint(QPainter::SmoothPixmapTransform, true); painter->drawImage(QPoint(), m_img); } private: QImage m_img; }; void blur( QImage& image, int radius ) { int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 }; int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1]; int r1 = 0; int r2 = image.height() - 1; int c1 = 0; int c2 = image.width() - 1; int bpl = image.bytesPerLine(); int rgba[4]; unsigned char* p; int i1 = 0; int i2 = 3; for (int col = c1; col <= c2; col++) { p = image.scanLine(r1) + col * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p += bpl; for (int j = r1; j < r2; j++, p += bpl) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int row = r1; row <= r2; row++) { p = image.scanLine(row) + c1 * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p += 4; for (int j = c1; j < c2; j++, p += 4) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int col = c1; col <= c2; col++) { p = image.scanLine(r2) + col * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p -= bpl; for (int j = r1; j < r2; j++, p -= bpl) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } for (int row = r1; row <= r2; row++) { p = image.scanLine(row) + c2 * 4; for (int i = i1; i <= i2; i++) rgba[i] = p[i] << 4; p -= 4; for (int j = c1; j < c2; j++, p -= 4) for (int i = i1; i <= i2; i++) p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; } } class PlainTextItem: public QGraphicsItem { public: PlainTextItem(QString text, QFont font, double width, double height, QBrush brush, QColor outlineColor, double outline, int align, int lineSpacing) { m_boundingRect = QRectF(0, 0, width, height); m_brush = brush; m_outline = outline; m_pen = QPen(outlineColor); m_pen.setWidthF(outline); QFontMetrics metrics(font); lineSpacing += metrics.lineSpacing(); m_path.setFillRule(Qt::WindingFill); // Calculate line width QStringList lines = text.split('\n'); double linePos = metrics.ascent(); foreach(const QString &line, lines) { QPainterPath linePath; linePath.addText(0, linePos, font, line); linePos += lineSpacing; if ( align == Qt::AlignHCenter ) { double offset = (width - metrics.width(line)) / 2; linePath.translate(offset, 0); } else if ( align == Qt::AlignRight ) { double offset = (width - metrics.width(line)); linePath.translate(offset, 0); } m_path.addPath(linePath); } } virtual QRectF boundingRect() const { return m_boundingRect; } virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem * option, QWidget* w) { if ( !m_shadow.isNull() ) { painter->drawImage(m_shadowOffset, m_shadow); } painter->fillPath(m_path, m_brush); if ( m_outline > 0 ) { painter->strokePath(m_path, m_pen); } } void addShadow(QStringList params) { if (params.count() < 5 || params.at( 0 ).toInt() == false) { // Invalid or no shadow wanted return; } // Build shadow image QColor shadowColor = QColor( params.at( 1 ) ); int blurRadius = params.at( 2 ).toInt(); int offsetX = params.at( 3 ).toInt(); int offsetY = params.at( 4 ).toInt(); m_shadow = QImage( m_boundingRect.width() + abs( offsetX ) + 4 * blurRadius, m_boundingRect.height() + abs( offsetY ) + 4 * blurRadius, QImage::Format_ARGB32_Premultiplied ); m_shadow.fill( Qt::transparent ); QPainterPath shadowPath = m_path; offsetX -= 2 * blurRadius; offsetY -= 2 * blurRadius; m_shadowOffset = QPoint( offsetX, offsetY ); shadowPath.translate(2 * blurRadius, 2 * blurRadius); QPainter shadowPainter( &m_shadow ); shadowPainter.fillPath( shadowPath, QBrush( shadowColor ) ); shadowPainter.end(); blur( m_shadow, blurRadius ); } private: QRectF m_boundingRect; QImage m_shadow; QPoint m_shadowOffset; QPainterPath m_path; QBrush m_brush; QPen m_pen; double m_outline; }; QRectF stringToRect( const QString & s ) { QStringList l = s.split( ',' ); if ( l.size() < 4 ) return QRectF(); return QRectF( l.at( 0 ).toDouble(), l.at( 1 ).toDouble(), l.at( 2 ).toDouble(), l.at( 3 ).toDouble() ).normalized(); } QColor stringToColor( const QString & s ) { QStringList l = s.split( ',' ); if ( l.size() < 4 ) return QColor(); return QColor( l.at( 0 ).toInt(), l.at( 1 ).toInt(), l.at( 2 ).toInt(), l.at( 3 ).toInt() ); ; } QTransform stringToTransform( const QString& s ) { QStringList l = s.split( ',' ); if ( l.size() < 9 ) return QTransform(); return QTransform( l.at( 0 ).toDouble(), l.at( 1 ).toDouble(), l.at( 2 ).toDouble(), l.at( 3 ).toDouble(), l.at( 4 ).toDouble(), l.at( 5 ).toDouble(), l.at( 6 ).toDouble(), l.at( 7 ).toDouble(), l.at( 8 ).toDouble() ); } static void qscene_delete( void *data ) { QGraphicsScene *scene = ( QGraphicsScene * )data; if (scene) delete scene; scene = NULL; } void loadFromXml( producer_ktitle self, QGraphicsScene *scene, const char *templateXml, const char *templateText ) { scene->clear(); mlt_producer producer = &self->parent; self->has_alpha = true; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); QDomDocument doc; QString data = QString::fromUtf8(templateXml); QString replacementText = QString::fromUtf8(templateText); doc.setContent(data); QDomElement title = doc.documentElement(); // Check for invalid title if ( title.isNull() || title.tagName() != "kdenlivetitle" ) return; // Check title locale if ( title.hasAttribute( "LC_NUMERIC" ) ) { QString locale = title.attribute( "LC_NUMERIC" ); QLocale::setDefault( locale ); } int originalWidth; int originalHeight; if ( title.hasAttribute("width") ) { originalWidth = title.attribute("width").toInt(); originalHeight = title.attribute("height").toInt(); scene->setSceneRect(0, 0, originalWidth, originalHeight); } else { originalWidth = scene->sceneRect().width(); originalHeight = scene->sceneRect().height(); } if ( title.hasAttribute( "out" ) ) { mlt_properties_set_position( producer_props, "_animation_out", title.attribute( "out" ).toDouble() ); } else { mlt_properties_set_position( producer_props, "_animation_out", mlt_producer_get_out( producer ) ); } mlt_properties_set_int( producer_props, "meta.media.width", originalWidth ); mlt_properties_set_int( producer_props, "meta.media.height", originalHeight ); QDomNode node; QDomNodeList items = title.elementsByTagName("item"); for ( int i = 0; i < items.count(); i++ ) { QGraphicsItem *gitem = NULL; node = items.item( i ); QDomNamedNodeMap nodeAttributes = node.attributes(); int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); if ( zValue > -1000 ) { if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsTextItem" ) { QDomNamedNodeMap txtProperties = node.namedItem( "content" ).attributes(); QFont font( txtProperties.namedItem( "font" ).nodeValue() ); QDomNode propsNode = txtProperties.namedItem( "font-bold" ); if ( !propsNode.isNull() ) { // Old: Bold/Not bold. font.setBold( propsNode.nodeValue().toInt() ); } else { // New: Font weight (QFont::) font.setWeight( txtProperties.namedItem( "font-weight" ).nodeValue().toInt() ); } font.setItalic( txtProperties.namedItem( "font-italic" ).nodeValue().toInt() ); font.setUnderline( txtProperties.namedItem( "font-underline" ).nodeValue().toInt() ); int letterSpacing = txtProperties.namedItem( "font-spacing" ).nodeValue().toInt(); if ( letterSpacing != 0 ) { font.setLetterSpacing( QFont::AbsoluteSpacing, letterSpacing ); } // Older Kdenlive version did not store pixel size but point size if ( txtProperties.namedItem( "font-pixel-size" ).isNull() ) { QFont f2; f2.setPointSize( txtProperties.namedItem( "font-size" ).nodeValue().toInt() ); font.setPixelSize( QFontInfo( f2 ).pixelSize() ); } else { font.setPixelSize( txtProperties.namedItem( "font-pixel-size" ).nodeValue().toInt() ); } if ( !txtProperties.namedItem( "letter-spacing" ).isNull() ) { font.setLetterSpacing(QFont::AbsoluteSpacing, txtProperties.namedItem( "letter-spacing" ).nodeValue().toInt()); } QColor col( stringToColor( txtProperties.namedItem( "font-color" ).nodeValue() ) ); QString text = node.namedItem( "content" ).firstChild().nodeValue(); if ( !replacementText.isEmpty() ) { text = text.replace( "%s", replacementText ); } QColor outlineColor(stringToColor( txtProperties.namedItem( "font-outline-color" ).nodeValue() ) ); int align = 1; if ( txtProperties.namedItem( "alignment" ).isNull() == false ) { align = txtProperties.namedItem( "alignment" ).nodeValue().toInt(); } double boxWidth = 0; double boxHeight = 0; if ( txtProperties.namedItem( "box-width" ).isNull() ) { // This is an old version title, find out dimensions from QGraphicsTextItem QGraphicsTextItem *txt = scene->addText(text, font); QRectF br = txt->boundingRect(); boxWidth = br.width(); boxHeight = br.height(); scene->removeItem(txt); delete txt; } else { boxWidth = txtProperties.namedItem( "box-width" ).nodeValue().toDouble(); boxHeight = txtProperties.namedItem( "box-height" ).nodeValue().toDouble(); } QBrush brush; if ( txtProperties.namedItem( "gradient" ).isNull() == false ) { // Calculate gradient QString gradientData = txtProperties.namedItem( "gradient" ).nodeValue(); QStringList values = gradientData.split(";"); if (values.count() < 5) { // invalid gradient, use default values = QStringList() << "#ff0000" << "#2e0046" << "0" << "100" << "90"; } QLinearGradient gr; gr.setColorAt(values.at(2).toDouble() / 100, values.at(0)); gr.setColorAt(values.at(3).toDouble() / 100, values.at(1)); double angle = values.at(4).toDouble(); if (angle <= 90) { gr.setStart(0, 0); gr.setFinalStop(boxWidth * cos( angle * PI / 180 ), boxHeight * sin( angle * PI / 180 )); } else { gr.setStart(boxWidth, 0); gr.setFinalStop(boxWidth + boxWidth * cos( angle * PI / 180 ), boxHeight * sin( angle * PI / 180 )); } brush = QBrush(gr); } else { brush = QBrush(col); } if ( txtProperties.namedItem( "compatibility" ).isNull() ) { // Workaround Qt5 crash in threaded drawing of QGraphicsTextItem, paint by ourselves PlainTextItem *txt = new PlainTextItem(text, font, boxWidth, boxHeight, brush, outlineColor, txtProperties.namedItem("font-outline").nodeValue().toDouble(), align, txtProperties.namedItem("line-spacing").nodeValue().toInt()); if ( txtProperties.namedItem( "shadow" ).isNull() == false ) { QStringList values = txtProperties.namedItem( "shadow" ).nodeValue().split(";"); txt->addShadow(values); } scene->addItem( txt ); gitem = txt; } else { QGraphicsTextItem *txt = scene->addText(text, font); gitem = txt; if (txtProperties.namedItem("font-outline").nodeValue().toDouble()>0.0){ QTextDocument *doc = txt->document(); // Make sure some that the text item does not request refresh by itself doc->blockSignals(true); QTextCursor cursor(doc); cursor.select(QTextCursor::Document); QTextCharFormat format; format.setTextOutline( QPen(QColor( stringToColor( txtProperties.namedItem( "font-outline-color" ).nodeValue() ) ), txtProperties.namedItem("font-outline").nodeValue().toDouble(), Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin) ); format.setForeground(QBrush(col)); cursor.mergeCharFormat(format); } else { txt->setDefaultTextColor( col ); } // Effects if (!txtProperties.namedItem( "typewriter" ).isNull()) { // typewriter effect mlt_properties_set_int( producer_props, "_animated", 1 ); QStringList effetData = QStringList() << "typewriter" << text << txtProperties.namedItem( "typewriter" ).nodeValue(); txt->setData(0, effetData); if ( !txtProperties.namedItem( "textwidth" ).isNull() ) txt->setData( 1, txtProperties.namedItem( "textwidth" ).nodeValue() ); } if ( txtProperties.namedItem( "alignment" ).isNull() == false ) { txt->setTextWidth( txt->boundingRect().width() ); QTextOption opt = txt->document()->defaultTextOption (); opt.setAlignment(( Qt::Alignment ) txtProperties.namedItem( "alignment" ).nodeValue().toInt() ); txt->document()->setDefaultTextOption (opt); } if ( !txtProperties.namedItem( "kdenlive-axis-x-inverted" ).isNull() ) { //txt->setData(OriginXLeft, txtProperties.namedItem("kdenlive-axis-x-inverted").nodeValue().toInt()); } if ( !txtProperties.namedItem( "kdenlive-axis-y-inverted" ).isNull() ) { //txt->setData(OriginYTop, txtProperties.namedItem("kdenlive-axis-y-inverted").nodeValue().toInt()); } if ( !txtProperties.namedItem("preferred-width").isNull() ) { txt->setTextWidth( txtProperties.namedItem("preferred-width").nodeValue().toInt() ); } } } else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsRectItem" ) { QDomNamedNodeMap rectProperties = node.namedItem( "content" ).attributes(); QRectF rect = stringToRect( rectProperties.namedItem( "rect" ).nodeValue() ); QString pen_str = rectProperties.namedItem( "pencolor" ).nodeValue(); double penwidth = rectProperties.namedItem( "penwidth") .nodeValue().toDouble(); QBrush brush; if ( !rectProperties.namedItem( "gradient" ).isNull() ) { // Calculate gradient QString gradientData = rectProperties.namedItem( "gradient" ).nodeValue(); QStringList values = gradientData.split(";"); if (values.count() < 5) { // invalid gradient, use default values = QStringList() << "#ff0000" << "#2e0046" << "0" << "100" << "90"; } QLinearGradient gr; gr.setColorAt(values.at(2).toDouble() / 100, values.at(0)); gr.setColorAt(values.at(3).toDouble() / 100, values.at(1)); double angle = values.at(4).toDouble(); if (angle <= 90) { gr.setStart(0, 0); gr.setFinalStop(rect.width() * cos( angle * PI / 180 ), rect.height() * sin( angle * PI / 180 )); } else { gr.setStart(rect.width(), 0); gr.setFinalStop(rect.width() + rect.width()* cos( angle * PI / 180 ), rect.height() * sin( angle * PI / 180 )); } brush = QBrush(gr); } else { brush = QBrush(stringToColor( rectProperties.namedItem( "brushcolor" ).nodeValue() ) ); } QPen pen; if ( penwidth == 0 ) { pen = QPen( Qt::NoPen ); } else { pen = QPen( QBrush( stringToColor( pen_str ) ), penwidth, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin ); } QGraphicsRectItem *rec = scene->addRect( rect, pen, brush ); gitem = rec; } else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsPixmapItem" ) { const QString url = node.namedItem( "content" ).attributes().namedItem( "url" ).nodeValue(); const QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); QImage img; if (base64.isEmpty()){ img.load(url); }else{ img.loadFromData(QByteArray::fromBase64(base64.toLatin1())); } ImageItem *rec = new ImageItem(img); scene->addItem( rec ); gitem = rec; } else if ( nodeAttributes.namedItem( "type" ).nodeValue() == "QGraphicsSvgItem" ) { QString url = items.item(i).namedItem("content").attributes().namedItem("url").nodeValue(); QString base64 = items.item(i).namedItem("content").attributes().namedItem("base64").nodeValue(); QGraphicsSvgItem *rec = NULL; if (base64.isEmpty()){ rec = new QGraphicsSvgItem(url); }else{ rec = new QGraphicsSvgItem(); QSvgRenderer *renderer= new QSvgRenderer(QByteArray::fromBase64(base64.toLatin1()), rec ); rec->setSharedRenderer(renderer); } if (rec){ scene->addItem(rec); gitem = rec; } } } //pos and transform if ( gitem ) { QPointF p( node.namedItem( "position" ).attributes().namedItem( "x" ).nodeValue().toDouble(), node.namedItem( "position" ).attributes().namedItem( "y" ).nodeValue().toDouble() ); gitem->setPos( p ); gitem->setTransform( stringToTransform( node.namedItem( "position" ).firstChild().firstChild().nodeValue() ) ); int zValue = nodeAttributes.namedItem( "z-index" ).nodeValue().toInt(); gitem->setZValue( zValue ); #if QT_VERSION >= 0x040600 // effects QDomNode eff = items.item(i).namedItem("effect"); if (!eff.isNull()) { QDomElement e = eff.toElement(); if (e.attribute("type") == "blur") { QGraphicsBlurEffect *blur = new QGraphicsBlurEffect(); blur->setBlurRadius(e.attribute("blurradius").toInt()); gitem->setGraphicsEffect(blur); } else if (e.attribute("type") == "shadow") { QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); shadow->setBlurRadius(e.attribute("blurradius").toInt()); shadow->setOffset(e.attribute("xoffset").toInt(), e.attribute("yoffset").toInt()); gitem->setGraphicsEffect(shadow); } } #endif } } QDomNode n = title.firstChildElement("background"); if (!n.isNull()) { QColor color = QColor( stringToColor( n.attributes().namedItem( "color" ).nodeValue() ) ); self->has_alpha = color.alpha() != 255; if (color.alpha() > 0) { QGraphicsRectItem *rec = scene->addRect(0, 0, scene->width(), scene->height() , QPen( Qt::NoPen ), QBrush( color ) ); rec->setZValue(-1100); } } QString startRect; n = title.firstChildElement( "startviewport" ); // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport if (!n.isNull() && !n.toElement().hasAttribute("x")) { startRect = n.attributes().namedItem( "rect" ).nodeValue(); } n = title.firstChildElement( "endviewport" ); // Check if node exists, if it has an x attribute, it is an old version title, don't use viewport if (!n.isNull() && !n.toElement().hasAttribute("x")) { QString rect = n.attributes().namedItem( "rect" ).nodeValue(); if (startRect != rect) mlt_properties_set( producer_props, "_endrect", rect.toUtf8().data() ); } if (!startRect.isEmpty()) { mlt_properties_set( producer_props, "_startrect", startRect.toUtf8().data() ); } return; } void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, mlt_image_format format, int width, int height, double position, int force_refresh ) { // Obtain the producer mlt_producer producer = &self->parent; mlt_profile profile = mlt_service_profile ( MLT_PRODUCER_SERVICE( producer ) ) ; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); // Obtain properties of frame mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); pthread_mutex_lock( &self->mutex ); // Check if user wants us to reload the image or if we need animation bool animated = mlt_properties_get( producer_props, "_endrect" ) != NULL; if ( mlt_properties_get( producer_props, "_animated" ) != NULL || force_refresh == 1 || width != self->current_width || height != self->current_height || animated ) { if ( !animated ) { // Cache image only if no animation self->current_image = NULL; mlt_properties_set_data( producer_props, "_cached_image", NULL, 0, NULL, NULL ); } mlt_properties_set_int( producer_props, "force_reload", 0 ); } int image_size = width * height * 4; if ( self->current_image == NULL || animated ) { // restore QGraphicsScene QGraphicsScene *scene = static_cast (mlt_properties_get_data( producer_props, "qscene", NULL )); self->current_alpha = NULL; if ( force_refresh == 1 && scene ) { scene = NULL; mlt_properties_set_data( producer_props, "qscene", NULL, 0, NULL, NULL ); } if ( scene == NULL ) { if ( !createQApplicationIfNeeded( MLT_PRODUCER_SERVICE(producer) ) ) { pthread_mutex_unlock( &self->mutex ); return; } if ( !QMetaType::type("QTextCursor") ) qRegisterMetaType( "QTextCursor" ); scene = new QGraphicsScene(); scene->setItemIndexMethod( QGraphicsScene::NoIndex ); scene->setSceneRect(0, 0, mlt_properties_get_int( properties, "width" ), mlt_properties_get_int( properties, "height" )); if ( mlt_properties_get( producer_props, "resource" ) && mlt_properties_get( producer_props, "resource" )[0] != '\0' ) { // The title has a resource property, so we read all properties from the resource. // Do not serialize the xmldata loadFromXml( self, scene, mlt_properties_get( producer_props, "_xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); } else { // The title has no resource, all data should be serialized loadFromXml( self, scene, mlt_properties_get( producer_props, "xmldata" ), mlt_properties_get( producer_props, "templatetext" ) ); } mlt_properties_set_data( producer_props, "qscene", scene, 0, ( mlt_destructor )qscene_delete, NULL ); } QRectF start = stringToRect( QString( mlt_properties_get( producer_props, "_startrect" ) ) ); QRectF end = stringToRect( QString( mlt_properties_get( producer_props, "_endrect" ) ) ); const QRectF source( 0, 0, width, height ); if (start.isNull()) { start = QRectF( 0, 0, mlt_properties_get_int( producer_props, "meta.media.width" ), mlt_properties_get_int( producer_props, "meta.media.height" ) ); } // Effects QList items = scene->items(); QGraphicsTextItem *titem = NULL; for (int i = 0; i < items.count(); i++) { titem = static_cast ( items.at( i ) ); if (titem && !titem->data( 0 ).isNull()) { QStringList params = titem->data( 0 ).toStringList(); if (params.at( 0 ) == "typewriter" ) { // typewriter effect has 2 param values: // the keystroke delay and a start offset, both in frames QStringList values = params.at( 2 ).split( ";" ); int interval = qMax( 0, ( ( int ) position - values.at( 1 ).toInt()) / values.at( 0 ).toInt() ); QTextCursor cursor = titem->textCursor(); cursor.movePosition(QTextCursor::EndOfBlock); // get the font format QTextCharFormat format = cursor.charFormat(); cursor.select(QTextCursor::Document); QString txt = params.at( 1 ).left( interval ); // If the string to insert is empty, insert a space / linebreak so that we don't loose // formatting infos for the next iterations int lines = params.at( 1 ).count( '\n' ); QString empty = " "; for (int i = 0; i < lines; i++) empty.append( "\n " ); cursor.insertText( txt.isEmpty() ? empty : txt, format ); if ( !titem->data( 1 ).isNull() ) titem->setTextWidth( titem->data( 1 ).toDouble() ); } } } //must be extracted from kdenlive title self->rgba_image = (uint8_t *) mlt_pool_alloc( image_size ); #if QT_VERSION >= 0x050200 // QImage::Format_RGBA8888 was added in Qt5.2 // Initialize the QImage with the MLT image because the data formats match. QImage img( self->rgba_image, width, height, QImage::Format_RGBA8888 ); #else QImage img( width, height, QImage::Format_ARGB32 ); #endif img.fill( 0 ); QPainter p1; p1.begin( &img ); p1.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); //| QPainter::SmoothPixmapTransform ); mlt_position anim_out = mlt_properties_get_position( producer_props, "_animation_out" ); if (end.isNull()) { scene->render( &p1, source, start, Qt::IgnoreAspectRatio ); } else if ( position > anim_out ) { scene->render( &p1, source, end, Qt::IgnoreAspectRatio ); } else { double percentage = 0; if ( position && anim_out ) percentage = position / anim_out; QPointF topleft = start.topLeft() + ( end.topLeft() - start.topLeft() ) * percentage; QPointF bottomRight = start.bottomRight() + ( end.bottomRight() - start.bottomRight() ) * percentage; const QRectF r1( topleft, bottomRight ); scene->render( &p1, source, r1, Qt::IgnoreAspectRatio ); if ( profile && !profile->progressive ){ int line=0; double percentage_next_filed = ( position + 0.5 ) / anim_out; QPointF topleft_next_field = start.topLeft() + ( end.topLeft() - start.topLeft() ) * percentage_next_filed; QPointF bottomRight_next_field = start.bottomRight() + ( end.bottomRight() - start.bottomRight() ) * percentage_next_filed; const QRectF r2( topleft_next_field, bottomRight_next_field ); QImage img1( width, height, QImage::Format_ARGB32 ); img1.fill( 0 ); QPainter p2; p2.begin(&img1); p2.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); scene->render(&p2,source,r2, Qt::IgnoreAspectRatio ); p2.end(); int next_field_line = ( mlt_properties_get_int( producer_props, "top_field_first" ) ? 1 : 0 ); for (line = next_field_line ;lineformat = mlt_image_rgb24a; convert_qimage_to_mlt_rgba(&img, self->rgba_image, width, height); self->current_image = (uint8_t *) mlt_pool_alloc( image_size ); memcpy( self->current_image, self->rgba_image, image_size ); mlt_properties_set_data( producer_props, "_cached_buffer", self->rgba_image, image_size, mlt_pool_release, NULL ); mlt_properties_set_data( producer_props, "_cached_image", self->current_image, image_size, mlt_pool_release, NULL ); self->current_width = width; self->current_height = height; uint8_t *alpha = NULL; if ( ( alpha = mlt_frame_get_alpha( frame ) ) ) { self->current_alpha = (uint8_t*) mlt_pool_alloc( width * height ); memcpy( self->current_alpha, alpha, width * height ); mlt_properties_set_data( producer_props, "_cached_alpha", self->current_alpha, width * height, mlt_pool_release, NULL ); } } // Convert image to requested format if ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) { uint8_t *buffer = NULL; if ( self->format != mlt_image_rgb24a ) { // Image buffer was previously converted, revert to original rgba buffer self->current_image = (uint8_t *) mlt_pool_alloc( image_size ); memcpy(self->current_image, self->rgba_image, image_size); mlt_properties_set_data( producer_props, "_cached_image", self->current_image, image_size, mlt_pool_release, NULL ); self->format = mlt_image_rgb24a; } // First, set the image so it can be converted when we get it mlt_frame_replace_image( frame, self->current_image, self->format, width, height ); mlt_frame_set_image( frame, self->current_image, image_size, NULL ); self->format = format; // get_image will do the format conversion mlt_frame_get_image( frame, &buffer, &format, &width, &height, 0 ); // cache copies of the image and alpha buffers if ( buffer ) { image_size = mlt_image_format_size( format, width, height, NULL ); self->current_image = (uint8_t*) mlt_pool_alloc( image_size ); memcpy( self->current_image, buffer, image_size ); mlt_properties_set_data( producer_props, "_cached_image", self->current_image, image_size, mlt_pool_release, NULL ); } if ( ( buffer = mlt_frame_get_alpha( frame ) ) ) { self->current_alpha = (uint8_t*) mlt_pool_alloc( width * height ); memcpy( self->current_alpha, buffer, width * height ); mlt_properties_set_data( producer_props, "_cached_alpha", self->current_alpha, width * height, mlt_pool_release, NULL ); } } pthread_mutex_unlock( &self->mutex ); mlt_properties_set_int( properties, "width", self->current_width ); mlt_properties_set_int( properties, "height", self->current_height ); } mlt-6.20.0/src/modules/qt/kdenlivetitle_wrapper.h000066400000000000000000000030101362234133600220270ustar00rootroot00000000000000/* * kdenlivetitle_wrapper.h -- kdenlivetitle wrapper * Copyright (c) 2009 Marco Gittler * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #ifdef __cplusplus extern "C" { #endif #include #include #include struct producer_ktitle_s { struct mlt_producer_s parent; uint8_t *rgba_image; uint8_t *current_image; uint8_t *current_alpha; mlt_image_format format; int current_width; int current_height; int has_alpha; pthread_mutex_t mutex; }; typedef struct producer_ktitle_s *producer_ktitle; extern void drawKdenliveTitle( producer_ktitle self, mlt_frame frame, mlt_image_format format, int, int, double, int ); #ifdef __cplusplus } #endif mlt-6.20.0/src/modules/qt/producer_kdenlivetitle.c000066400000000000000000000153171362234133600222020ustar00rootroot00000000000000/* * producer_kdenlivetitle.c -- kdenlive producer * Copyright (c) 2009 Marco Gittler * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "kdenlivetitle_wrapper.h" #include #include #include void read_xml(mlt_properties properties) { const char *resource = mlt_properties_get( properties, "resource" ); FILE *f = mlt_fopen( resource, "r" ); if ( f != NULL ) { int size = 0; long lSize; if ( fseek (f , 0 , SEEK_END) < 0 ) goto error; lSize = ftell (f); if ( lSize <= 0 ) goto error; rewind (f); char *infile = (char*) mlt_pool_alloc(lSize + 1); if ( infile ) { size = fread(infile,1,lSize,f); if ( size ) { infile[size] = '\0'; mlt_properties_set(properties, "_xmldata", infile); } mlt_pool_release( infile ); } error: fclose(f); } } static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; /* Obtain properties of frame */ mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); /* Obtain the producer for this frame */ producer_ktitle self = mlt_properties_get_data( properties, "producer_kdenlivetitle", NULL ); /* Obtain properties of producer */ mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 ) *width = mlt_properties_get_int( properties, "rescale_width" ); if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 ) *height = mlt_properties_get_int( properties, "rescale_height" ); mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); /* Allocate the image */ if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { if ( mlt_properties_get_int( producer_props, "force_reload" ) > 1 ) read_xml( producer_props ); mlt_properties_set_int( producer_props, "force_reload", 0 ); drawKdenliveTitle( self, frame, *format, *width, *height, mlt_frame_original_position( frame ), 1 ); } else { drawKdenliveTitle( self, frame, *format, *width, *height, mlt_frame_original_position( frame ), 0 ); } // Get width and height (may have changed during the refresh) *width = mlt_properties_get_int( properties, "width" ); *height = mlt_properties_get_int( properties, "height" ); *format = self->format; if ( self->current_image ) { // Clone the image and the alpha int image_size = mlt_image_format_size( self->format, self->current_width, self->current_height, NULL ); uint8_t *image_copy = mlt_pool_alloc( image_size ); // We use height-1 because mlt_image_format_size() uses height + 1. // XXX Remove -1 when mlt_image_format_size() is changed. memcpy( image_copy, self->current_image, mlt_image_format_size( self->format, self->current_width, self->current_height - 1, NULL ) ); // Now update properties so we free the copy after mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); // We're going to pass the copy on *buffer = image_copy; // Clone the alpha channel if ( self->current_alpha ) { image_copy = mlt_pool_alloc( self->current_width * self->current_height ); memcpy( image_copy, self->current_alpha, self->current_width * self->current_height ); mlt_frame_set_alpha( frame, image_copy, self->current_width * self->current_height, mlt_pool_release ); } } else { error = 1; } mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); return error; } static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { // Get the real structure for this producer producer_ktitle this = producer->child; /* 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_kdenlivetitle", this, 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", mlt_properties_get_int( producer_props, "progressive" ) ); double force_ratio = mlt_properties_get_double( producer_props, "force_aspect_ratio" ); if ( force_ratio > 0.0 ) mlt_properties_set_double( properties, "aspect_ratio", force_ratio ); else 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; } static void producer_close( mlt_producer producer ) { producer_ktitle self = producer->child; producer->close = NULL; mlt_service_cache_purge( MLT_PRODUCER_SERVICE( producer ) ); mlt_producer_close( producer ); free( self ); } mlt_producer producer_kdenlivetitle_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) { /* Create a new producer object */ producer_ktitle self = calloc( 1, sizeof( struct producer_ktitle_s ) ); if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) { mlt_producer producer = &self->parent; /* 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; mlt_properties_set( properties, "resource", filename ); mlt_properties_set_int( properties, "progressive", 1 ); mlt_properties_set_int( properties, "aspect_ratio", 1 ); mlt_properties_set_int( properties, "seekable", 1 ); read_xml(properties); return producer; } free( self ); return NULL; } mlt-6.20.0/src/modules/qt/producer_kdenlivetitle.yml000066400000000000000000000003561362234133600225560ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: kdenlivetitle title: Kdenlive Titler version: 2 copyright: Marco Gittler, Jean-Baptiste Mardelle creator: Marco Gittler, Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video mlt-6.20.0/src/modules/qt/producer_qimage.c000066400000000000000000000277041362234133600206050ustar00rootroot00000000000000/* * producer_image.c -- a QT/QImage based producer for MLT * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "qimage_wrapper.h" #include #include #include #include #include #include #include static void load_filenames( producer_qimage self, 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 ); static void refresh_length( mlt_properties properties, producer_qimage self ) { if ( self->count > mlt_properties_get_int( properties, "length" ) || mlt_properties_get_int( properties, "autolength" ) ) { int ttl = mlt_properties_get_int( properties, "ttl" ); mlt_position length = self->count * ttl; mlt_properties_set_position( properties, "length", length ); mlt_properties_set_position( properties, "out", length - 1 ); } } static void on_property_changed( mlt_service owner, mlt_producer producer, char *name ) { if ( !strcmp( name, "ttl" ) ) refresh_length( MLT_PRODUCER_PROPERTIES(producer), producer->child ); } mlt_producer producer_qimage_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) { producer_qimage self = calloc( 1, sizeof( struct producer_qimage_s ) ); if ( self != NULL && mlt_producer_init( &self->parent, self ) == 0 ) { mlt_producer producer = &self->parent; // Get the properties interface mlt_properties properties = MLT_PRODUCER_PROPERTIES( &self->parent ); // Initialize KDE image plugins if ( !init_qimage( filename ) ) { // Reject if animation. mlt_producer_close( producer ); free( self ); return NULL; } // 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 ); mlt_properties_set_int( properties, "seekable", 1 ); // Validate the resource if ( filename ) load_filenames( self, properties ); if ( self->count ) { mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); if ( frame ) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_properties_set_data( frame_properties, "producer_qimage", self, 0, NULL, NULL ); mlt_frame_set_position( frame, mlt_producer_position( producer ) ); refresh_qimage( self, frame ); mlt_cache_item_close( self->qimage_cache ); mlt_frame_close( frame ); } } if ( self->current_width == 0 ) { producer_close( producer ); producer = NULL; } else { mlt_events_listen( properties, self, "property-changed", (mlt_listener) on_property_changed ); } return producer; } free( self ); return NULL; } static int load_svg( producer_qimage self, mlt_properties properties, const char *filename ) { int result = 0; // Read xml string if ( strstr( filename, " start && ( end[0] == 'd' || end[0] == 'i' || end[0] == 'u' ) ) { int n = end - start; char *s = calloc( 1, n + 1 ); strncpy( s, start, n ); mlt_properties_set( properties, "begin", s ); free( s ); s = calloc( 1, strlen( filename ) + 2 ); strncpy( s, filename, start - filename ); sprintf( s + ( start - filename ), ".%d%s", n, end ); result = load_sequence_sprintf( self, properties, s ); free( s ); } } return result; } static int load_sequence_querystring( producer_qimage self, mlt_properties properties, const char *filename ) { int result = 0; // Obtain filenames with pattern and begin value in query string if ( strchr( filename, '%' ) && strchr( filename, '?' ) ) { // Split filename into pattern and query string char *s = strdup( filename ); char *querystring = strrchr( s, '?' ); *querystring++ = '\0'; if ( strstr( filename, "begin=" ) ) mlt_properties_set( properties, "begin", strstr( querystring, "begin=" ) + 6 ); else if ( strstr( filename, "begin:" ) ) mlt_properties_set( properties, "begin", strstr( querystring, "begin:" ) + 6 ); // Coerce to an int value so serialization does not have any extra query string cruft mlt_properties_set_int( properties, "begin", mlt_properties_get_int( properties, "begin" ) ); result = load_sequence_sprintf( self, properties, s ); free( s ); } return result; } static int load_folder( producer_qimage self, mlt_properties properties, const char *filename ) { int result = 0; // Obtain filenames within folder 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( self->filenames, dir_name, wildcard, 1 ); free( dir_name ); result = 1; } return result; } static void load_filenames( producer_qimage self, mlt_properties properties ) { char *filename = mlt_properties_get( properties, "resource" ); self->filenames = mlt_properties_new( ); if (!load_svg( self, properties, filename ) && !load_sequence_querystring( self, properties, filename ) && !load_sequence_sprintf( self, properties, filename ) && !load_sequence_deprecated( self, properties, filename ) && !load_folder( self, properties, filename ) ) { mlt_properties_set( self->filenames, "0", filename ); } self->count = mlt_properties_count( self->filenames ); refresh_length( properties, self ); } static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); producer_qimage self = mlt_properties_get_data( properties, "producer_qimage", NULL ); mlt_producer producer = &self->parent; // Use the width and height suggested by the rescale filter because we can do our own scaling. if ( mlt_properties_get_int( properties, "rescale_width" ) > 0 ) *width = mlt_properties_get_int( properties, "rescale_width" ); if ( mlt_properties_get_int( properties, "rescale_height" ) > 0 ) *height = mlt_properties_get_int( properties, "rescale_height" ); mlt_service_lock( MLT_PRODUCER_SERVICE( &self->parent ) ); // Refresh the image self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); self->qimage = mlt_cache_item_data( self->qimage_cache, NULL ); self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" ); self->current_image = mlt_cache_item_data( self->image_cache, NULL ); self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" ); self->current_alpha = mlt_cache_item_data( self->alpha_cache, &self->alpha_size ); refresh_image( self, frame, *format, *width, *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" ); *format = self->format; // 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 ( self->current_image ) { // Clone the image and the alpha int image_size = mlt_image_format_size( self->format, self->current_width, self->current_height, NULL ); uint8_t *image_copy = mlt_pool_alloc( image_size ); memcpy( image_copy, self->current_image, image_size ); // Now update properties so we free the copy after mlt_frame_set_image( frame, image_copy, image_size, mlt_pool_release ); // We're going to pass the copy on *buffer = image_copy; mlt_log_debug( MLT_PRODUCER_SERVICE( &self->parent ), "%dx%d (%s)\n", self->current_width, self->current_height, mlt_image_format_name( *format ) ); // Clone the alpha channel if ( self->current_alpha ) { if ( !self->alpha_size ) self->alpha_size = self->current_width * self->current_height; uint8_t * alpha_copy = mlt_pool_alloc( self->alpha_size ); memcpy( alpha_copy, self->current_alpha, self->alpha_size ); mlt_frame_set_alpha( frame, alpha_copy, self->alpha_size, mlt_pool_release ); } } else { error = 1; } // Release references and locks mlt_cache_item_close( self->qimage_cache ); mlt_cache_item_close( self->image_cache ); mlt_cache_item_close( self->alpha_cache ); mlt_service_unlock( MLT_PRODUCER_SERVICE( &self->parent ) ); return error; } static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { // Get the real structure for this producer producer_qimage self = producer->child; // Fetch the producers properties mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); if ( self->filenames == NULL && mlt_properties_get( producer_properties, "resource" ) != NULL ) load_filenames( self, producer_properties ); // Generate a frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); if ( *frame != NULL && self->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", self, 0, NULL, NULL ); // Update timecode on the frame we're creating mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Refresh the image self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); self->qimage = mlt_cache_item_data( self->qimage_cache, NULL ); refresh_qimage( self, *frame ); mlt_cache_item_close( self->qimage_cache ); // Set producer-specific frame properties mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( producer_properties, "progressive" ) ); double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" ); if ( force_ratio > 0.0 ) mlt_properties_set_double( properties, "aspect_ratio", force_ratio ); else mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "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; } static void producer_close( mlt_producer parent ) { producer_qimage self = parent->child; parent->close = NULL; mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) ); mlt_producer_close( parent ); mlt_properties_close( self->filenames ); free( self ); } mlt-6.20.0/src/modules/qt/producer_qimage.yml000066400000000000000000000062541362234133600211610ustar00rootroot00000000000000schema_version: 0.3 type: producer identifier: qimage title: Qt QImage version: 2 copyright: Visual Media ? creator: Charles Yates license: GPLv2 language: en tags: - Video description: > A still graphics to video generator using Qt QImage notes: > QImage 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. parameters: - identifier: resource title: File type: string description: > The name of a graphics file loadable by Qt. If "%" in filename, the filename is used with sprintf to generate a filename from a counter for multi-file/flipbook animation. The file sequence ends when numeric discontinuity exceeds 100. If the file sequence does not begin within the count of 100 you can pass the begin property like a query string parameter, for example: anim-%04d.png?begin=1000. If filename contains "/.all.", suffix with an extension to load all pictures with matching extension from a directory. If filename contains the string " Reload the file instead of using its cached image. This property automatically resets itself once it has been set 1 and processed. minimum: 0 maximum: 1 mutable: yes - identifier: disable_exif title: Disable auto-rotation type: boolean default: 0 widget: checkbox - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio mutable: yes - identifier: autolength title: Automatically compute length description: Whether to automatically compute the length and out point for an image sequence. type: boolean default: 0 widget: checkbox mlt-6.20.0/src/modules/qt/producer_qtext.cpp000066400000000000000000000415571362234133600210510ustar00rootroot00000000000000/* * producer_qtext.c -- text generating producer * Copyright (C) 2013 Brian Matherly * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include #include #include #include static void close_qimg( void* qimg ) { delete static_cast( qimg ); } static void close_qpath( void* qpath ) { delete static_cast( qpath ); } static void copy_qimage_to_mlt_image( QImage* qImg, uint8_t* mImg ) { int height = qImg->height(); int width = qImg->width(); int y = 0; // convert qimage to mlt y = height + 1; while ( --y ) { QRgb* src = (QRgb*) qImg->scanLine( height - y ); int x = width + 1; while ( --x ) { *mImg++ = qRed( *src ); *mImg++ = qGreen( *src ); *mImg++ = qBlue( *src ); *mImg++ = qAlpha( *src ); src++; } } } static void copy_image_to_alpha( uint8_t* image, uint8_t* alpha, int width, int height ) { register int len = width * height; // Extract the alpha mask from the RGBA image using Duff's Device register uint8_t *s = image + 3; // start on the alpha component register uint8_t *d = alpha; register int n = ( len + 7 ) / 8; switch ( len % 8 ) { case 0: do { *d++ = *s; s += 4; case 7: *d++ = *s; s += 4; case 6: *d++ = *s; s += 4; case 5: *d++ = *s; s += 4; case 4: *d++ = *s; s += 4; case 3: *d++ = *s; s += 4; case 2: *d++ = *s; s += 4; case 1: *d++ = *s; s += 4; } while ( --n > 0 ); } } /** Check if the qpath needs to be regenerated. Return true if regeneration is required. */ static bool check_qpath( mlt_properties producer_properties ) { #define MAX_SIG 1000 bool result = false; char* new_path_sig = new char[MAX_SIG]; // Generate a signature that represents the current properties snprintf( new_path_sig, MAX_SIG, "%s%s%s%s%s%s%s%s%s%s%s%s", mlt_properties_get( producer_properties, "text" ), mlt_properties_get( producer_properties, "fgcolour" ), mlt_properties_get( producer_properties, "bgcolour" ), mlt_properties_get( producer_properties, "olcolour" ), mlt_properties_get( producer_properties, "outline" ), mlt_properties_get( producer_properties, "align" ), mlt_properties_get( producer_properties, "pad" ), mlt_properties_get( producer_properties, "family" ), mlt_properties_get( producer_properties, "size" ), mlt_properties_get( producer_properties, "style" ), mlt_properties_get( producer_properties, "weight" ), mlt_properties_get( producer_properties, "encoding" ) ); new_path_sig[ MAX_SIG - 1 ] = '\0'; // Check if the properties have changed by comparing this signature with the // last one. char* last_path_sig = mlt_properties_get( producer_properties, "_path_sig" ); if( !last_path_sig || strcmp( new_path_sig, last_path_sig ) ) { mlt_properties_set( producer_properties, "_path_sig", new_path_sig ); result = true; } delete[] new_path_sig; return result; } static void generate_qpath( mlt_properties producer_properties ) { QPainterPath* qPath = static_cast( mlt_properties_get_data( producer_properties, "_qpath", NULL ) ); int outline = mlt_properties_get_int( producer_properties, "outline" ); char* align = mlt_properties_get( producer_properties, "align" ); char* style = mlt_properties_get( producer_properties, "style" ); char* text = mlt_properties_get( producer_properties, "text" ); char* encoding = mlt_properties_get( producer_properties, "encoding" ); int pad = mlt_properties_get_int( producer_properties, "pad" ); int offset = pad + ( outline / 2 ); int width = 0; int height = 0; // Make the path empty *qPath = QPainterPath(); qPath->setFillRule(Qt::WindingFill); // Get the strings to display QTextCodec *codec = QTextCodec::codecForName( encoding ); QTextDecoder *decoder = codec->makeDecoder(); QString s = decoder->toUnicode( text ); delete decoder; QStringList lines = s.split( "\n" ); // Configure the font QFont font; font.setPixelSize( mlt_properties_get_int( producer_properties, "size" ) ); font.setFamily( mlt_properties_get( producer_properties, "family" ) ); font.setWeight( ( mlt_properties_get_int( producer_properties, "weight" ) / 10 ) -1 ); switch( style[0] ) { case 'i': case 'I': font.setStyle( QFont::StyleItalic ); break; } QFontMetrics fm( font ); // Determine the text rectangle size height = fm.lineSpacing() * lines.size(); for( int i = 0; i < lines.size(); ++i ) { const QString line = lines[i]; int line_width = fm.width(line); int bearing = (line.size() > 0) ? fm.leftBearing(line.at(0)) : 0; if (bearing < 0) line_width -= bearing; bearing = (line.size() > 0) ? fm.rightBearing(line.at(line.size() - 1)) : 0; if (bearing < 0) line_width -= bearing; if (line_width > width) width = line_width; } // Lay out the text in the path int x = 0; int y = fm.ascent() + offset; for( int i = 0; i < lines.size(); ++i ) { QString line = lines.at(i); x = offset; int line_width = fm.width(line); int bearing = (line.size() > 0)? fm.leftBearing(line.at(0)) : 0; if (bearing < 0) { line_width -= bearing; x -= bearing; } bearing = (line.size() > 0)? fm.rightBearing(line.at(line.size() - 1)) : 0; if (bearing < 0) line_width -= bearing; switch( align[0] ) { default: case 'l': case 'L': break; case 'c': case 'C': x += ( width - line_width ) / 2; break; case 'r': case 'R': x += width - line_width; break; } qPath->addText( x, y, font, line ); y += fm.lineSpacing(); } // Account for outline and pad width += offset * 2; height += offset * 2; // Sanity check if( width == 0 ) width = 1; height += 2; // I found some fonts whose descenders get cut off. mlt_properties_set_int( producer_properties, "meta.media.width", width ); mlt_properties_set_int( producer_properties, "meta.media.height", height ); } static bool check_qimage( mlt_properties frame_properties ) { mlt_producer producer = static_cast( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); QImage* qImg = static_cast( mlt_properties_get_data( producer_properties, "_qimg", NULL ) ); QSize target_size( mlt_properties_get_int( frame_properties, "rescale_width" ), mlt_properties_get_int( frame_properties, "rescale_height" ) ); QSize native_size( mlt_properties_get_int( frame_properties, "meta.media.width" ), mlt_properties_get_int( frame_properties, "meta.media.height" ) ); // Check if the last image signature is different from the path signature // for this frame. char* last_img_sig = mlt_properties_get( producer_properties, "_img_sig" ); char* path_sig = mlt_properties_get( frame_properties, "_path_sig" ); if( !last_img_sig || strcmp( path_sig, last_img_sig ) ) { mlt_properties_set( producer_properties, "_img_sig", path_sig ); return true; } // Check if the last image size matches the requested image size QSize output_size = target_size; if( output_size.isEmpty() ) { output_size = native_size; } if( output_size != qImg->size() ) { return true; } return false; } static void generate_qimage( mlt_properties frame_properties ) { mlt_producer producer = static_cast( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); QImage* qImg = static_cast( mlt_properties_get_data( producer_properties, "_qimg", NULL ) ); QSize target_size( mlt_properties_get_int( frame_properties, "rescale_width" ), mlt_properties_get_int( frame_properties, "rescale_height" ) ); QSize native_size( mlt_properties_get_int( frame_properties, "meta.media.width" ), mlt_properties_get_int( frame_properties, "meta.media.height" ) ); QPainterPath* qPath = static_cast( mlt_properties_get_data( frame_properties, "_qpath", NULL ) ); mlt_color bg_color = mlt_properties_get_color( frame_properties, "_bgcolour" ); mlt_color fg_color = mlt_properties_get_color( frame_properties, "_fgcolour" ); mlt_color ol_color = mlt_properties_get_color( frame_properties, "_olcolour" ); int outline = mlt_properties_get_int( frame_properties, "_outline" ); qreal sx = 1.0; qreal sy = 1.0; // Create a new image and set up scaling if( !target_size.isEmpty() && target_size != native_size ) { *qImg = QImage( target_size, QImage::Format_ARGB32 ); sx = (qreal)target_size.width() / (qreal)native_size.width(); sy = (qreal)target_size.height() / (qreal)native_size.height(); } else { *qImg = QImage( native_size, QImage::Format_ARGB32 ); } qImg->fill( QColor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ).rgba() ); // Draw the text QPainter painter( qImg ); // Scale the painter rather than the image for better looking results. painter.scale( sx, sy ); painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); QPen pen; pen.setWidth( outline ); if( outline ) { pen.setColor( QColor( ol_color.r, ol_color.g, ol_color.b, ol_color.a ) ); } else { pen.setColor( QColor( bg_color.r, bg_color.g, bg_color.b, bg_color.a ) ); } painter.setPen( pen ); QBrush brush( QColor( fg_color.r, fg_color.g, fg_color.b, fg_color.a ) ); painter.setBrush( brush ); painter.drawPath( *qPath ); } static int producer_get_image( mlt_frame frame, uint8_t** buffer, mlt_image_format* format, int* width, int* height, int writable ) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = static_cast( mlt_properties_get_data( frame_properties, "_producer_qtext", NULL ) ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); int img_size = 0; int alpha_size = 0; uint8_t* alpha = NULL; QImage* qImg = static_cast( mlt_properties_get_data( producer_properties, "_qimg", NULL ) ); mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); // Regenerate the qimage if necessary if( check_qimage( frame_properties ) == true ) { generate_qimage( frame_properties ); } *format = mlt_image_rgb24a; *width = qImg->width(); *height = qImg->height(); // Allocate and fill the image buffer img_size = mlt_image_format_size( *format, *width, *height, NULL ); *buffer = static_cast( mlt_pool_alloc( img_size ) ); copy_qimage_to_mlt_image( qImg, *buffer ); mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); // Allocate and fill the alpha buffer alpha_size = *width * *height; alpha = static_cast( mlt_pool_alloc( alpha_size ) ); copy_image_to_alpha( *buffer, alpha, *width, *height ); // Update the frame mlt_frame_set_image( frame, *buffer, img_size, mlt_pool_release ); mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release ); mlt_properties_set_int( frame_properties, "width", *width ); mlt_properties_set_int( frame_properties, "height", *height ); 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 ) { mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); // Regenerate the QPainterPath if necessary if( check_qpath( producer_properties ) ) { generate_qpath( producer_properties ); } // Give the frame a copy of the painter path QPainterPath* prodPath = static_cast( mlt_properties_get_data( producer_properties, "_qpath", NULL ) ); QPainterPath* framePath = new QPainterPath( *prodPath ); mlt_properties_set_data( frame_properties, "_qpath", static_cast( framePath ), 0, close_qpath, NULL ); // Pass properties to the frame that will be needed to render the path mlt_properties_set( frame_properties, "_path_sig", mlt_properties_get( producer_properties, "_path_sig" ) ); mlt_properties_set( frame_properties, "_bgcolour", mlt_properties_get( producer_properties, "bgcolour" ) ); mlt_properties_set( frame_properties, "_fgcolour", mlt_properties_get( producer_properties, "fgcolour" ) ); mlt_properties_set( frame_properties, "_olcolour", mlt_properties_get( producer_properties, "olcolour" ) ); mlt_properties_set( frame_properties, "_outline", mlt_properties_get( producer_properties, "outline" ) ); mlt_properties_set_data( frame_properties, "_producer_qtext", static_cast( producer ), 0, NULL, NULL ); // Set frame properties mlt_properties_set_int( frame_properties, "progressive", 1 ); double force_ratio = mlt_properties_get_double( producer_properties, "force_aspect_ratio" ); if ( force_ratio > 0.0 ) mlt_properties_set_double( frame_properties, "aspect_ratio", force_ratio ); else mlt_properties_set_double( frame_properties, "aspect_ratio", 1.0); // Update time code on the frame mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Configure callbacks mlt_frame_push_get_image( *frame, producer_get_image ); } // Calculate the next time code mlt_producer_prepare_next( producer ); return 0; } static void producer_close( mlt_producer producer ) { producer->close = NULL; mlt_producer_close( producer ); free( producer ); } /** Initialize. */ extern "C" { mlt_producer producer_qtext_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) { // Create a new producer object mlt_producer producer = mlt_producer_new( profile ); mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer ); // Initialize the producer if ( producer ) { if ( !createQApplicationIfNeeded( MLT_PRODUCER_SERVICE(producer) ) ) { mlt_producer_close( producer ); return NULL; } mlt_properties_set( producer_properties, "text", "" ); mlt_properties_set( producer_properties, "fgcolour", "0xffffffff" ); mlt_properties_set( producer_properties, "bgcolour", "0x00000000" ); mlt_properties_set( producer_properties, "olcolour", "0x00000000" ); mlt_properties_set( producer_properties, "outline", "0" ); mlt_properties_set( producer_properties, "align", "left" ); mlt_properties_set( producer_properties, "pad", "0" ); mlt_properties_set( producer_properties, "family", "Sans" ); mlt_properties_set( producer_properties, "size", "48" ); mlt_properties_set( producer_properties, "style", "normal" ); mlt_properties_set( producer_properties, "weight", "400" ); mlt_properties_set( producer_properties, "encoding", "UTF-8" ); // Parse the filename argument if ( filename == NULL || !strcmp( filename, "" ) || strstr( filename, "" ) ) { } else if( filename[ 0 ] == '+' || strstr( filename, "/+" ) ) { char *copy = strdup( filename + 1 ); char *tmp = copy; if ( strstr( tmp, "/+" ) ) tmp = strstr( tmp, "/+" ) + 2; if ( strrchr( tmp, '.' ) ) ( *strrchr( tmp, '.' ) ) = '\0'; while ( strchr( tmp, '~' ) ) ( *strchr( tmp, '~' ) ) = '\n'; mlt_properties_set( producer_properties, "text", tmp ); mlt_properties_set( producer_properties, "resource", filename ); free( copy ); } else { mlt_properties_set( producer_properties, "resource", filename ); FILE *f = mlt_fopen( filename, "r" ); if ( f != NULL ) { char line[81]; char *tmp = NULL; size_t size = 0; line[80] = '\0'; while ( fgets( line, 80, f ) ) { size += strlen( line ) + 1; if ( tmp ) { tmp = (char*)realloc( tmp, size ); if ( tmp ) strcat( tmp, line ); } else { tmp = strdup( line ); } } fclose( f ); if ( tmp && tmp[ strlen( tmp ) - 1 ] == '\n' ) tmp[ strlen( tmp ) - 1 ] = '\0'; if ( tmp ) mlt_properties_set( producer_properties, "text", tmp ); free( tmp ); } } // Create QT objects to be reused. mlt_properties_set_data( producer_properties, "_qimg", static_cast( new QImage() ), 0, close_qimg, NULL ); mlt_properties_set_data( producer_properties, "_qpath", static_cast( new QPainterPath() ), 0, close_qpath, NULL ); // Callback registration producer->get_frame = producer_get_frame; producer->close = ( mlt_destructor )producer_close; } return producer; } } mlt-6.20.0/src/modules/qt/producer_qtext.yml000066400000000000000000000114521362234133600210570ustar00rootroot00000000000000schema_version: 0.3 type: producer identifier: qtext title: Qt Titler version: 1 copyright: Brian Matherly creator: Brian Matherly license: LGPLv2.1 language: en tags: - Video description: > A title generator that uses the Qt framework to render text. notes: > qtext accepts a file name with at ".txt" extension. If the filename begins with "+" the qtext producer interprets the filename as text. This is a shortcut to embed titles in melt commands. For MLT XML, it is recommended that you embed the title text in the "text" property value. qtext 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. parameters: - identifier: resource title: File type: string description: | A text file containing text to be rendered. The text file contents initialize the value of the "text" parameter. readonly: no argument: yes mutable: no widget: fileopen - identifier: text title: Text type: string description: | A text string to be rendered. readonly: no argument: yes mutable: yes widget: textbox - identifier: fgcolour title: Foreground color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: bgcolour title: Background color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: olcolour title: Outline color type: string description: > A color value is a hexadecimal representation of RGB plus alpha channel as 0xrrggbbaa. Colors can also be the words: white, black, red, green, or blue. You can also use a HTML-style color values #rrggbb or #aarrggbb. readonly: no mutable: yes widget: color - identifier: outline title: Outline Width type: string description: > The width of the outline in pixels. readonly: no default: 0 minimum: 0 maximum: 3 mutable: yes widget: spinner - identifier: align title: Paragraph alignment type: string description: > left, center, right readonly: no default: left mutable: yes widget: combo - identifier: pad title: Padding type: integer description: > The number of pixels to pad the background rectangle beyond edges of text. readonly: no default: 0 mutable: yes widget: spinner - identifier: family title: Font family type: string description: > The font typeface. default: Sans readonly: no mutable: yes widget: combo - identifier: size title: Font size type: integer description: > The size in pixels of the font. default: 48 readonly: no mutable: yes widget: spinner - identifier: style title: Font style type: string description: > The style of the font. values: - normal - italic default: normal readonly: no mutable: yes widget: combo - identifier: weight title: Font weight type: integer description: The weight of the font. minimum: 100 maximum: 1000 default: 400 readonly: no mutable: yes widget: spinner - identifier: encoding title: Encoding type: string description: > The text encoding type of the "text" parameter. default: UTF-8 readonly: no mutable: yes widget: combo - identifier: force_aspect_ratio title: Sample aspect ratio type: float description: Optionally override a (mis)detected aspect ratio mutable: yes - identifier: meta.media.width title: Real width type: integer description: The original, unscaled width of the rendered image. readonly: yes - identifier: meta.media.height title: Real height type: integer description: The original, unscaled height of the rendered image. readonly: yes - identifier: width title: Width type: integer description: The last requested scaled image width. readonly: yes - identifier: height title: Height type: integer description: The last requested scaled image height. readonly: yes mlt-6.20.0/src/modules/qt/qimage_wrapper.cpp000066400000000000000000000307651362234133600210030ustar00rootroot00000000000000/* * qimage_wrapper.cpp -- a QT/QImage based producer for MLT * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qimage_wrapper.h" #include "common.h" #ifdef USE_KDE4 #include #endif #include #include #include #include #include #include #ifdef USE_EXIF #include #endif #include #include #include #include extern "C" { #include #include #ifdef USE_KDE4 static KComponentData *instance = 0L; #endif static void qimage_delete( void *data ) { QImage *image = ( QImage * )data; delete image; image = NULL; #if defined(USE_KDE4) delete instance; instance = 0L; #endif } /// Returns false if this is animated. int init_qimage(const char *filename) { QImageReader reader; reader.setDecideFormatFromContent( true ); reader.setFileName( filename ); if ( reader.canRead() && reader.imageCount() > 1 ) { return 0; } #ifdef USE_KDE4 if ( !instance ) { instance = new KComponentData( "qimage_prod" ); } #endif return 1; } static QImage* reorient_with_exif( producer_qimage self, int image_idx, QImage *qimage ) { #ifdef USE_EXIF mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( &self->parent ); ExifData *d = exif_data_new_from_file( mlt_properties_get_value( self->filenames, image_idx ) ); ExifEntry *entry; int exif_orientation = 0; /* get orientation and rotate image accordingly if necessary */ if (d) { if ( ( entry = exif_content_get_entry ( d->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION ) ) ) exif_orientation = exif_get_short (entry->data, exif_data_get_byte_order (d)); /* Free the EXIF data */ exif_data_unref(d); } // Remember EXIF value, might be useful for someone mlt_properties_set_int( producer_props, "_exif_orientation" , exif_orientation ); if ( exif_orientation > 1 ) { // Rotate image according to exif data QImage processed; QMatrix matrix; switch ( exif_orientation ) { case 2: matrix.scale( -1, 1 ); break; case 3: matrix.rotate( 180 ); break; case 4: matrix.scale( 1, -1 ); break; case 5: matrix.rotate( 270 ); matrix.scale( -1, 1 ); break; case 6: matrix.rotate( 90 ); break; case 7: matrix.rotate( 90 ); matrix.scale( -1, 1 ); break; case 8: matrix.rotate( 270 ); break; } processed = qimage->transformed( matrix ); delete qimage; qimage = new QImage( processed ); } #endif return qimage; } int refresh_qimage( producer_qimage self, mlt_frame frame ) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = &self->parent; mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); // Check if user wants us to reload the image if ( mlt_properties_get_int( producer_props, "force_reload" ) ) { self->qimage = NULL; self->current_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_frame_original_position( frame ); position += mlt_producer_get_in( producer ); // Image index int image_idx = ( int )floor( ( double )position / ttl ) % self->count; int disable_exif = mlt_properties_get_int( producer_props, "disable_exif" ); if ( !createQApplicationIfNeeded( MLT_PRODUCER_SERVICE(producer) ) ) return -1; if ( image_idx != self->qimage_idx ) self->qimage = NULL; if ( !self->qimage || mlt_properties_get_int( producer_props, "_disable_exif" ) != disable_exif ) { self->current_image = NULL; QImageReader reader; reader.setDecideFormatFromContent( true ); reader.setFileName( QString::fromUtf8( mlt_properties_get_value( self->filenames, image_idx ) ) ); QImage *qimage = new QImage( reader.read() ); self->qimage = qimage; if ( !qimage->isNull( ) ) { // Read the exif value for this file if ( !disable_exif ) qimage = reorient_with_exif( self, image_idx, qimage ); // Register qimage for destruction and reuse mlt_cache_item_close( self->qimage_cache ); mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage", qimage, 0, ( mlt_destructor )qimage_delete ); self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); self->qimage_idx = image_idx; // Store the width/height of the qimage self->current_width = qimage->width( ); self->current_height = qimage->height( ); mlt_events_block( producer_props, NULL ); mlt_properties_set_int( producer_props, "meta.media.width", self->current_width ); mlt_properties_set_int( producer_props, "meta.media.height", self->current_height ); mlt_properties_set_int( producer_props, "_disable_exif", disable_exif ); mlt_events_unblock( producer_props, NULL ); } else { delete qimage; self->qimage = NULL; } } // Set width/height of frame mlt_properties_set_int( properties, "width", self->current_width ); mlt_properties_set_int( properties, "height", self->current_height ); return image_idx; } void refresh_image( producer_qimage self, mlt_frame frame, mlt_image_format format, int width, int height ) { // Obtain properties of frame and producer mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_producer producer = &self->parent; // Get index and qimage int image_idx = refresh_qimage( self, frame ); // optimization for subsequent iterations on single picture if ( image_idx != self->image_idx || width != self->current_width || height != self->current_height ) self->current_image = NULL; // If we have a qimage and need a new scaled image if ( self->qimage && ( !self->current_image || ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) ) ) { QString interps = mlt_properties_get( properties, "rescale.interp" ); bool interp = ( interps != "nearest" ) && ( interps != "none" ); QImage *qimage = static_cast( self->qimage ); int has_alpha = qimage->hasAlphaChannel(); QImage::Format qimageFormat = has_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32; // Note - the original qimage is already safe and ready for destruction if ( qimage->format() != qimageFormat ) { QImage temp = qimage->convertToFormat( qimageFormat ); qimage = new QImage( temp ); self->qimage = qimage; mlt_cache_item_close( self->qimage_cache ); mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage", qimage, 0, ( mlt_destructor )qimage_delete ); self->qimage_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.qimage" ); } QImage scaled = interp? qimage->scaled( QSize( width, height ), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) : qimage->scaled( QSize(width, height) ); // Convert scaled image to target format (it might be premultiplied after scaling). scaled = scaled.convertToFormat( qimageFormat ); // Store width and height self->current_width = width; self->current_height = height; // Allocate/define image self->format = has_alpha ? mlt_image_rgb24a : mlt_image_rgb24; int image_size = mlt_image_format_size( self->format, self->current_width, self->current_height, NULL ); self->current_image = ( uint8_t * )mlt_pool_alloc( image_size ); self->current_alpha = NULL; self->alpha_size = 0; // Copy the image int y = self->current_height + 1; uint8_t *dst = self->current_image; while ( --y ) { QRgb *src = (QRgb*) scaled.scanLine( self->current_height - y ); int x = self->current_width + 1; while ( --x ) { *dst++ = qRed(*src); *dst++ = qGreen(*src); *dst++ = qBlue(*src); if ( has_alpha ) *dst++ = qAlpha(*src); ++src; } } // Convert image to requested format if ( format != mlt_image_none && format != mlt_image_glsl && format != self->format ) { uint8_t *buffer = NULL; // First, set the image so it can be converted when we get it mlt_frame_replace_image( frame, self->current_image, self->format, width, height ); mlt_frame_set_image( frame, self->current_image, image_size, mlt_pool_release ); // get_image will do the format conversion mlt_frame_get_image( frame, &buffer, &format, &width, &height, 0 ); // cache copies of the image and alpha buffers if ( buffer ) { self->current_width = width; self->current_height = height; self->format = format; image_size = mlt_image_format_size( format, width, height, NULL ); self->current_image = (uint8_t*) mlt_pool_alloc( image_size ); memcpy( self->current_image, buffer, image_size ); } if ( ( buffer = (uint8_t*) mlt_properties_get_data( properties, "alpha", &self->alpha_size ) ) ) { if ( !self->alpha_size ) self->alpha_size = self->current_width * self->current_height; self->current_alpha = (uint8_t*) mlt_pool_alloc( self->alpha_size ); memcpy( self->current_alpha, buffer, self->alpha_size ); } } // Update the cache mlt_cache_item_close( self->image_cache ); mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.image", self->current_image, image_size, mlt_pool_release ); self->image_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.image" ); self->image_idx = image_idx; mlt_cache_item_close( self->alpha_cache ); self->alpha_cache = NULL; if ( self->current_alpha ) { mlt_service_cache_put( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha", self->current_alpha, self->alpha_size, mlt_pool_release ); self->alpha_cache = mlt_service_cache_get( MLT_PRODUCER_SERVICE( producer ), "qimage.alpha" ); } } // Set width/height of frame mlt_properties_set_int( properties, "width", self->current_width ); mlt_properties_set_int( properties, "height", self->current_height ); } extern void make_tempfile( producer_qimage self, const char *xml ) { // Generate a temporary file for the svg QTemporaryFile tempFile( "mlt.XXXXXX" ); tempFile.setAutoRemove( false ); if ( tempFile.open() ) { // Write the svg into the temp file QByteArray fullname = tempFile.fileName().toUtf8(); // Strip leading crap while ( xml[0] != '<' ) xml++; qint64 remaining_bytes = strlen( xml ); while ( remaining_bytes > 0 ) remaining_bytes -= tempFile.write( xml + strlen( xml ) - remaining_bytes, remaining_bytes ); tempFile.close(); mlt_properties_set( self->filenames, "0", fullname.data() ); mlt_properties_set_data( MLT_PRODUCER_PROPERTIES( &self->parent ), "__temporary_file__", fullname.data(), 0, ( mlt_destructor )unlink, NULL ); } } int load_sequence_sprintf(producer_qimage self, mlt_properties properties, const char *filename) { int result = 0; // Obtain filenames with pattern if (filename && strchr(filename, '%')) { // handle picture sequences int i = mlt_properties_get_int( properties, "begin" ); int keyvalue = 0; #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) for (int gap = 0; gap < 100;) { QString full = QString::asprintf(filename, i++); if (QFile::exists(full)) { QString key = QString::asprintf("%d", keyvalue++); mlt_properties_set(self->filenames, key.toLatin1().constData(), full.toUtf8().constData()); gap = 0; } else { gap ++; } } #else char full[1024]; char key[ 50 ]; for (int gap = 0; gap < 100;) { struct stat buf; snprintf(full, 1023, filename, i++); if (stat(full, &buf ) == 0) { sprintf(key, "%d", keyvalue ++); mlt_properties_set(self->filenames, key, full); gap = 0; } else { gap ++; } } #endif if (mlt_properties_count(self->filenames) > 0) { mlt_properties_set_int(properties, "ttl", 1); result = 1; } } return result; } } // extern "C" mlt-6.20.0/src/modules/qt/qimage_wrapper.h000066400000000000000000000035671362234133600204500ustar00rootroot00000000000000/* * qimage_wrapper.h -- a QT/QImage based producer for MLT * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MLT_QIMAGE_WRAPPER #define MLT_QIMAGE_WRAPPER #include #include #ifdef __cplusplus extern "C" { #endif struct producer_qimage_s { struct mlt_producer_s parent; mlt_properties filenames; int count; int image_idx; int qimage_idx; uint8_t *current_image; uint8_t *current_alpha; int current_width; int current_height; int alpha_size; mlt_cache_item image_cache; mlt_cache_item alpha_cache; mlt_cache_item qimage_cache; void *qimage; mlt_image_format format; }; typedef struct producer_qimage_s *producer_qimage; extern int refresh_qimage( producer_qimage self, mlt_frame frame ); extern void refresh_image( producer_qimage, mlt_frame, mlt_image_format, int width, int height ); extern void make_tempfile( producer_qimage, const char *xml ); extern int init_qimage(const char *filename); extern int load_sequence_sprintf( producer_qimage self, mlt_properties properties, const char *filename ); #ifdef __cplusplus } #endif #endif mlt-6.20.0/src/modules/qt/transition_qtblend.cpp000066400000000000000000000211131362234133600216660ustar00rootroot00000000000000/* * transition_qtblend.cpp -- Qt composite transition * Copyright (c) 2016 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_frame b_frame = mlt_frame_pop_frame( a_frame ); mlt_properties b_properties = MLT_FRAME_PROPERTIES( b_frame ); mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame ); mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) ); mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition ); uint8_t *b_image = NULL; bool hasAlpha = false; double opacity = 1.0; QTransform transform; // reference rect mlt_rect rect; // Determine length mlt_position length = mlt_transition_get_length( transition ); // Get current position mlt_position position = mlt_transition_get_position( transition, a_frame ); // Obtain the normalised width and height from the a_frame mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) ); int normalised_width = profile->width; int normalised_height = profile->height; double consumer_ar = mlt_profile_sar( profile ); int b_width = mlt_properties_get_int( b_properties, "meta.media.width" ); int b_height = mlt_properties_get_int( b_properties, "meta.media.height" ); if ( b_height == 0 ) { b_width = normalised_width; b_height = normalised_height; } double b_ar = mlt_frame_get_aspect_ratio( b_frame ); double b_dar = b_ar * b_width / b_height; rect.w = -1; rect.h = -1; bool consumerScaling = false; // Check transform if ( mlt_properties_get( transition_properties, "rect" ) ) { rect = mlt_properties_anim_get_rect( transition_properties, "rect", position, length ); if (mlt_properties_get(transition_properties, "rect") && ::strchr(mlt_properties_get(transition_properties, "rect"), '%')) { rect.x *= normalised_width; rect.y *= normalised_height; rect.w *= normalised_width; rect.h *= normalised_height; } double scale = mlt_profile_scale_width(profile, *width); if ( scale != 1.0 ) { consumerScaling = true; } rect.x *= scale; rect.w *= scale; scale = mlt_profile_scale_height(profile, *height); if ( !consumerScaling && scale != 1.0 ) { consumerScaling = true; } rect.y *= scale; rect.h *= scale; transform.translate(rect.x, rect.y); opacity = rect.o; } double output_ar = mlt_profile_sar( profile ); if ( mlt_frame_get_aspect_ratio( b_frame ) == 0 ) { mlt_frame_set_aspect_ratio( b_frame, output_ar ); } if ( mlt_properties_get( transition_properties, "rotation" ) ) { double angle = mlt_properties_anim_get_double( transition_properties, "rotation", position, length ); if (angle != 0.0) { if ( mlt_properties_get_int( transition_properties, "rotate_center" ) ) { transform.translate( rect.w / 2.0, rect.h / 2.0 ); transform.rotate( angle ); transform.translate( -rect.w / 2.0, -rect.h / 2.0 ); } else { transform.rotate( angle ); } hasAlpha = true; } } // This is not a field-aware transform. mlt_properties_set_int( b_properties, "consumer_deinterlace", 1 ); // Suppress padding and aspect normalization. char *interps = mlt_properties_get( properties, "rescale.interp" ); if ( interps ) interps = strdup( interps ); if ( error ) { return error; } // Adjust if consumer is scaling if ( consumerScaling ) { // Scale request of b frame image to consumer scale maintaining its aspect ratio. b_height = *height; b_width = b_height * b_dar / b_ar; } if ( rect.w != -1 ) { if ( mlt_properties_get_int( transition_properties, "distort" ) && b_width != 0 && b_height != 0 ) { transform.scale( rect.w / b_width, rect.h / b_height ); } else { // Determine scale with respect to aspect ratio. double geometry_dar = rect.w * consumer_ar / rect.h; double scale; if ( b_dar > geometry_dar ) { scale = rect.w / b_width; } else { scale = rect.h / b_height * b_ar; } transform.translate((rect.w - (b_width * scale)) / 2.0, (rect.h - (b_height * scale)) / 2.0); transform.scale( scale, scale ); } if ( opacity < 1 || rect.x > 0 || rect.y > 0 || (rect.x + rect.w < *width ) || (rect.y + rect.w < *height ) ) { // we will process operations on top frame, so also process b_frame hasAlpha = true; } } else { // No transform, request profile sized image if (b_dar != mlt_profile_dar( profile ) ) { // Activate transparency if the clips don't have the same aspect ratio hasAlpha = true; } // resize to consumer request b_width = *width; b_height = *height; } if ( !hasAlpha && ( mlt_properties_get_int( transition_properties, "compositing" ) != 0 || b_width < *width || b_height < *height ) ) { hasAlpha = true; } // Check if we have transparency if ( !hasAlpha ) { // fetch image error = mlt_frame_get_image( b_frame, &b_image, format, width, height, 1 ); if ( *format == mlt_image_rgb24a || mlt_frame_get_alpha( b_frame ) ) { hasAlpha = true; } } if ( !hasAlpha ) { // Prepare output image *image = b_image; mlt_frame_replace_image( a_frame, b_image, *format, *width, *height ); free( interps ); return 0; } // Get RGBA image to process *format = mlt_image_rgb24a; error = mlt_frame_get_image( b_frame, &b_image, format, &b_width, &b_height, writable ); // Get bottom frame uint8_t *a_image = NULL; error = mlt_frame_get_image( a_frame, &a_image, format, width, height, 1 ); if (error) { free( interps ); return error; } // Prepare output image int image_size = mlt_image_format_size( *format, *width, *height, NULL ); *image = (uint8_t *) mlt_pool_alloc( image_size ); // Copy bottom frame in output memcpy( *image, a_image, image_size ); bool hqPainting = false; if ( interps ) { if ( strcmp( interps, "bilinear" ) == 0 || strcmp( interps, "bicubic" ) == 0 ) { hqPainting = true; } } // convert bottom mlt image to qimage QImage bottomImg; convert_mlt_to_qimage_rgba( *image, &bottomImg, *width, *height ); // convert top mlt image to qimage QImage topImg; convert_mlt_to_qimage_rgba( b_image, &topImg, b_width, b_height ); // setup Qt drawing QPainter painter( &bottomImg ); painter.setCompositionMode( ( QPainter::CompositionMode ) mlt_properties_get_int( transition_properties, "compositing" ) ); painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform, hqPainting ); painter.setTransform(transform); painter.setOpacity(opacity); // Composite top frame painter.drawImage(0, 0, topImg); // finish Qt drawing painter.end(); convert_qimage_to_mlt_rgba( &bottomImg, *image, *width, *height ); mlt_frame_set_image( a_frame, *image, image_size, mlt_pool_release); free( interps ); return error; } static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { mlt_frame_push_service( a_frame, transition ); mlt_frame_push_frame( a_frame, b_frame ); mlt_frame_push_get_image( a_frame, get_image ); return a_frame; } extern "C" { mlt_transition transition_qtblend_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ) { mlt_transition transition = mlt_transition_new(); if ( transition ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); if ( !createQApplicationIfNeeded( MLT_TRANSITION_SERVICE(transition) ) ) { mlt_transition_close( transition ); return NULL; } transition->process = process; mlt_properties_set_int( properties, "_transition_type", 1 ); // video only mlt_properties_set( properties, "rect", (char *) arg ); mlt_properties_set_int( properties, "compositing", 0 ); mlt_properties_set_int( properties, "distort", 0 ); mlt_properties_set_int( properties, "rotate_center", 0 ); } return transition; } } // extern "C" mlt-6.20.0/src/modules/qt/transition_qtblend.yml000066400000000000000000000025721362234133600217150ustar00rootroot00000000000000schema_version: 0.1 type: transition identifier: qtblend title: Composite and transform version: 2 copyright: Meltytech, LLC creator: Jean-Baptiste Mardelle license: LGPLv2.1 language: en tags: - Video description: > A transition allowing compositing and transform. parameters: - identifier: rect title: Rectangle type: rect description: > Keyframable rectangle specification. mutable: yes - identifier: distort title: Ignore aspect ratio description: > Determines whether the image aspect ratio will be distorted while scaling to completely fill the geometry rectangle. type: boolean default: 0 mutable: yes widget: checkbox - identifier: compositing title: Composition mode description: > Defines which composition operation will be performed (see QPainter CompositionMode for doc). type: integer default: 0 minimum: 0 maximum: 40 mutable: yes widget: spinner - identifier: rotation title: Rotation angle description: > Angle for rotation. type: float default: 1 minimum: 0 maximum: 360 mutable: yes unit: degrees - identifier: rotate_center title: Rotate from center type: integer description: Process the rotation from center if set, otherwise from top left corner minimum: 0 maximum: 1 mutable: yes widget: checkbox mlt-6.20.0/src/modules/qt/transition_vqm.cpp000066400000000000000000000176161362234133600210550ustar00rootroot00000000000000/* * transition_vqm.c -- video quality measurement * Copyright (c) 2012 Dan Dennedy * Core psnr and ssim routines based on code from * qsnr (C) 2010 E. Oriani, ema fastwebnet it * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 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, see */ #include "common.h" #include #include #include #include #include #include #include #include #include #include static double calc_psnr( const uint8_t *a, const uint8_t *b, int size, int bpp ) { double mse = 0.0; int n = size + 1; while ( --n ) { int diff = *a - *b; mse += diff * diff; a += bpp; b += bpp; } return 10.0 * log10( 255.0 * 255.0 / ( mse == 0 ? 1e-10 : mse/size ) ); } static double calc_ssim( const uint8_t *a, const uint8_t *b, int width, int height, int window_size, int bpp ) { int windows_x = width / window_size; int windows_y = height / window_size; double avg = 0.0; if ( !windows_x || !windows_y ) return 0.0; // for each window for ( int y = 0; y < windows_y; ++y ) for ( int x = 0; x < windows_x; ++x ) { int base_offset = x * window_size + y * window_size * width; double ref_acc = 0.0, ref_acc_2 = 0.0, cmp_acc = 0.0, cmp_acc_2 = 0.0, ref_cmp_acc = 0.0; // accumulate the pixel values for this window for ( int j = 0; j < window_size; ++j ) for ( int i = 0; i < window_size; ++i ) { uint8_t c_a = a[bpp * (base_offset + j * width + i)]; uint8_t c_b = b[bpp * (base_offset + j * width + i)]; ref_acc += c_a; ref_acc_2 += c_a * c_a; cmp_acc += c_b; cmp_acc_2 += c_b * c_b; ref_cmp_acc += c_a * c_b; } // compute the SSIM for this window // http://en.wikipedia.org/wiki/SSIM // http://en.wikipedia.org/wiki/Variance // http://en.wikipedia.org/wiki/Covariance double n_samples = window_size * window_size, ref_avg = ref_acc / n_samples, ref_var = ref_acc_2 / n_samples - ref_avg * ref_avg, cmp_avg = cmp_acc / n_samples, cmp_var = cmp_acc_2 / n_samples - cmp_avg * cmp_avg, ref_cmp_cov = ref_cmp_acc / n_samples - ref_avg * cmp_avg, c1 = 6.5025, // (0.01*255.0)^2 c2 = 58.5225, // (0.03*255)^2 ssim_num = (2.0 * ref_avg * cmp_avg + c1) * (2.0 * ref_cmp_cov + c2), ssim_den = (ref_avg * ref_avg + cmp_avg * cmp_avg + c1) * (ref_var + cmp_var + c2); // accumulate the SSIM avg += ssim_num / ssim_den; } // return the average SSIM return avg / windows_x / windows_y; } static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_frame b_frame = mlt_frame_pop_frame( a_frame ); mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame ); mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) ); uint8_t *b_image; int window_size = mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "window_size" ); double psnr[3], ssim[3]; *format = mlt_image_yuv422; mlt_frame_get_image( b_frame, &b_image, format, width, height, writable ); mlt_frame_get_image( a_frame, image, format, width, height, writable ); psnr[0] = calc_psnr( *image, b_image, *width * *height, 2 ); psnr[1] = calc_psnr( *image + 1, b_image + 1, *width * *height / 2, 4 ); psnr[2] = calc_psnr( *image + 3, b_image + 3, *width * *height / 2, 4 ); ssim[0] = calc_ssim( *image, b_image, *width, *height, window_size, 2 ); ssim[1] = calc_ssim( *image + 1, b_image + 1, *width / 2, *height, window_size, 4 ); ssim[2] = calc_ssim( *image + 3, b_image + 3, *width / 2, *height, window_size, 4 ); mlt_properties_set_double( properties, "meta.vqm.psnr.y", psnr[0] ); mlt_properties_set_double( properties, "meta.vqm.psnr.cb", psnr[1] ); mlt_properties_set_double( properties, "meta.vqm.psnr.cr", psnr[2] ); mlt_properties_set_double( properties, "meta.vqm.ssim.y", ssim[0] ); mlt_properties_set_double( properties, "meta.vqm.ssim.cb", ssim[1] ); mlt_properties_set_double( properties, "meta.vqm.ssim.cr", ssim[2] ); printf( "%05d %05.2f %05.2f %05.2f %5.3f %5.3f %5.3f\n", mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2], ssim[0], ssim[1], ssim[2] ); // copy the B frame to the bottom of the A frame for comparison window_size = mlt_image_format_size( *format, *width, *height, NULL ) / 2; memcpy( *image + window_size, b_image + window_size, window_size ); if ( !mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( transition ), "render" ) ) return 0; // get RGBA image for Qt drawing *format = mlt_image_rgb24a; mlt_frame_get_image( a_frame, image, format, width, height, 1 ); // convert mlt image to qimage QImage img( *width, *height, QImage::Format_ARGB32 ); int y = *height + 1; uint8_t *src = *image; while ( --y ) { QRgb *dst = (QRgb*) img.scanLine( *height - y ); int x = *width + 1; while ( --x ) { *dst++ = qRgba( src[0], src[1], src[2], 255 ); src += 4; } } // setup Qt drawing QPainter painter; painter.begin( &img ); painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing ); // draw some stuff with Qt QPalette palette; QFont font; QString s; font.setBold( true ); font.setPointSize( 30 * *height / 1080 ); painter.setPen( QColor("black") ); painter.drawLine( 0, *height/2 + 1, *width, *height/2 ); painter.setPen( QColor("white") ); painter.drawLine( 0, *height/2 - 1, *width, *height/2 ); painter.setFont( font ); s.sprintf( "Frame: %05d\nPSNR: %05.2f (Y) %05.2f (Cb) %05.2f (Cr)\nSSIM: %5.3f (Y) %5.3f (Cb) %5.3f (Cr)", mlt_frame_get_position( a_frame ), psnr[0], psnr[1], psnr[2], ssim[0], ssim[1], ssim[2] ); painter.setPen( QColor("black") ); painter.drawText( 52, *height * 8 / 10 + 2, *width, *height, 0, s ); painter.setPen( QColor("white") ); painter.drawText( 50, *height * 8 / 10, *width, *height, 0, s ); // finish Qt drawing painter.end(); window_size = mlt_image_format_size( *format, *width, *height, NULL ); uint8_t *dst = (uint8_t *) mlt_pool_alloc( window_size ); mlt_properties_set_data( MLT_FRAME_PROPERTIES(a_frame), "image", dst, window_size, mlt_pool_release, NULL ); *image = dst; // convert qimage to mlt y = *height + 1; while ( --y ) { QRgb *src = (QRgb*) img.scanLine( *height - y ); int x = *width + 1; while ( --x ) { *dst++ = qRed( *src ); *dst++ = qGreen( *src ); *dst++ = qBlue( *src ); *dst++ = qAlpha( *src ); src++; } } return 0; } static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame ) { mlt_frame_push_service( a_frame, transition ); mlt_frame_push_frame( a_frame, b_frame ); mlt_frame_push_get_image( a_frame, get_image ); return a_frame; } extern "C" { mlt_transition transition_vqm_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg ) { mlt_transition transition = mlt_transition_new(); if ( transition ) { mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition ); if ( !createQApplicationIfNeeded( MLT_TRANSITION_SERVICE(transition) ) ) { mlt_transition_close( transition ); return NULL; } transition->process = process; mlt_properties_set_int( properties, "_transition_type", 1 ); // video only mlt_properties_set_int( properties, "window_size", 8 ); printf( "frame psnr[Y] psnr[Cb] psnr[Cr] ssim[Y] ssim[Cb] ssim[Cr]\n" ); } return transition; } } // extern "C" mlt-6.20.0/src/modules/qt/transition_vqm.yml000066400000000000000000000013471362234133600210660ustar00rootroot00000000000000schema_version: 0.1 type: transition identifier: vqm title: Video Quality Measurement version: 1 copyright: Dan Dennedy creator: Dan Dennedy license: GPLv3 language: en description: > This performs the PSNR and SSIM video quality measurements by comparing the B frames to the reference frame A. It outputs the numbers to stdout in space-delimited format for easy by another tool. The bottom half of the B frame is placed below the top half of the A frame for visual comparison. tags: - Video parameters: - identifier: render title: Render description: > Render a line between top and bottom halves and the values atop the video. type: integer default: 0 minimum: 0 maximum: 1 widget: checkbox mlt-6.20.0/src/modules/resample/000077500000000000000000000000001362234133600164455ustar00rootroot00000000000000mlt-6.20.0/src/modules/resample/Makefile000066400000000000000000000013061362234133600201050ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt include ../../../config.mak TARGET = ../libmltresample$(LIBSUF) OBJS = factory.o \ filter_resample.o CFLAGS += $(shell pkg-config --cflags samplerate) LDFLAGS += $(shell 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/resample" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/resample" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/resample/configure000077500000000000000000000003501362234133600203520ustar00rootroot00000000000000#!/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 mlt-6.20.0/src/modules/resample/factory.c000066400000000000000000000026101362234133600202570ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include extern mlt_filter filter_resample_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/resample/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( filter_type, "resample", filter_resample_init ); MLT_REGISTER_METADATA( filter_type, "resample", metadata, "filter_resample.yml" ); } mlt-6.20.0/src/modules/resample/filter_resample.c000066400000000000000000000113231362234133600217660ustar00rootroot00000000000000/* * filter_resample.c -- adjust audio sample frequency * Copyright (C) 2003-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include // BUFFER_LEN is based on a maximum of 96KHz, 5 fps, 8 channels // TODO: dynamically allocate larger buffer size #define BUFFER_LEN ((96000/5) * 8 * sizeof(float)) #define RESAMPLE_TYPE SRC_SINC_FASTEST /** Get the audio. */ static int resample_get_audio( mlt_frame frame, void **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 resample information int output_rate = mlt_properties_get_int( filter_properties, "frequency" ); // If no resample frequency is specified, default to requested value if ( output_rate == 0 ) output_rate = *frequency; // Get the producer's audio int error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); if ( error ) return error; // Return now if no work to do if ( output_rate != *frequency && *frequency > 0 && *channels > 0 ) { mlt_log_debug( MLT_FILTER_SERVICE(filter), "channels %d samples %d frequency %d -> %d\n", *channels, *samples, *frequency, output_rate ); // Do not convert to float unless we need to change the rate if ( *format != mlt_audio_f32le ) frame->convert_audio( frame, buffer, format, mlt_audio_f32le ); mlt_service_lock( MLT_FILTER_SERVICE(filter) ); SRC_DATA data; data.data_in = *buffer; data.data_out = mlt_properties_get_data( filter_properties, "output_buffer", NULL ); data.src_ratio = ( float ) output_rate / ( float ) *frequency; data.input_frames = *samples; data.output_frames = BUFFER_LEN / sizeof(float) / *channels; data.end_of_input = 0; SRC_STATE *state = mlt_properties_get_data( filter_properties, "state", NULL ); if ( !state || mlt_properties_get_int( filter_properties, "channels" ) != *channels ) { // Recreate the resampler if the number of channels changed state = src_new( RESAMPLE_TYPE, *channels, &error ); mlt_properties_set_data( filter_properties, "state", state, 0, (mlt_destructor) src_delete, NULL ); mlt_properties_set_int( filter_properties, "channels", *channels ); } // Resample the audio error = src_process( state, &data ); if ( !error ) { // Update output variables *samples = data.output_frames_gen; *frequency = output_rate; *buffer = data.data_out; } else { mlt_log_error( MLT_FILTER_SERVICE( filter ), "%s %d,%d,%d\n", src_strerror( error ), *frequency, *samples, output_rate ); } mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); } return error; } /** 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 *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 ), "output_buffer", output_buffer, BUFFER_LEN, mlt_pool_release, NULL ); } else { fprintf( stderr, "filter_resample_init: %s\n", src_strerror( error ) ); } } return this; } mlt-6.20.0/src/modules/resample/filter_resample.yml000066400000000000000000000013261362234133600223470ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: resample title: Resample version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Hidden description: > Adjust an audio stream's sampling rate, and duplicate channels if producer provides less than consumer requested. This filter is automatically invoked by the loader producer for the sake of normalisation over inputs and with the consumer. bugs: - > Assumes 2 channels during libsamplerate initialisation. Untested with >2 channels. parameters: - identifier: argument title: Frequency type: integer description: The target sample rate. required: no readonly: no mlt-6.20.0/src/modules/resample/gpl000066400000000000000000000000001362234133600171400ustar00rootroot00000000000000mlt-6.20.0/src/modules/rtaudio/000077500000000000000000000000001362234133600163045ustar00rootroot00000000000000mlt-6.20.0/src/modules/rtaudio/CMakeLists.txt000066400000000000000000000030201362234133600210370ustar00rootroot00000000000000set(mltrtaudio_src consumer_rtaudio.cpp) set(mltrtaudio_lib mlt Threads::Threads) pkg_check_modules(rtaudio IMPORTED_TARGET rtaudio) if(TARGET PkgConfig::rtaudio) list(APPEND mltrtaudio_lib PkgConfig::rtaudio) else() list(APPEND mltrtaudio_src RtAudio.cpp) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) if(APPLE) list(APPEND mltrtaudio_lib CoreAudio CoreFoundation) add_compile_definitions(__MACOSX_CORE__) elseif(WIN32) list(APPEND mltrtaudio_lib ole32 dsound winmm ksuser) add_compile_definitions(__WINDOWS_DS__ __WINDOWS_WASAPI__) else() pkg_check_modules(alsa IMPORTED_TARGET alsa) if(TARGET PkgConfig::alsa) list(APPEND mltrtaudio_lib PkgConfig::alsa) add_compile_definitions(__LINUX_ALSA__) endif() pkg_check_modules(libpulse-simple IMPORTED_TARGET libpulse-simple) if(TARGET PkgConfig::libpulse-simple) list(APPEND mltrtaudio_lib PkgConfig::libpulse-simple) add_compile_definitions(__LINUX_PULSE__) endif() if(NOT (TARGET PkgConfig::alsa OR TARGET PkgConfig::libpulse-simple)) list(APPEND mltrtaudio_lib ossaudio) add_compile_definitions(__LINUX_OSS__) endif() endif() endif() add_library(mltrtaudio MODULE ${mltrtaudio_src}) target_link_libraries(mltrtaudio ${mltrtaudio_lib}) install(TARGETS mltrtaudio LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/rtaudio) mlt-6.20.0/src/modules/rtaudio/Makefile000066400000000000000000000033031362234133600177430ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread include ../../../config.mak include config.mak TARGET = ../libmltrtaudio$(LIBSUF) OBJS = consumer_rtaudio.o SRCS := $(OBJS:.o=.cpp) CXXFLAGS += $(CFLAGS) -Wno-deprecated -Wno-multichar -fno-rtti ifdef USE_INTERNAL_RTAUDIO OBJS += RtAudio.o ifeq ($(targetos), Darwin) CXXFLAGS += -D__MACOSX_CORE__ LDFLAGS += -framework CoreAudio -framework CoreFoundation else ifeq ($(targetos), MinGW) CXXFLAGS += -D__WINDOWS_DS__ LDFLAGS += -lole32 -ldsound -lwinmm ifdef ARCH_X86_64 CXXFLAGS += -D__WINDOWS_WASAPI__ LDFLAGS += -lksuser endif # For ASIO when ready to try that: #OBJS += asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o #CXXFLAGS +=-D__WINDOWS_ASIO__ else ifeq ($(targetos), Linux) CXXFLAGS += -D__LINUX_ALSA__ CXXFLAGS += $(shell pkg-config --cflags alsa) LDFLAGS += $(shell pkg-config --libs alsa) CXXFLAGS += -D__LINUX_PULSE__ CXXFLAGS += $(shell pkg-config --cflags libpulse libpulse-simple) LDFLAGS += $(shell pkg-config --libs libpulse libpulse-simple) else ifeq ($(targetos), NetBSD) CXXFLAGS += -D__LINUX_OSS__ LDFLAGS += -lossaudio else # FreeBSD CXXFLAGS += -D__LINUX_OSS__ endif endif # USE_INTERNAL_RTAUDIO all: $(TARGET) $(TARGET): $(OBJS) $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS) depend: $(SRCS) $(CXX) -MM $(CXXFLAGS) $^ 1>.depend distclean: clean rm -f .depend clean: rm -f $(OBJS) $(TARGET) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/rtaudio" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/rtaudio" uninstall: rm -f "$(DESTDIR)$(moduledir)/libmltrtaudio$(LIBSUF)" rm -rf "$(DESTDIR)$(mltdatadir)/rtaudio" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/rtaudio/RtAudio.cpp000066400000000000000000013220561362234133600203700ustar00rootroot00000000000000/************************************************************************/ /*! \class RtAudio \brief Realtime audio i/o C++ classes. RtAudio provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, Jack, and OSS), Macintosh OS X (CoreAudio and Jack), and Windows (DirectSound, ASIO and WASAPI) operating systems. RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio: realtime audio i/o C++ classes Copyright (c) 2001-2016 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is asked to send the modifications to the original developer so that they can be incorporated into the canonical version. This is, however, not a binding provision of this license. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /************************************************************************/ // RtAudio: Version 4.1.2 #include "RtAudio.h" #include #include #include #include #include // Static variable definitions. const unsigned int RtApi::MAX_SAMPLE_RATES = 14; const unsigned int RtApi::SAMPLE_RATES[] = { 4000, 5512, 8000, 9600, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A) #define MUTEX_DESTROY(A) DeleteCriticalSection(A) #define MUTEX_LOCK(A) EnterCriticalSection(A) #define MUTEX_UNLOCK(A) LeaveCriticalSection(A) #include "tchar.h" static std::string convertCharPointerToStdString(const char *text) { return std::string(text); } static std::string convertCharPointerToStdString(const wchar_t *text) { int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL); std::string s( length-1, '\0' ); WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL); return s; } #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) // pthread API #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL) #define MUTEX_DESTROY(A) pthread_mutex_destroy(A) #define MUTEX_LOCK(A) pthread_mutex_lock(A) #define MUTEX_UNLOCK(A) pthread_mutex_unlock(A) #else #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions #define MUTEX_DESTROY(A) abs(*A) // dummy definitions #endif // *************************************************** // // // RtAudio definitions. // // *************************************************** // std::string RtAudio :: getVersion( void ) throw() { return RTAUDIO_VERSION; } void RtAudio :: getCompiledApi( std::vector &apis ) throw() { apis.clear(); // The order here will control the order of RtAudio's API search in // the constructor. #if defined(__UNIX_JACK__) apis.push_back( UNIX_JACK ); #endif #if defined(__LINUX_ALSA__) apis.push_back( LINUX_ALSA ); #endif #if defined(__LINUX_PULSE__) apis.push_back( LINUX_PULSE ); #endif #if defined(__LINUX_OSS__) apis.push_back( LINUX_OSS ); #endif #if defined(__WINDOWS_ASIO__) apis.push_back( WINDOWS_ASIO ); #endif #if defined(__WINDOWS_WASAPI__) apis.push_back( WINDOWS_WASAPI ); #endif #if defined(__WINDOWS_DS__) apis.push_back( WINDOWS_DS ); #endif #if defined(__MACOSX_CORE__) apis.push_back( MACOSX_CORE ); #endif #if defined(__RTAUDIO_DUMMY__) apis.push_back( RTAUDIO_DUMMY ); #endif } void RtAudio :: openRtApi( RtAudio::Api api ) { if ( rtapi_ ) delete rtapi_; rtapi_ = 0; #if defined(__UNIX_JACK__) if ( api == UNIX_JACK ) rtapi_ = new RtApiJack(); #endif #if defined(__LINUX_ALSA__) if ( api == LINUX_ALSA ) rtapi_ = new RtApiAlsa(); #endif #if defined(__LINUX_PULSE__) if ( api == LINUX_PULSE ) rtapi_ = new RtApiPulse(); #endif #if defined(__LINUX_OSS__) if ( api == LINUX_OSS ) rtapi_ = new RtApiOss(); #endif #if defined(__WINDOWS_ASIO__) if ( api == WINDOWS_ASIO ) rtapi_ = new RtApiAsio(); #endif #if defined(__WINDOWS_WASAPI__) if ( api == WINDOWS_WASAPI ) rtapi_ = new RtApiWasapi(); #endif #if defined(__WINDOWS_DS__) if ( api == WINDOWS_DS ) rtapi_ = new RtApiDs(); #endif #if defined(__MACOSX_CORE__) if ( api == MACOSX_CORE ) rtapi_ = new RtApiCore(); #endif #if defined(__RTAUDIO_DUMMY__) if ( api == RTAUDIO_DUMMY ) rtapi_ = new RtApiDummy(); #endif } RtAudio :: RtAudio( RtAudio::Api api ) { rtapi_ = 0; if ( api != UNSPECIFIED ) { // Attempt to open the specified API. openRtApi( api ); if ( rtapi_ ) return; // No compiled support for specified API value. Issue a debug // warning and continue as if no API was specified. std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl; } // Iterate through the compiled APIs and return as soon as we find // one with at least one device or we reach the end of the list. std::vector< RtAudio::Api > apis; getCompiledApi( apis ); for ( unsigned int i=0; igetDeviceCount() ) break; } if ( rtapi_ ) return; // It should not be possible to get here because the preprocessor // definition __RTAUDIO_DUMMY__ is automatically defined if no // API-specific definitions are passed to the compiler. But just in // case something weird happens, we'll throw an error. std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n"; throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) ); } RtAudio :: ~RtAudio() throw() { if ( rtapi_ ) delete rtapi_; } void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters, RtAudio::StreamParameters *inputParameters, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData, RtAudio::StreamOptions *options, RtAudioErrorCallback errorCallback ) { return rtapi_->openStream( outputParameters, inputParameters, format, sampleRate, bufferFrames, callback, userData, options, errorCallback ); } // *************************************************** // // // Public RtApi definitions (see end of file for // private or protected utility functions). // // *************************************************** // RtApi :: RtApi() { stream_.state = STREAM_CLOSED; stream_.mode = UNINITIALIZED; stream_.apiHandle = 0; stream_.userBuffer[0] = 0; stream_.userBuffer[1] = 0; MUTEX_INITIALIZE( &stream_.mutex ); showWarnings_ = true; firstErrorOccurred_ = false; } RtApi :: ~RtApi() { MUTEX_DESTROY( &stream_.mutex ); } void RtApi :: openStream( RtAudio::StreamParameters *oParams, RtAudio::StreamParameters *iParams, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData, RtAudio::StreamOptions *options, RtAudioErrorCallback errorCallback ) { if ( stream_.state != STREAM_CLOSED ) { errorText_ = "RtApi::openStream: a stream is already open!"; error( RtAudioError::INVALID_USE ); return; } // Clear stream information potentially left from a previously open stream. clearStreamInfo(); if ( oParams && oParams->nChannels < 1 ) { errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one."; error( RtAudioError::INVALID_USE ); return; } if ( iParams && iParams->nChannels < 1 ) { errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one."; error( RtAudioError::INVALID_USE ); return; } if ( oParams == NULL && iParams == NULL ) { errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!"; error( RtAudioError::INVALID_USE ); return; } if ( formatBytes(format) == 0 ) { errorText_ = "RtApi::openStream: 'format' parameter value is undefined."; error( RtAudioError::INVALID_USE ); return; } unsigned int nDevices = getDeviceCount(); unsigned int oChannels = 0; if ( oParams ) { oChannels = oParams->nChannels; if ( oParams->deviceId >= nDevices ) { errorText_ = "RtApi::openStream: output device parameter value is invalid."; error( RtAudioError::INVALID_USE ); return; } } unsigned int iChannels = 0; if ( iParams ) { iChannels = iParams->nChannels; if ( iParams->deviceId >= nDevices ) { errorText_ = "RtApi::openStream: input device parameter value is invalid."; error( RtAudioError::INVALID_USE ); return; } } bool result; if ( oChannels > 0 ) { result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel, sampleRate, format, bufferFrames, options ); if ( result == false ) { error( RtAudioError::SYSTEM_ERROR ); return; } } if ( iChannels > 0 ) { result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel, sampleRate, format, bufferFrames, options ); if ( result == false ) { if ( oChannels > 0 ) closeStream(); error( RtAudioError::SYSTEM_ERROR ); return; } } stream_.callbackInfo.callback = (void *) callback; stream_.callbackInfo.userData = userData; stream_.callbackInfo.errorCallback = (void *) errorCallback; if ( options ) options->numberOfBuffers = stream_.nBuffers; stream_.state = STREAM_STOPPED; } unsigned int RtApi :: getDefaultInputDevice( void ) { // Should be implemented in subclasses if possible. return 0; } unsigned int RtApi :: getDefaultOutputDevice( void ) { // Should be implemented in subclasses if possible. return 0; } void RtApi :: closeStream( void ) { // MUST be implemented in subclasses! return; } bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/, unsigned int /*firstChannel*/, unsigned int /*sampleRate*/, RtAudioFormat /*format*/, unsigned int * /*bufferSize*/, RtAudio::StreamOptions * /*options*/ ) { // MUST be implemented in subclasses! return FAILURE; } void RtApi :: tickStreamTime( void ) { // Subclasses that do not provide their own implementation of // getStreamTime should call this function once per buffer I/O to // provide basic stream time support. stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate ); #if defined( HAVE_GETTIMEOFDAY ) gettimeofday( &stream_.lastTickTimestamp, NULL ); #endif } long RtApi :: getStreamLatency( void ) { verifyStream(); long totalLatency = 0; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) totalLatency = stream_.latency[0]; if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) totalLatency += stream_.latency[1]; return totalLatency; } double RtApi :: getStreamTime( void ) { verifyStream(); #if defined( HAVE_GETTIMEOFDAY ) // Return a very accurate estimate of the stream time by // adding in the elapsed time since the last tick. struct timeval then; struct timeval now; if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 ) return stream_.streamTime; gettimeofday( &now, NULL ); then = stream_.lastTickTimestamp; return stream_.streamTime + ((now.tv_sec + 0.000001 * now.tv_usec) - (then.tv_sec + 0.000001 * then.tv_usec)); #else return stream_.streamTime; #endif } void RtApi :: setStreamTime( double time ) { verifyStream(); if ( time >= 0.0 ) stream_.streamTime = time; } unsigned int RtApi :: getStreamSampleRate( void ) { verifyStream(); return stream_.sampleRate; } // *************************************************** // // // OS/API-specific methods. // // *************************************************** // #if defined(__MACOSX_CORE__) // The OS X CoreAudio API is designed to use a separate callback // procedure for each of its audio devices. A single RtAudio duplex // stream using two different devices is supported here, though it // cannot be guaranteed to always behave correctly because we cannot // synchronize these two callbacks. // // A property listener is installed for over/underrun information. // However, no functionality is currently provided to allow property // listeners to trigger user handlers because it is unclear what could // be done if a critical stream parameter (buffer size, sample rate, // device disconnect) notification arrived. The listeners entail // quite a bit of extra code and most likely, a user program wouldn't // be prepared for the result anyway. However, we do provide a flag // to the client callback function to inform of an over/underrun. // A structure to hold various information related to the CoreAudio API // implementation. struct CoreHandle { AudioDeviceID id[2]; // device ids #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) AudioDeviceIOProcID procId[2]; #endif UInt32 iStream[2]; // device stream index (or first if using multiple) UInt32 nStreams[2]; // number of streams to use bool xrun[2]; char *deviceBuffer; pthread_cond_t condition; int drainCounter; // Tracks callback counts when draining bool internalDrain; // Indicates if stop is initiated from callback or not. CoreHandle() :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } }; RtApiCore:: RtApiCore() { #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER ) // This is a largely undocumented but absolutely necessary // requirement starting with OS-X 10.6. If not called, queries and // updates to various audio device properties are not handled // correctly. CFRunLoopRef theRunLoop = NULL; AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); if ( result != noErr ) { errorText_ = "RtApiCore::RtApiCore: error setting run loop property!"; error( RtAudioError::WARNING ); } #endif } RtApiCore :: ~RtApiCore() { // The subclass destructor gets called before the base class // destructor, so close an existing stream before deallocating // apiDeviceId memory. if ( stream_.state != STREAM_CLOSED ) closeStream(); } unsigned int RtApiCore :: getDeviceCount( void ) { // Find out how many audio devices there are, if any. UInt32 dataSize; AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize ); if ( result != noErr ) { errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!"; error( RtAudioError::WARNING ); return 0; } return dataSize / sizeof( AudioDeviceID ); } unsigned int RtApiCore :: getDefaultInputDevice( void ) { unsigned int nDevices = getDeviceCount(); if ( nDevices <= 1 ) return 0; AudioDeviceID id; UInt32 dataSize = sizeof( AudioDeviceID ); AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device."; error( RtAudioError::WARNING ); return 0; } dataSize *= nDevices; AudioDeviceID deviceList[ nDevices ]; property.mSelector = kAudioHardwarePropertyDevices; result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs."; error( RtAudioError::WARNING ); return 0; } for ( unsigned int i=0; i= nDevices ) { errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!"; error( RtAudioError::INVALID_USE ); return info; } AudioDeviceID deviceList[ nDevices ]; UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices; AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs."; error( RtAudioError::WARNING ); return info; } AudioDeviceID id = deviceList[ device ]; // Get the device name. info.name.erase(); CFStringRef cfname; dataSize = sizeof( CFStringRef ); property.mSelector = kAudioObjectPropertyManufacturer; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); int length = CFStringGetLength(cfname); char *mname = (char *)malloc(length * 3 + 1); #if defined( UNICODE ) || defined( _UNICODE ) CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8); #else CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding()); #endif info.name.append( (const char *)mname, strlen(mname) ); info.name.append( ": " ); CFRelease( cfname ); free(mname); property.mSelector = kAudioObjectPropertyName; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); length = CFStringGetLength(cfname); char *name = (char *)malloc(length * 3 + 1); #if defined( UNICODE ) || defined( _UNICODE ) CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8); #else CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding()); #endif info.name.append( (const char *)name, strlen(name) ); CFRelease( cfname ); free(name); // Get the output stream "configuration". AudioBufferList *bufferList = nil; property.mSelector = kAudioDevicePropertyStreamConfiguration; property.mScope = kAudioDevicePropertyScopeOutput; // property.mElement = kAudioObjectPropertyElementWildcard; dataSize = 0; result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc( dataSize ); if ( bufferList == NULL ) { errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList."; error( RtAudioError::WARNING ); return info; } result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); if ( result != noErr || dataSize == 0 ) { free( bufferList ); errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Get output channel information. unsigned int i, nStreams = bufferList->mNumberBuffers; for ( i=0; imBuffers[i].mNumberChannels; free( bufferList ); // Get the input stream "configuration". property.mScope = kAudioDevicePropertyScopeInput; result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc( dataSize ); if ( bufferList == NULL ) { errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList."; error( RtAudioError::WARNING ); return info; } result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); if (result != noErr || dataSize == 0) { free( bufferList ); errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Get input channel information. nStreams = bufferList->mNumberBuffers; for ( i=0; imBuffers[i].mNumberChannels; free( bufferList ); // If device opens for both playback and capture, we determine the channels. if ( info.outputChannels > 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; // Probe the device sample rates. bool isInput = false; if ( info.outputChannels == 0 ) isInput = true; // Determine the supported sample rates. property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput; result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); if ( result != kAudioHardwareNoError || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } UInt32 nRanges = dataSize / sizeof( AudioValueRange ); AudioValueRange rangeList[ nRanges ]; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList ); if ( result != kAudioHardwareNoError ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // The sample rate reporting mechanism is a bit of a mystery. It // seems that it can either return individual rates or a range of // rates. I assume that if the min / max range values are the same, // then that represents a single supported rate and if the min / max // range values are different, the device supports an arbitrary // range of values (though there might be multiple ranges, so we'll // use the most conservative range). Float64 minimumRate = 1.0, maximumRate = 10000000000.0; bool haveValueRange = false; info.sampleRates.clear(); for ( UInt32 i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = tmpSr; } else { haveValueRange = true; if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum; if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum; } } if ( haveValueRange ) { for ( unsigned int k=0; k= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) { info.sampleRates.push_back( SAMPLE_RATES[k] ); if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[k]; } } } // Sort and remove any redundant values std::sort( info.sampleRates.begin(), info.sampleRates.end() ); info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() ); if ( info.sampleRates.size() == 0 ) { errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // CoreAudio always uses 32-bit floating point data for PCM streams. // Thus, any other "physical" formats supported by the device are of // no interest to the client. info.nativeFormats = RTAUDIO_FLOAT32; if ( info.outputChannels > 0 ) if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; if ( info.inputChannels > 0 ) if ( getDefaultInputDevice() == device ) info.isDefaultInput = true; info.probed = true; return info; } static OSStatus callbackHandler( AudioDeviceID inDevice, const AudioTimeStamp* /*inNow*/, const AudioBufferList* inInputData, const AudioTimeStamp* /*inInputTime*/, AudioBufferList* outOutputData, const AudioTimeStamp* /*inOutputTime*/, void* infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; RtApiCore *object = (RtApiCore *) info->object; if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false ) return kAudioHardwareUnspecifiedError; else return kAudioHardwareNoError; } static OSStatus xrunListener( AudioObjectID /*inDevice*/, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void* handlePointer ) { CoreHandle *handle = (CoreHandle *) handlePointer; for ( UInt32 i=0; ixrun[1] = true; else handle->xrun[0] = true; } } return kAudioHardwareNoError; } static OSStatus rateListener( AudioObjectID inDevice, UInt32 /*nAddresses*/, const AudioObjectPropertyAddress /*properties*/[], void* ratePointer ) { Float64 *rate = (Float64 *) ratePointer; UInt32 dataSize = sizeof( Float64 ); AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate ); return kAudioHardwareNoError; } bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { // Get device ID unsigned int nDevices = getDeviceCount(); if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiCore::probeDeviceOpen: no devices found!"; return FAILURE; } if ( device >= nDevices ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!"; return FAILURE; } AudioDeviceID deviceList[ nDevices ]; UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices; AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs."; return FAILURE; } AudioDeviceID id = deviceList[ device ]; // Setup for stream mode. bool isInput = false; if ( mode == INPUT ) { isInput = true; property.mScope = kAudioDevicePropertyScopeInput; } else property.mScope = kAudioDevicePropertyScopeOutput; // Get the stream "configuration". AudioBufferList *bufferList = nil; dataSize = 0; property.mSelector = kAudioDevicePropertyStreamConfiguration; result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize ); if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Allocate the AudioBufferList. bufferList = (AudioBufferList *) malloc( dataSize ); if ( bufferList == NULL ) { errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList."; return FAILURE; } result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList ); if (result != noErr || dataSize == 0) { free( bufferList ); errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Search for one or more streams that contain the desired number of // channels. CoreAudio devices can have an arbitrary number of // streams and each stream can have an arbitrary number of channels. // For each stream, a single buffer of interleaved samples is // provided. RtAudio prefers the use of one stream of interleaved // data or multiple consecutive single-channel streams. However, we // now support multiple consecutive multi-channel streams of // interleaved data as well. UInt32 iStream, offsetCounter = firstChannel; UInt32 nStreams = bufferList->mNumberBuffers; bool monoMode = false; bool foundStream = false; // First check that the device supports the requested number of // channels. UInt32 deviceChannels = 0; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; if ( deviceChannels < ( channels + firstChannel ) ) { free( bufferList ); errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count."; errorText_ = errorStream_.str(); return FAILURE; } // Look for a single stream meeting our needs. UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; if ( streamChannels >= channels + offsetCounter ) { firstStream = iStream; channelOffset = offsetCounter; foundStream = true; break; } if ( streamChannels > offsetCounter ) break; offsetCounter -= streamChannels; } // If we didn't find a single stream above, then we should be able // to meet the channel specification with multiple streams. if ( foundStream == false ) { monoMode = true; offsetCounter = firstChannel; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels; if ( streamChannels > offsetCounter ) break; offsetCounter -= streamChannels; } firstStream = iStream; channelOffset = offsetCounter; Int32 channelCounter = channels + offsetCounter - streamChannels; if ( streamChannels > 1 ) monoMode = false; while ( channelCounter > 0 ) { streamChannels = bufferList->mBuffers[++iStream].mNumberChannels; if ( streamChannels > 1 ) monoMode = false; channelCounter -= streamChannels; streamCount++; } } free( bufferList ); // Determine the buffer size. AudioValueRange bufferRange; dataSize = sizeof( AudioValueRange ); property.mSelector = kAudioDevicePropertyBufferFrameSizeRange; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum; else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum; // Set the buffer size. For multiple streams, I'm assuming we only // need to make this setting for the master channel. UInt32 theSize = (UInt32) *bufferSize; dataSize = sizeof( UInt32 ); property.mSelector = kAudioDevicePropertyBufferFrameSize; result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! *bufferSize = theSize; if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.bufferSize = *bufferSize; stream_.nBuffers = 1; // Try to set "hog" mode ... it's not clear to me this is working. if ( options && options->flags & RTAUDIO_HOG_DEVICE ) { pid_t hog_pid; dataSize = sizeof( hog_pid ); property.mSelector = kAudioDevicePropertyHogMode; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!"; errorText_ = errorStream_.str(); return FAILURE; } if ( hog_pid != getpid() ) { hog_pid = getpid(); result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!"; errorText_ = errorStream_.str(); return FAILURE; } } } // Check and if necessary, change the sample rate for the device. Float64 nominalRate; dataSize = sizeof( Float64 ); property.mSelector = kAudioDevicePropertyNominalSampleRate; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate."; errorText_ = errorStream_.str(); return FAILURE; } // Only change the sample rate if off by more than 1 Hz. if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) { // Set a property listener for the sample rate change Float64 reportedRate = 0.0; AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } nominalRate = (Float64) sampleRate; result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate ); if ( result != noErr ) { AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate ); errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Now wait until the reported nominal rate is what we just set. UInt32 microCounter = 0; while ( reportedRate != nominalRate ) { microCounter += 5000; if ( microCounter > 5000000 ) break; usleep( 5000 ); } // Remove the property listener. AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate ); if ( microCounter > 5000000 ) { errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } } // Now set the stream format for all streams. Also, check the // physical format of the device and change that if necessary. AudioStreamBasicDescription description; dataSize = sizeof( AudioStreamBasicDescription ); property.mSelector = kAudioStreamPropertyVirtualFormat; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Set the sample rate and data format id. However, only make the // change if the sample rate is not within 1.0 of the desired // rate and the format is not linear pcm. bool updateFormat = false; if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) { description.mSampleRate = (Float64) sampleRate; updateFormat = true; } if ( description.mFormatID != kAudioFormatLinearPCM ) { description.mFormatID = kAudioFormatLinearPCM; updateFormat = true; } if ( updateFormat ) { result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } } // Now check the physical format. property.mSelector = kAudioStreamPropertyPhysicalFormat; result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description ); if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ")."; errorText_ = errorStream_.str(); return FAILURE; } //std::cout << "Current physical stream format:" << std::endl; //std::cout << " mBitsPerChan = " << description.mBitsPerChannel << std::endl; //std::cout << " aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl; //std::cout << " bytesPerFrame = " << description.mBytesPerFrame << std::endl; //std::cout << " sample rate = " << description.mSampleRate << std::endl; if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) { description.mFormatID = kAudioFormatLinearPCM; //description.mSampleRate = (Float64) sampleRate; AudioStreamBasicDescription testDescription = description; UInt32 formatFlags; // We'll try higher bit rates first and then work our way down. std::vector< std::pair > physicalFormats; formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger; physicalFormats.push_back( std::pair( 32, formatFlags ) ); formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; physicalFormats.push_back( std::pair( 32, formatFlags ) ); physicalFormats.push_back( std::pair( 24, formatFlags ) ); // 24-bit packed formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh ); physicalFormats.push_back( std::pair( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low formatFlags |= kAudioFormatFlagIsAlignedHigh; physicalFormats.push_back( std::pair( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat; physicalFormats.push_back( std::pair( 16, formatFlags ) ); physicalFormats.push_back( std::pair( 8, formatFlags ) ); bool setPhysicalFormat = false; for( unsigned int i=0; iflags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; if ( monoMode == true ) stream_.deviceInterleaved[mode] = false; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; if ( streamCount == 1 ) { if ( stream_.nUserChannels[mode] > 1 && stream_.userInterleaved != stream_.deviceInterleaved[mode] ) stream_.doConvertBuffer[mode] = true; } else if ( monoMode && stream_.userInterleaved ) stream_.doConvertBuffer[mode] = true; // Allocate our CoreHandle structure for the stream. CoreHandle *handle = 0; if ( stream_.apiHandle == 0 ) { try { handle = new CoreHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory."; goto error; } if ( pthread_cond_init( &handle->condition, NULL ) ) { errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable."; goto error; } stream_.apiHandle = (void *) handle; } else handle = (CoreHandle *) stream_.apiHandle; handle->iStream[mode] = firstStream; handle->nStreams[mode] = streamCount; handle->id[mode] = id; // Allocate necessary internal buffers. unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); // stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) ); memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory."; goto error; } // If possible, we will make use of the CoreAudio stream buffers as // "device buffers". However, we can't do this if using multiple // streams. if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } stream_.sampleRate = sampleRate; stream_.device[mode] = device; stream_.state = STREAM_STOPPED; stream_.callbackInfo.object = (void *) this; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if ( streamCount > 1 ) setConvertInfo( mode, 0 ); else setConvertInfo( mode, channelOffset ); } if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device ) // Only one callback procedure per device. stream_.mode = DUPLEX; else { #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] ); #else // deprecated in favor of AudioDeviceCreateIOProcID() result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo ); #endif if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ")."; errorText_ = errorStream_.str(); goto error; } if ( stream_.mode == OUTPUT && mode == INPUT ) stream_.mode = DUPLEX; else stream_.mode = mode; } // Setup the device property listener for over/underload. property.mSelector = kAudioDeviceProcessorOverload; property.mScope = kAudioObjectPropertyScopeGlobal; result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle ); return SUCCESS; error: if ( handle ) { pthread_cond_destroy( &handle->condition ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.state = STREAM_CLOSED; return FAILURE; } void RtApiCore :: closeStream( void ) { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiCore::closeStream(): no open stream to close!"; error( RtAudioError::WARNING ); return; } CoreHandle *handle = (CoreHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if (handle) { AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; property.mSelector = kAudioDeviceProcessorOverload; property.mScope = kAudioObjectPropertyScopeGlobal; if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) { errorText_ = "RtApiCore::closeStream(): error removing property listener!"; error( RtAudioError::WARNING ); } } if ( stream_.state == STREAM_RUNNING ) AudioDeviceStop( handle->id[0], callbackHandler ); #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] ); #else // deprecated in favor of AudioDeviceDestroyIOProcID() AudioDeviceRemoveIOProc( handle->id[0], callbackHandler ); #endif } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { if (handle) { AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; property.mSelector = kAudioDeviceProcessorOverload; property.mScope = kAudioObjectPropertyScopeGlobal; if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) { errorText_ = "RtApiCore::closeStream(): error removing property listener!"; error( RtAudioError::WARNING ); } } if ( stream_.state == STREAM_RUNNING ) AudioDeviceStop( handle->id[1], callbackHandler ); #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] ); #else // deprecated in favor of AudioDeviceDestroyIOProcID() AudioDeviceRemoveIOProc( handle->id[1], callbackHandler ); #endif } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } // Destroy pthread condition variable. pthread_cond_destroy( &handle->condition ); delete handle; stream_.apiHandle = 0; stream_.mode = UNINITIALIZED; stream_.state = STREAM_CLOSED; } void RtApiCore :: startStream( void ) { verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiCore::startStream(): the stream is already running!"; error( RtAudioError::WARNING ); return; } OSStatus result = noErr; CoreHandle *handle = (CoreHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { result = AudioDeviceStart( handle->id[0], callbackHandler ); if ( result != noErr ) { errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { result = AudioDeviceStart( handle->id[1], callbackHandler ); if ( result != noErr ) { errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ")."; errorText_ = errorStream_.str(); goto unlock; } } handle->drainCounter = 0; handle->internalDrain = false; stream_.state = STREAM_RUNNING; unlock: if ( result == noErr ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiCore :: stopStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiCore::stopStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } OSStatus result = noErr; CoreHandle *handle = (CoreHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { handle->drainCounter = 2; pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled } result = AudioDeviceStop( handle->id[0], callbackHandler ); if ( result != noErr ) { errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { result = AudioDeviceStop( handle->id[1], callbackHandler ); if ( result != noErr ) { errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ")."; errorText_ = errorStream_.str(); goto unlock; } } stream_.state = STREAM_STOPPED; unlock: if ( result == noErr ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiCore :: abortStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiCore::abortStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } CoreHandle *handle = (CoreHandle *) stream_.apiHandle; handle->drainCounter = 2; stopStream(); } // This function will be called by a spawned thread when the user // callback function signals that the stream should be stopped or // aborted. It is better to handle it this way because the // callbackEvent() function probably should return before the AudioDeviceStop() // function is called. static void *coreStopStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiCore *object = (RtApiCore *) info->object; object->stopStream(); pthread_exit( NULL ); } bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, const AudioBufferList *inBufferList, const AudioBufferList *outBufferList ) { if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RtAudioError::WARNING ); return FAILURE; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; CoreHandle *handle = (CoreHandle *) stream_.apiHandle; // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > 3 ) { ThreadHandle threadId; stream_.state = STREAM_STOPPING; if ( handle->internalDrain == true ) pthread_create( &threadId, NULL, coreStopStream, info ); else // external call to stopStream() pthread_cond_signal( &handle->condition ); return SUCCESS; } AudioDeviceID outputDevice = handle->id[0]; // Invoke user callback to get fresh output data UNLESS we are // draining stream or duplex mode AND the input/output devices are // different AND this function is called for the input device. if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) { RtAudioCallback callback = (RtAudioCallback) info->callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, info->userData ); if ( cbReturnValue == 2 ) { stream_.state = STREAM_STOPPING; handle->drainCounter = 2; abortStream(); return SUCCESS; } else if ( cbReturnValue == 1 ) { handle->drainCounter = 1; handle->internalDrain = true; } } if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) { if ( handle->drainCounter > 1 ) { // write zeros to the output stream if ( handle->nStreams[0] == 1 ) { memset( outBufferList->mBuffers[handle->iStream[0]].mData, 0, outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); } else { // fill multiple streams with zeros for ( unsigned int i=0; inStreams[0]; i++ ) { memset( outBufferList->mBuffers[handle->iStream[0]+i].mData, 0, outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize ); } } } else if ( handle->nStreams[0] == 1 ) { if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData, stream_.userBuffer[0], stream_.convertInfo[0] ); } else { // copy from user buffer memcpy( outBufferList->mBuffers[handle->iStream[0]].mData, stream_.userBuffer[0], outBufferList->mBuffers[handle->iStream[0]].mDataByteSize ); } } else { // fill multiple streams Float32 *inBuffer = (Float32 *) stream_.userBuffer[0]; if ( stream_.doConvertBuffer[0] ) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); inBuffer = (Float32 *) stream_.deviceBuffer; } if ( stream_.deviceInterleaved[0] == false ) { // mono mode UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; for ( unsigned int i=0; imBuffers[handle->iStream[0]+i].mData, (void *)&inBuffer[i*stream_.bufferSize], bufferBytes ); } } else { // fill multiple multi-channel streams with interleaved data UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset; Float32 *out, *in; bool inInterleaved = ( stream_.userInterleaved ) ? true : false; UInt32 inChannels = stream_.nUserChannels[0]; if ( stream_.doConvertBuffer[0] ) { inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode inChannels = stream_.nDeviceChannels[0]; } if ( inInterleaved ) inOffset = 1; else inOffset = stream_.bufferSize; channelsLeft = inChannels; for ( unsigned int i=0; inStreams[0]; i++ ) { in = inBuffer; out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData; streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels; outJump = 0; // Account for possible channel offset in first stream if ( i == 0 && stream_.channelOffset[0] > 0 ) { streamChannels -= stream_.channelOffset[0]; outJump = stream_.channelOffset[0]; out += outJump; } // Account for possible unfilled channels at end of the last stream if ( streamChannels > channelsLeft ) { outJump = streamChannels - channelsLeft; streamChannels = channelsLeft; } // Determine input buffer offsets and skips if ( inInterleaved ) { inJump = inChannels; in += inChannels - channelsLeft; } else { inJump = 1; in += (inChannels - channelsLeft) * inOffset; } for ( unsigned int i=0; idrainCounter ) { handle->drainCounter++; goto unlock; } AudioDeviceID inputDevice; inputDevice = handle->id[1]; if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) { if ( handle->nStreams[1] == 1 ) { if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer convertBuffer( stream_.userBuffer[1], (char *) inBufferList->mBuffers[handle->iStream[1]].mData, stream_.convertInfo[1] ); } else { // copy to user buffer memcpy( stream_.userBuffer[1], inBufferList->mBuffers[handle->iStream[1]].mData, inBufferList->mBuffers[handle->iStream[1]].mDataByteSize ); } } else { // read from multiple streams Float32 *outBuffer = (Float32 *) stream_.userBuffer[1]; if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer; if ( stream_.deviceInterleaved[1] == false ) { // mono mode UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize; for ( unsigned int i=0; imBuffers[handle->iStream[1]+i].mData, bufferBytes ); } } else { // read from multiple multi-channel streams UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset; Float32 *out, *in; bool outInterleaved = ( stream_.userInterleaved ) ? true : false; UInt32 outChannels = stream_.nUserChannels[1]; if ( stream_.doConvertBuffer[1] ) { outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode outChannels = stream_.nDeviceChannels[1]; } if ( outInterleaved ) outOffset = 1; else outOffset = stream_.bufferSize; channelsLeft = outChannels; for ( unsigned int i=0; inStreams[1]; i++ ) { out = outBuffer; in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData; streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels; inJump = 0; // Account for possible channel offset in first stream if ( i == 0 && stream_.channelOffset[1] > 0 ) { streamChannels -= stream_.channelOffset[1]; inJump = stream_.channelOffset[1]; in += inJump; } // Account for possible unread channels at end of the last stream if ( streamChannels > channelsLeft ) { inJump = streamChannels - channelsLeft; streamChannels = channelsLeft; } // Determine output buffer offsets and skips if ( outInterleaved ) { outJump = outChannels; out += outChannels - channelsLeft; } else { outJump = 1; out += (outChannels - channelsLeft) * outOffset; } for ( unsigned int i=0; i #include #include // A structure to hold various information related to the Jack API // implementation. struct JackHandle { jack_client_t *client; jack_port_t **ports[2]; std::string deviceName[2]; bool xrun[2]; pthread_cond_t condition; int drainCounter; // Tracks callback counts when draining bool internalDrain; // Indicates if stop is initiated from callback or not. JackHandle() :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; } }; static void jackSilentError( const char * ) {}; RtApiJack :: RtApiJack() { // Nothing to do here. #if !defined(__RTAUDIO_DEBUG__) // Turn off Jack's internal error reporting. jack_set_error_function( &jackSilentError ); #endif } RtApiJack :: ~RtApiJack() { if ( stream_.state != STREAM_CLOSED ) closeStream(); } unsigned int RtApiJack :: getDeviceCount( void ) { // See if we can become a jack client. jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption; jack_status_t *status = NULL; jack_client_t *client = jack_client_open( "RtApiJackCount", options, status ); if ( client == 0 ) return 0; const char **ports; std::string port, previousPort; unsigned int nChannels = 0, nDevices = 0; ports = jack_get_ports( client, NULL, NULL, 0 ); if ( ports ) { // Parse the port names up to the first colon (:). size_t iColon = 0; do { port = (char *) ports[ nChannels ]; iColon = port.find(":"); if ( iColon != std::string::npos ) { port = port.substr( 0, iColon + 1 ); if ( port != previousPort ) { nDevices++; previousPort = port; } } } while ( ports[++nChannels] ); free( ports ); } jack_client_close( client ); return nDevices; } RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) { RtAudio::DeviceInfo info; info.probed = false; jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption jack_status_t *status = NULL; jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status ); if ( client == 0 ) { errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!"; error( RtAudioError::WARNING ); return info; } const char **ports; std::string port, previousPort; unsigned int nPorts = 0, nDevices = 0; ports = jack_get_ports( client, NULL, NULL, 0 ); if ( ports ) { // Parse the port names up to the first colon (:). size_t iColon = 0; do { port = (char *) ports[ nPorts ]; iColon = port.find(":"); if ( iColon != std::string::npos ) { port = port.substr( 0, iColon ); if ( port != previousPort ) { if ( nDevices == device ) info.name = port; nDevices++; previousPort = port; } } } while ( ports[++nPorts] ); free( ports ); } if ( device >= nDevices ) { jack_client_close( client ); errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!"; error( RtAudioError::INVALID_USE ); return info; } // Get the current jack server sample rate. info.sampleRates.clear(); info.preferredSampleRate = jack_get_sample_rate( client ); info.sampleRates.push_back( info.preferredSampleRate ); // Count the available ports containing the client name as device // channels. Jack "input ports" equal RtAudio output channels. unsigned int nChannels = 0; ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput ); if ( ports ) { while ( ports[ nChannels ] ) nChannels++; free( ports ); info.outputChannels = nChannels; } // Jack "output ports" equal RtAudio input channels. nChannels = 0; ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput ); if ( ports ) { while ( ports[ nChannels ] ) nChannels++; free( ports ); info.inputChannels = nChannels; } if ( info.outputChannels == 0 && info.inputChannels == 0 ) { jack_client_close(client); errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!"; error( RtAudioError::WARNING ); return info; } // If device opens for both playback and capture, we determine the channels. if ( info.outputChannels > 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; // Jack always uses 32-bit floats. info.nativeFormats = RTAUDIO_FLOAT32; // Jack doesn't provide default devices so we'll use the first available one. if ( device == 0 && info.outputChannels > 0 ) info.isDefaultOutput = true; if ( device == 0 && info.inputChannels > 0 ) info.isDefaultInput = true; jack_client_close(client); info.probed = true; return info; } static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; RtApiJack *object = (RtApiJack *) info->object; if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1; return 0; } // This function will be called by a spawned thread when the Jack // server signals that it is shutting down. It is necessary to handle // it this way because the jackShutdown() function must return before // the jack_deactivate() function (in closeStream()) will return. static void *jackCloseStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiJack *object = (RtApiJack *) info->object; object->closeStream(); pthread_exit( NULL ); } static void jackShutdown( void *infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; RtApiJack *object = (RtApiJack *) info->object; // Check current stream state. If stopped, then we'll assume this // was called as a result of a call to RtApiJack::stopStream (the // deactivation of a client handle causes this function to be called). // If not, we'll assume the Jack server is shutting down or some // other problem occurred and we should close the stream. if ( object->isStreamRunning() == false ) return; ThreadHandle threadId; pthread_create( &threadId, NULL, jackCloseStream, info ); std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl; } static int jackXrun( void *infoPointer ) { JackHandle *handle = (JackHandle *) infoPointer; if ( handle->ports[0] ) handle->xrun[0] = true; if ( handle->ports[1] ) handle->xrun[1] = true; return 0; } bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { JackHandle *handle = (JackHandle *) stream_.apiHandle; // Look for jack server and try to become a client (only do once per stream). jack_client_t *client = 0; if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) { jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption; jack_status_t *status = NULL; if ( options && !options->streamName.empty() ) client = jack_client_open( options->streamName.c_str(), jackoptions, status ); else client = jack_client_open( "RtApiJack", jackoptions, status ); if ( client == 0 ) { errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!"; error( RtAudioError::WARNING ); return FAILURE; } } else { // The handle must have been created on an earlier pass. client = handle->client; } const char **ports; std::string port, previousPort, deviceName; unsigned int nPorts = 0, nDevices = 0; ports = jack_get_ports( client, NULL, NULL, 0 ); if ( ports ) { // Parse the port names up to the first colon (:). size_t iColon = 0; do { port = (char *) ports[ nPorts ]; iColon = port.find(":"); if ( iColon != std::string::npos ) { port = port.substr( 0, iColon ); if ( port != previousPort ) { if ( nDevices == device ) deviceName = port; nDevices++; previousPort = port; } } } while ( ports[++nPorts] ); free( ports ); } if ( device >= nDevices ) { errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!"; return FAILURE; } // Count the available ports containing the client name as device // channels. Jack "input ports" equal RtAudio output channels. unsigned int nChannels = 0; unsigned long flag = JackPortIsInput; if ( mode == INPUT ) flag = JackPortIsOutput; ports = jack_get_ports( client, deviceName.c_str(), NULL, flag ); if ( ports ) { while ( ports[ nChannels ] ) nChannels++; free( ports ); } // Compare the jack ports for specified client to the requested number of channels. if ( nChannels < (channels + firstChannel) ) { errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Check the jack server sample rate. unsigned int jackRate = jack_get_sample_rate( client ); if ( sampleRate != jackRate ) { jack_client_close( client ); errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.sampleRate = jackRate; // Get the latency of the JACK port. ports = jack_get_ports( client, deviceName.c_str(), NULL, flag ); if ( ports[ firstChannel ] ) { // Added by Ge Wang jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency); // the range (usually the min and max are equal) jack_latency_range_t latrange; latrange.min = latrange.max = 0; // get the latency range jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange ); // be optimistic, use the min! stream_.latency[mode] = latrange.min; //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) ); } free( ports ); // The jack server always uses 32-bit floating-point data. stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; stream_.userFormat = format; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; // Jack always uses non-interleaved buffers. stream_.deviceInterleaved[mode] = false; // Jack always provides host byte-ordered data. stream_.doByteSwap[mode] = false; // Get the buffer size. The buffer size and number of buffers // (periods) is set when the jack server is started. stream_.bufferSize = (int) jack_get_buffer_size( client ); *bufferSize = stream_.bufferSize; stream_.nDeviceChannels[mode] = channels; stream_.nUserChannels[mode] = channels; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate our JackHandle structure for the stream. if ( handle == 0 ) { try { handle = new JackHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory."; goto error; } if ( pthread_cond_init(&handle->condition, NULL) ) { errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable."; goto error; } stream_.apiHandle = (void *) handle; handle->client = client; } handle->deviceName[mode] = deviceName; // Allocate necessary internal buffers. unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; if ( mode == OUTPUT ) bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); else { // mode == INPUT bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] ); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( bufferBytes < bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } // Allocate memory for the Jack ports (channels) identifiers. handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels ); if ( handle->ports[mode] == NULL ) { errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory."; goto error; } stream_.device[mode] = device; stream_.channelOffset[mode] = firstChannel; stream_.state = STREAM_STOPPED; stream_.callbackInfo.object = (void *) this; if ( stream_.mode == OUTPUT && mode == INPUT ) // We had already set up the stream for output. stream_.mode = DUPLEX; else { stream_.mode = mode; jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo ); jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle ); jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); } // Register our ports. char label[64]; if ( mode == OUTPUT ) { for ( unsigned int i=0; iports[0][i] = jack_port_register( handle->client, (const char *)label, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); } } else { for ( unsigned int i=0; iports[1][i] = jack_port_register( handle->client, (const char *)label, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); } } // Setup the buffer conversion information structure. We don't use // buffers to do channel offsets, so we override that parameter // here. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 ); return SUCCESS; error: if ( handle ) { pthread_cond_destroy( &handle->condition ); jack_client_close( handle->client ); if ( handle->ports[0] ) free( handle->ports[0] ); if ( handle->ports[1] ) free( handle->ports[1] ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } return FAILURE; } void RtApiJack :: closeStream( void ) { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiJack::closeStream(): no open stream to close!"; error( RtAudioError::WARNING ); return; } JackHandle *handle = (JackHandle *) stream_.apiHandle; if ( handle ) { if ( stream_.state == STREAM_RUNNING ) jack_deactivate( handle->client ); jack_client_close( handle->client ); } if ( handle ) { if ( handle->ports[0] ) free( handle->ports[0] ); if ( handle->ports[1] ) free( handle->ports[1] ); pthread_cond_destroy( &handle->condition ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; stream_.state = STREAM_CLOSED; } void RtApiJack :: startStream( void ) { verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiJack::startStream(): the stream is already running!"; error( RtAudioError::WARNING ); return; } JackHandle *handle = (JackHandle *) stream_.apiHandle; int result = jack_activate( handle->client ); if ( result ) { errorText_ = "RtApiJack::startStream(): unable to activate JACK client!"; goto unlock; } const char **ports; // Get the list of available ports. if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { result = 1; ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput); if ( ports == NULL) { errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!"; goto unlock; } // Now make the port connections. Since RtAudio wasn't designed to // allow the user to select particular channels of a device, we'll // just open the first "nChannels" ports with offset. for ( unsigned int i=0; iclient, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] ); if ( result ) { free( ports ); errorText_ = "RtApiJack::startStream(): error connecting output ports!"; goto unlock; } } free(ports); } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { result = 1; ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput ); if ( ports == NULL) { errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!"; goto unlock; } // Now make the port connections. See note above. for ( unsigned int i=0; iclient, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) ); if ( result ) { free( ports ); errorText_ = "RtApiJack::startStream(): error connecting input ports!"; goto unlock; } } free(ports); } handle->drainCounter = 0; handle->internalDrain = false; stream_.state = STREAM_RUNNING; unlock: if ( result == 0 ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiJack :: stopStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiJack::stopStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } JackHandle *handle = (JackHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { handle->drainCounter = 2; pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled } } jack_deactivate( handle->client ); stream_.state = STREAM_STOPPED; } void RtApiJack :: abortStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiJack::abortStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } JackHandle *handle = (JackHandle *) stream_.apiHandle; handle->drainCounter = 2; stopStream(); } // This function will be called by a spawned thread when the user // callback function signals that the stream should be stopped or // aborted. It is necessary to handle it this way because the // callbackEvent() function must return before the jack_deactivate() // function will return. static void *jackStopStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiJack *object = (RtApiJack *) info->object; object->stopStream(); pthread_exit( NULL ); } bool RtApiJack :: callbackEvent( unsigned long nframes ) { if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RtAudioError::WARNING ); return FAILURE; } if ( stream_.bufferSize != nframes ) { errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!"; error( RtAudioError::WARNING ); return FAILURE; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; JackHandle *handle = (JackHandle *) stream_.apiHandle; // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > 3 ) { ThreadHandle threadId; stream_.state = STREAM_STOPPING; if ( handle->internalDrain == true ) pthread_create( &threadId, NULL, jackStopStream, info ); else pthread_cond_signal( &handle->condition ); return SUCCESS; } // Invoke user callback first, to get fresh output data. if ( handle->drainCounter == 0 ) { RtAudioCallback callback = (RtAudioCallback) info->callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, info->userData ); if ( cbReturnValue == 2 ) { stream_.state = STREAM_STOPPING; handle->drainCounter = 2; ThreadHandle id; pthread_create( &id, NULL, jackStopStream, info ); return SUCCESS; } else if ( cbReturnValue == 1 ) { handle->drainCounter = 1; handle->internalDrain = true; } } jack_default_audio_sample_t *jackbuffer; unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t ); if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter > 1 ) { // write zeros to the output stream for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); memset( jackbuffer, 0, bufferBytes ); } } else if ( stream_.doConvertBuffer[0] ) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); } } else { // no buffer conversion for ( unsigned int i=0; iports[0][i], (jack_nframes_t) nframes ); memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes ); } } } // Don't bother draining input if ( handle->drainCounter ) { handle->drainCounter++; goto unlock; } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { if ( stream_.doConvertBuffer[1] ) { for ( unsigned int i=0; iports[1][i], (jack_nframes_t) nframes ); memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes ); } convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); } else { // no buffer conversion for ( unsigned int i=0; iports[1][i], (jack_nframes_t) nframes ); memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes ); } } } unlock: RtApi::tickStreamTime(); return SUCCESS; } //******************** End of __UNIX_JACK__ *********************// #endif #if defined(__WINDOWS_ASIO__) // ASIO API on Windows // The ASIO API is designed around a callback scheme, so this // implementation is similar to that used for OS-X CoreAudio and Linux // Jack. The primary constraint with ASIO is that it only allows // access to a single driver at a time. Thus, it is not possible to // have more than one simultaneous RtAudio stream. // // This implementation also requires a number of external ASIO files // and a few global variables. The ASIO callback scheme does not // allow for the passing of user data, so we must create a global // pointer to our callbackInfo structure. // // On unix systems, we make use of a pthread condition variable. // Since there is no equivalent in Windows, I hacked something based // on information found in // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html. #include "asiosys.h" #include "asio.h" #include "iasiothiscallresolver.h" #include "asiodrivers.h" #include static AsioDrivers drivers; static ASIOCallbacks asioCallbacks; static ASIODriverInfo driverInfo; static CallbackInfo *asioCallbackInfo; static bool asioXRun; struct AsioHandle { int drainCounter; // Tracks callback counts when draining bool internalDrain; // Indicates if stop is initiated from callback or not. ASIOBufferInfo *bufferInfos; HANDLE condition; AsioHandle() :drainCounter(0), internalDrain(false), bufferInfos(0) {} }; // Function declarations (definitions at end of section) static const char* getAsioErrorString( ASIOError result ); static void sampleRateChanged( ASIOSampleRate sRate ); static long asioMessages( long selector, long value, void* message, double* opt ); RtApiAsio :: RtApiAsio() { // ASIO cannot run on a multi-threaded apartment. You can call // CoInitialize beforehand, but it must be for apartment threading // (in which case, CoInitilialize will return S_FALSE here). coInitialized_ = false; HRESULT hr = CoInitialize( NULL ); if ( FAILED(hr) ) { errorText_ = "RtApiAsio::ASIO requires a single-threaded apartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)"; error( RtAudioError::WARNING ); } coInitialized_ = true; drivers.removeCurrentDriver(); driverInfo.asioVersion = 2; // See note in DirectSound implementation about GetDesktopWindow(). driverInfo.sysRef = GetForegroundWindow(); } RtApiAsio :: ~RtApiAsio() { if ( stream_.state != STREAM_CLOSED ) closeStream(); if ( coInitialized_ ) CoUninitialize(); } unsigned int RtApiAsio :: getDeviceCount( void ) { return (unsigned int) drivers.asioGetNumDev(); } RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) { RtAudio::DeviceInfo info; info.probed = false; // Get device ID unsigned int nDevices = getDeviceCount(); if ( nDevices == 0 ) { errorText_ = "RtApiAsio::getDeviceInfo: no devices found!"; error( RtAudioError::INVALID_USE ); return info; } if ( device >= nDevices ) { errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!"; error( RtAudioError::INVALID_USE ); return info; } // If a stream is already open, we cannot probe other devices. Thus, use the saved results. if ( stream_.state != STREAM_CLOSED ) { if ( device >= devices_.size() ) { errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened."; error( RtAudioError::WARNING ); return info; } return devices_[ device ]; } char driverName[32]; ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } info.name = driverName; if ( !drivers.loadDriver( driverName ) ) { errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } result = ASIOInit( &driverInfo ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Determine the device channel information. long inputChannels, outputChannels; result = ASIOGetChannels( &inputChannels, &outputChannels ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } info.outputChannels = outputChannels; info.inputChannels = inputChannels; if ( info.outputChannels > 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; // Determine the supported sample rates. info.sampleRates.clear(); for ( unsigned int i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[i]; } } // Determine supported data types ... just check first channel and assume rest are the same. ASIOChannelInfo channelInfo; channelInfo.channel = 0; channelInfo.isInput = true; if ( info.inputChannels <= 0 ) channelInfo.isInput = false; result = ASIOGetChannelInfo( &channelInfo ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } info.nativeFormats = 0; if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) info.nativeFormats |= RTAUDIO_SINT16; else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) info.nativeFormats |= RTAUDIO_SINT32; else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) info.nativeFormats |= RTAUDIO_FLOAT32; else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) info.nativeFormats |= RTAUDIO_FLOAT64; else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) info.nativeFormats |= RTAUDIO_SINT24; if ( info.outputChannels > 0 ) if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; if ( info.inputChannels > 0 ) if ( getDefaultInputDevice() == device ) info.isDefaultInput = true; info.probed = true; drivers.removeCurrentDriver(); return info; } static void bufferSwitch( long index, ASIOBool /*processNow*/ ) { RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object; object->callbackEvent( index ); } void RtApiAsio :: saveDeviceInfo( void ) { devices_.clear(); unsigned int nDevices = getDeviceCount(); devices_.resize( nDevices ); for ( unsigned int i=0; isaveDeviceInfo(); if ( !drivers.loadDriver( driverName ) ) { errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ")."; errorText_ = errorStream_.str(); return FAILURE; } result = ASIOInit( &driverInfo ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ")."; errorText_ = errorStream_.str(); return FAILURE; } } // keep them before any "goto error", they are used for error cleanup + goto device boundary checks bool buffersAllocated = false; AsioHandle *handle = (AsioHandle *) stream_.apiHandle; unsigned int nChannels; // Check the device channel count. long inputChannels, outputChannels; result = ASIOGetChannels( &inputChannels, &outputChannels ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; errorText_ = errorStream_.str(); goto error; } if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) || ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ")."; errorText_ = errorStream_.str(); goto error; } stream_.nDeviceChannels[mode] = channels; stream_.nUserChannels[mode] = channels; stream_.channelOffset[mode] = firstChannel; // Verify the sample rate is supported. result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ")."; errorText_ = errorStream_.str(); goto error; } // Get the current sample rate ASIOSampleRate currentRate; result = ASIOGetSampleRate( ¤tRate ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate."; errorText_ = errorStream_.str(); goto error; } // Set the sample rate only if necessary if ( currentRate != sampleRate ) { result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ")."; errorText_ = errorStream_.str(); goto error; } } // Determine the driver data type. ASIOChannelInfo channelInfo; channelInfo.channel = 0; if ( mode == OUTPUT ) channelInfo.isInput = false; else channelInfo.isInput = true; result = ASIOGetChannelInfo( &channelInfo ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format."; errorText_ = errorStream_.str(); goto error; } // Assuming WINDOWS host is always little-endian. stream_.doByteSwap[mode] = false; stream_.userFormat = format; stream_.deviceFormat[mode] = 0; if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) { stream_.deviceFormat[mode] = RTAUDIO_SINT16; if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) { stream_.deviceFormat[mode] = RTAUDIO_SINT32; if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) { stream_.deviceFormat[mode] = RTAUDIO_SINT24; if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true; } if ( stream_.deviceFormat[mode] == 0 ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); goto error; } // Set the buffer size. For a duplex stream, this will end up // setting the buffer size based on the input constraints, which // should be ok. long minSize, maxSize, preferSize, granularity; result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size."; errorText_ = errorStream_.str(); goto error; } if ( isDuplexInput ) { // When this is the duplex input (output was opened before), then we have to use the same // buffersize as the output, because it might use the preferred buffer size, which most // likely wasn't passed as input to this. The buffer sizes have to be identically anyway, // So instead of throwing an error, make them equal. The caller uses the reference // to the "bufferSize" param as usual to set up processing buffers. *bufferSize = stream_.bufferSize; } else { if ( *bufferSize == 0 ) *bufferSize = preferSize; else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; else if ( granularity == -1 ) { // Make sure bufferSize is a power of two. int log2_of_min_size = 0; int log2_of_max_size = 0; for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) { if ( minSize & ((long)1 << i) ) log2_of_min_size = i; if ( maxSize & ((long)1 << i) ) log2_of_max_size = i; } long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) ); int min_delta_num = log2_of_min_size; for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) { long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) ); if (current_delta < min_delta) { min_delta = current_delta; min_delta_num = i; } } *bufferSize = ( (unsigned int)1 << min_delta_num ); if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; } else if ( granularity != 0 ) { // Set to an even multiple of granularity, rounding up. *bufferSize = (*bufferSize + granularity-1) / granularity * granularity; } } /* // we don't use it anymore, see above! // Just left it here for the case... if ( isDuplexInput && stream_.bufferSize != *bufferSize ) { errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!"; goto error; } */ stream_.bufferSize = *bufferSize; stream_.nBuffers = 2; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; // ASIO always uses non-interleaved buffers. stream_.deviceInterleaved[mode] = false; // Allocate, if necessary, our AsioHandle structure for the stream. if ( handle == 0 ) { try { handle = new AsioHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory."; goto error; } handle->bufferInfos = 0; // Create a manual-reset event. handle->condition = CreateEvent( NULL, // no security TRUE, // manual-reset FALSE, // non-signaled initially NULL ); // unnamed stream_.apiHandle = (void *) handle; } // Create the ASIO internal buffers. Since RtAudio sets up input // and output separately, we'll have to dispose of previously // created output buffers for a duplex stream. if ( mode == INPUT && stream_.mode == OUTPUT ) { ASIODisposeBuffers(); if ( handle->bufferInfos ) free( handle->bufferInfos ); } // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. unsigned int i; nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) ); if ( handle->bufferInfos == NULL ) { errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ")."; errorText_ = errorStream_.str(); goto error; } ASIOBufferInfo *infos; infos = handle->bufferInfos; for ( i=0; iisInput = ASIOFalse; infos->channelNum = i + stream_.channelOffset[0]; infos->buffers[0] = infos->buffers[1] = 0; } for ( i=0; iisInput = ASIOTrue; infos->channelNum = i + stream_.channelOffset[1]; infos->buffers[0] = infos->buffers[1] = 0; } // prepare for callbacks stream_.sampleRate = sampleRate; stream_.device[mode] = device; stream_.mode = isDuplexInput ? DUPLEX : mode; // store this class instance before registering callbacks, that are going to use it asioCallbackInfo = &stream_.callbackInfo; stream_.callbackInfo.object = (void *) this; // Set up the ASIO callback structure and create the ASIO data buffers. asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.asioMessage = &asioMessages; asioCallbacks.bufferSwitchTimeInfo = NULL; result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); if ( result != ASE_OK ) { // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges // but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver // in that case, let's be naïve and try that instead *bufferSize = preferSize; stream_.bufferSize = *bufferSize; result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); } if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers."; errorText_ = errorStream_.str(); goto error; } buffersAllocated = true; stream_.state = STREAM_STOPPED; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( isDuplexInput && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } // Determine device latencies long inputLatency, outputLatency; result = ASIOGetLatencies( &inputLatency, &outputLatency ); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING); // warn but don't fail } else { stream_.latency[0] = outputLatency; stream_.latency[1] = inputLatency; } // Setup the buffer conversion information structure. We don't use // buffers to do channel offsets, so we override that parameter // here. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 ); return SUCCESS; error: if ( !isDuplexInput ) { // the cleanup for error in the duplex input, is done by RtApi::openStream // So we clean up for single channel only if ( buffersAllocated ) ASIODisposeBuffers(); drivers.removeCurrentDriver(); if ( handle ) { CloseHandle( handle->condition ); if ( handle->bufferInfos ) free( handle->bufferInfos ); delete handle; stream_.apiHandle = 0; } if ( stream_.userBuffer[mode] ) { free( stream_.userBuffer[mode] ); stream_.userBuffer[mode] = 0; } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } } return FAILURE; }//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void RtApiAsio :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAsio::closeStream(): no open stream to close!"; error( RtAudioError::WARNING ); return; } if ( stream_.state == STREAM_RUNNING ) { stream_.state = STREAM_STOPPED; ASIOStop(); } ASIODisposeBuffers(); drivers.removeCurrentDriver(); AsioHandle *handle = (AsioHandle *) stream_.apiHandle; if ( handle ) { CloseHandle( handle->condition ); if ( handle->bufferInfos ) free( handle->bufferInfos ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; stream_.state = STREAM_CLOSED; } bool stopThreadCalled = false; void RtApiAsio :: startStream() { verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiAsio::startStream(): the stream is already running!"; error( RtAudioError::WARNING ); return; } AsioHandle *handle = (AsioHandle *) stream_.apiHandle; ASIOError result = ASIOStart(); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device."; errorText_ = errorStream_.str(); goto unlock; } handle->drainCounter = 0; handle->internalDrain = false; ResetEvent( handle->condition ); stream_.state = STREAM_RUNNING; asioXRun = false; unlock: stopThreadCalled = false; if ( result == ASE_OK ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiAsio :: stopStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } AsioHandle *handle = (AsioHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { handle->drainCounter = 2; WaitForSingleObject( handle->condition, INFINITE ); // block until signaled } } stream_.state = STREAM_STOPPED; ASIOError result = ASIOStop(); if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device."; errorText_ = errorStream_.str(); } if ( result == ASE_OK ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiAsio :: abortStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } // The following lines were commented-out because some behavior was // noted where the device buffers need to be zeroed to avoid // continuing sound, even when the device buffers are completely // disposed. So now, calling abort is the same as calling stop. // AsioHandle *handle = (AsioHandle *) stream_.apiHandle; // handle->drainCounter = 2; stopStream(); } // This function will be called by a spawned thread when the user // callback function signals that the stream should be stopped or // aborted. It is necessary to handle it this way because the // callbackEvent() function must return before the ASIOStop() // function will return. static unsigned __stdcall asioStopStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiAsio *object = (RtApiAsio *) info->object; object->stopStream(); _endthreadex( 0 ); return 0; } bool RtApiAsio :: callbackEvent( long bufferIndex ) { if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RtAudioError::WARNING ); return FAILURE; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; AsioHandle *handle = (AsioHandle *) stream_.apiHandle; // Check if we were draining the stream and signal if finished. if ( handle->drainCounter > 3 ) { stream_.state = STREAM_STOPPING; if ( handle->internalDrain == false ) SetEvent( handle->condition ); else { // spawn a thread to stop the stream unsigned threadId; stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream, &stream_.callbackInfo, 0, &threadId ); } return SUCCESS; } // Invoke user callback to get fresh output data UNLESS we are // draining stream. if ( handle->drainCounter == 0 ) { RtAudioCallback callback = (RtAudioCallback) info->callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && asioXRun == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; asioXRun = false; } if ( stream_.mode != OUTPUT && asioXRun == true ) { status |= RTAUDIO_INPUT_OVERFLOW; asioXRun = false; } int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, info->userData ); if ( cbReturnValue == 2 ) { stream_.state = STREAM_STOPPING; handle->drainCounter = 2; unsigned threadId; stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream, &stream_.callbackInfo, 0, &threadId ); return SUCCESS; } else if ( cbReturnValue == 1 ) { handle->drainCounter = 1; handle->internalDrain = true; } } unsigned int nChannels, bufferBytes, i, j; nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] ); if ( handle->drainCounter > 1 ) { // write zeros to the output stream for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes ); } } else if ( stream_.doConvertBuffer[0] ) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] ); if ( stream_.doByteSwap[0] ) byteSwapBuffer( stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[0], stream_.deviceFormat[0] ); for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) memcpy( handle->bufferInfos[i].buffers[bufferIndex], &stream_.deviceBuffer[j++*bufferBytes], bufferBytes ); } } else { if ( stream_.doByteSwap[0] ) byteSwapBuffer( stream_.userBuffer[0], stream_.bufferSize * stream_.nUserChannels[0], stream_.userFormat ); for ( i=0, j=0; ibufferInfos[i].isInput != ASIOTrue ) memcpy( handle->bufferInfos[i].buffers[bufferIndex], &stream_.userBuffer[0][bufferBytes*j++], bufferBytes ); } } } // Don't bother draining input if ( handle->drainCounter ) { handle->drainCounter++; goto unlock; } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]); if (stream_.doConvertBuffer[1]) { // Always interleave ASIO input data. for ( i=0, j=0; ibufferInfos[i].isInput == ASIOTrue ) memcpy( &stream_.deviceBuffer[j++*bufferBytes], handle->bufferInfos[i].buffers[bufferIndex], bufferBytes ); } if ( stream_.doByteSwap[1] ) byteSwapBuffer( stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[1], stream_.deviceFormat[1] ); convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); } else { for ( i=0, j=0; ibufferInfos[i].isInput == ASIOTrue ) { memcpy( &stream_.userBuffer[1][bufferBytes*j++], handle->bufferInfos[i].buffers[bufferIndex], bufferBytes ); } } if ( stream_.doByteSwap[1] ) byteSwapBuffer( stream_.userBuffer[1], stream_.bufferSize * stream_.nUserChannels[1], stream_.userFormat ); } } unlock: // The following call was suggested by Malte Clasen. While the API // documentation indicates it should not be required, some device // drivers apparently do not function correctly without it. ASIOOutputReady(); RtApi::tickStreamTime(); return SUCCESS; } static void sampleRateChanged( ASIOSampleRate sRate ) { // The ASIO documentation says that this usually only happens during // external sync. Audio processing is not stopped by the driver, // actual sample rate might not have even changed, maybe only the // sample rate status of an AES/EBU or S/PDIF digital input at the // audio device. RtApi *object = (RtApi *) asioCallbackInfo->object; try { object->stopStream(); } catch ( RtAudioError &exception ) { std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl; return; } std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl; } static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ ) { long ret = 0; switch( selector ) { case kAsioSelectorSupported: if ( value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest || value == kAsioLatenciesChanged // The following three were added for ASIO 2.0, you don't // necessarily have to support them. || value == kAsioSupportsTimeInfo || value == kAsioSupportsTimeCode || value == kAsioSupportsInputMonitor) ret = 1L; break; case kAsioResetRequest: // Defer the task and perform the reset of the driver during the // next "safe" situation. You cannot reset the driver right now, // as this code is called from the driver. Reset the driver is // done by completely destruct is. I.e. ASIOStop(), // ASIODisposeBuffers(), Destruction Afterwards you initialize the // driver again. std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl; ret = 1L; break; case kAsioResyncRequest: // This informs the application that the driver encountered some // non-fatal data loss. It is used for synchronization purposes // of different media. Added mainly to work around the Win16Mutex // problems in Windows 95/98 with the Windows Multimedia system, // which could lose data because the Mutex was held too long by // another thread. However a driver can issue it in other // situations, too. // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl; asioXRun = true; ret = 1L; break; case kAsioLatenciesChanged: // This will inform the host application that the drivers were // latencies changed. Beware, it this does not mean that the // buffer sizes have changed! You might need to update internal // delay data. std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl; ret = 1L; break; case kAsioEngineVersion: // Return the supported ASIO version of the host application. If // a host application does not implement this selector, ASIO 1.0 // is assumed by the driver. ret = 2L; break; case kAsioSupportsTimeInfo: // Informs the driver whether the // asioCallbacks.bufferSwitchTimeInfo() callback is supported. // For compatibility with ASIO 1.0 drivers the host application // should always support the "old" bufferSwitch method, too. ret = 0; break; case kAsioSupportsTimeCode: // Informs the driver whether application is interested in time // code info. If an application does not need to know about time // code, the driver has less work to do. ret = 0; break; } return ret; } static const char* getAsioErrorString( ASIOError result ) { struct Messages { ASIOError value; const char*message; }; static const Messages m[] = { { ASE_NotPresent, "Hardware input or output is not present or available." }, { ASE_HWMalfunction, "Hardware is malfunctioning." }, { ASE_InvalidParameter, "Invalid input parameter." }, { ASE_InvalidMode, "Invalid mode." }, { ASE_SPNotAdvancing, "Sample position not advancing." }, { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." }, { ASE_NoMemory, "Not enough memory to complete the request." } }; for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i ) if ( m[i].value == result ) return m[i].message; return "Unknown error."; } //******************** End of __WINDOWS_ASIO__ *********************// #endif #if defined(__WINDOWS_WASAPI__) // Windows WASAPI API // Authored by Marcus Tomlinson , April 2014 // - Introduces support for the Windows WASAPI API // - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required // - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface // - Includes automatic internal conversion of sample rate and buffer size between hardware and the user #ifndef INITGUID #define INITGUID #endif #include #include #include #include #include //============================================================================= #define SAFE_RELEASE( objectPtr )\ if ( objectPtr )\ {\ objectPtr->Release();\ objectPtr = NULL;\ } typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex ); //----------------------------------------------------------------------------- // WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size. // Therefore we must perform all necessary conversions to user buffers in order to satisfy these // requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to // provide intermediate storage for read / write synchronization. class WasapiBuffer { public: WasapiBuffer() : buffer_( NULL ), bufferSize_( 0 ), inIndex_( 0 ), outIndex_( 0 ) {} ~WasapiBuffer() { free( buffer_ ); } // sets the length of the internal ring buffer void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) { free( buffer_ ); buffer_ = ( char* ) calloc( bufferSize, formatBytes ); bufferSize_ = bufferSize; inIndex_ = 0; outIndex_ = 0; } // attempt to push a buffer into the ring buffer at the current "in" index bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format ) { if ( !buffer || // incoming buffer is NULL bufferSize == 0 || // incoming buffer has no data bufferSize > bufferSize_ ) // incoming buffer too large { return false; } unsigned int relOutIndex = outIndex_; unsigned int inIndexEnd = inIndex_ + bufferSize; if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) { relOutIndex += bufferSize_; } // "in" index can end on the "out" index but cannot begin at it if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) { return false; // not enough space between "in" index and "out" index } // copy buffer from external to internal int fromZeroSize = inIndex_ + bufferSize - bufferSize_; fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize; int fromInSize = bufferSize - fromZeroSize; switch( format ) { case RTAUDIO_SINT8: memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) ); memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) ); break; case RTAUDIO_SINT16: memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) ); memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) ); break; case RTAUDIO_SINT24: memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) ); memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) ); break; case RTAUDIO_SINT32: memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) ); memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) ); break; case RTAUDIO_FLOAT32: memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) ); memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) ); break; case RTAUDIO_FLOAT64: memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) ); memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) ); break; } // update "in" index inIndex_ += bufferSize; inIndex_ %= bufferSize_; return true; } // attempt to pull a buffer from the ring buffer from the current "out" index bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format ) { if ( !buffer || // incoming buffer is NULL bufferSize == 0 || // incoming buffer has no data bufferSize > bufferSize_ ) // incoming buffer too large { return false; } unsigned int relInIndex = inIndex_; unsigned int outIndexEnd = outIndex_ + bufferSize; if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) { relInIndex += bufferSize_; } // "out" index can begin at and end on the "in" index if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) { return false; // not enough space between "out" index and "in" index } // copy buffer from internal to external int fromZeroSize = outIndex_ + bufferSize - bufferSize_; fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize; int fromOutSize = bufferSize - fromZeroSize; switch( format ) { case RTAUDIO_SINT8: memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) ); memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) ); break; case RTAUDIO_SINT16: memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) ); memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) ); break; case RTAUDIO_SINT24: memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) ); memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) ); break; case RTAUDIO_SINT32: memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) ); memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) ); break; case RTAUDIO_FLOAT32: memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) ); memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) ); break; case RTAUDIO_FLOAT64: memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) ); memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) ); break; } // update "out" index outIndex_ += bufferSize; outIndex_ %= bufferSize_; return true; } private: char* buffer_; unsigned int bufferSize_; unsigned int inIndex_; unsigned int outIndex_; }; //----------------------------------------------------------------------------- // In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate // between HW and the user. The convertBufferWasapi function is used to perform this conversion // between HwIn->UserIn and UserOut->HwOut during the stream callback loop. // This sample rate converter favors speed over quality, and works best with conversions between // one rate and its multiple. void convertBufferWasapi( char* outBuffer, const char* inBuffer, const unsigned int& channelCount, const unsigned int& inSampleRate, const unsigned int& outSampleRate, const unsigned int& inSampleCount, unsigned int& outSampleCount, const RtAudioFormat& format ) { // calculate the new outSampleCount and relative sampleStep float sampleRatio = ( float ) outSampleRate / inSampleRate; float sampleStep = 1.0f / sampleRatio; float inSampleFraction = 0.0f; outSampleCount = ( unsigned int ) roundf( inSampleCount * sampleRatio ); // frame-by-frame, copy each relative input sample into it's corresponding output sample for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ ) { unsigned int inSample = ( unsigned int ) inSampleFraction; switch ( format ) { case RTAUDIO_SINT8: memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) ); break; case RTAUDIO_SINT16: memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) ); break; case RTAUDIO_SINT24: memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) ); break; case RTAUDIO_SINT32: memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) ); break; case RTAUDIO_FLOAT32: memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) ); break; case RTAUDIO_FLOAT64: memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) ); break; } // jump to next in sample inSampleFraction += sampleStep; } } //----------------------------------------------------------------------------- // A structure to hold various information related to the WASAPI implementation. struct WasapiHandle { IAudioClient* captureAudioClient; IAudioClient* renderAudioClient; IAudioCaptureClient* captureClient; IAudioRenderClient* renderClient; HANDLE captureEvent; HANDLE renderEvent; WasapiHandle() : captureAudioClient( NULL ), renderAudioClient( NULL ), captureClient( NULL ), renderClient( NULL ), captureEvent( NULL ), renderEvent( NULL ) {} }; //============================================================================= RtApiWasapi::RtApiWasapi() : coInitialized_( false ), deviceEnumerator_( NULL ) { // WASAPI can run either apartment or multi-threaded HRESULT hr = CoInitialize( NULL ); if ( !FAILED( hr ) ) coInitialized_ = true; // Instantiate device enumerator hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ), ( void** ) &deviceEnumerator_ ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator"; error( RtAudioError::DRIVER_ERROR ); } } //----------------------------------------------------------------------------- RtApiWasapi::~RtApiWasapi() { if ( stream_.state != STREAM_CLOSED ) closeStream(); SAFE_RELEASE( deviceEnumerator_ ); // If this object previously called CoInitialize() if ( coInitialized_ ) CoUninitialize(); } //============================================================================= unsigned int RtApiWasapi::getDeviceCount( void ) { unsigned int captureDeviceCount = 0; unsigned int renderDeviceCount = 0; IMMDeviceCollection* captureDevices = NULL; IMMDeviceCollection* renderDevices = NULL; // Count capture devices errorText_.clear(); HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection."; goto Exit; } hr = captureDevices->GetCount( &captureDeviceCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count."; goto Exit; } // Count render devices hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection."; goto Exit; } hr = renderDevices->GetCount( &renderDeviceCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count."; goto Exit; } Exit: // release all references SAFE_RELEASE( captureDevices ); SAFE_RELEASE( renderDevices ); if ( errorText_.empty() ) return captureDeviceCount + renderDeviceCount; error( RtAudioError::DRIVER_ERROR ); return 0; } //----------------------------------------------------------------------------- RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device ) { RtAudio::DeviceInfo info; unsigned int captureDeviceCount = 0; unsigned int renderDeviceCount = 0; std::string defaultDeviceName; bool isCaptureDevice = false; PROPVARIANT deviceNameProp; PROPVARIANT defaultDeviceNameProp; IMMDeviceCollection* captureDevices = NULL; IMMDeviceCollection* renderDevices = NULL; IMMDevice* devicePtr = NULL; IMMDevice* defaultDevicePtr = NULL; IAudioClient* audioClient = NULL; IPropertyStore* devicePropStore = NULL; IPropertyStore* defaultDevicePropStore = NULL; WAVEFORMATEX* deviceFormat = NULL; WAVEFORMATEX* closestMatchFormat = NULL; // probed info.probed = false; // Count capture devices errorText_.clear(); RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR; HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection."; goto Exit; } hr = captureDevices->GetCount( &captureDeviceCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count."; goto Exit; } // Count render devices hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection."; goto Exit; } hr = renderDevices->GetCount( &renderDeviceCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count."; goto Exit; } // validate device index if ( device >= captureDeviceCount + renderDeviceCount ) { errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index."; errorType = RtAudioError::INVALID_USE; goto Exit; } // determine whether index falls within capture or render devices if ( device >= renderDeviceCount ) { hr = captureDevices->Item( device - renderDeviceCount, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle."; goto Exit; } isCaptureDevice = true; } else { hr = renderDevices->Item( device, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle."; goto Exit; } isCaptureDevice = false; } // get default device name if ( isCaptureDevice ) { hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle."; goto Exit; } } else { hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle."; goto Exit; } } hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store."; goto Exit; } PropVariantInit( &defaultDeviceNameProp ); hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName."; goto Exit; } defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal); // name hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store."; goto Exit; } PropVariantInit( &deviceNameProp ); hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName."; goto Exit; } info.name =convertCharPointerToStdString(deviceNameProp.pwszVal); // is default if ( isCaptureDevice ) { info.isDefaultInput = info.name == defaultDeviceName; info.isDefaultOutput = false; } else { info.isDefaultInput = false; info.isDefaultOutput = info.name == defaultDeviceName; } // channel count hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client."; goto Exit; } hr = audioClient->GetMixFormat( &deviceFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format."; goto Exit; } if ( isCaptureDevice ) { info.inputChannels = deviceFormat->nChannels; info.outputChannels = 0; info.duplexChannels = 0; } else { info.inputChannels = 0; info.outputChannels = deviceFormat->nChannels; info.duplexChannels = 0; } // sample rates info.sampleRates.clear(); // allow support for all sample rates as we have a built-in sample rate converter for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) { info.sampleRates.push_back( SAMPLE_RATES[i] ); } info.preferredSampleRate = deviceFormat->nSamplesPerSec; // native format info.nativeFormats = 0; if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT || ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE && ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) ) { if ( deviceFormat->wBitsPerSample == 32 ) { info.nativeFormats |= RTAUDIO_FLOAT32; } else if ( deviceFormat->wBitsPerSample == 64 ) { info.nativeFormats |= RTAUDIO_FLOAT64; } } else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM || ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE && ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) ) { if ( deviceFormat->wBitsPerSample == 8 ) { info.nativeFormats |= RTAUDIO_SINT8; } else if ( deviceFormat->wBitsPerSample == 16 ) { info.nativeFormats |= RTAUDIO_SINT16; } else if ( deviceFormat->wBitsPerSample == 24 ) { info.nativeFormats |= RTAUDIO_SINT24; } else if ( deviceFormat->wBitsPerSample == 32 ) { info.nativeFormats |= RTAUDIO_SINT32; } } // probed info.probed = true; Exit: // release all references PropVariantClear( &deviceNameProp ); PropVariantClear( &defaultDeviceNameProp ); SAFE_RELEASE( captureDevices ); SAFE_RELEASE( renderDevices ); SAFE_RELEASE( devicePtr ); SAFE_RELEASE( defaultDevicePtr ); SAFE_RELEASE( audioClient ); SAFE_RELEASE( devicePropStore ); SAFE_RELEASE( defaultDevicePropStore ); CoTaskMemFree( deviceFormat ); CoTaskMemFree( closestMatchFormat ); if ( !errorText_.empty() ) error( errorType ); return info; } //----------------------------------------------------------------------------- unsigned int RtApiWasapi::getDefaultOutputDevice( void ) { for ( unsigned int i = 0; i < getDeviceCount(); i++ ) { if ( getDeviceInfo( i ).isDefaultOutput ) { return i; } } return 0; } //----------------------------------------------------------------------------- unsigned int RtApiWasapi::getDefaultInputDevice( void ) { for ( unsigned int i = 0; i < getDeviceCount(); i++ ) { if ( getDeviceInfo( i ).isDefaultInput ) { return i; } } return 0; } //----------------------------------------------------------------------------- void RtApiWasapi::closeStream( void ) { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiWasapi::closeStream: No open stream to close."; error( RtAudioError::WARNING ); return; } if ( stream_.state != STREAM_STOPPED ) stopStream(); // clean up stream memory SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient ) SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient ) if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent ) CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent ); if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent ) CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent ); delete ( WasapiHandle* ) stream_.apiHandle; stream_.apiHandle = NULL; for ( int i = 0; i < 2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } // update stream state stream_.state = STREAM_CLOSED; } //----------------------------------------------------------------------------- void RtApiWasapi::startStream( void ) { verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiWasapi::startStream: The stream is already running."; error( RtAudioError::WARNING ); return; } // update stream state stream_.state = STREAM_RUNNING; // create WASAPI stream thread stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL ); if ( !stream_.callbackInfo.thread ) { errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread."; error( RtAudioError::THREAD_ERROR ); } else { SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority ); ResumeThread( ( void* ) stream_.callbackInfo.thread ); } } //----------------------------------------------------------------------------- void RtApiWasapi::stopStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiWasapi::stopStream: The stream is already stopped."; error( RtAudioError::WARNING ); return; } // inform stream thread by setting stream state to STREAM_STOPPING stream_.state = STREAM_STOPPING; // wait until stream thread is stopped while( stream_.state != STREAM_STOPPED ) { Sleep( 1 ); } // Wait for the last buffer to play before stopping. Sleep( 1000 * stream_.bufferSize / stream_.sampleRate ); // stop capture client if applicable if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) { HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop(); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream."; error( RtAudioError::DRIVER_ERROR ); return; } } // stop render client if applicable if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) { HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop(); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream."; error( RtAudioError::DRIVER_ERROR ); return; } } // close thread handle if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread."; error( RtAudioError::THREAD_ERROR ); return; } stream_.callbackInfo.thread = (ThreadHandle) NULL; } //----------------------------------------------------------------------------- void RtApiWasapi::abortStream( void ) { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiWasapi::abortStream: The stream is already stopped."; error( RtAudioError::WARNING ); return; } // inform stream thread by setting stream state to STREAM_STOPPING stream_.state = STREAM_STOPPING; // wait until stream thread is stopped while ( stream_.state != STREAM_STOPPED ) { Sleep( 1 ); } // stop capture client if applicable if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) { HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop(); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream."; error( RtAudioError::DRIVER_ERROR ); return; } } // stop render client if applicable if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) { HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop(); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream."; error( RtAudioError::DRIVER_ERROR ); return; } } // close thread handle if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread."; error( RtAudioError::THREAD_ERROR ); return; } stream_.callbackInfo.thread = (ThreadHandle) NULL; } //----------------------------------------------------------------------------- bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int* bufferSize, RtAudio::StreamOptions* options ) { bool methodResult = FAILURE; unsigned int captureDeviceCount = 0; unsigned int renderDeviceCount = 0; IMMDeviceCollection* captureDevices = NULL; IMMDeviceCollection* renderDevices = NULL; IMMDevice* devicePtr = NULL; WAVEFORMATEX* deviceFormat = NULL; unsigned int bufferBytes; stream_.state = STREAM_STOPPED; // create API Handle if not already created if ( !stream_.apiHandle ) stream_.apiHandle = ( void* ) new WasapiHandle(); // Count capture devices errorText_.clear(); RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR; HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection."; goto Exit; } hr = captureDevices->GetCount( &captureDeviceCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count."; goto Exit; } // Count render devices hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection."; goto Exit; } hr = renderDevices->GetCount( &renderDeviceCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count."; goto Exit; } // validate device index if ( device >= captureDeviceCount + renderDeviceCount ) { errorType = RtAudioError::INVALID_USE; errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index."; goto Exit; } // determine whether index falls within capture or render devices if ( device >= renderDeviceCount ) { if ( mode != INPUT ) { errorType = RtAudioError::INVALID_USE; errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device."; goto Exit; } // retrieve captureAudioClient from devicePtr IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient; hr = captureDevices->Item( device - renderDeviceCount, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle."; goto Exit; } hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &captureAudioClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client."; goto Exit; } hr = captureAudioClient->GetMixFormat( &deviceFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format."; goto Exit; } stream_.nDeviceChannels[mode] = deviceFormat->nChannels; captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); } else { if ( mode != OUTPUT ) { errorType = RtAudioError::INVALID_USE; errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device."; goto Exit; } // retrieve renderAudioClient from devicePtr IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; hr = renderDevices->Item( device, &devicePtr ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle."; goto Exit; } hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &renderAudioClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client."; goto Exit; } hr = renderAudioClient->GetMixFormat( &deviceFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format."; goto Exit; } stream_.nDeviceChannels[mode] = deviceFormat->nChannels; renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] ); } // fill stream data if ( ( stream_.mode == OUTPUT && mode == INPUT ) || ( stream_.mode == INPUT && mode == OUTPUT ) ) { stream_.mode = DUPLEX; } else { stream_.mode = mode; } stream_.device[mode] = device; stream_.doByteSwap[mode] = false; stream_.sampleRate = sampleRate; stream_.bufferSize = *bufferSize; stream_.nBuffers = 1; stream_.nUserChannels[mode] = channels; stream_.channelOffset[mode] = firstChannel; stream_.userFormat = format; stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] || stream_.nUserChannels != stream_.nDeviceChannels ) stream_.doConvertBuffer[mode] = true; else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 ); // Allocate necessary internal buffers bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 ); if ( !stream_.userBuffer[mode] ) { errorType = RtAudioError::MEMORY_ERROR; errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory."; goto Exit; } if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) stream_.callbackInfo.priority = 15; else stream_.callbackInfo.priority = 0; ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback ///! TODO: RTAUDIO_HOG_DEVICE // Exclusive mode methodResult = SUCCESS; Exit: //clean up SAFE_RELEASE( captureDevices ); SAFE_RELEASE( renderDevices ); SAFE_RELEASE( devicePtr ); CoTaskMemFree( deviceFormat ); // if method failed, close the stream if ( methodResult == FAILURE ) closeStream(); if ( !errorText_.empty() ) error( errorType ); return methodResult; } //============================================================================= DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr ) { if ( wasapiPtr ) ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread(); return 0; } DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr ) { if ( wasapiPtr ) ( ( RtApiWasapi* ) wasapiPtr )->stopStream(); return 0; } DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr ) { if ( wasapiPtr ) ( ( RtApiWasapi* ) wasapiPtr )->abortStream(); return 0; } //----------------------------------------------------------------------------- void RtApiWasapi::wasapiThread() { // as this is a new thread, we must CoInitialize it CoInitialize( NULL ); HRESULT hr; IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient; IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient; IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient; IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient; HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent; HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent; WAVEFORMATEX* captureFormat = NULL; WAVEFORMATEX* renderFormat = NULL; float captureSrRatio = 0.0f; float renderSrRatio = 0.0f; WasapiBuffer captureBuffer; WasapiBuffer renderBuffer; // declare local stream variables RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback; BYTE* streamBuffer = NULL; unsigned long captureFlags = 0; unsigned int bufferFrameCount = 0; unsigned int numFramesPadding = 0; unsigned int convBufferSize = 0; bool callbackPushed = false; bool callbackPulled = false; bool callbackStopped = false; int callbackResult = 0; // convBuffer is used to store converted buffers between WASAPI and the user char* convBuffer = NULL; unsigned int convBuffSize = 0; unsigned int deviceBuffSize = 0; errorText_.clear(); RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR; // Attempt to assign "Pro Audio" characteristic to thread HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" ); if ( AvrtDll ) { DWORD taskIndex = 0; TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" ); AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex ); FreeLibrary( AvrtDll ); } // start capture stream if applicable if ( captureAudioClient ) { hr = captureAudioClient->GetMixFormat( &captureFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format."; goto Exit; } captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate ); // initialize capture stream according to desire buffer size float desiredBufferSize = stream_.bufferSize * captureSrRatio; REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec ); if ( !captureClient ) { hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, desiredBufferPeriod, desiredBufferPeriod, captureFormat, NULL ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client."; goto Exit; } hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ), ( void** ) &captureClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle."; goto Exit; } // configure captureEvent to trigger on every available capture buffer captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( !captureEvent ) { errorType = RtAudioError::SYSTEM_ERROR; errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event."; goto Exit; } hr = captureAudioClient->SetEventHandle( captureEvent ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle."; goto Exit; } ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient; ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent; } unsigned int inBufferSize = 0; hr = captureAudioClient->GetBufferSize( &inBufferSize ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size."; goto Exit; } // scale outBufferSize according to stream->user sample rate ratio unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT]; inBufferSize *= stream_.nDeviceChannels[INPUT]; // set captureBuffer size captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) ); // reset the capture stream hr = captureAudioClient->Reset(); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream."; goto Exit; } // start the capture stream hr = captureAudioClient->Start(); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream."; goto Exit; } } // start render stream if applicable if ( renderAudioClient ) { hr = renderAudioClient->GetMixFormat( &renderFormat ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format."; goto Exit; } renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate ); // initialize render stream according to desire buffer size float desiredBufferSize = stream_.bufferSize * renderSrRatio; REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec ); if ( !renderClient ) { hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, desiredBufferPeriod, desiredBufferPeriod, renderFormat, NULL ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client."; goto Exit; } hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ), ( void** ) &renderClient ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle."; goto Exit; } // configure renderEvent to trigger on every available render buffer renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if ( !renderEvent ) { errorType = RtAudioError::SYSTEM_ERROR; errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event."; goto Exit; } hr = renderAudioClient->SetEventHandle( renderEvent ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle."; goto Exit; } ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient; ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent; } unsigned int outBufferSize = 0; hr = renderAudioClient->GetBufferSize( &outBufferSize ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size."; goto Exit; } // scale inBufferSize according to user->stream sample rate ratio unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT]; outBufferSize *= stream_.nDeviceChannels[OUTPUT]; // set renderBuffer size renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) ); // reset the render stream hr = renderAudioClient->Reset(); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream."; goto Exit; } // start the render stream hr = renderAudioClient->Start(); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream."; goto Exit; } } if ( stream_.mode == INPUT ) { convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ); } else if ( stream_.mode == OUTPUT ) { convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ); deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ); } else if ( stream_.mode == DUPLEX ) { convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ); deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ), stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ); } convBuffer = ( char* ) malloc( convBuffSize ); stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize ); if ( !convBuffer || !stream_.deviceBuffer ) { errorType = RtAudioError::MEMORY_ERROR; errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory."; goto Exit; } // stream process loop while ( stream_.state != STREAM_STOPPING ) { if ( !callbackPulled ) { // Callback Input // ============== // 1. Pull callback buffer from inputBuffer // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count // Convert callback buffer to user format if ( captureAudioClient ) { // Pull callback buffer from inputBuffer callbackPulled = captureBuffer.pullBuffer( convBuffer, ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT], stream_.deviceFormat[INPUT] ); if ( callbackPulled ) { // Convert callback buffer to user sample rate convertBufferWasapi( stream_.deviceBuffer, convBuffer, stream_.nDeviceChannels[INPUT], captureFormat->nSamplesPerSec, stream_.sampleRate, ( unsigned int ) ( stream_.bufferSize * captureSrRatio ), convBufferSize, stream_.deviceFormat[INPUT] ); if ( stream_.doConvertBuffer[INPUT] ) { // Convert callback buffer to user format convertBuffer( stream_.userBuffer[INPUT], stream_.deviceBuffer, stream_.convertInfo[INPUT] ); } else { // no further conversion, simple copy deviceBuffer to userBuffer memcpy( stream_.userBuffer[INPUT], stream_.deviceBuffer, stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) ); } } } else { // if there is no capture stream, set callbackPulled flag callbackPulled = true; } // Execute Callback // ================ // 1. Execute user callback method // 2. Handle return value from callback // if callback has not requested the stream to stop if ( callbackPulled && !callbackStopped ) { // Execute user callback method callbackResult = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT], stream_.bufferSize, getStreamTime(), captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0, stream_.callbackInfo.userData ); // Handle return value from callback if ( callbackResult == 1 ) { // instantiate a thread to stop this thread HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL ); if ( !threadHandle ) { errorType = RtAudioError::THREAD_ERROR; errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread."; goto Exit; } else if ( !CloseHandle( threadHandle ) ) { errorType = RtAudioError::THREAD_ERROR; errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle."; goto Exit; } callbackStopped = true; } else if ( callbackResult == 2 ) { // instantiate a thread to stop this thread HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL ); if ( !threadHandle ) { errorType = RtAudioError::THREAD_ERROR; errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread."; goto Exit; } else if ( !CloseHandle( threadHandle ) ) { errorType = RtAudioError::THREAD_ERROR; errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle."; goto Exit; } callbackStopped = true; } } } // Callback Output // =============== // 1. Convert callback buffer to stream format // 2. Convert callback buffer to stream sample rate and channel count // 3. Push callback buffer into outputBuffer if ( renderAudioClient && callbackPulled ) { if ( stream_.doConvertBuffer[OUTPUT] ) { // Convert callback buffer to stream format convertBuffer( stream_.deviceBuffer, stream_.userBuffer[OUTPUT], stream_.convertInfo[OUTPUT] ); } // Convert callback buffer to stream sample rate convertBufferWasapi( convBuffer, stream_.deviceBuffer, stream_.nDeviceChannels[OUTPUT], stream_.sampleRate, renderFormat->nSamplesPerSec, stream_.bufferSize, convBufferSize, stream_.deviceFormat[OUTPUT] ); // Push callback buffer into outputBuffer callbackPushed = renderBuffer.pushBuffer( convBuffer, convBufferSize * stream_.nDeviceChannels[OUTPUT], stream_.deviceFormat[OUTPUT] ); } else { // if there is no render stream, set callbackPushed flag callbackPushed = true; } // Stream Capture // ============== // 1. Get capture buffer from stream // 2. Push capture buffer into inputBuffer // 3. If 2. was successful: Release capture buffer if ( captureAudioClient ) { // if the callback input buffer was not pulled from captureBuffer, wait for next capture event if ( !callbackPulled ) { WaitForSingleObject( captureEvent, INFINITE ); } // Get capture buffer from stream hr = captureClient->GetBuffer( &streamBuffer, &bufferFrameCount, &captureFlags, NULL, NULL ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer."; goto Exit; } if ( bufferFrameCount != 0 ) { // Push capture buffer into inputBuffer if ( captureBuffer.pushBuffer( ( char* ) streamBuffer, bufferFrameCount * stream_.nDeviceChannels[INPUT], stream_.deviceFormat[INPUT] ) ) { // Release capture buffer hr = captureClient->ReleaseBuffer( bufferFrameCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; goto Exit; } } else { // Inform WASAPI that capture was unsuccessful hr = captureClient->ReleaseBuffer( 0 ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; goto Exit; } } } else { // Inform WASAPI that capture was unsuccessful hr = captureClient->ReleaseBuffer( 0 ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer."; goto Exit; } } } // Stream Render // ============= // 1. Get render buffer from stream // 2. Pull next buffer from outputBuffer // 3. If 2. was successful: Fill render buffer with next buffer // Release render buffer if ( renderAudioClient ) { // if the callback output buffer was not pushed to renderBuffer, wait for next render event if ( callbackPulled && !callbackPushed ) { WaitForSingleObject( renderEvent, INFINITE ); } // Get render buffer from stream hr = renderAudioClient->GetBufferSize( &bufferFrameCount ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size."; goto Exit; } hr = renderAudioClient->GetCurrentPadding( &numFramesPadding ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding."; goto Exit; } bufferFrameCount -= numFramesPadding; if ( bufferFrameCount != 0 ) { hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer."; goto Exit; } // Pull next buffer from outputBuffer // Fill render buffer with next buffer if ( renderBuffer.pullBuffer( ( char* ) streamBuffer, bufferFrameCount * stream_.nDeviceChannels[OUTPUT], stream_.deviceFormat[OUTPUT] ) ) { // Release render buffer hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer."; goto Exit; } } else { // Inform WASAPI that render was unsuccessful hr = renderClient->ReleaseBuffer( 0, 0 ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer."; goto Exit; } } } else { // Inform WASAPI that render was unsuccessful hr = renderClient->ReleaseBuffer( 0, 0 ); if ( FAILED( hr ) ) { errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer."; goto Exit; } } } // if the callback buffer was pushed renderBuffer reset callbackPulled flag if ( callbackPushed ) { callbackPulled = false; // tick stream time RtApi::tickStreamTime(); } } Exit: // clean up CoTaskMemFree( captureFormat ); CoTaskMemFree( renderFormat ); free ( convBuffer ); CoUninitialize(); // update stream state stream_.state = STREAM_STOPPED; if ( errorText_.empty() ) return; else error( errorType ); } //******************** End of __WINDOWS_WASAPI__ *********************// #endif #if defined(__WINDOWS_DS__) // Windows DirectSound API // Modified by Robin Davies, October 2005 // - Improvements to DirectX pointer chasing. // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30. // - Auto-call CoInitialize for DSOUND and ASIO platforms. // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007 // Changed device query structure for RtAudio 4.0.7, January 2010 #include #include #include #if defined(__MINGW32__) // missing from latest mingw winapi #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */ #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */ #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ #endif #define MINIMUM_DEVICE_BUFFER_SIZE 32768 #ifdef _MSC_VER // if Microsoft Visual C++ #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually. #endif static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize ) { if ( pointer > bufferSize ) pointer -= bufferSize; if ( laterPointer < earlierPointer ) laterPointer += bufferSize; if ( pointer < earlierPointer ) pointer += bufferSize; return pointer >= earlierPointer && pointer < laterPointer; } // A structure to hold various information related to the DirectSound // API implementation. struct DsHandle { unsigned int drainCounter; // Tracks callback counts when draining bool internalDrain; // Indicates if stop is initiated from callback or not. void *id[2]; void *buffer[2]; bool xrun[2]; UINT bufferPointer[2]; DWORD dsBufferSize[2]; DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by. HANDLE condition; DsHandle() :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; } }; // Declarations for utility functions, callbacks, and structures // specific to the DirectSound implementation. static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext ); static const char* getErrorString( int code ); static unsigned __stdcall callbackHandler( void *ptr ); struct DsDevice { LPGUID id[2]; bool validId[2]; bool found; std::string name; DsDevice() : found(false) { validId[0] = false; validId[1] = false; } }; struct DsProbeData { bool isInput; std::vector* dsDevices; }; RtApiDs :: RtApiDs() { // Dsound will run both-threaded. If CoInitialize fails, then just // accept whatever the mainline chose for a threading model. coInitialized_ = false; HRESULT hr = CoInitialize( NULL ); if ( !FAILED( hr ) ) coInitialized_ = true; } RtApiDs :: ~RtApiDs() { if ( coInitialized_ ) CoUninitialize(); // balanced call. if ( stream_.state != STREAM_CLOSED ) closeStream(); } // The DirectSound default output is always the first device. unsigned int RtApiDs :: getDefaultOutputDevice( void ) { return 0; } // The DirectSound default input is always the first input device, // which is the first capture device enumerated. unsigned int RtApiDs :: getDefaultInputDevice( void ) { return 0; } unsigned int RtApiDs :: getDeviceCount( void ) { // Set query flag for previously found devices to false, so that we // can check for any devices that have disappeared. for ( unsigned int i=0; i(dsDevices.size()); } RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) { RtAudio::DeviceInfo info; info.probed = false; if ( dsDevices.size() == 0 ) { // Force a query of all devices getDeviceCount(); if ( dsDevices.size() == 0 ) { errorText_ = "RtApiDs::getDeviceInfo: no devices found!"; error( RtAudioError::INVALID_USE ); return info; } } if ( device >= dsDevices.size() ) { errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!"; error( RtAudioError::INVALID_USE ); return info; } HRESULT result; if ( dsDevices[ device ].validId[0] == false ) goto probeInput; LPDIRECTSOUND output; DSCAPS outCaps; result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); goto probeInput; } outCaps.dwSize = sizeof( outCaps ); result = output->GetCaps( &outCaps ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!"; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); goto probeInput; } // Get output channel information. info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; // Get sample rate information. info.sampleRates.clear(); for ( unsigned int k=0; k= (unsigned int) outCaps.dwMinSecondarySampleRate && SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) { info.sampleRates.push_back( SAMPLE_RATES[k] ); if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[k]; } } // Get format information. if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16; if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8; output->Release(); if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; if ( dsDevices[ device ].validId[1] == false ) { info.name = dsDevices[ device ].name; info.probed = true; return info; } probeInput: LPDIRECTSOUNDCAPTURE input; result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } DSCCAPS inCaps; inCaps.dwSize = sizeof( inCaps ); result = input->GetCaps( &inCaps ); if ( FAILED( result ) ) { input->Release(); errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Get input channel information. info.inputChannels = inCaps.dwChannels; // Get sample rate and format information. std::vector rates; if ( inCaps.dwChannels >= 2 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( info.nativeFormats & RTAUDIO_SINT16 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 ); if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 ); if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 ); if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 ); } else if ( info.nativeFormats & RTAUDIO_SINT8 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 ); if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 ); if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 ); if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 ); } } else if ( inCaps.dwChannels == 1 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16; if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8; if ( info.nativeFormats & RTAUDIO_SINT16 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 ); if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 ); if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 ); if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 ); } else if ( info.nativeFormats & RTAUDIO_SINT8 ) { if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 ); if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 ); if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 ); if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 ); } } else info.inputChannels = 0; // technically, this would be an error input->Release(); if ( info.inputChannels == 0 ) return info; // Copy the supported rates to the info structure but avoid duplication. bool found; for ( unsigned int i=0; i 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; if ( device == 0 ) info.isDefaultInput = true; // Copy name and return. info.name = dsDevices[ device ].name; info.probed = true; return info; } bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { if ( channels + firstChannel > 2 ) { errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device."; return FAILURE; } size_t nDevices = dsDevices.size(); if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiDs::probeDeviceOpen: no devices found!"; return FAILURE; } if ( device >= nDevices ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!"; return FAILURE; } if ( mode == OUTPUT ) { if ( dsDevices[ device ].validId[0] == false ) { errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!"; errorText_ = errorStream_.str(); return FAILURE; } } else { // mode == INPUT if ( dsDevices[ device ].validId[1] == false ) { errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!"; errorText_ = errorStream_.str(); return FAILURE; } } // According to a note in PortAudio, using GetDesktopWindow() // instead of GetForegroundWindow() is supposed to avoid problems // that occur when the application's window is not the foreground // window. Also, if the application window closes before the // DirectSound buffer, DirectSound can crash. In the past, I had // problems when using GetDesktopWindow() but it seems fine now // (January 2010). I'll leave it commented here. // HWND hWnd = GetForegroundWindow(); HWND hWnd = GetDesktopWindow(); // Check the numberOfBuffers parameter and limit the lowest value to // two. This is a judgement call and a value of two is probably too // low for capture, but it should work for playback. int nBuffers = 0; if ( options ) nBuffers = options->numberOfBuffers; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2; if ( nBuffers < 2 ) nBuffers = 3; // Check the lower range of the user-specified buffer size and set // (arbitrarily) to a lower bound of 32. if ( *bufferSize < 32 ) *bufferSize = 32; // Create the wave format structure. The data format setting will // be determined later. WAVEFORMATEX waveFormat; ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) ); waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nChannels = channels + firstChannel; waveFormat.nSamplesPerSec = (unsigned long) sampleRate; // Determine the device buffer size. By default, we'll use the value // defined above (32K), but we will grow it to make allowances for // very large software buffer sizes. DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE; DWORD dsPointerLeadTime = 0; void *ohandle = 0, *bhandle = 0; HRESULT result; if ( mode == OUTPUT ) { LPDIRECTSOUND output; result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } DSCAPS outCaps; outCaps.dwSize = sizeof( outCaps ); result = output->GetCaps( &outCaps ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Check channel information. if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) { errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback."; errorText_ = errorStream_.str(); return FAILURE; } // Check format information. Use 16-bit format unless not // supported or user requests 8-bit. if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT && !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) { waveFormat.wBitsPerSample = 16; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } else { waveFormat.wBitsPerSample = 8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } stream_.userFormat = format; // Update wave format structure and buffer information. waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; // If the user wants an even bigger buffer, increase the device buffer size accordingly. while ( dsPointerLeadTime * 2U > dsBufferSize ) dsBufferSize *= 2; // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes. // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE ); // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes. result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Even though we will write to the secondary buffer, we need to // access the primary buffer to set the correct output format // (since the default is 8-bit, 22 kHz!). Setup the DS primary // buffer description. DSBUFFERDESC bufferDescription; ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSBUFFERDESC ); bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; // Obtain the primary buffer LPDIRECTSOUNDBUFFER buffer; result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Set the primary DS buffer sound format. result = buffer->SetFormat( &waveFormat ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Setup the secondary DS buffer description. ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSBUFFERDESC ); bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCHARDWARE ); // Force hardware mixing bufferDescription.dwBufferBytes = dsBufferSize; bufferDescription.lpwfxFormat = &waveFormat; // Try to create the secondary DS buffer. If that doesn't work, // try to use software mixing. Otherwise, there's a problem. result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE ); // Force software mixing result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { output->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } } // Get the buffer size ... might be different from what we specified. DSBCAPS dsbcaps; dsbcaps.dwSize = sizeof( DSBCAPS ); result = buffer->GetCaps( &dsbcaps ); if ( FAILED( result ) ) { output->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } dsBufferSize = dsbcaps.dwBufferBytes; // Lock the DS buffer LPVOID audioPtr; DWORD dataLen; result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { output->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Zero the DS buffer ZeroMemory( audioPtr, dataLen ); // Unlock the DS buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { output->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } ohandle = (void *) output; bhandle = (void *) buffer; } if ( mode == INPUT ) { LPDIRECTSOUNDCAPTURE input; result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } DSCCAPS inCaps; inCaps.dwSize = sizeof( inCaps ); result = input->GetCaps( &inCaps ); if ( FAILED( result ) ) { input->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Check channel information. if ( inCaps.dwChannels < channels + firstChannel ) { errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels."; return FAILURE; } // Check format information. Use 16-bit format unless user // requests 8-bit. DWORD deviceFormats; if ( channels + firstChannel == 2 ) { deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08; if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) { waveFormat.wBitsPerSample = 8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } else { // assume 16-bit is supported waveFormat.wBitsPerSample = 16; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } } else { // channel == 1 deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08; if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) { waveFormat.wBitsPerSample = 8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } else { // assume 16-bit is supported waveFormat.wBitsPerSample = 16; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } } stream_.userFormat = format; // Update wave format structure and buffer information. waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; // If the user wants an even bigger buffer, increase the device buffer size accordingly. while ( dsPointerLeadTime * 2U > dsBufferSize ) dsBufferSize *= 2; // Setup the secondary DS buffer description. DSCBUFFERDESC bufferDescription; ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) ); bufferDescription.dwSize = sizeof( DSCBUFFERDESC ); bufferDescription.dwFlags = 0; bufferDescription.dwReserved = 0; bufferDescription.dwBufferBytes = dsBufferSize; bufferDescription.lpwfxFormat = &waveFormat; // Create the capture buffer. LPDIRECTSOUNDCAPTUREBUFFER buffer; result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL ); if ( FAILED( result ) ) { input->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Get the buffer size ... might be different from what we specified. DSCBCAPS dscbcaps; dscbcaps.dwSize = sizeof( DSCBCAPS ); result = buffer->GetCaps( &dscbcaps ); if ( FAILED( result ) ) { input->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } dsBufferSize = dscbcaps.dwBufferBytes; // NOTE: We could have a problem here if this is a duplex stream // and the play and capture hardware buffer sizes are different // (I'm actually not sure if that is a problem or not). // Currently, we are not verifying that. // Lock the capture buffer LPVOID audioPtr; DWORD dataLen; result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { input->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } // Zero the buffer ZeroMemory( audioPtr, dataLen ); // Unlock the buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { input->Release(); buffer->Release(); errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); return FAILURE; } ohandle = (void *) input; bhandle = (void *) buffer; } // Set various stream parameters DsHandle *handle = 0; stream_.nDeviceChannels[mode] = channels + firstChannel; stream_.nUserChannels[mode] = channels; stream_.bufferSize = *bufferSize; stream_.channelOffset[mode] = firstChannel; stream_.deviceInterleaved[mode] = true; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; // Set flag for buffer conversion stream_.doConvertBuffer[mode] = false; if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.userFormat != stream_.deviceFormat[mode]) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= (long) bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } // Allocate our DsHandle structures for the stream. if ( stream_.apiHandle == 0 ) { try { handle = new DsHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory."; goto error; } // Create a manual-reset event. handle->condition = CreateEvent( NULL, // no security TRUE, // manual-reset FALSE, // non-signaled initially NULL ); // unnamed stream_.apiHandle = (void *) handle; } else handle = (DsHandle *) stream_.apiHandle; handle->id[mode] = ohandle; handle->buffer[mode] = bhandle; handle->dsBufferSize[mode] = dsBufferSize; handle->dsPointerLeadTime[mode] = dsPointerLeadTime; stream_.device[mode] = device; stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT && mode == INPUT ) // We had already set up an output stream. stream_.mode = DUPLEX; else stream_.mode = mode; stream_.nBuffers = nBuffers; stream_.sampleRate = sampleRate; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); // Setup the callback thread. if ( stream_.callbackInfo.isRunning == false ) { unsigned threadId; stream_.callbackInfo.isRunning = true; stream_.callbackInfo.object = (void *) this; stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler, &stream_.callbackInfo, 0, &threadId ); if ( stream_.callbackInfo.thread == 0 ) { errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!"; goto error; } // Boost DS thread priority SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST ); } return SUCCESS; error: if ( handle ) { if ( handle->buffer[0] ) { // the object pointer can be NULL and valid LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; if ( buffer ) buffer->Release(); object->Release(); } if ( handle->buffer[1] ) { LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; if ( buffer ) buffer->Release(); object->Release(); } CloseHandle( handle->condition ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.state = STREAM_CLOSED; return FAILURE; } void RtApiDs :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiDs::closeStream(): no open stream to close!"; error( RtAudioError::WARNING ); return; } // Stop the callback thread. stream_.callbackInfo.isRunning = false; WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE ); CloseHandle( (HANDLE) stream_.callbackInfo.thread ); DsHandle *handle = (DsHandle *) stream_.apiHandle; if ( handle ) { if ( handle->buffer[0] ) { // the object pointer can be NULL and valid LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; if ( buffer ) { buffer->Stop(); buffer->Release(); } object->Release(); } if ( handle->buffer[1] ) { LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; if ( buffer ) { buffer->Stop(); buffer->Release(); } object->Release(); } CloseHandle( handle->condition ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; stream_.state = STREAM_CLOSED; } void RtApiDs :: startStream() { verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiDs::startStream(): the stream is already running!"; error( RtAudioError::WARNING ); return; } DsHandle *handle = (DsHandle *) stream_.apiHandle; // Increase scheduler frequency on lesser windows (a side-effect of // increasing timer accuracy). On greater windows (Win2K or later), // this is already in effect. timeBeginPeriod( 1 ); buffersRolling = false; duplexPrerollBytes = 0; if ( stream_.mode == DUPLEX ) { // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize. duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] ); } HRESULT result = 0; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; result = buffer->Play( 0, 0, DSBPLAY_LOOPING ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!"; errorText_ = errorStream_.str(); goto unlock; } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; result = buffer->Start( DSCBSTART_LOOPING ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!"; errorText_ = errorStream_.str(); goto unlock; } } handle->drainCounter = 0; handle->internalDrain = false; ResetEvent( handle->condition ); stream_.state = STREAM_RUNNING; unlock: if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR ); } void RtApiDs :: stopStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiDs::stopStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } HRESULT result = 0; LPVOID audioPtr; DWORD dataLen; DsHandle *handle = (DsHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( handle->drainCounter == 0 ) { handle->drainCounter = 2; WaitForSingleObject( handle->condition, INFINITE ); // block until signaled } stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); // Stop the buffer and clear memory LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; result = buffer->Stop(); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!"; errorText_ = errorStream_.str(); goto unlock; } // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!"; errorText_ = errorStream_.str(); goto unlock; } // Zero the DS buffer ZeroMemory( audioPtr, dataLen ); // Unlock the DS buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!"; errorText_ = errorStream_.str(); goto unlock; } // If we start playing again, we must begin at beginning of buffer. handle->bufferPointer[0] = 0; } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; audioPtr = NULL; dataLen = 0; stream_.state = STREAM_STOPPED; if ( stream_.mode != DUPLEX ) MUTEX_LOCK( &stream_.mutex ); result = buffer->Stop(); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!"; errorText_ = errorStream_.str(); goto unlock; } // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!"; errorText_ = errorStream_.str(); goto unlock; } // Zero the DS buffer ZeroMemory( audioPtr, dataLen ); // Unlock the DS buffer result = buffer->Unlock( audioPtr, dataLen, NULL, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!"; errorText_ = errorStream_.str(); goto unlock; } // If we start recording again, we must begin at beginning of buffer. handle->bufferPointer[1] = 0; } unlock: timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows. MUTEX_UNLOCK( &stream_.mutex ); if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR ); } void RtApiDs :: abortStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiDs::abortStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } DsHandle *handle = (DsHandle *) stream_.apiHandle; handle->drainCounter = 2; stopStream(); } void RtApiDs :: callbackEvent() { if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) { Sleep( 50 ); // sleep 50 milliseconds return; } if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RtAudioError::WARNING ); return; } CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; DsHandle *handle = (DsHandle *) stream_.apiHandle; // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > stream_.nBuffers + 2 ) { stream_.state = STREAM_STOPPING; if ( handle->internalDrain == false ) SetEvent( handle->condition ); else stopStream(); return; } // Invoke user callback to get fresh output data UNLESS we are // draining stream. if ( handle->drainCounter == 0 ) { RtAudioCallback callback = (RtAudioCallback) info->callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, info->userData ); if ( cbReturnValue == 2 ) { stream_.state = STREAM_STOPPING; handle->drainCounter = 2; abortStream(); return; } else if ( cbReturnValue == 1 ) { handle->drainCounter = 1; handle->internalDrain = true; } } HRESULT result; DWORD currentWritePointer, safeWritePointer; DWORD currentReadPointer, safeReadPointer; UINT nextWritePointer; LPVOID buffer1 = NULL; LPVOID buffer2 = NULL; DWORD bufferSize1 = 0; DWORD bufferSize2 = 0; char *buffer; long bufferBytes; MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_STOPPED ) { MUTEX_UNLOCK( &stream_.mutex ); return; } if ( buffersRolling == false ) { if ( stream_.mode == DUPLEX ) { //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); // It takes a while for the devices to get rolling. As a result, // there's no guarantee that the capture and write device pointers // will move in lockstep. Wait here for both devices to start // rolling, and then set our buffer pointers accordingly. // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600 // bytes later than the write buffer. // Stub: a serious risk of having a pre-emptive scheduling round // take place between the two GetCurrentPosition calls... but I'm // really not sure how to solve the problem. Temporarily boost to // Realtime priority, maybe; but I'm not sure what priority the // DirectSound service threads run at. We *should* be roughly // within a ms or so of correct. LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; DWORD startSafeWritePointer, startSafeReadPointer; result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } while ( true ) { result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break; Sleep( 1 ); } //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] ); handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; handle->bufferPointer[1] = safeReadPointer; } else if ( stream_.mode == OUTPUT ) { // Set the proper nextWritePosition after initial startup. LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; } buffersRolling = true; } if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; if ( handle->drainCounter > 1 ) { // write zeros to the output stream bufferBytes = stream_.bufferSize * stream_.nUserChannels[0]; bufferBytes *= formatBytes( stream_.userFormat ); memset( stream_.userBuffer[0], 0, bufferBytes ); } // Setup parameters and do buffer conversion if necessary. if ( stream_.doConvertBuffer[0] ) { buffer = stream_.deviceBuffer; convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0]; bufferBytes *= formatBytes( stream_.deviceFormat[0] ); } else { buffer = stream_.userBuffer[0]; bufferBytes = stream_.bufferSize * stream_.nUserChannels[0]; bufferBytes *= formatBytes( stream_.userFormat ); } // No byte swapping necessary in DirectSound implementation. // Ahhh ... windoze. 16-bit data is signed but 8-bit data is // unsigned. So, we need to convert our signed 8-bit data here to // unsigned. if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 ) for ( int i=0; idsBufferSize[0]; nextWritePointer = handle->bufferPointer[0]; DWORD endWrite, leadPointer; while ( true ) { // Find out where the read and "safe write" pointers are. result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } // We will copy our output buffer into the region between // safeWritePointer and leadPointer. If leadPointer is not // beyond the next endWrite position, wait until it is. leadPointer = safeWritePointer + handle->dsPointerLeadTime[0]; //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl; if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize; if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset endWrite = nextWritePointer + bufferBytes; // Check whether the entire write region is behind the play pointer. if ( leadPointer >= endWrite ) break; // If we are here, then we must wait until the leadPointer advances // beyond the end of our next write region. We use the // Sleep() function to suspend operation until that happens. double millis = ( endWrite - leadPointer ) * 1000.0; millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); } if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize ) || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { // We've strayed into the forbidden zone ... resync the read pointer. handle->xrun[0] = true; nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes; if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize; handle->bufferPointer[0] = nextWritePointer; endWrite = nextWritePointer + bufferBytes; } // Lock free space in the buffer result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } // Copy our buffer into the DS buffer CopyMemory( buffer1, buffer, bufferSize1 ); if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 ); // Update our buffer offset and unlock sound buffer dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize; handle->bufferPointer[0] = nextWritePointer; } // Don't bother draining input if ( handle->drainCounter ) { handle->drainCounter++; goto unlock; } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { // Setup parameters. if ( stream_.doConvertBuffer[1] ) { buffer = stream_.deviceBuffer; bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1]; bufferBytes *= formatBytes( stream_.deviceFormat[1] ); } else { buffer = stream_.userBuffer[1]; bufferBytes = stream_.bufferSize * stream_.nUserChannels[1]; bufferBytes *= formatBytes( stream_.userFormat ); } LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; long nextReadPointer = handle->bufferPointer[1]; DWORD dsBufferSize = handle->dsBufferSize[1]; // Find out where the write and "safe read" pointers are. result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset DWORD endRead = nextReadPointer + bufferBytes; // Handling depends on whether we are INPUT or DUPLEX. // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode, // then a wait here will drag the write pointers into the forbidden zone. // // In DUPLEX mode, rather than wait, we will back off the read pointer until // it's in a safe position. This causes dropouts, but it seems to be the only // practical way to sync up the read and write pointers reliably, given the // the very complex relationship between phase and increment of the read and write // pointers. // // In order to minimize audible dropouts in DUPLEX mode, we will // provide a pre-roll period of 0.5 seconds in which we return // zeros from the read buffer while the pointers sync up. if ( stream_.mode == DUPLEX ) { if ( safeReadPointer < endRead ) { if ( duplexPrerollBytes <= 0 ) { // Pre-roll time over. Be more aggressive. int adjustment = endRead-safeReadPointer; handle->xrun[1] = true; // Two cases: // - large adjustments: we've probably run out of CPU cycles, so just resync exactly, // and perform fine adjustments later. // - small adjustments: back off by twice as much. if ( adjustment >= 2*bufferBytes ) nextReadPointer = safeReadPointer-2*bufferBytes; else nextReadPointer = safeReadPointer-bufferBytes-adjustment; if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; } else { // In pre=roll time. Just do it. nextReadPointer = safeReadPointer - bufferBytes; while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize; } endRead = nextReadPointer + bufferBytes; } } else { // mode == INPUT while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) { // See comments for playback. double millis = (endRead - safeReadPointer) * 1000.0; millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); // Wake up and find out where we are now. result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset } } // Lock free space in the buffer result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } if ( duplexPrerollBytes <= 0 ) { // Copy our buffer into the DS buffer CopyMemory( buffer, buffer1, bufferSize1 ); if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 ); } else { memset( buffer, 0, bufferSize1 ); if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 ); duplexPrerollBytes -= bufferSize1 + bufferSize2; } // Update our buffer offset and unlock sound buffer nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize; dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!"; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } handle->bufferPointer[1] = nextReadPointer; // No byte swapping necessary in DirectSound implementation. // If necessary, convert 8-bit data from unsigned to signed. if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 ) for ( int j=0; jobject; bool* isRunning = &info->isRunning; while ( *isRunning == true ) { object->callbackEvent(); } _endthreadex( 0 ); return 0; } static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, LPCTSTR description, LPCTSTR /*module*/, LPVOID lpContext ) { struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext; std::vector& dsDevices = *probeInfo.dsDevices; HRESULT hr; bool validDevice = false; if ( probeInfo.isInput == true ) { DSCCAPS caps; LPDIRECTSOUNDCAPTURE object; hr = DirectSoundCaptureCreate( lpguid, &object, NULL ); if ( hr != DS_OK ) return TRUE; caps.dwSize = sizeof(caps); hr = object->GetCaps( &caps ); if ( hr == DS_OK ) { if ( caps.dwChannels > 0 && caps.dwFormats > 0 ) validDevice = true; } object->Release(); } else { DSCAPS caps; LPDIRECTSOUND object; hr = DirectSoundCreate( lpguid, &object, NULL ); if ( hr != DS_OK ) return TRUE; caps.dwSize = sizeof(caps); hr = object->GetCaps( &caps ); if ( hr == DS_OK ) { if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO ) validDevice = true; } object->Release(); } // If good device, then save its name and guid. std::string name = convertCharPointerToStdString( description ); //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" ) if ( lpguid == NULL ) name = "Default Device"; if ( validDevice ) { for ( unsigned int i=0; i #include // A structure to hold various information related to the ALSA API // implementation. struct AlsaHandle { snd_pcm_t *handles[2]; bool synchronized; bool xrun[2]; pthread_cond_t runnable_cv; bool runnable; AlsaHandle() :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; } }; static void *alsaCallbackHandler( void * ptr ); RtApiAlsa :: RtApiAlsa() { // Nothing to do here. } RtApiAlsa :: ~RtApiAlsa() { if ( stream_.state != STREAM_CLOSED ) closeStream(); } unsigned int RtApiAlsa :: getDeviceCount( void ) { unsigned nDevices = 0; int result, subdevice, card; char name[64]; snd_ctl_t *handle; // Count cards and devices card = -1; snd_card_next( &card ); while ( card >= 0 ) { sprintf( name, "hw:%d", card ); result = snd_ctl_open( &handle, name, 0 ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); goto nextcard; } subdevice = -1; while( 1 ) { result = snd_ctl_pcm_next_device( handle, &subdevice ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); break; } if ( subdevice < 0 ) break; nDevices++; } nextcard: snd_ctl_close( handle ); snd_card_next( &card ); } result = snd_ctl_open( &handle, "default", 0 ); if (result == 0) { nDevices++; snd_ctl_close( handle ); } return nDevices; } RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) { RtAudio::DeviceInfo info; info.probed = false; unsigned nDevices = 0; int result, subdevice, card; char name[64]; snd_ctl_t *chandle; // Count cards and devices card = -1; subdevice = -1; snd_card_next( &card ); while ( card >= 0 ) { sprintf( name, "hw:%d", card ); result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); goto nextcard; } subdevice = -1; while( 1 ) { result = snd_ctl_pcm_next_device( chandle, &subdevice ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); break; } if ( subdevice < 0 ) break; if ( nDevices == device ) { sprintf( name, "hw:%d,%d", card, subdevice ); goto foundDevice; } nDevices++; } nextcard: snd_ctl_close( chandle ); snd_card_next( &card ); } result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); if ( result == 0 ) { if ( nDevices == device ) { strcpy( name, "default" ); goto foundDevice; } nDevices++; } if ( nDevices == 0 ) { errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!"; error( RtAudioError::INVALID_USE ); return info; } if ( device >= nDevices ) { errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!"; error( RtAudioError::INVALID_USE ); return info; } foundDevice: // If a stream is already open, we cannot probe the stream devices. // Thus, use the saved results. if ( stream_.state != STREAM_CLOSED && ( stream_.device[0] == device || stream_.device[1] == device ) ) { snd_ctl_close( chandle ); if ( device >= devices_.size() ) { errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened."; error( RtAudioError::WARNING ); return info; } return devices_[ device ]; } int openMode = SND_PCM_ASYNC; snd_pcm_stream_t stream; snd_pcm_info_t *pcminfo; snd_pcm_info_alloca( &pcminfo ); snd_pcm_t *phandle; snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca( ¶ms ); // First try for playback unless default device (which has subdev -1) stream = SND_PCM_STREAM_PLAYBACK; snd_pcm_info_set_stream( pcminfo, stream ); if ( subdevice != -1 ) { snd_pcm_info_set_device( pcminfo, subdevice ); snd_pcm_info_set_subdevice( pcminfo, 0 ); result = snd_ctl_pcm_info( chandle, pcminfo ); if ( result < 0 ) { // Device probably doesn't support playback. goto captureProbe; } } result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); goto captureProbe; } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any( phandle, params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); goto captureProbe; } // Get output channel information. unsigned int value; result = snd_pcm_hw_params_get_channels_max( params, &value ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); goto captureProbe; } info.outputChannels = value; snd_pcm_close( phandle ); captureProbe: stream = SND_PCM_STREAM_CAPTURE; snd_pcm_info_set_stream( pcminfo, stream ); // Now try for capture unless default device (with subdev = -1) if ( subdevice != -1 ) { result = snd_ctl_pcm_info( chandle, pcminfo ); snd_ctl_close( chandle ); if ( result < 0 ) { // Device probably doesn't support capture. if ( info.outputChannels == 0 ) return info; goto probeParameters; } } else snd_ctl_close( chandle ); result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); if ( info.outputChannels == 0 ) return info; goto probeParameters; } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any( phandle, params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); if ( info.outputChannels == 0 ) return info; goto probeParameters; } result = snd_pcm_hw_params_get_channels_max( params, &value ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); if ( info.outputChannels == 0 ) return info; goto probeParameters; } info.inputChannels = value; snd_pcm_close( phandle ); // If device opens for both playback and capture, we determine the channels. if ( info.outputChannels > 0 && info.inputChannels > 0 ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; // ALSA doesn't provide default devices so we'll use the first available one. if ( device == 0 && info.outputChannels > 0 ) info.isDefaultOutput = true; if ( device == 0 && info.inputChannels > 0 ) info.isDefaultInput = true; probeParameters: // At this point, we just need to figure out the supported data // formats and sample rates. We'll proceed by opening the device in // the direction with the maximum number of channels, or playback if // they are equal. This might limit our sample rate options, but so // be it. if ( info.outputChannels >= info.inputChannels ) stream = SND_PCM_STREAM_PLAYBACK; else stream = SND_PCM_STREAM_CAPTURE; snd_pcm_info_set_stream( pcminfo, stream ); result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any( phandle, params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Test our discrete set of sample rate values. info.sampleRates.clear(); for ( unsigned int i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[i]; } } if ( info.sampleRates.size() == 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Probe the supported data formats ... we don't care about endian-ness just yet snd_pcm_format_t format; info.nativeFormats = 0; format = SND_PCM_FORMAT_S8; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_SINT8; format = SND_PCM_FORMAT_S16; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_SINT16; format = SND_PCM_FORMAT_S24; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_SINT24; format = SND_PCM_FORMAT_S32; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_SINT32; format = SND_PCM_FORMAT_FLOAT; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_FLOAT32; format = SND_PCM_FORMAT_FLOAT64; if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 ) info.nativeFormats |= RTAUDIO_FLOAT64; // Check that we have at least one supported format if ( info.nativeFormats == 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Get the device name char *cardname; result = snd_card_get_name( card, &cardname ); if ( result >= 0 ) { sprintf( name, "hw:%s,%d", cardname, subdevice ); free( cardname ); } info.name = name; // That's all ... close the device and return snd_pcm_close( phandle ); info.probed = true; return info; } void RtApiAlsa :: saveDeviceInfo( void ) { devices_.clear(); unsigned int nDevices = getDeviceCount(); devices_.resize( nDevices ); for ( unsigned int i=0; iflags & RTAUDIO_ALSA_USE_DEFAULT ) snprintf(name, sizeof(name), "%s", "default"); else { // Count cards and devices card = -1; snd_card_next( &card ); while ( card >= 0 ) { sprintf( name, "hw:%d", card ); result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } subdevice = -1; while( 1 ) { result = snd_ctl_pcm_next_device( chandle, &subdevice ); if ( result < 0 ) break; if ( subdevice < 0 ) break; if ( nDevices == device ) { sprintf( name, "hw:%d,%d", card, subdevice ); snd_ctl_close( chandle ); goto foundDevice; } nDevices++; } snd_ctl_close( chandle ); snd_card_next( &card ); } result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); if ( result == 0 ) { if ( nDevices == device ) { strcpy( name, "default" ); goto foundDevice; } nDevices++; } if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!"; return FAILURE; } if ( device >= nDevices ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!"; return FAILURE; } } foundDevice: // The getDeviceInfo() function will not work for a device that is // already open. Thus, we'll probe the system before opening a // stream and save the results for use by getDeviceInfo(). if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once this->saveDeviceInfo(); snd_pcm_stream_t stream; if ( mode == OUTPUT ) stream = SND_PCM_STREAM_PLAYBACK; else stream = SND_PCM_STREAM_CAPTURE; snd_pcm_t *phandle; int openMode = SND_PCM_ASYNC; result = snd_pcm_open( &phandle, name, stream, openMode ); if ( result < 0 ) { if ( mode == OUTPUT ) errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output."; else errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input."; errorText_ = errorStream_.str(); return FAILURE; } // Fill the parameter structure. snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca( &hw_params ); result = snd_pcm_hw_params_any( phandle, hw_params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" ); snd_pcm_hw_params_dump( hw_params, out ); #endif // Set access ... check user preference. if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) { stream_.userInterleaved = false; result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED ); if ( result < 0 ) { result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ); stream_.deviceInterleaved[mode] = true; } else stream_.deviceInterleaved[mode] = false; } else { stream_.userInterleaved = true; result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ); if ( result < 0 ) { result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED ); stream_.deviceInterleaved[mode] = false; } else stream_.deviceInterleaved[mode] = true; } if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // Determine how to set the device format. stream_.userFormat = format; snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN; if ( format == RTAUDIO_SINT8 ) deviceFormat = SND_PCM_FORMAT_S8; else if ( format == RTAUDIO_SINT16 ) deviceFormat = SND_PCM_FORMAT_S16; else if ( format == RTAUDIO_SINT24 ) deviceFormat = SND_PCM_FORMAT_S24; else if ( format == RTAUDIO_SINT32 ) deviceFormat = SND_PCM_FORMAT_S32; else if ( format == RTAUDIO_FLOAT32 ) deviceFormat = SND_PCM_FORMAT_FLOAT; else if ( format == RTAUDIO_FLOAT64 ) deviceFormat = SND_PCM_FORMAT_FLOAT64; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) { stream_.deviceFormat[mode] = format; goto setFormat; } // The user requested format is not natively supported by the device. deviceFormat = SND_PCM_FORMAT_FLOAT64; if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; goto setFormat; } deviceFormat = SND_PCM_FORMAT_FLOAT; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; goto setFormat; } deviceFormat = SND_PCM_FORMAT_S32; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_SINT32; goto setFormat; } deviceFormat = SND_PCM_FORMAT_S24; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_SINT24; goto setFormat; } deviceFormat = SND_PCM_FORMAT_S16; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_SINT16; goto setFormat; } deviceFormat = SND_PCM_FORMAT_S8; if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) { stream_.deviceFormat[mode] = RTAUDIO_SINT8; goto setFormat; } // If we get here, no supported format was found. snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio."; errorText_ = errorStream_.str(); return FAILURE; setFormat: result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // Determine whether byte-swaping is necessary. stream_.doByteSwap[mode] = false; if ( deviceFormat != SND_PCM_FORMAT_S8 ) { result = snd_pcm_format_cpu_endian( deviceFormat ); if ( result == 0 ) stream_.doByteSwap[mode] = true; else if (result < 0) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } } // Set the sample rate. result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // Determine the number of channels for this device. We support a possible // minimum device channel number > than the value requested by the user. stream_.nUserChannels[mode] = channels; unsigned int value; result = snd_pcm_hw_params_get_channels_max( hw_params, &value ); unsigned int deviceChannels = value; if ( result < 0 || deviceChannels < channels + firstChannel ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } result = snd_pcm_hw_params_get_channels_min( hw_params, &value ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } deviceChannels = value; if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel; stream_.nDeviceChannels[mode] = deviceChannels; // Set the device channels. result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // Set the buffer (or period) size. int dir = 0; snd_pcm_uframes_t periodSize = *bufferSize; result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } *bufferSize = periodSize; // Set the buffer number, which in ALSA is referred to as the "period". unsigned int periods = 0; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2; if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers; if ( periods < 2 ) periods = 4; // a fairly safe default value result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.bufferSize = *bufferSize; // Install the hardware configuration result = snd_pcm_hw_params( phandle, hw_params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n"); snd_pcm_hw_params_dump( hw_params, out ); #endif // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns. snd_pcm_sw_params_t *sw_params = NULL; snd_pcm_sw_params_alloca( &sw_params ); snd_pcm_sw_params_current( phandle, sw_params ); snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize ); snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX ); snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 ); // The following two settings were suggested by Theo Veenker //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize ); //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 ); // here are two options for a fix //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX ); snd_pcm_uframes_t val; snd_pcm_sw_params_get_boundary( sw_params, &val ); snd_pcm_sw_params_set_silence_size( phandle, sw_params, val ); result = snd_pcm_sw_params( phandle, sw_params ); if ( result < 0 ) { snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n"); snd_pcm_sw_params_dump( sw_params, out ); #endif // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate the ApiHandle if necessary and then save. AlsaHandle *apiInfo = 0; if ( stream_.apiHandle == 0 ) { try { apiInfo = (AlsaHandle *) new AlsaHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory."; goto error; } if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) { errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable."; goto error; } stream_.apiHandle = (void *) apiInfo; apiInfo->handles[0] = 0; apiInfo->handles[1] = 0; } else { apiInfo = (AlsaHandle *) stream_.apiHandle; } apiInfo->handles[mode] = phandle; phandle = 0; // Allocate necessary internal buffers. unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } stream_.sampleRate = sampleRate; stream_.nBuffers = periods; stream_.device[mode] = device; stream_.state = STREAM_STOPPED; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); // Setup thread if necessary. if ( stream_.mode == OUTPUT && mode == INPUT ) { // We had already set up an output stream. stream_.mode = DUPLEX; // Link the streams if possible. apiInfo->synchronized = false; if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 ) apiInfo->synchronized = true; else { errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices."; error( RtAudioError::WARNING ); } } else { stream_.mode = mode; // Setup callback thread. stream_.callbackInfo.object = (void *) this; // Set the thread attributes for joinable and realtime scheduling // priority (optional). The higher priority will only take affect // if the program is run as root or suid. Note, under Linux // processes with CAP_SYS_NICE privilege, a user can change // scheduling policy and priority (thus need not be root). See // POSIX "capabilities". pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { // We previously attempted to increase the audio callback priority // to SCHED_RR here via the attributes. However, while no errors // were reported in doing so, it did not work. So, now this is // done in the alsaCallbackHandler function. stream_.callbackInfo.doRealtime = true; int priority = options->priority; int min = sched_get_priority_min( SCHED_RR ); int max = sched_get_priority_max( SCHED_RR ); if ( priority < min ) priority = min; else if ( priority > max ) priority = max; stream_.callbackInfo.priority = priority; } #endif stream_.callbackInfo.isRunning = true; result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo ); pthread_attr_destroy( &attr ); if ( result ) { stream_.callbackInfo.isRunning = false; errorText_ = "RtApiAlsa::error creating callback thread!"; goto error; } } return SUCCESS; error: if ( apiInfo ) { pthread_cond_destroy( &apiInfo->runnable_cv ); if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] ); if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] ); delete apiInfo; stream_.apiHandle = 0; } if ( phandle) snd_pcm_close( phandle ); for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.state = STREAM_CLOSED; return FAILURE; } void RtApiAlsa :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAlsa::closeStream(): no open stream to close!"; error( RtAudioError::WARNING ); return; } AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; stream_.callbackInfo.isRunning = false; MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_STOPPED ) { apiInfo->runnable = true; pthread_cond_signal( &apiInfo->runnable_cv ); } MUTEX_UNLOCK( &stream_.mutex ); pthread_join( stream_.callbackInfo.thread, NULL ); if ( stream_.state == STREAM_RUNNING ) { stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) snd_pcm_drop( apiInfo->handles[0] ); if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) snd_pcm_drop( apiInfo->handles[1] ); } if ( apiInfo ) { pthread_cond_destroy( &apiInfo->runnable_cv ); if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] ); if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] ); delete apiInfo; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; stream_.state = STREAM_CLOSED; } void RtApiAlsa :: startStream() { // This method calls snd_pcm_prepare if the device isn't already in that state. verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiAlsa::startStream(): the stream is already running!"; error( RtAudioError::WARNING ); return; } MUTEX_LOCK( &stream_.mutex ); int result = 0; snd_pcm_state_t state; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { state = snd_pcm_state( handle[0] ); if ( state != SND_PCM_STATE_PREPARED ) { result = snd_pcm_prepare( handle[0] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } } if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open state = snd_pcm_state( handle[1] ); if ( state != SND_PCM_STATE_PREPARED ) { result = snd_pcm_prepare( handle[1] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } } stream_.state = STREAM_RUNNING; unlock: apiInfo->runnable = true; pthread_cond_signal( &apiInfo->runnable_cv ); MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiAlsa :: stopStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); int result = 0; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( apiInfo->synchronized ) result = snd_pcm_drop( handle[0] ); else result = snd_pcm_drain( handle[0] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { result = snd_pcm_drop( handle[1] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } unlock: apiInfo->runnable = false; // fixes high CPU usage when stopped MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiAlsa :: abortStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); int result = 0; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { result = snd_pcm_drop( handle[0] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { result = snd_pcm_drop( handle[1] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); goto unlock; } } unlock: apiInfo->runnable = false; // fixes high CPU usage when stopped MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiAlsa :: callbackEvent() { AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; if ( stream_.state == STREAM_STOPPED ) { MUTEX_LOCK( &stream_.mutex ); while ( !apiInfo->runnable ) pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex ); if ( stream_.state != STREAM_RUNNING ) { MUTEX_UNLOCK( &stream_.mutex ); return; } MUTEX_UNLOCK( &stream_.mutex ); } if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RtAudioError::WARNING ); return; } int doStopStream = 0; RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; apiInfo->xrun[0] = false; } if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; apiInfo->xrun[1] = false; } doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); if ( doStopStream == 2 ) { abortStream(); return; } MUTEX_LOCK( &stream_.mutex ); // The state might change while waiting on a mutex. if ( stream_.state == STREAM_STOPPED ) goto unlock; int result; char *buffer; int channels; snd_pcm_t **handle; snd_pcm_sframes_t frames; RtAudioFormat format; handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { // Setup parameters. if ( stream_.doConvertBuffer[1] ) { buffer = stream_.deviceBuffer; channels = stream_.nDeviceChannels[1]; format = stream_.deviceFormat[1]; } else { buffer = stream_.userBuffer[1]; channels = stream_.nUserChannels[1]; format = stream_.userFormat; } // Read samples from device in interleaved/non-interleaved format. if ( stream_.deviceInterleaved[1] ) result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize ); else { void *bufs[channels]; size_t offset = stream_.bufferSize * formatBytes( format ); for ( int i=0; ixrun[1] = true; result = snd_pcm_prepare( handle[1] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } } else { errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } } else { errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } error( RtAudioError::WARNING ); goto tryOutput; } // Do byte swapping if necessary. if ( stream_.doByteSwap[1] ) byteSwapBuffer( buffer, stream_.bufferSize * channels, format ); // Do buffer conversion if necessary. if ( stream_.doConvertBuffer[1] ) convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); // Check stream latency result = snd_pcm_delay( handle[1], &frames ); if ( result == 0 && frames > 0 ) stream_.latency[1] = frames; } tryOutput: if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { // Setup parameters and do buffer conversion if necessary. if ( stream_.doConvertBuffer[0] ) { buffer = stream_.deviceBuffer; convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); channels = stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { buffer = stream_.userBuffer[0]; channels = stream_.nUserChannels[0]; format = stream_.userFormat; } // Do byte swapping if necessary. if ( stream_.doByteSwap[0] ) byteSwapBuffer(buffer, stream_.bufferSize * channels, format); // Write samples to device in interleaved/non-interleaved format. if ( stream_.deviceInterleaved[0] ) result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize ); else { void *bufs[channels]; size_t offset = stream_.bufferSize * formatBytes( format ); for ( int i=0; ixrun[0] = true; result = snd_pcm_prepare( handle[0] ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } else errorText_ = "RtApiAlsa::callbackEvent: audio write error, underrun."; } else { errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } } else { errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } error( RtAudioError::WARNING ); goto unlock; } // Check stream latency result = snd_pcm_delay( handle[0], &frames ); if ( result == 0 && frames > 0 ) stream_.latency[0] = frames; } unlock: MUTEX_UNLOCK( &stream_.mutex ); RtApi::tickStreamTime(); if ( doStopStream == 1 ) this->stopStream(); } static void *alsaCallbackHandler( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiAlsa *object = (RtApiAlsa *) info->object; bool *isRunning = &info->isRunning; #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) if ( info->doRealtime ) { pthread_t tID = pthread_self(); // ID of this thread sched_param prio = { info->priority }; // scheduling priority of thread pthread_setschedparam( tID, SCHED_RR, &prio ); } #endif while ( *isRunning == true ) { pthread_testcancel(); object->callbackEvent(); } pthread_exit( NULL ); } //******************** End of __LINUX_ALSA__ *********************// #endif #if defined(__LINUX_PULSE__) // Code written by Peter Meerwald, pmeerw@pmeerw.net // and Tristan Matthews. #include #include #include static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000, 44100, 48000, 96000, 0}; struct rtaudio_pa_format_mapping_t { RtAudioFormat rtaudio_format; pa_sample_format_t pa_format; }; static const rtaudio_pa_format_mapping_t supported_sampleformats[] = { {RTAUDIO_SINT16, PA_SAMPLE_S16LE}, {RTAUDIO_SINT32, PA_SAMPLE_S32LE}, {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE}, {0, PA_SAMPLE_INVALID}}; struct PulseAudioHandle { pa_simple *s_play; pa_simple *s_rec; pthread_t thread; pthread_cond_t runnable_cv; bool runnable; PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { } }; RtApiPulse::~RtApiPulse() { if ( stream_.state != STREAM_CLOSED ) closeStream(); } unsigned int RtApiPulse::getDeviceCount( void ) { return 1; } RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; info.probed = true; info.name = "PulseAudio"; info.outputChannels = 2; info.inputChannels = 2; info.duplexChannels = 2; info.isDefaultOutput = true; info.isDefaultInput = true; for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) info.sampleRates.push_back( *sr ); info.preferredSampleRate = 48000; info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32; return info; } static void *pulseaudio_callback( void * user ) { CallbackInfo *cbi = static_cast( user ); RtApiPulse *context = static_cast( cbi->object ); volatile bool *isRunning = &cbi->isRunning; while ( *isRunning ) { pthread_testcancel(); context->callbackEvent(); } pthread_exit( NULL ); } void RtApiPulse::closeStream( void ) { PulseAudioHandle *pah = static_cast( stream_.apiHandle ); stream_.callbackInfo.isRunning = false; if ( pah ) { MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_STOPPED ) { pah->runnable = true; pthread_cond_signal( &pah->runnable_cv ); } MUTEX_UNLOCK( &stream_.mutex ); pthread_join( pah->thread, 0 ); if ( pah->s_play ) { pa_simple_flush( pah->s_play, NULL ); pa_simple_free( pah->s_play ); } if ( pah->s_rec ) pa_simple_free( pah->s_rec ); pthread_cond_destroy( &pah->runnable_cv ); delete pah; stream_.apiHandle = 0; } if ( stream_.userBuffer[0] ) { free( stream_.userBuffer[0] ); stream_.userBuffer[0] = 0; } if ( stream_.userBuffer[1] ) { free( stream_.userBuffer[1] ); stream_.userBuffer[1] = 0; } stream_.state = STREAM_CLOSED; stream_.mode = UNINITIALIZED; } void RtApiPulse::callbackEvent( void ) { PulseAudioHandle *pah = static_cast( stream_.apiHandle ); if ( stream_.state == STREAM_STOPPED ) { MUTEX_LOCK( &stream_.mutex ); while ( !pah->runnable ) pthread_cond_wait( &pah->runnable_cv, &stream_.mutex ); if ( stream_.state != STREAM_RUNNING ) { MUTEX_UNLOCK( &stream_.mutex ); return; } MUTEX_UNLOCK( &stream_.mutex ); } if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... " "this shouldn't happen!"; error( RtAudioError::WARNING ); return; } RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); if ( doStopStream == 2 ) { abortStream(); return; } MUTEX_LOCK( &stream_.mutex ); void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT]; void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT]; if ( stream_.state != STREAM_RUNNING ) goto unlock; int pa_error; size_t bytes; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( stream_.doConvertBuffer[OUTPUT] ) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer[OUTPUT], stream_.convertInfo[OUTPUT] ); bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize * formatBytes( stream_.deviceFormat[OUTPUT] ); } else bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize * formatBytes( stream_.userFormat ); if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::callbackEvent: audio write error, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX) { if ( stream_.doConvertBuffer[INPUT] ) bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize * formatBytes( stream_.deviceFormat[INPUT] ); else bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize * formatBytes( stream_.userFormat ); if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::callbackEvent: audio read error, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); } if ( stream_.doConvertBuffer[INPUT] ) { convertBuffer( stream_.userBuffer[INPUT], stream_.deviceBuffer, stream_.convertInfo[INPUT] ); } } unlock: MUTEX_UNLOCK( &stream_.mutex ); RtApi::tickStreamTime(); if ( doStopStream == 1 ) stopStream(); } void RtApiPulse::startStream( void ) { PulseAudioHandle *pah = static_cast( stream_.apiHandle ); if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::startStream(): the stream is not open!"; error( RtAudioError::INVALID_USE ); return; } if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiPulse::startStream(): the stream is already running!"; error( RtAudioError::WARNING ); return; } MUTEX_LOCK( &stream_.mutex ); stream_.state = STREAM_RUNNING; pah->runnable = true; pthread_cond_signal( &pah->runnable_cv ); MUTEX_UNLOCK( &stream_.mutex ); } void RtApiPulse::stopStream( void ) { PulseAudioHandle *pah = static_cast( stream_.apiHandle ); if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::stopStream(): the stream is not open!"; error( RtAudioError::INVALID_USE ); return; } if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); if ( pah && pah->s_play ) { int pa_error; if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::stopStream: error draining output device, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } } stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); } void RtApiPulse::abortStream( void ) { PulseAudioHandle *pah = static_cast( stream_.apiHandle ); if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::abortStream(): the stream is not open!"; error( RtAudioError::INVALID_USE ); return; } if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } stream_.state = STREAM_STOPPED; MUTEX_LOCK( &stream_.mutex ); if ( pah && pah->s_play ) { int pa_error; if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::abortStream: error flushing output device, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); error( RtAudioError::SYSTEM_ERROR ); return; } } stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); } bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { PulseAudioHandle *pah = 0; unsigned long bufferBytes = 0; pa_sample_spec ss; if ( device != 0 ) return false; if ( mode != INPUT && mode != OUTPUT ) return false; if ( channels != 1 && channels != 2 ) { errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels."; return false; } ss.channels = channels; if ( firstChannel != 0 ) return false; bool sr_found = false; for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) { if ( sampleRate == *sr ) { sr_found = true; stream_.sampleRate = sampleRate; ss.rate = sampleRate; break; } } if ( !sr_found ) { errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate."; return false; } bool sf_found = 0; for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats; sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) { if ( format == sf->rtaudio_format ) { sf_found = true; stream_.userFormat = sf->rtaudio_format; stream_.deviceFormat[mode] = stream_.userFormat; ss.format = sf->pa_format; break; } } if ( !sf_found ) { // Use internal data format conversion. stream_.userFormat = format; stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; ss.format = PA_SAMPLE_FLOAT32LE; } // Set other stream parameters. if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; else stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; stream_.nBuffers = 1; stream_.doByteSwap[mode] = false; stream_.nUserChannels[mode] = channels; stream_.nDeviceChannels[mode] = channels + firstChannel; stream_.channelOffset[mode] = 0; std::string streamName = "RtAudio"; // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers. bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory."; goto error; } stream_.bufferSize = *bufferSize; if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } stream_.device[mode] = device; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); if ( !stream_.apiHandle ) { PulseAudioHandle *pah = new PulseAudioHandle; if ( !pah ) { errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle."; goto error; } stream_.apiHandle = pah; if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) { errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable."; goto error; } } pah = static_cast( stream_.apiHandle ); int error; if ( options && !options->streamName.empty() ) streamName = options->streamName; switch ( mode ) { case INPUT: pa_buffer_attr buffer_attr; buffer_attr.fragsize = bufferBytes; buffer_attr.maxlength = -1; pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error ); if ( !pah->s_rec ) { errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server."; goto error; } break; case OUTPUT: pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error ); if ( !pah->s_play ) { errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server."; goto error; } break; default: goto error; } if ( stream_.mode == UNINITIALIZED ) stream_.mode = mode; else if ( stream_.mode == mode ) goto error; else stream_.mode = DUPLEX; if ( !stream_.callbackInfo.isRunning ) { stream_.callbackInfo.object = this; stream_.callbackInfo.isRunning = true; if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) { errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread."; goto error; } } stream_.state = STREAM_STOPPED; return true; error: if ( pah && stream_.callbackInfo.isRunning ) { pthread_cond_destroy( &pah->runnable_cv ); delete pah; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } return FAILURE; } //******************** End of __LINUX_PULSE__ *********************// #endif #if defined(__LINUX_OSS__) #include #include #include #include #include #include #include static void *ossCallbackHandler(void * ptr); // A structure to hold various information related to the OSS API // implementation. struct OssHandle { int id[2]; // device ids bool xrun[2]; bool triggered; pthread_cond_t runnable; OssHandle() :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } }; RtApiOss :: RtApiOss() { // Nothing to do here. } RtApiOss :: ~RtApiOss() { if ( stream_.state != STREAM_CLOSED ) closeStream(); } unsigned int RtApiOss :: getDeviceCount( void ) { int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); if ( mixerfd == -1 ) { errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'."; error( RtAudioError::WARNING ); return 0; } oss_sysinfo sysinfo; if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required."; error( RtAudioError::WARNING ); return 0; } close( mixerfd ); return sysinfo.numaudios; } RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) { RtAudio::DeviceInfo info; info.probed = false; int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); if ( mixerfd == -1 ) { errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'."; error( RtAudioError::WARNING ); return info; } oss_sysinfo sysinfo; int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ); if ( result == -1 ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required."; error( RtAudioError::WARNING ); return info; } unsigned nDevices = sysinfo.numaudios; if ( nDevices == 0 ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceInfo: no devices found!"; error( RtAudioError::INVALID_USE ); return info; } if ( device >= nDevices ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!"; error( RtAudioError::INVALID_USE ); return info; } oss_audioinfo ainfo; ainfo.dev = device; result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo ); close( mixerfd ); if ( result == -1 ) { errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Probe channels if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels; if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels; if ( ainfo.caps & PCM_CAP_DUPLEX ) { if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX ) info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels; } // Probe data formats ... do for input unsigned long mask = ainfo.iformats; if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE ) info.nativeFormats |= RTAUDIO_SINT16; if ( mask & AFMT_S8 ) info.nativeFormats |= RTAUDIO_SINT8; if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE ) info.nativeFormats |= RTAUDIO_SINT32; if ( mask & AFMT_FLOAT ) info.nativeFormats |= RTAUDIO_FLOAT32; if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE ) info.nativeFormats |= RTAUDIO_SINT24; // Check that we have at least one supported format if ( info.nativeFormats == 0 ) { errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); return info; } // Probe the supported sample rates. info.sampleRates.clear(); if ( ainfo.nrates ) { for ( unsigned int i=0; i info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[k]; break; } } } } else { // Check min and max rate values; for ( unsigned int k=0; k= (int) SAMPLE_RATES[k] ) { info.sampleRates.push_back( SAMPLE_RATES[k] ); if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) ) info.preferredSampleRate = SAMPLE_RATES[k]; } } } if ( info.sampleRates.size() == 0 ) { errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); error( RtAudioError::WARNING ); } else { info.probed = true; info.name = ainfo.name; } return info; } bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ) { int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); if ( mixerfd == -1 ) { errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'."; return FAILURE; } oss_sysinfo sysinfo; int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ); if ( result == -1 ) { close( mixerfd ); errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required."; return FAILURE; } unsigned nDevices = sysinfo.numaudios; if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. close( mixerfd ); errorText_ = "RtApiOss::probeDeviceOpen: no devices found!"; return FAILURE; } if ( device >= nDevices ) { // This should not happen because a check is made before this function is called. close( mixerfd ); errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!"; return FAILURE; } oss_audioinfo ainfo; ainfo.dev = device; result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo ); close( mixerfd ); if ( result == -1 ) { errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info."; errorText_ = errorStream_.str(); return FAILURE; } // Check if device supports input or output if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) || ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) { if ( mode == OUTPUT ) errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output."; else errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input."; errorText_ = errorStream_.str(); return FAILURE; } int flags = 0; OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( mode == OUTPUT ) flags |= O_WRONLY; else { // mode == INPUT if (stream_.mode == OUTPUT && stream_.device[0] == device) { // We just set the same device for playback ... close and reopen for duplex (OSS only). close( handle->id[0] ); handle->id[0] = 0; if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) { errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode."; errorText_ = errorStream_.str(); return FAILURE; } // Check that the number previously set channels is the same. if ( stream_.nUserChannels[0] != channels ) { errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } flags |= O_RDWR; } else flags |= O_RDONLY; } // Set exclusive access if specified. if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL; // Try to open the device. int fd; fd = open( ainfo.devnode, flags, 0 ); if ( fd == -1 ) { if ( errno == EBUSY ) errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy."; else errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } // For duplex operation, specifically set this mode (this doesn't seem to work). /* if ( flags | O_RDWR ) { result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL ); if ( result == -1) { errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } } */ // Check the device channel support. stream_.nUserChannels[mode] = channels; if ( ainfo.max_channels < (int)(channels + firstChannel) ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters."; errorText_ = errorStream_.str(); return FAILURE; } // Set the number of channels. int deviceChannels = channels + firstChannel; result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels ); if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.nDeviceChannels[mode] = deviceChannels; // Get the data format mask int mask; result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask ); if ( result == -1 ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats."; errorText_ = errorStream_.str(); return FAILURE; } // Determine how to set the device format. stream_.userFormat = format; int deviceFormat = -1; stream_.doByteSwap[mode] = false; if ( format == RTAUDIO_SINT8 ) { if ( mask & AFMT_S8 ) { deviceFormat = AFMT_S8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } } else if ( format == RTAUDIO_SINT16 ) { if ( mask & AFMT_S16_NE ) { deviceFormat = AFMT_S16_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } else if ( mask & AFMT_S16_OE ) { deviceFormat = AFMT_S16_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.doByteSwap[mode] = true; } } else if ( format == RTAUDIO_SINT24 ) { if ( mask & AFMT_S24_NE ) { deviceFormat = AFMT_S24_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT24; } else if ( mask & AFMT_S24_OE ) { deviceFormat = AFMT_S24_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT24; stream_.doByteSwap[mode] = true; } } else if ( format == RTAUDIO_SINT32 ) { if ( mask & AFMT_S32_NE ) { deviceFormat = AFMT_S32_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; } else if ( mask & AFMT_S32_OE ) { deviceFormat = AFMT_S32_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; stream_.doByteSwap[mode] = true; } } if ( deviceFormat == -1 ) { // The user requested format is not natively supported by the device. if ( mask & AFMT_S16_NE ) { deviceFormat = AFMT_S16_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } else if ( mask & AFMT_S32_NE ) { deviceFormat = AFMT_S32_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; } else if ( mask & AFMT_S24_NE ) { deviceFormat = AFMT_S24_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT24; } else if ( mask & AFMT_S16_OE ) { deviceFormat = AFMT_S16_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.doByteSwap[mode] = true; } else if ( mask & AFMT_S32_OE ) { deviceFormat = AFMT_S32_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; stream_.doByteSwap[mode] = true; } else if ( mask & AFMT_S24_OE ) { deviceFormat = AFMT_S24_OE; stream_.deviceFormat[mode] = RTAUDIO_SINT24; stream_.doByteSwap[mode] = true; } else if ( mask & AFMT_S8) { deviceFormat = AFMT_S8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } } if ( stream_.deviceFormat[mode] == 0 ) { // This really shouldn't happen ... close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); return FAILURE; } // Set the data format. int temp = deviceFormat; result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat ); if ( result == -1 || deviceFormat != temp ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Attempt to set the buffer size. According to OSS, the minimum // number of buffers is two. The supposed minimum buffer size is 16 // bytes, so that will be our lower bound. The argument to this // call is in the form 0xMMMMSSSS (hex), where the buffer size (in // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM. // We'll check the actual value used near the end of the setup // procedure. int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels; if ( ossBufferBytes < 16 ) ossBufferBytes = 16; int buffers = 0; if ( options ) buffers = options->numberOfBuffers; if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2; if ( buffers < 2 ) buffers = 3; temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) ); result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp ); if ( result == -1 ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.nBuffers = buffers; // Save buffer size (in sample frames). *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels ); stream_.bufferSize = *bufferSize; // Set the sample rate. int srate = sampleRate; result = ioctl( fd, SNDCTL_DSP_SPEED, &srate ); if ( result == -1 ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); return FAILURE; } // Verify the sample rate setup worked. if ( abs( srate - sampleRate ) > 100 ) { close( fd ); errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ")."; errorText_ = errorStream_.str(); return FAILURE; } stream_.sampleRate = sampleRate; if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) { // We're doing duplex setup here. stream_.deviceFormat[0] = stream_.deviceFormat[1]; stream_.nDeviceChannels[0] = deviceChannels; } // Set interleaving parameters. stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; if ( stream_.userFormat != stream_.deviceFormat[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] ) stream_.doConvertBuffer[mode] = true; if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] && stream_.nUserChannels[mode] > 1 ) stream_.doConvertBuffer[mode] = true; // Allocate the stream handles if necessary and then save. if ( stream_.apiHandle == 0 ) { try { handle = new OssHandle; } catch ( std::bad_alloc& ) { errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory."; goto error; } if ( pthread_cond_init( &handle->runnable, NULL ) ) { errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable."; goto error; } stream_.apiHandle = (void *) handle; } else { handle = (OssHandle *) stream_.apiHandle; } handle->id[mode] = fd; // Allocate necessary internal buffers. unsigned long bufferBytes; bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat ); stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 ); if ( stream_.userBuffer[mode] == NULL ) { errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory."; goto error; } if ( stream_.doConvertBuffer[mode] ) { bool makeBuffer = true; bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); if ( mode == INPUT ) { if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false; } } if ( makeBuffer ) { bufferBytes *= *bufferSize; if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); if ( stream_.deviceBuffer == NULL ) { errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory."; goto error; } } } stream_.device[mode] = device; stream_.state = STREAM_STOPPED; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); // Setup thread if necessary. if ( stream_.mode == OUTPUT && mode == INPUT ) { // We had already set up an output stream. stream_.mode = DUPLEX; if ( stream_.device[0] == device ) handle->id[0] = fd; } else { stream_.mode = mode; // Setup callback thread. stream_.callbackInfo.object = (void *) this; // Set the thread attributes for joinable and realtime scheduling // priority. The higher priority will only take affect if the // program is run as root or suid. pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { struct sched_param param; int priority = options->priority; int min = sched_get_priority_min( SCHED_RR ); int max = sched_get_priority_max( SCHED_RR ); if ( priority < min ) priority = min; else if ( priority > max ) priority = max; param.sched_priority = priority; pthread_attr_setschedparam( &attr, ¶m ); pthread_attr_setschedpolicy( &attr, SCHED_RR ); } else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif stream_.callbackInfo.isRunning = true; result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo ); pthread_attr_destroy( &attr ); if ( result ) { stream_.callbackInfo.isRunning = false; errorText_ = "RtApiOss::error creating callback thread!"; goto error; } } return SUCCESS; error: if ( handle ) { pthread_cond_destroy( &handle->runnable ); if ( handle->id[0] ) close( handle->id[0] ); if ( handle->id[1] ) close( handle->id[1] ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } return FAILURE; } void RtApiOss :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiOss::closeStream(): no open stream to close!"; error( RtAudioError::WARNING ); return; } OssHandle *handle = (OssHandle *) stream_.apiHandle; stream_.callbackInfo.isRunning = false; MUTEX_LOCK( &stream_.mutex ); if ( stream_.state == STREAM_STOPPED ) pthread_cond_signal( &handle->runnable ); MUTEX_UNLOCK( &stream_.mutex ); pthread_join( stream_.callbackInfo.thread, NULL ); if ( stream_.state == STREAM_RUNNING ) { if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); else ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); stream_.state = STREAM_STOPPED; } if ( handle ) { pthread_cond_destroy( &handle->runnable ); if ( handle->id[0] ) close( handle->id[0] ); if ( handle->id[1] ) close( handle->id[1] ); delete handle; stream_.apiHandle = 0; } for ( int i=0; i<2; i++ ) { if ( stream_.userBuffer[i] ) { free( stream_.userBuffer[i] ); stream_.userBuffer[i] = 0; } } if ( stream_.deviceBuffer ) { free( stream_.deviceBuffer ); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; stream_.state = STREAM_CLOSED; } void RtApiOss :: startStream() { verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiOss::startStream(): the stream is already running!"; error( RtAudioError::WARNING ); return; } MUTEX_LOCK( &stream_.mutex ); stream_.state = STREAM_RUNNING; // No need to do anything else here ... OSS automatically starts // when fed samples. MUTEX_UNLOCK( &stream_.mutex ); OssHandle *handle = (OssHandle *) stream_.apiHandle; pthread_cond_signal( &handle->runnable ); } void RtApiOss :: stopStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiOss::stopStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } MUTEX_LOCK( &stream_.mutex ); // The state might change while waiting on a mutex. if ( stream_.state == STREAM_STOPPED ) { MUTEX_UNLOCK( &stream_.mutex ); return; } int result = 0; OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { // Flush the output with zeros a few times. char *buffer; int samples; RtAudioFormat format; if ( stream_.doConvertBuffer[0] ) { buffer = stream_.deviceBuffer; samples = stream_.bufferSize * stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { buffer = stream_.userBuffer[0]; samples = stream_.bufferSize * stream_.nUserChannels[0]; format = stream_.userFormat; } memset( buffer, 0, samples * formatBytes(format) ); for ( unsigned int i=0; iid[0], buffer, samples * formatBytes(format) ); if ( result == -1 ) { errorText_ = "RtApiOss::stopStream: audio write error."; error( RtAudioError::WARNING ); } } result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); if ( result == -1 ) { errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } handle->triggered = false; } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) { result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); if ( result == -1 ) { errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } } unlock: stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); if ( result != -1 ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiOss :: abortStream() { verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiOss::abortStream(): the stream is already stopped!"; error( RtAudioError::WARNING ); return; } MUTEX_LOCK( &stream_.mutex ); // The state might change while waiting on a mutex. if ( stream_.state == STREAM_STOPPED ) { MUTEX_UNLOCK( &stream_.mutex ); return; } int result = 0; OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 ); if ( result == -1 ) { errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } handle->triggered = false; } if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) { result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 ); if ( result == -1 ) { errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ")."; errorText_ = errorStream_.str(); goto unlock; } } unlock: stream_.state = STREAM_STOPPED; MUTEX_UNLOCK( &stream_.mutex ); if ( result != -1 ) return; error( RtAudioError::SYSTEM_ERROR ); } void RtApiOss :: callbackEvent() { OssHandle *handle = (OssHandle *) stream_.apiHandle; if ( stream_.state == STREAM_STOPPED ) { MUTEX_LOCK( &stream_.mutex ); pthread_cond_wait( &handle->runnable, &stream_.mutex ); if ( stream_.state != STREAM_RUNNING ) { MUTEX_UNLOCK( &stream_.mutex ); return; } MUTEX_UNLOCK( &stream_.mutex ); } if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!"; error( RtAudioError::WARNING ); return; } // Invoke user callback to get fresh output data. int doStopStream = 0; RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; if ( stream_.mode != INPUT && handle->xrun[0] == true ) { status |= RTAUDIO_OUTPUT_UNDERFLOW; handle->xrun[0] = false; } if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) { status |= RTAUDIO_INPUT_OVERFLOW; handle->xrun[1] = false; } doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); if ( doStopStream == 2 ) { this->abortStream(); return; } MUTEX_LOCK( &stream_.mutex ); // The state might change while waiting on a mutex. if ( stream_.state == STREAM_STOPPED ) goto unlock; int result; char *buffer; int samples; RtAudioFormat format; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { // Setup parameters and do buffer conversion if necessary. if ( stream_.doConvertBuffer[0] ) { buffer = stream_.deviceBuffer; convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] ); samples = stream_.bufferSize * stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { buffer = stream_.userBuffer[0]; samples = stream_.bufferSize * stream_.nUserChannels[0]; format = stream_.userFormat; } // Do byte swapping if necessary. if ( stream_.doByteSwap[0] ) byteSwapBuffer( buffer, samples, format ); if ( stream_.mode == DUPLEX && handle->triggered == false ) { int trig = 0; ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig ); result = write( handle->id[0], buffer, samples * formatBytes(format) ); trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT; ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig ); handle->triggered = true; } else // Write samples to device. result = write( handle->id[0], buffer, samples * formatBytes(format) ); if ( result == -1 ) { // We'll assume this is an underrun, though there isn't a // specific means for determining that. handle->xrun[0] = true; errorText_ = "RtApiOss::callbackEvent: audio write error."; error( RtAudioError::WARNING ); // Continue on to input section. } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { // Setup parameters. if ( stream_.doConvertBuffer[1] ) { buffer = stream_.deviceBuffer; samples = stream_.bufferSize * stream_.nDeviceChannels[1]; format = stream_.deviceFormat[1]; } else { buffer = stream_.userBuffer[1]; samples = stream_.bufferSize * stream_.nUserChannels[1]; format = stream_.userFormat; } // Read samples from device. result = read( handle->id[1], buffer, samples * formatBytes(format) ); if ( result == -1 ) { // We'll assume this is an overrun, though there isn't a // specific means for determining that. handle->xrun[1] = true; errorText_ = "RtApiOss::callbackEvent: audio read error."; error( RtAudioError::WARNING ); goto unlock; } // Do byte swapping if necessary. if ( stream_.doByteSwap[1] ) byteSwapBuffer( buffer, samples, format ); // Do buffer conversion if necessary. if ( stream_.doConvertBuffer[1] ) convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] ); } unlock: MUTEX_UNLOCK( &stream_.mutex ); RtApi::tickStreamTime(); if ( doStopStream == 1 ) this->stopStream(); } static void *ossCallbackHandler( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiOss *object = (RtApiOss *) info->object; bool *isRunning = &info->isRunning; while ( *isRunning == true ) { pthread_testcancel(); object->callbackEvent(); } pthread_exit( NULL ); } //******************** End of __LINUX_OSS__ *********************// #endif // *************************************************** // // // Protected common (OS-independent) RtAudio methods. // // *************************************************** // // This method can be modified to control the behavior of error // message printing. void RtApi :: error( RtAudioError::Type type ) { errorStream_.str(""); // clear the ostringstream RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback; if ( errorCallback ) { // abortStream() can generate new error messages. Ignore them. Just keep original one. if ( firstErrorOccurred_ ) return; firstErrorOccurred_ = true; const std::string errorMessage = errorText_; if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) { stream_.callbackInfo.isRunning = false; // exit from the thread abortStream(); } errorCallback( type, errorMessage ); firstErrorOccurred_ = false; return; } if ( type == RtAudioError::WARNING && showWarnings_ == true ) std::cerr << '\n' << errorText_ << "\n\n"; else if ( type != RtAudioError::WARNING ) throw( RtAudioError( errorText_, type ) ); } void RtApi :: verifyStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApi:: a stream is not open!"; error( RtAudioError::INVALID_USE ); } } void RtApi :: clearStreamInfo() { stream_.mode = UNINITIALIZED; stream_.state = STREAM_CLOSED; stream_.sampleRate = 0; stream_.bufferSize = 0; stream_.nBuffers = 0; stream_.userFormat = 0; stream_.userInterleaved = true; stream_.streamTime = 0.0; stream_.apiHandle = 0; stream_.deviceBuffer = 0; stream_.callbackInfo.callback = 0; stream_.callbackInfo.userData = 0; stream_.callbackInfo.isRunning = false; stream_.callbackInfo.errorCallback = 0; for ( int i=0; i<2; i++ ) { stream_.device[i] = 11111; stream_.doConvertBuffer[i] = false; stream_.deviceInterleaved[i] = true; stream_.doByteSwap[i] = false; stream_.nUserChannels[i] = 0; stream_.nDeviceChannels[i] = 0; stream_.channelOffset[i] = 0; stream_.deviceFormat[i] = 0; stream_.latency[i] = 0; stream_.userBuffer[i] = 0; stream_.convertInfo[i].channels = 0; stream_.convertInfo[i].inJump = 0; stream_.convertInfo[i].outJump = 0; stream_.convertInfo[i].inFormat = 0; stream_.convertInfo[i].outFormat = 0; stream_.convertInfo[i].inOffset.clear(); stream_.convertInfo[i].outOffset.clear(); } } unsigned int RtApi :: formatBytes( RtAudioFormat format ) { if ( format == RTAUDIO_SINT16 ) return 2; else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 ) return 4; else if ( format == RTAUDIO_FLOAT64 ) return 8; else if ( format == RTAUDIO_SINT24 ) return 3; else if ( format == RTAUDIO_SINT8 ) return 1; errorText_ = "RtApi::formatBytes: undefined format."; error( RtAudioError::WARNING ); return 0; } void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel ) { if ( mode == INPUT ) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) { if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) || ( mode == INPUT && stream_.userInterleaved ) ) { for ( int k=0; k 0 ) { if ( stream_.deviceInterleaved[mode] ) { if ( mode == OUTPUT ) { for ( int k=0; k> 8); //out[info.outOffset[j]] >>= 8; } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_FLOAT32) { Float32 *in = (Float32 *)inBuffer; for (unsigned int i=0; i> 8); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT32) { Int32 *in = (Int32 *)inBuffer; for (unsigned int i=0; i> 16) & 0x0000ffff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_FLOAT32) { Float32 *in = (Float32 *)inBuffer; for (unsigned int i=0; i> 8) & 0x00ff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT24) { Int24 *in = (Int24 *)inBuffer; for (unsigned int i=0; i> 16); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT32) { Int32 *in = (Int32 *)inBuffer; for (unsigned int i=0; i> 24) & 0x000000ff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_FLOAT32) { Float32 *in = (Float32 *)inBuffer; for (unsigned int i=0; i>8) | (x<<8); } //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); } //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); } void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ) { char val; char *ptr; ptr = buffer; if ( format == RTAUDIO_SINT16 ) { for ( unsigned int i=0; i #include #include #include /*! \typedef typedef unsigned long RtAudioFormat; \brief RtAudio data format type. Support for signed integers and floats. Audio data fed to/from an RtAudio stream is assumed to ALWAYS be in host byte order. The internal routines will automatically take care of any necessary byte-swapping between the host format and the soundcard. Thus, endian-ness is not a concern in the following format definitions. - \e RTAUDIO_SINT8: 8-bit signed integer. - \e RTAUDIO_SINT16: 16-bit signed integer. - \e RTAUDIO_SINT24: 24-bit signed integer. - \e RTAUDIO_SINT32: 32-bit signed integer. - \e RTAUDIO_FLOAT32: Normalized between plus/minus 1.0. - \e RTAUDIO_FLOAT64: Normalized between plus/minus 1.0. */ typedef unsigned long RtAudioFormat; static const RtAudioFormat RTAUDIO_SINT8 = 0x1; // 8-bit signed integer. static const RtAudioFormat RTAUDIO_SINT16 = 0x2; // 16-bit signed integer. static const RtAudioFormat RTAUDIO_SINT24 = 0x4; // 24-bit signed integer. static const RtAudioFormat RTAUDIO_SINT32 = 0x8; // 32-bit signed integer. static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0. static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0. /*! \typedef typedef unsigned long RtAudioStreamFlags; \brief RtAudio stream option flags. The following flags can be OR'ed together to allow a client to make changes to the default stream behavior: - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). By default, RtAudio streams pass and receive audio data from the client in an interleaved format. By passing the RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio data will instead be presented in non-interleaved buffers. In this case, each buffer argument in the RtAudioCallback function will point to a single array of data, with \c nFrames samples for each channel concatenated back-to-back. For example, the first sample of data for the second channel would be located at index \c nFrames (assuming the \c buffer pointer was recast to the correct data type for the stream). Certain audio APIs offer a number of parameters that influence the I/O latency of a stream. By default, RtAudio will attempt to set these parameters internally for robust (glitch-free) performance (though some APIs, like Windows Direct Sound, make this difficult). By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() function, internal stream settings will be influenced in an attempt to minimize stream latency, though possibly at the expense of stream performance. If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to open the input and/or output stream device(s) for exclusive use. Note that this is not possible with all supported audio APIs. If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt to select realtime scheduling (round-robin) for the callback thread. If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to open the "default" PCM device when using the ALSA API. Note that this will override any specified input or output device id. */ typedef unsigned int RtAudioStreamFlags; static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1; // Use non-interleaved buffers (default = interleaved). static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2; // Attempt to set stream parameters for lowest possible latency. static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4; // Attempt grab device and prevent use by others. static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread. static const RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT = 0x10; // Use the "default" PCM device (ALSA only). /*! \typedef typedef unsigned long RtAudioStreamStatus; \brief RtAudio stream status (over- or underflow) flags. Notification of a stream over- or underflow is indicated by a non-zero stream \c status argument in the RtAudioCallback function. The stream status can be one of the following two options, depending on whether the stream is open for output and/or input: - \e RTAUDIO_INPUT_OVERFLOW: Input data was discarded because of an overflow condition at the driver. - \e RTAUDIO_OUTPUT_UNDERFLOW: The output buffer ran low, likely producing a break in the output sound. */ typedef unsigned int RtAudioStreamStatus; static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1; // Input data was discarded because of an overflow condition at the driver. static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2; // The output buffer ran low, likely causing a gap in the output sound. //! RtAudio callback function prototype. /*! All RtAudio clients must create a function of type RtAudioCallback to read and/or write data from/to the audio stream. When the underlying audio system is ready for new input or output data, this function will be invoked. \param outputBuffer For output (or duplex) streams, the client should write \c nFrames of audio sample frames into this buffer. This argument should be recast to the datatype specified when the stream was opened. For input-only streams, this argument will be NULL. \param inputBuffer For input (or duplex) streams, this buffer will hold \c nFrames of input audio sample frames. This argument should be recast to the datatype specified when the stream was opened. For output-only streams, this argument will be NULL. \param nFrames The number of sample frames of input or output data in the buffers. The actual buffer size in bytes is dependent on the data type and number of channels in use. \param streamTime The number of seconds that have elapsed since the stream was started. \param status If non-zero, this argument indicates a data overflow or underflow condition for the stream. The particular condition can be determined by comparison with the RtAudioStreamStatus flags. \param userData A pointer to optional data provided by the client when opening the stream (default = NULL). To continue normal stream operation, the RtAudioCallback function should return a value of zero. To stop the stream and drain the output buffer, the function should return a value of one. To abort the stream immediately, the client should return a value of two. */ typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData ); /************************************************************************/ /*! \class RtAudioError \brief Exception handling class for RtAudio. The RtAudioError class is quite simple but it does allow errors to be "caught" by RtAudioError::Type. See the RtAudio documentation to know which methods can throw an RtAudioError. */ /************************************************************************/ class RtAudioError : public std::exception { public: //! Defined RtAudioError types. enum Type { WARNING, /*!< A non-critical error. */ DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ UNSPECIFIED, /*!< The default, unspecified error type. */ NO_DEVICES_FOUND, /*!< No devices found on system. */ INVALID_DEVICE, /*!< An invalid device ID was specified. */ MEMORY_ERROR, /*!< An error occurred during memory allocation. */ INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ INVALID_USE, /*!< The function was called incorrectly. */ DRIVER_ERROR, /*!< A system driver error occurred. */ SYSTEM_ERROR, /*!< A system error occurred. */ THREAD_ERROR /*!< A thread error occurred. */ }; //! The constructor. RtAudioError( const std::string& message, Type type = RtAudioError::UNSPECIFIED ) throw() : message_(message), type_(type) {} //! The destructor. virtual ~RtAudioError( void ) throw() {} //! Prints thrown error message to stderr. virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } //! Returns the thrown error message type. virtual const Type& getType(void) const throw() { return type_; } //! Returns the thrown error message string. virtual const std::string& getMessage(void) const throw() { return message_; } //! Returns the thrown error message as a c-style string. virtual const char* what( void ) const throw() { return message_.c_str(); } protected: std::string message_; Type type_; }; //! RtAudio error callback function prototype. /*! \param type Type of error. \param errorText Error description. */ typedef void (*RtAudioErrorCallback)( RtAudioError::Type type, const std::string &errorText ); // **************************************************************** // // // RtAudio class declaration. // // RtAudio is a "controller" used to select an available audio i/o // interface. It presents a common API for the user to call but all // functionality is implemented by the class RtApi and its // subclasses. RtAudio creates an instance of an RtApi subclass // based on the user's API choice. If no choice is made, RtAudio // attempts to make a "logical" API selection. // // **************************************************************** // class RtApi; class RtAudio { public: //! Audio API specifier arguments. enum Api { UNSPECIFIED, /*!< Search for a working compiled API. */ LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ LINUX_PULSE, /*!< The Linux PulseAudio API. */ LINUX_OSS, /*!< The Linux Open Sound System API. */ UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */ MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */ WINDOWS_WASAPI, /*!< The Microsoft WASAPI API. */ WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */ WINDOWS_DS, /*!< The Microsoft Direct Sound API. */ RTAUDIO_DUMMY /*!< A compilable but non-functional API. */ }; //! The public device information structure for returning queried values. struct DeviceInfo { bool probed; /*!< true if the device capabilities were successfully probed. */ std::string name; /*!< Character string device identifier. */ unsigned int outputChannels; /*!< Maximum output channels supported by device. */ unsigned int inputChannels; /*!< Maximum input channels supported by device. */ unsigned int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */ bool isDefaultOutput; /*!< true if this is the default output device. */ bool isDefaultInput; /*!< true if this is the default input device. */ std::vector sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ unsigned int preferredSampleRate; /*!< Preferred sample rate, eg. for WASAPI the system sample rate. */ RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ // Default constructor. DeviceInfo() :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), isDefaultOutput(false), isDefaultInput(false), preferredSampleRate(0), nativeFormats(0) {} }; //! The structure for specifying input or output stream parameters. struct StreamParameters { unsigned int deviceId; /*!< Device index (0 to getDeviceCount() - 1). */ unsigned int nChannels; /*!< Number of channels. */ unsigned int firstChannel; /*!< First channel index on device (default = 0). */ // Default constructor. StreamParameters() : deviceId(0), nChannels(0), firstChannel(0) {} }; //! The structure for specifying stream options. /*! The following flags can be OR'ed together to allow a client to make changes to the default stream behavior: - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved). - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency. - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use. - \e RTAUDIO_SCHEDULE_REALTIME: Attempt to select realtime scheduling for callback thread. - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only). By default, RtAudio streams pass and receive audio data from the client in an interleaved format. By passing the RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio data will instead be presented in non-interleaved buffers. In this case, each buffer argument in the RtAudioCallback function will point to a single array of data, with \c nFrames samples for each channel concatenated back-to-back. For example, the first sample of data for the second channel would be located at index \c nFrames (assuming the \c buffer pointer was recast to the correct data type for the stream). Certain audio APIs offer a number of parameters that influence the I/O latency of a stream. By default, RtAudio will attempt to set these parameters internally for robust (glitch-free) performance (though some APIs, like Windows Direct Sound, make this difficult). By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream() function, internal stream settings will be influenced in an attempt to minimize stream latency, though possibly at the expense of stream performance. If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to open the input and/or output stream device(s) for exclusive use. Note that this is not possible with all supported audio APIs. If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt to select realtime scheduling (round-robin) for the callback thread. The \c priority parameter will only be used if the RTAUDIO_SCHEDULE_REALTIME flag is set. It defines the thread's realtime priority. If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to open the "default" PCM device when using the ALSA API. Note that this will override any specified input or output device id. The \c numberOfBuffers parameter can be used to control stream latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs only. A value of two is usually the smallest allowed. Larger numbers can potentially result in more robust stream performance, though likely at the cost of stream latency. The value set by the user is replaced during execution of the RtAudio::openStream() function by the value actually used by the system. The \c streamName parameter can be used to set the client name when using the Jack API. By default, the client name is set to RtApiJack. However, if you wish to create multiple instances of RtAudio with Jack, each instance must have a unique client name. */ struct StreamOptions { RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */ unsigned int numberOfBuffers; /*!< Number of stream buffers. */ std::string streamName; /*!< A stream name (currently used only in Jack). */ int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */ // Default constructor. StreamOptions() : flags(0), numberOfBuffers(0), priority(0) {} }; //! A static function to determine the current RtAudio version. static std::string getVersion( void ) throw(); //! A static function to determine the available compiled audio APIs. /*! The values returned in the std::vector can be compared against the enumerated list values. Note that there can be more than one API compiled for certain operating systems. */ static void getCompiledApi( std::vector &apis ) throw(); //! The class constructor. /*! The constructor performs minor initialization tasks. An exception can be thrown if no API support is compiled. If no API argument is specified and multiple API support has been compiled, the default order of use is JACK, ALSA, OSS (Linux systems) and ASIO, DS (Windows systems). */ RtAudio( RtAudio::Api api=UNSPECIFIED ); //! The destructor. /*! If a stream is running or open, it will be stopped and closed automatically. */ ~RtAudio() throw(); //! Returns the audio API specifier for the current instance of RtAudio. RtAudio::Api getCurrentApi( void ) throw(); //! A public function that queries for the number of audio devices available. /*! This function performs a system query of available devices each time it is called, thus supporting devices connected \e after instantiation. If a system error occurs during processing, a warning will be issued. */ unsigned int getDeviceCount( void ) throw(); //! Return an RtAudio::DeviceInfo structure for a specified device number. /*! Any device integer between 0 and getDeviceCount() - 1 is valid. If an invalid argument is provided, an RtAudioError (type = INVALID_USE) will be thrown. If a device is busy or otherwise unavailable, the structure member "probed" will have a value of "false" and all other members are undefined. If the specified device is the current default input or output device, the corresponding "isDefault" member will have a value of "true". */ RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); //! A function that returns the index of the default output device. /*! If the underlying audio API does not provide a "default device", or if no devices are available, the return value will be 0. Note that this is a valid device identifier and it is the client's responsibility to verify that a device is available before attempting to open a stream. */ unsigned int getDefaultOutputDevice( void ) throw(); //! A function that returns the index of the default input device. /*! If the underlying audio API does not provide a "default device", or if no devices are available, the return value will be 0. Note that this is a valid device identifier and it is the client's responsibility to verify that a device is available before attempting to open a stream. */ unsigned int getDefaultInputDevice( void ) throw(); //! A public function for opening a stream with the specified parameters. /*! An RtAudioError (type = SYSTEM_ERROR) is thrown if a stream cannot be opened with the specified parameters or an error occurs during processing. An RtAudioError (type = INVALID_USE) is thrown if any invalid device ID or channel number parameters are specified. \param outputParameters Specifies output stream parameters to use when opening a stream, including a device ID, number of channels, and starting channel number. For input-only streams, this argument should be NULL. The device ID is an index value between 0 and getDeviceCount() - 1. \param inputParameters Specifies input stream parameters to use when opening a stream, including a device ID, number of channels, and starting channel number. For output-only streams, this argument should be NULL. The device ID is an index value between 0 and getDeviceCount() - 1. \param format An RtAudioFormat specifying the desired sample data format. \param sampleRate The desired sample rate (sample frames per second). \param *bufferFrames A pointer to a value indicating the desired internal buffer size in sample frames. The actual value used by the device is returned via the same pointer. A value of zero can be specified, in which case the lowest allowable value is determined. \param callback A client-defined function that will be invoked when input data is available and/or output data is needed. \param userData An optional pointer to data that can be accessed from within the callback function. \param options An optional pointer to a structure containing various global stream options, including a list of OR'ed RtAudioStreamFlags and a suggested number of stream buffers that can be used to control stream latency. More buffers typically result in more robust performance, though at a cost of greater latency. If a value of zero is specified, a system-specific median value is chosen. If the RTAUDIO_MINIMIZE_LATENCY flag bit is set, the lowest allowable value is used. The actual value used is returned via the structure argument. The parameter is API dependent. \param errorCallback A client-defined function that will be invoked when an error has occurred. */ void openStream( RtAudio::StreamParameters *outputParameters, RtAudio::StreamParameters *inputParameters, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData = NULL, RtAudio::StreamOptions *options = NULL, RtAudioErrorCallback errorCallback = NULL ); //! A function that closes a stream and frees any associated stream memory. /*! If a stream is not open, this function issues a warning and returns (no exception is thrown). */ void closeStream( void ) throw(); //! A function that starts a stream. /*! An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs during processing. An RtAudioError (type = INVALID_USE) is thrown if a stream is not open. A warning is issued if the stream is already running. */ void startStream( void ); //! Stop a stream, allowing any samples remaining in the output queue to be played. /*! An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs during processing. An RtAudioError (type = INVALID_USE) is thrown if a stream is not open. A warning is issued if the stream is already stopped. */ void stopStream( void ); //! Stop a stream, discarding any samples remaining in the input/output queue. /*! An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs during processing. An RtAudioError (type = INVALID_USE) is thrown if a stream is not open. A warning is issued if the stream is already stopped. */ void abortStream( void ); //! Returns true if a stream is open and false if not. bool isStreamOpen( void ) const throw(); //! Returns true if the stream is running and false if it is stopped or not open. bool isStreamRunning( void ) const throw(); //! Returns the number of elapsed seconds since the stream was started. /*! If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. */ double getStreamTime( void ); //! Set the stream time to a time in seconds greater than or equal to 0.0. /*! If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. */ void setStreamTime( double time ); //! Returns the internal stream latency in sample frames. /*! The stream latency refers to delay in audio input and/or output caused by internal buffering by the audio system and/or hardware. For duplex streams, the returned value will represent the sum of the input and output latencies. If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. If the API does not report latency, the return value will be zero. */ long getStreamLatency( void ); //! Returns actual sample rate in use by the stream. /*! On some systems, the sample rate used may be slightly different than that specified in the stream parameters. If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown. */ unsigned int getStreamSampleRate( void ); //! Specify whether warning messages should be printed to stderr. void showWarnings( bool value = true ) throw(); protected: void openRtApi( RtAudio::Api api ); RtApi *rtapi_; }; // Operating system dependent thread functionality. #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__) #ifndef NOMINMAX #define NOMINMAX #endif #include #include typedef uintptr_t ThreadHandle; typedef CRITICAL_SECTION StreamMutex; #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) // Using pthread library for various flavors of unix. #include typedef pthread_t ThreadHandle; typedef pthread_mutex_t StreamMutex; #else // Setup for "dummy" behavior #define __RTAUDIO_DUMMY__ typedef int ThreadHandle; typedef int StreamMutex; #endif // This global structure type is used to pass callback information // between the private RtAudio stream structure and global callback // handling functions. struct CallbackInfo { void *object; // Used as a "this" pointer. ThreadHandle thread; void *callback; void *userData; void *errorCallback; void *apiInfo; // void pointer for API specific callback information bool isRunning; bool doRealtime; int priority; // Default constructor. CallbackInfo() :object(0), callback(0), userData(0), errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false) {} }; // **************************************************************** // // // RtApi class declaration. // // Subclasses of RtApi contain all API- and OS-specific code necessary // to fully implement the RtAudio API. // // Note that RtApi is an abstract base class and cannot be // explicitly instantiated. The class RtAudio will create an // instance of an RtApi subclass (RtApiOss, RtApiAlsa, // RtApiJack, RtApiCore, RtApiDs, or RtApiAsio). // // **************************************************************** // #pragma pack(push, 1) class S24 { protected: unsigned char c3[3]; public: S24() {} S24& operator = ( const int& i ) { c3[0] = (i & 0x000000ff); c3[1] = (i & 0x0000ff00) >> 8; c3[2] = (i & 0x00ff0000) >> 16; return *this; } S24( const S24& v ) { *this = v; } S24( const double& d ) { *this = (int) d; } S24( const float& f ) { *this = (int) f; } S24( const signed short& s ) { *this = (int) s; } S24( const char& c ) { *this = (int) c; } int asInt() { int i = c3[0] | (c3[1] << 8) | (c3[2] << 16); if (i & 0x800000) i |= ~0xffffff; return i; } }; #pragma pack(pop) #if defined( HAVE_GETTIMEOFDAY ) #include #endif #include class RtApi { public: RtApi(); virtual ~RtApi(); virtual RtAudio::Api getCurrentApi( void ) = 0; virtual unsigned int getDeviceCount( void ) = 0; virtual RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) = 0; virtual unsigned int getDefaultInputDevice( void ); virtual unsigned int getDefaultOutputDevice( void ); void openStream( RtAudio::StreamParameters *outputParameters, RtAudio::StreamParameters *inputParameters, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData, RtAudio::StreamOptions *options, RtAudioErrorCallback errorCallback ); virtual void closeStream( void ); virtual void startStream( void ) = 0; virtual void stopStream( void ) = 0; virtual void abortStream( void ) = 0; long getStreamLatency( void ); unsigned int getStreamSampleRate( void ); virtual double getStreamTime( void ); virtual void setStreamTime( double time ); bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; } bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; } void showWarnings( bool value ) { showWarnings_ = value; } protected: static const unsigned int MAX_SAMPLE_RATES; static const unsigned int SAMPLE_RATES[]; enum { FAILURE, SUCCESS }; enum StreamState { STREAM_STOPPED, STREAM_STOPPING, STREAM_RUNNING, STREAM_CLOSED = -50 }; enum StreamMode { OUTPUT, INPUT, DUPLEX, UNINITIALIZED = -75 }; // A protected structure used for buffer conversion. struct ConvertInfo { int channels; int inJump, outJump; RtAudioFormat inFormat, outFormat; std::vector inOffset; std::vector outOffset; }; // A protected structure for audio streams. struct RtApiStream { unsigned int device[2]; // Playback and record, respectively. void *apiHandle; // void pointer for API specific stream handle information StreamMode mode; // OUTPUT, INPUT, or DUPLEX. StreamState state; // STOPPED, RUNNING, or CLOSED char *userBuffer[2]; // Playback and record, respectively. char *deviceBuffer; bool doConvertBuffer[2]; // Playback and record, respectively. bool userInterleaved; bool deviceInterleaved[2]; // Playback and record, respectively. bool doByteSwap[2]; // Playback and record, respectively. unsigned int sampleRate; unsigned int bufferSize; unsigned int nBuffers; unsigned int nUserChannels[2]; // Playback and record, respectively. unsigned int nDeviceChannels[2]; // Playback and record channels, respectively. unsigned int channelOffset[2]; // Playback and record, respectively. unsigned long latency[2]; // Playback and record, respectively. RtAudioFormat userFormat; RtAudioFormat deviceFormat[2]; // Playback and record, respectively. StreamMutex mutex; CallbackInfo callbackInfo; ConvertInfo convertInfo[2]; double streamTime; // Number of elapsed seconds since the stream started. #if defined(HAVE_GETTIMEOFDAY) struct timeval lastTickTimestamp; #endif RtApiStream() :apiHandle(0), deviceBuffer(0) { device[0] = 11111; device[1] = 11111; } }; typedef S24 Int24; typedef signed short Int16; typedef signed int Int32; typedef float Float32; typedef double Float64; std::ostringstream errorStream_; std::string errorText_; bool showWarnings_; RtApiStream stream_; bool firstErrorOccurred_; /*! Protected, api-specific method that attempts to open a device with the given parameters. This function MUST be implemented by all subclasses. If an error is encountered during the probe, a "warning" message is reported and FAILURE is returned. A successful probe is indicated by a return value of SUCCESS. */ virtual bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); //! A protected function used to increment the stream time. void tickStreamTime( void ); //! Protected common method to clear an RtApiStream structure. void clearStreamInfo(); /*! Protected common method that throws an RtAudioError (type = INVALID_USE) if a stream is not open. */ void verifyStream( void ); //! Protected common error method to allow global control over error handling. void error( RtAudioError::Type type ); /*! Protected method used to perform format, channel number, and/or interleaving conversions between the user and device buffers. */ void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info ); //! Protected common method used to perform byte-swapping on buffers. void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ); //! Protected common method that returns the number of bytes for a given format. unsigned int formatBytes( RtAudioFormat format ); //! Protected common method that sets up the parameters for buffer conversion. void setConvertInfo( StreamMode mode, unsigned int firstChannel ); }; // **************************************************************** // // // Inline RtAudio definitions. // // **************************************************************** // inline RtAudio::Api RtAudio :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } inline unsigned int RtAudio :: getDeviceCount( void ) throw() { return rtapi_->getDeviceCount(); } inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); } inline unsigned int RtAudio :: getDefaultInputDevice( void ) throw() { return rtapi_->getDefaultInputDevice(); } inline unsigned int RtAudio :: getDefaultOutputDevice( void ) throw() { return rtapi_->getDefaultOutputDevice(); } inline void RtAudio :: closeStream( void ) throw() { return rtapi_->closeStream(); } inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); } inline void RtAudio :: stopStream( void ) { return rtapi_->stopStream(); } inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); } inline bool RtAudio :: isStreamOpen( void ) const throw() { return rtapi_->isStreamOpen(); } inline bool RtAudio :: isStreamRunning( void ) const throw() { return rtapi_->isStreamRunning(); } inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); } inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); } inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); } inline void RtAudio :: setStreamTime( double time ) { return rtapi_->setStreamTime( time ); } inline void RtAudio :: showWarnings( bool value ) throw() { rtapi_->showWarnings( value ); } // RtApi Subclass prototypes. #if defined(__MACOSX_CORE__) #include class RtApiCore: public RtApi { public: RtApiCore(); ~RtApiCore(); RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; } unsigned int getDeviceCount( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); unsigned int getDefaultOutputDevice( void ); unsigned int getDefaultInputDevice( void ); void closeStream( void ); void startStream( void ); void stopStream( void ); void abortStream( void ); long getStreamLatency( void ); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! bool callbackEvent( AudioDeviceID deviceId, const AudioBufferList *inBufferList, const AudioBufferList *outBufferList ); private: bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); static const char* getErrorCode( OSStatus code ); }; #endif #if defined(__UNIX_JACK__) class RtApiJack: public RtApi { public: RtApiJack(); ~RtApiJack(); RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; } unsigned int getDeviceCount( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); void closeStream( void ); void startStream( void ); void stopStream( void ); void abortStream( void ); long getStreamLatency( void ); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! bool callbackEvent( unsigned long nframes ); private: bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); }; #endif #if defined(__WINDOWS_ASIO__) class RtApiAsio: public RtApi { public: RtApiAsio(); ~RtApiAsio(); RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; } unsigned int getDeviceCount( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); void closeStream( void ); void startStream( void ); void stopStream( void ); void abortStream( void ); long getStreamLatency( void ); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! bool callbackEvent( long bufferIndex ); private: std::vector devices_; void saveDeviceInfo( void ); bool coInitialized_; bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); }; #endif #if defined(__WINDOWS_DS__) class RtApiDs: public RtApi { public: RtApiDs(); ~RtApiDs(); RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; } unsigned int getDeviceCount( void ); unsigned int getDefaultOutputDevice( void ); unsigned int getDefaultInputDevice( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); void closeStream( void ); void startStream( void ); void stopStream( void ); void abortStream( void ); long getStreamLatency( void ); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! void callbackEvent( void ); private: bool coInitialized_; bool buffersRolling; long duplexPrerollBytes; std::vector dsDevices; bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); }; #endif #if defined(__WINDOWS_WASAPI__) struct IMMDeviceEnumerator; class RtApiWasapi : public RtApi { public: RtApiWasapi(); ~RtApiWasapi(); RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; } unsigned int getDeviceCount( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); unsigned int getDefaultOutputDevice( void ); unsigned int getDefaultInputDevice( void ); void closeStream( void ); void startStream( void ); void stopStream( void ); void abortStream( void ); private: bool coInitialized_; IMMDeviceEnumerator* deviceEnumerator_; bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int* bufferSize, RtAudio::StreamOptions* options ); static DWORD WINAPI runWasapiThread( void* wasapiPtr ); static DWORD WINAPI stopWasapiThread( void* wasapiPtr ); static DWORD WINAPI abortWasapiThread( void* wasapiPtr ); void wasapiThread(); }; #endif #if defined(__LINUX_ALSA__) class RtApiAlsa: public RtApi { public: RtApiAlsa(); ~RtApiAlsa(); RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; } unsigned int getDeviceCount( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); void closeStream( void ); void startStream( void ); void stopStream( void ); void abortStream( void ); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! void callbackEvent( void ); private: std::vector devices_; void saveDeviceInfo( void ); bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); }; #endif #if defined(__LINUX_PULSE__) class RtApiPulse: public RtApi { public: ~RtApiPulse(); RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; } unsigned int getDeviceCount( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); void closeStream( void ); void startStream( void ); void stopStream( void ); void abortStream( void ); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! void callbackEvent( void ); private: std::vector devices_; void saveDeviceInfo( void ); bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); }; #endif #if defined(__LINUX_OSS__) class RtApiOss: public RtApi { public: RtApiOss(); ~RtApiOss(); RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; } unsigned int getDeviceCount( void ); RtAudio::DeviceInfo getDeviceInfo( unsigned int device ); void closeStream( void ); void startStream( void ); void stopStream( void ); void abortStream( void ); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesirable results! void callbackEvent( void ); private: bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); }; #endif #if defined(__RTAUDIO_DUMMY__) class RtApiDummy: public RtApi { public: RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); } RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; } unsigned int getDeviceCount( void ) { return 0; } RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; return info; } void closeStream( void ) {} void startStream( void ) {} void stopStream( void ) {} void abortStream( void ) {} private: bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/, unsigned int /*firstChannel*/, unsigned int /*sampleRate*/, RtAudioFormat /*format*/, unsigned int * /*bufferSize*/, RtAudio::StreamOptions * /*options*/ ) { return false; } }; #endif #endif // Indentation settings for Vim and Emacs // // Local Variables: // c-basic-offset: 2 // indent-tabs-mode: nil // End: // // vim: et sts=2 sw=2 mlt-6.20.0/src/modules/rtaudio/configure000077500000000000000000000012511362234133600202120ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ] then if pkg-config rtaudio then echo "CXXFLAGS += $(pkg-config --cflags rtaudio)" > config.mak echo "LDFLAGS += $(pkg-config --libs rtaudio)" >> config.mak elif rtaudio-config >/dev/null 2>&1 then echo "CXXFLAGS += $(rtaudio-config --cppflags)" > config.mak echo "LDFLAGS += $(rtaudio-config --libs)" -lrtaudio >> config.mak elif [ "$targetos" = "Darwin" ] || [ "$targetos" = "MinGW" ] || [ "$targetos" = "Linux" ] then echo "USE_INTERNAL_RTAUDIO=1" > config.mak echo "CXXFLAGS += -DUSE_INTERNAL_RTAUDIO" >> config.mak else echo "- only builds on Linux, OS X, or Windows: disabling" touch ../disable-rtaudio exit 0 fi fi mlt-6.20.0/src/modules/rtaudio/consumer_rtaudio.cpp000066400000000000000000000560551362234133600224050ustar00rootroot00000000000000/* * consumer_rtaudio.c -- output through RtAudio audio wrapper * Copyright (C) 2011-2016 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 consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #ifdef USE_INTERNAL_RTAUDIO #include "RtAudio.h" #else #include #endif static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer consumer, char *name ); static int rtaudio_callback( void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData ); static void *consumer_thread_proxy( void *arg ); static void *video_thread_proxy( void *arg ); static const char *rtaudio_api_str( RtAudio::Api api ) { switch( api ) { case RtAudio::UNSPECIFIED: return "UNSPECIFIED"; case RtAudio::LINUX_ALSA: return "LINUX_ALSA"; case RtAudio::LINUX_PULSE: return "LINUX_PULSE"; case RtAudio::LINUX_OSS: return "LINUX_OSS"; case RtAudio::UNIX_JACK: return "UNIX_JACK"; case RtAudio::MACOSX_CORE: return "MACOSX_CORE"; case RtAudio::WINDOWS_WASAPI: return "WINDOWS_WASAPI"; case RtAudio::WINDOWS_ASIO: return "WINDOWS_ASIO"; case RtAudio::WINDOWS_DS: return "WINDOWS_DS"; case RtAudio::RTAUDIO_DUMMY: return "RTAUDIO_DUMMY"; } return "UNKNOWN!?!"; } class RtAudioConsumer { public: struct mlt_consumer_s consumer; RtAudio* rt; int device_id; mlt_deque queue; pthread_t thread; int joined; int running; int out_channels; 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 playing; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; bool is_purge; mlt_consumer getConsumer() { return &consumer; } RtAudioConsumer() : rt(NULL) , device_id(-1) , queue(NULL) , joined(0) , running(0) , audio_avail(0) , playing(0) , refresh_count(0) , is_purge(false) { memset( &consumer, 0, sizeof( consumer ) ); } ~RtAudioConsumer() { // Close the queue mlt_deque_close( queue ); // Destroy mutexes pthread_mutex_destroy( &audio_mutex ); pthread_cond_destroy( &audio_cond ); pthread_mutex_destroy( &video_mutex ); pthread_cond_destroy( &video_cond ); pthread_mutex_destroy( &refresh_mutex ); pthread_cond_destroy( &refresh_cond ); if ( rt && rt->isStreamOpen() ) rt->closeStream(); delete rt; rt = NULL; } bool create_rtaudio( RtAudio::Api api, int channels, int frequency ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); const char *resource = mlt_properties_get( properties, "resource" ); unsigned int bufferFrames = mlt_properties_get_int( properties, "audio_buffer" ); mlt_log_info( getConsumer(), "Attempt to open RtAudio: %s\t%d\t%d\n", rtaudio_api_str( api ), channels, frequency ); rt = new RtAudio( api ); if( !rt ) { return false; } if ( rt->getDeviceCount() < 1 ) { mlt_log_warning( getConsumer(), "no audio devices found\n" ); delete rt; rt = NULL; return false; } #ifndef __LINUX_ALSA__ device_id = rt->getDefaultOutputDevice(); #endif if ( resource && strcmp( resource, "" ) && strcmp( resource, "default" ) ) { // Get device ID by name unsigned int n = rt->getDeviceCount(); RtAudio::DeviceInfo info; unsigned int i; for ( i = 0; i < n; i++ ) { info = rt->getDeviceInfo( i ); mlt_log_verbose( NULL, "RtAudio device %d = %s\n", i, info.name.c_str() ); if ( info.probed && info.name == resource ) { device_id = i; break; } } // Name selection failed, try arg as numeric if ( i == n ) device_id = (int) strtol( resource, NULL, 0 ); } RtAudio::StreamParameters parameters; parameters.deviceId = device_id; parameters.nChannels = channels; parameters.firstChannel = 0; RtAudio::StreamOptions options; if ( device_id == -1 ) { options.flags = RTAUDIO_ALSA_USE_DEFAULT; parameters.deviceId = 0; } if ( resource ) { unsigned n = rt->getDeviceCount(); for (unsigned i = 0; i < n; i++) { RtAudio::DeviceInfo info = rt->getDeviceInfo( i ); if ( info.name == resource ) { device_id = parameters.deviceId = i; break; } } } try { if ( rt->isStreamOpen() ) { rt->closeStream(); } rt->openStream( ¶meters, NULL, RTAUDIO_SINT16, frequency, &bufferFrames, &rtaudio_callback, this, &options ); rt->startStream(); } #ifdef RTERROR_H catch ( RtError& e ) { #else catch ( RtAudioError& e ) { #endif mlt_log_info( getConsumer(), "%s\n", e.getMessage().c_str() ); delete rt; rt = NULL; return false; } mlt_log_info( getConsumer(), "Opened RtAudio: %s\t%d\t%d\n", rtaudio_api_str( rt->getCurrentApi() ), channels, frequency ); return true; } bool find_and_create_rtaudio( int requested_channels, int frequency, int* actual_channels ) { bool result = false; #ifdef __WINDOWS_DS__ // Prefer DirectSound on Windows RtAudio::Api PREFERRED_API = RtAudio::WINDOWS_DS; #else RtAudio::Api PREFERRED_API = RtAudio::UNSPECIFIED; #endif *actual_channels = requested_channels; // First try with preferred API. result = create_rtaudio( PREFERRED_API, *actual_channels, frequency ); if( !result ) { // If the preferred API fails, try other APIs that are available. std::vector apis; RtAudio::getCompiledApi( apis ); for( size_t i = 0; i < apis.size(); i++ ) { if( apis[i] == PREFERRED_API || apis[i] == RtAudio::RTAUDIO_DUMMY ) { continue; } result = create_rtaudio( apis[i], *actual_channels, frequency ); if( result ) { break; } } } if( !result && *actual_channels != 2 ) { // If surround has failed for all APIs, try stereo. *actual_channels = 2; mlt_log_info( getConsumer(), "Unable to open %d channels. Try %d channels\n", requested_channels, *actual_channels ); std::vector apis; RtAudio::getCompiledApi( apis ); for( size_t i = 0; i < apis.size(); i++ ) { if( apis[i] == RtAudio::RTAUDIO_DUMMY ) { continue; } result = create_rtaudio( apis[i], *actual_channels, frequency ); if( result ) { break; } } } return result; } bool open( const char* arg ) { // Create the queue queue = mlt_deque_init( ); // get a handle on properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( &consumer ); // Set the default volume mlt_properties_set_double( properties, "volume", 1.0 ); // This is the initialisation of the consumer pthread_mutex_init( &audio_mutex, NULL ); pthread_cond_init( &audio_cond, NULL); pthread_mutex_init( &video_mutex, NULL ); pthread_cond_init( &video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set( properties, "rescale", "nearest" ); mlt_properties_set( properties, "deinterlace_method", "onefield" ); // Default buffer for low latency mlt_properties_set_int( properties, "buffer", 1 ); // Default audio buffer mlt_properties_set_int( properties, "audio_buffer", 1024 ); // Set the resource to the device name arg mlt_properties_set( properties, "resource", arg ); // Ensure we don't join on a non-running object joined = 1; // Initialize the refresh handler pthread_cond_init( &refresh_cond, NULL ); pthread_mutex_init( &refresh_mutex, NULL ); mlt_events_listen( properties, this, "property-changed", ( mlt_listener )consumer_refresh_cb ); return true; } int start() { if ( !running ) { stop(); running = 1; joined = 0; pthread_create( &thread, NULL, consumer_thread_proxy, this ); } return 0; } int stop() { if ( running && !joined ) { // Kill the thread and clean up joined = 1; running = 0; // Unlatch the consumer thread pthread_mutex_lock( &refresh_mutex ); pthread_cond_broadcast( &refresh_cond ); pthread_mutex_unlock( &refresh_mutex ); // Cleanup the main thread pthread_join( thread, NULL ); // Unlatch the video thread pthread_mutex_lock( &video_mutex ); pthread_cond_broadcast( &video_cond ); pthread_mutex_unlock( &video_mutex ); // Unlatch the audio callback pthread_mutex_lock( &audio_mutex ); pthread_cond_broadcast( &audio_cond ); pthread_mutex_unlock( &audio_mutex ); if ( rt && rt->isStreamOpen() ) try { // Stop the stream rt->stopStream(); } #ifdef RTERROR_H catch ( RtError& e ) { #else catch ( RtAudioError& e ) { #endif mlt_log_error( getConsumer(), "%s\n", e.getMessage().c_str() ); } delete rt; rt = NULL; } return 0; } void purge() { if ( running ) { pthread_mutex_lock( &video_mutex ); mlt_frame frame = MLT_FRAME( mlt_deque_peek_back( queue ) ); // When playing rewind or fast forward then we need to keep one // frame in the queue to prevent playback stalling. double speed = frame? mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" ) : 0; int n = ( speed == 0.0 || speed == 1.0 ) ? 0 : 1; while ( mlt_deque_count( queue ) > n ) mlt_frame_close( MLT_FRAME( mlt_deque_pop_back( queue ) ) ); is_purge = true; pthread_cond_broadcast( &video_cond ); pthread_mutex_unlock( &video_mutex ); } } void consumer_thread() { // Get the properties mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( getConsumer() ); // Video thread pthread_t thread; // internal initialization 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 }; // int last_position = -1; pthread_mutex_lock( &refresh_mutex ); refresh_count = 0; pthread_mutex_unlock( &refresh_mutex ); // Loop until told not to while ( running ) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame( getConsumer() ); // Ensure that we have a frame if ( frame ) { // Get the frame properties properties = MLT_FRAME_PROPERTIES( frame ); // Get the speed of the frame double speed = mlt_properties_get_double( properties, "_speed" ); // Get refresh request for the current frame int refresh = mlt_properties_get_int( consumer_props, "refresh" ); // Clear refresh mlt_events_block( consumer_props, consumer_props ); mlt_properties_set_int( consumer_props, "refresh", 0 ); mlt_events_unblock( consumer_props, consumer_props ); // Play audio init_audio = play_audio( frame, init_audio, &duration ); // Determine the start time now if ( playing && init_video ) { // Create the video thread pthread_create( &thread, NULL, video_thread_proxy, 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 ( running && speed != 0 && mlt_deque_count( queue ) > 15 ) nanosleep( &tm, NULL ); // Push this frame to the back of the queue if ( running && speed ) { pthread_mutex_lock( &video_mutex ); if ( is_purge && speed == 1.0 ) { mlt_frame_close( frame ); is_purge = false; } else { mlt_deque_push_back( queue, frame ); pthread_cond_broadcast( &video_cond ); } pthread_mutex_unlock( &video_mutex ); // Calculate the next playtime playtime += ( duration * 1000 ); } else if ( running ) { pthread_mutex_lock( &refresh_mutex ); if ( refresh == 0 && refresh_count <= 0 ) { play_video( frame ); pthread_cond_wait( &refresh_cond, &refresh_mutex ); } mlt_frame_close( frame ); refresh_count --; pthread_mutex_unlock( &refresh_mutex ); } else { mlt_frame_close( frame ); frame = NULL; } // Optimisation to reduce latency if ( frame && speed == 1.0 ) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else { mlt_consumer_purge( getConsumer() ); // last_position = -1; } } } // Kill the video thread if ( init_video == 0 ) { pthread_mutex_lock( &video_mutex ); pthread_cond_broadcast( &video_cond ); pthread_mutex_unlock( &video_mutex ); pthread_join( thread, NULL ); } while( mlt_deque_count( queue ) ) mlt_frame_close( (mlt_frame) mlt_deque_pop_back( queue ) ); audio_avail = 0; } int callback( int16_t *outbuf, int16_t *inbuf, unsigned int samples, double streamTime, RtAudioStreamStatus status ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); double volume = mlt_properties_get_double( properties, "volume" ); int len = mlt_audio_format_size( mlt_audio_s16, samples, out_channels ); pthread_mutex_lock( &audio_mutex ); // Block until audio received while ( running && len > audio_avail ) pthread_cond_wait( &audio_cond, &audio_mutex ); if ( audio_avail >= len ) { // Place in the audio buffer memcpy( outbuf, audio_buffer, len ); // Remove len from the audio available audio_avail -= len; // Remove the samples memmove( audio_buffer, audio_buffer + len, audio_avail ); } else { // Just to be safe, wipe the stream first memset( outbuf, 0, len ); // Copy what we have memcpy( outbuf, audio_buffer, audio_avail ); // No audio left audio_avail = 0; } if ( volume != 1.0 ) { int16_t *p = outbuf; int i = samples * out_channels + 1; while ( --i ) *p++ *= volume; } // We're definitely playing now playing = 1; pthread_cond_broadcast( &audio_cond ); pthread_mutex_unlock( &audio_mutex ); return 0; } int play_audio( mlt_frame frame, int init_audio, int *duration ) { // Get the properties of this consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); mlt_audio_format afmt = mlt_audio_s16; // 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" ); int scrub = mlt_properties_get_int( properties, "scrub_audio" ); static int counter = 0; int samples = mlt_sample_calculator( mlt_properties_get_double( properties, "fps" ), frequency, counter++ ); int16_t *pcm; mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples ); *duration = ( ( samples * 1000 ) / frequency ); if ( mlt_properties_get_int( properties, "audio_off" ) ) { playing = 1; return init_audio; } if ( init_audio == 1 ) { if( find_and_create_rtaudio( channels, frequency, &out_channels ) ) { init_audio = 0; playing = 1; } else { rt = NULL; mlt_log_error( getConsumer(), "Unable to initialize RtAudio\n" ); init_audio = 2; } } if ( init_audio == 0 ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int samples_copied = 0; int dst_stride = out_channels * sizeof( *pcm ); pthread_mutex_lock( &audio_mutex ); while ( running && samples_copied < samples ) { int sample_space = ( sizeof( audio_buffer ) - audio_avail ) / dst_stride; while ( running && sample_space == 0 ) { pthread_cond_wait( &audio_cond, &audio_mutex ); sample_space = ( sizeof( audio_buffer ) - audio_avail ) / dst_stride; } if ( running ) { int samples_to_copy = samples - samples_copied; if ( samples_to_copy > sample_space ) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 ) { if ( channels == out_channels ) { memcpy( &audio_buffer[ audio_avail ], pcm, dst_bytes ); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t*) &audio_buffer[ audio_avail ]; int i = samples_to_copy + 1; while ( --i ) { memcpy( dest, pcm, dst_stride ); pcm += channels; dest += out_channels; } } } else { memset( &audio_buffer[ audio_avail ], 0, dst_bytes ); pcm += samples_to_copy * channels; } audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast( &audio_cond ); } pthread_mutex_unlock( &audio_mutex ); } return init_audio; } int play_video( mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); if ( running && !mlt_consumer_is_stopped( getConsumer() ) ) mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); return 0; } void video_thread() { // 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 = MLT_CONSUMER_PROPERTIES( getConsumer() ); double speed = 0; // Get real time flag int real_time = mlt_properties_get_int( properties, "real_time" ); // Get the current time gettimeofday( &now, NULL ); // Determine start time start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec; while ( running ) { // Pop the next frame pthread_mutex_lock( &video_mutex ); next = (mlt_frame) mlt_deque_pop_front( queue ); while ( next == NULL && running ) { pthread_cond_wait( &video_cond, &video_mutex ); next = (mlt_frame) mlt_deque_pop_front( queue ); } pthread_mutex_unlock( &video_mutex ); if ( !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 && 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( queue ) < 2 ) ) play_video( next ); // If the queue is empty, recalculate start to allow build up again if ( real_time && ( mlt_deque_count( 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( getConsumer() ); } }; static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer consumer, char *name ) { if ( !strcmp( name, "refresh" ) ) { RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child; pthread_mutex_lock( &rtaudio->refresh_mutex ); rtaudio->refresh_count = rtaudio->refresh_count <= 0 ? 1 : rtaudio->refresh_count + 1; pthread_cond_broadcast( &rtaudio->refresh_cond ); pthread_mutex_unlock( &rtaudio->refresh_mutex ); } } static int rtaudio_callback( void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData ) { RtAudioConsumer* rtaudio = (RtAudioConsumer*) userData; return rtaudio->callback( (int16_t*) outputBuffer, (int16_t*) inputBuffer, nFrames, streamTime, status ); } static void *consumer_thread_proxy( void *arg ) { RtAudioConsumer* rtaudio = (RtAudioConsumer*) arg; rtaudio->consumer_thread(); return NULL; } static void *video_thread_proxy( void *arg ) { RtAudioConsumer* rtaudio = (RtAudioConsumer*) arg; rtaudio->video_thread(); return NULL; } /** Start the consumer. */ static int start( mlt_consumer consumer ) { RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child; return rtaudio->start(); } /** Stop the consumer. */ static int stop( mlt_consumer consumer ) { RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child; return rtaudio->stop(); } /** Determine if the consumer is stopped. */ static int is_stopped( mlt_consumer consumer ) { RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child; return !rtaudio->running; } static void purge( mlt_consumer consumer ) { RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child; rtaudio->purge(); } /** Close the consumer. */ static void close( mlt_consumer consumer ) { // Stop the consumer mlt_consumer_stop( consumer ); // Close the parent consumer->close = NULL; mlt_consumer_close( consumer ); // Free the memory delete (RtAudioConsumer*) consumer->child; } extern "C" { mlt_consumer consumer_rtaudio_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Allocate the consumer RtAudioConsumer* rtaudio = new RtAudioConsumer(); mlt_consumer consumer = NULL; // If allocated if ( rtaudio && !mlt_consumer_init( rtaudio->getConsumer(), rtaudio, profile ) ) { // If initialises without error if ( rtaudio->open( arg? arg : getenv( "AUDIODEV" ) ) ) { // Setup callbacks consumer = rtaudio->getConsumer(); consumer->close = close; consumer->start = start; consumer->stop = stop; consumer->is_stopped = is_stopped; consumer->purge = purge; } else { mlt_consumer_close( rtaudio->getConsumer() ); delete rtaudio; } } // Return consumer return consumer; } static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; const char *service_type = "consumer"; snprintf( file, PATH_MAX, "%s/rtaudio/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "rtaudio", consumer_rtaudio_init ); MLT_REGISTER_METADATA( consumer_type, "rtaudio", metadata, NULL ); } } // extern C mlt-6.20.0/src/modules/rtaudio/consumer_rtaudio.yml000066400000000000000000000022671362234133600224200ustar00rootroot00000000000000schema_version: 0.3 type: consumer identifier: rtaudio title: RtAudio description: > RtAudio provides native, realtime audio output across Linux, Macintosh OS X, Windows, and some BSD operating systems. url: http://www.music.mcgill.ca/~gary/rtaudio/ version: 1 copyright: Dan Dennedy creator: Dan Dennedy creator: Gary P. Scavone license: LGPLv2.1 language: en tags: - Audio parameters: - identifier: resource title: Device description: An optional device name, number, or ID to use. type: string required: no argument: yes - identifier: audio_buffer title: Audio buffer type: integer minimum: 256 maximum: 8192 default: 1024 unit: samples - identifier: volume title: Volume type: float minimum: 0.0 default: 1.0 mutable: yes - identifier: refresh description: > Applications should set this to update the video frame when paused. type: boolean minimum: 0 maximum: 1 - identifier: scrub_audio title: Audio scrubbing type: boolean description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/rubberband/000077500000000000000000000000001362234133600167435ustar00rootroot00000000000000mlt-6.20.0/src/modules/rubberband/CMakeLists.txt000066400000000000000000000010001362234133600214720ustar00rootroot00000000000000if(GPL) pkg_check_modules(rubberband IMPORTED_TARGET GLOBAL rubberband) if(TARGET PkgConfig::rubberband) file(GLOB mltrubberband_src *.c *.cpp) add_library(mltrubberband MODULE ${mltrubberband_src}) target_link_libraries(mltrubberband mlt PkgConfig::rubberband) install(TARGETS mltrubberband LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/rubberband) endif() endif() mlt-6.20.0/src/modules/rubberband/Makefile000066400000000000000000000015151362234133600204050ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS := -L../../framework -lmlt $(LDFLAGS) include ../../../config.mak TARGET = ../libmltrubberband$(LIBSUF) OBJS = factory.o CPPOBJS = filter_rbpitch.o CFLAGS += $(shell pkg-config --cflags rubberband) CXXFLAGS += -Wno-deprecated $(CFLAGS) LDFLAGS += $(shell pkg-config --libs rubberband) SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) all: $(TARGET) $(TARGET): $(OBJS) $(CPPOBJS) $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) depend: $(SRCS) $(CXX) -MM $(CXXFLAGS) $^ 1>.depend distclean: clean rm -f .depend config.h config.mak clean: rm -f $(OBJS) $(TARGET) $(CPPOBJS) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/rubberband" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/rubberband" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/rubberband/configure000077500000000000000000000003471362234133600206560ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ] then pkg-config rubberband 2> /dev/null disable_rubberband=$? if [ "$disable_rubberband" != "0" ] then echo "- rubberband not found: disabling" touch ../disable-rubberband fi exit 0 fi mlt-6.20.0/src/modules/rubberband/factory.c000066400000000000000000000025771362234133600205710ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2020 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include extern mlt_filter filter_rbpitch_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/rubberband/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( filter_type, "rbpitch", filter_rbpitch_init ); MLT_REGISTER_METADATA( filter_type, "rbpitch", metadata, "filter_rbpitch.yml" ); } mlt-6.20.0/src/modules/rubberband/filter_rbpitch.cpp000066400000000000000000000223051362234133600224510ustar00rootroot00000000000000/* * filter_rbpitch.c -- adjust audio pitch * Copyright (C) 2020 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include using namespace RubberBand; // Private Types typedef struct { RubberBandStretcher* s; int rubberband_frequency; uint64_t in_samples; uint64_t out_samples; } private_data; static const size_t MAX_CHANNELS = 10; static void collapse_channels( int channels, int allocated_samples, int used_samples, float* buffer ) { if ( allocated_samples != used_samples && used_samples != 0 ) { // The first channel will always be in the correct place (0). for ( int p = 1; p < channels; p++ ) { float* src = buffer + ( p * allocated_samples ); float* dst = buffer + ( p * used_samples ); memmove( dst, src, used_samples * sizeof(float) ); } } } static int rbpitch_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { mlt_filter filter = static_cast(mlt_frame_pop_audio( frame )); mlt_properties filter_properties = MLT_FILTER_PROPERTIES(filter); private_data* pdata = (private_data*)filter->child; if ( *channels > (int)MAX_CHANNELS ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "Too many channels requested: %d > %d\n", *channels, (int)MAX_CHANNELS ); return mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); } int requested_samples = *samples; mlt_properties unique_properties = mlt_frame_get_unique_properties( frame, MLT_FILTER_SERVICE(filter) ); if ( !unique_properties ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "Missing unique_properites\n" ); return mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); } // Get the producer's audio *format = mlt_audio_float; int error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); if ( error ) return error; // Make sure the audio is in the correct format // This is useful if the filter is encapsulated in a producer and does not // have a normalizing filter before it. if (*format != mlt_audio_float && frame->convert_audio != NULL) { frame->convert_audio( frame, buffer, format, mlt_audio_float ); } // Detect the last frame bool last_frame = false; if ( mlt_filter_get_position( filter, frame ) + 1 == mlt_filter_get_length2( filter, frame ) ) { // This is the last mlt frame. Drain the rest of the stretcher last_frame = true; } // Sanity check parameters // rubberband library crashes have been seen with a very large scale factor // or very small sampling frequency. Very small scale factor and very high // sampling frequency can result in too much audio lag. // Disallow these extreme scenarios for now. Maybe it will be improved in // the future. double pitchscale = mlt_properties_get_double( unique_properties, "pitchscale" ); pitchscale = CLAMP( pitchscale, 0.05, 50.0 ); int rubberband_frequency = CLAMP( *frequency, 10000, 300000 ); // Protect the RubberBandStretcher instance. mlt_service_lock( MLT_FILTER_SERVICE(filter) ); // Configure the stretcher. RubberBandStretcher* s = pdata->s; if ( !s || s->available() == -1 || (int)s->getChannelCount() != *channels || pdata->rubberband_frequency != rubberband_frequency ) { mlt_log_debug( MLT_FILTER_SERVICE(filter), "Create a new stretcher\t%d\t%d\t%f\n", *channels, rubberband_frequency, pitchscale ); delete s; // Create a rubberband instance RubberBandStretcher::Options options = RubberBandStretcher::OptionProcessRealTime | RubberBandStretcher::OptionPitchHighConsistency; s = new RubberBandStretcher(rubberband_frequency, *channels, options, 1.0, pitchscale); pdata->s = s; pdata->rubberband_frequency = rubberband_frequency; pdata->in_samples = 0; pdata->out_samples = 0; } s->setPitchScale(pitchscale); // Calculate the buffer size. int in_samples = *samples; int consumed_samples = 0; int out_samples = 0; int alloc_samples = in_samples; if ( last_frame ) { // Allocate enough space for the rest of the samples in the stretcher. alloc_samples += pdata->in_samples - pdata->out_samples; } int size = mlt_audio_format_size( *format, alloc_samples, *channels ); float* out_buffer = (float*)mlt_pool_alloc( size ); // Process all input samples while ( true ) { // Send more samples to the stretcher int required_samples = (int)s->getSamplesRequired(); int remaining_in_samples = in_samples - consumed_samples; int process_samples = std::min( remaining_in_samples, required_samples ); if ( process_samples > 0 ) { float* in_planes[MAX_CHANNELS]; for ( int i = 0; i < *channels; i++ ) { in_planes[i] = ((float*)*buffer) + (in_samples * i) + consumed_samples; } if ( last_frame && process_samples == remaining_in_samples ) { // This will be the last call to process. s->process( in_planes, process_samples, true ); mlt_log_debug( MLT_FILTER_SERVICE(filter), "Last call to Process()\n" ); } else { s->process( in_planes, process_samples, false ); } consumed_samples += process_samples; } // Receive samples from the stretcher int retrieve_samples = std::min( alloc_samples - out_samples, s->available() ); if ( retrieve_samples > 0 ) { float* out_planes[MAX_CHANNELS]; for ( int i = 0; i < *channels; i++ ) { out_planes[i] = out_buffer + (alloc_samples * i) + out_samples; } retrieve_samples = (int)s->retrieve( out_planes, retrieve_samples ); out_samples += retrieve_samples; } mlt_log_debug( MLT_FILTER_SERVICE(filter), "Process: %d\t Retrieve: %d\n", process_samples, retrieve_samples ); if ( process_samples <= 0 && retrieve_samples <= 0 ) { // There is nothing more to do; break; } } // Save the processed samples. collapse_channels( *channels, alloc_samples, out_samples, out_buffer ); mlt_frame_set_audio( frame, static_cast(out_buffer), *format, size, mlt_pool_release ); *buffer = static_cast(out_buffer); *samples = out_samples; pdata->in_samples += in_samples; pdata->out_samples += out_samples; // Report the latency. double latency = (double)(pdata->in_samples - pdata->out_samples) * 1000.0 / (double)*frequency; mlt_properties_set_double( filter_properties, "latency", latency ); mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); mlt_log_debug( MLT_FILTER_SERVICE(filter), "Requested: %d\tReceived: %d\tSent: %d\tLatency: %f\n", requested_samples, in_samples, out_samples, latency ); return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_position position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); // Determine the pitchscale double pitchscale = 1.0; if ( mlt_properties_get( filter_properties, "pitchscale" ) != NULL ) { pitchscale = mlt_properties_anim_get_double( filter_properties, "pitchscale", position, length ); } else { double octaveshift = mlt_properties_anim_get_double( filter_properties, "octaveshift", position, length ); pitchscale = pow(2, octaveshift); } if ( pitchscale <= 0.0 || /*check for nan:*/pitchscale != pitchscale ) { pitchscale = 1.0; } // Save the pitchscale on the frame to be used in rbpitch_get_audio mlt_properties unique_properties = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE(filter) ); mlt_properties_set_double( unique_properties, "pitchscale", pitchscale ); mlt_frame_push_audio( frame, (void*)filter ); mlt_frame_push_audio( frame, (void*)rbpitch_get_audio ); return frame; } static void close_filter( mlt_filter filter ) { private_data* pdata = (private_data*)filter->child; if ( pdata ) { RubberBandStretcher* s = static_cast(pdata->s); if ( s ) { delete s; } free( pdata ); filter->child = NULL; } } extern "C" { mlt_filter filter_rbpitch_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); private_data* pdata = (private_data*)calloc( 1, sizeof(private_data) ); if( filter && pdata ) { pdata->s = NULL; pdata->rubberband_frequency = 0; pdata->in_samples = 0; pdata->out_samples = 0; filter->process = filter_process; filter->close = close_filter; filter->child = pdata; } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Failed to initialize\n" ); if( filter ) { mlt_filter_close( filter ); } if( pdata ) { free( pdata ); } filter = NULL; } return filter; } } mlt-6.20.0/src/modules/rubberband/filter_rbpitch.yml000066400000000000000000000032141362234133600224660ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: rbpitch title: Rubberband Pitch version: 1 copyright: Meltytech, LLC creator: Brian Matherly license: GPLv2 language: en tags: - Audio description: Adjust the audio pitch using the Rubberband library. parameters: - identifier: octaveshift title: Octave Shift type: float description: > The octave shift. This is the octave shift of the source frequency. For example, a shift of +1 would double the frequency; -1 would halve the frequency and 0 would leave the pitch unaffected. To put this in frequency terms, a frequency shift f (where f greater than one for upwards shift and less than one for downwards) is: o = log(f) / log(2). Ignored if pitchscale is set. readonly: no mutable: yes default: 0.0 minimum: -3.3 maximum: 3.3 unit: octaves - identifier: pitchscale title: Pitch Scale type: float description: > The pitch scaling ratio. This is the ratio of target frequency to source frequency. For example, a ratio of 2.0 would shift up by one octave; 0.5 down by one octave; or 1.0 leave the pitch unaffected. To put this in musical terms, a pitch scaling ratio corresponding to a shift of o octaves (where o is positive for an upwards shift and negative for downwards) is: f = pow(2.0, o). Overrides octaveshift. readonly: no mutable: yes default: 1.0 minimum: 0.1 maximum: 10 - identifier: latency title: Latency type: float description: > The amount of delay for each sample from the input to the output. readonly: yes unit: ms mlt-6.20.0/src/modules/rubberband/gpl000066400000000000000000000000001362234133600174360ustar00rootroot00000000000000mlt-6.20.0/src/modules/sdl/000077500000000000000000000000001362234133600154175ustar00rootroot00000000000000mlt-6.20.0/src/modules/sdl/Makefile000066400000000000000000000024611362234133600170620ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread -lm include ../../../config.mak include config.mak TARGET = ../libmltsdl$(LIBSUF) OBJS = factory.o \ consumer_sdl_audio.o ifeq ($(HAVE_SDL2),1) OBJS += consumer_sdl2.o else OBJS += consumer_sdl.o OBJS += consumer_sdl_preview.o OBJS += consumer_sdl_still.o endif ifeq ($(targetos),Darwin) CFLAGS += -ObjC LDFLAGS += -lobjc -framework Foundation else ifneq ($(targetos), MinGW) LDFLAGS += -lX11 endif ifeq ($(USE_PKG_CONFIG), 1) CFLAGS += $(shell pkg-config --cflags sdl) LDFLAGS += $(shell pkg-config --libs sdl) else CFLAGS += $(shell sdl-config --cflags) LDFLAGS += $(shell sdl-config --libs) endif ifeq ($(WITH_SDL_IMAGE),1) OBJS += producer_sdl_image.o CFLAGS += -DWITH_SDL_IMAGE LDFLAGS += -lSDL_image endif SRCS := $(OBJS:.o=.c) ifeq ($(targetos),Darwin) OBJS += consumer_sdl_osx.o SRCS += consumer_sdl_osx.m consumer_sdl_osx.h endif 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/sdl" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/sdl" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/sdl/configure000077500000000000000000000014371362234133600173330ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ]; then if pkg-config --exists sdl > /dev/null 2>&1 ; then echo " - using SDL version $(pkg-config --modversion sdl)" echo "USE_PKG_CONFIG=1" > config.mak echo "HAVE_SDL1=1" >> config.mak image=`pkg-config --variable=prefix sdl`/include/SDL/SDL_image.h if [ -f "$image" ]; then echo " - found SDL_image" echo "WITH_SDL_IMAGE=1" >> config.mak fi elif sdl-config --version > /dev/null 2>&1 ; then echo " - using SDL version $(sdl-config --version)" echo "USE_PKG_CONFIG=0" > config.mak echo "HAVE_SDL1=1" >> 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 mlt-6.20.0/src/modules/sdl/consumer_sdl.c000066400000000000000000000676731362234133600203030ustar00rootroot00000000000000/* * consumer_sdl.c -- A Simple DirectMedia Layer consumer * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "consumer_sdl_osx.h" extern pthread_mutex_t mlt_sdl_mutex; /** 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; atomic_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; atomic_int playing; int sdl_flags; SDL_Overlay *sdl_overlay; SDL_Rect rect; uint8_t *buffer; int bpp; int is_purge; }; /** 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_purge( 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 self, 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 self = calloc( 1, sizeof( struct consumer_sdl_s ) ); // If no malloc'd and consumer init ok if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { // Create the queue self->queue = mlt_deque_init( ); // Get the parent consumer object mlt_consumer parent = &self->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 ); self->properties = MLT_SERVICE_PROPERTIES( service ); // Set the default volume mlt_properties_set_double( self->properties, "volume", 1.0 ); // This is the initialisation of the consumer pthread_mutex_init( &self->audio_mutex, NULL ); pthread_cond_init( &self->audio_cond, NULL); pthread_mutex_init( &self->video_mutex, NULL ); pthread_cond_init( &self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set( self->properties, "rescale", "nearest" ); mlt_properties_set( self->properties, "deinterlace_method", "onefield" ); mlt_properties_set_int( self->properties, "top_field_first", -1 ); // Default buffer for low latency mlt_properties_set_int( self->properties, "buffer", 1 ); // Default audio buffer mlt_properties_set_int( self->properties, "audio_buffer", 2048 ); // Default scrub audio mlt_properties_set_int( self->properties, "scrub_audio", 1 ); // Ensure we don't join on a non-running object self->joined = 1; // process actual param if ( arg && sscanf( arg, "%dx%d", &self->width, &self->height ) ) { mlt_properties_set_int( self->properties, "_arg_size", 1 ); } else { self->width = mlt_properties_get_int( self->properties, "width" ); self->height = mlt_properties_get_int( self->properties, "height" ); } // Set the sdl flags self->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_DOUBLEBUF; #if !defined(__APPLE__) self->sdl_flags |= SDL_RESIZABLE; #endif // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Register specific events mlt_events_register( self->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event ); // Return the consumer produced return parent; } // malloc or consumer init failed free( self ); // Indicate failure return NULL; } static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener != NULL ) listener( owner, self, ( SDL_Event * )args[ 0 ] ); } int consumer_start( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( !self->running ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); int video_off = mlt_properties_get_int( properties, "video_off" ); int preview_off = mlt_properties_get_int( properties, "preview_off" ); int display_off = video_off | preview_off; int audio_off = mlt_properties_get_int( properties, "audio_off" ); int sdl_started = mlt_properties_get_int( properties, "sdl_started" ); char *output_display = mlt_properties_get( properties, "output_display" ); 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" ); consumer_stop( parent ); self->running = 1; self->joined = 0; 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 ( ! mlt_properties_get_int( self->properties, "_arg_size" ) ) { if ( mlt_properties_get_int( self->properties, "width" ) > 0 ) self->width = mlt_properties_get_int( self->properties, "width" ); if ( mlt_properties_get_int( self->properties, "height" ) > 0 ) self->height = mlt_properties_get_int( self->properties, "height" ); } self->bpp = mlt_properties_get_int( self->properties, "bpp" ); if ( sdl_started == 0 && display_off == 0 ) { pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL ); SDL_EnableUNICODE( 1 ); } if ( audio_off == 0 ) SDL_InitSubSystem( SDL_INIT_AUDIO ); // Default window size if ( mlt_properties_get_int( self->properties, "_arg_size" ) ) { self->window_width = self->width; self->window_height = self->height; } else { double display_ratio = mlt_properties_get_double( self->properties, "display_ratio" ); self->window_width = ( double )self->height * display_ratio + 0.5; self->window_height = self->height; } pthread_mutex_lock( &mlt_sdl_mutex ); if ( !SDL_GetVideoSurface() && display_off == 0 ) { if ( mlt_properties_get_int( self->properties, "fullscreen" ) ) { const SDL_VideoInfo *vi; vi = SDL_GetVideoInfo(); self->window_width = vi->current_w; self->window_height = vi->current_h; self->sdl_flags |= SDL_FULLSCREEN; SDL_ShowCursor( SDL_DISABLE ); } SDL_SetVideoMode( self->window_width, self->window_height, 0, self->sdl_flags ); } pthread_mutex_unlock( &mlt_sdl_mutex ); pthread_create( &self->thread, NULL, consumer_thread, self ); } return 0; } int consumer_stop( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; if ( self->joined == 0 ) { // Kill the thread and clean up self->joined = 1; self->running = 0; if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "audio_off" ) ) { pthread_mutex_lock( &self->audio_mutex ); pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); } #ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); // cleanup SDL pthread_mutex_lock( &mlt_sdl_mutex ); if ( self->sdl_overlay != NULL ) SDL_FreeYUVOverlay( self->sdl_overlay ); self->sdl_overlay = NULL; if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "audio_off" ) ) SDL_QuitSubSystem( SDL_INIT_AUDIO ); if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" ) == 0 ) SDL_Quit( ); pthread_mutex_unlock( &mlt_sdl_mutex ); } return 0; } int consumer_is_stopped( mlt_consumer parent ) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( self->running ) { pthread_mutex_lock( &self->video_mutex ); while ( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); self->is_purge = 1; pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); } } static int sdl_lock_display( ) { pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Surface *screen = SDL_GetVideoSurface( ); int result = screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 ); pthread_mutex_unlock( &mlt_sdl_mutex ); return result; } static void sdl_unlock_display( ) { pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Surface *screen = SDL_GetVideoSurface( ); if ( screen != NULL && SDL_MUSTLOCK( screen ) ) SDL_UnlockSurface( screen ); pthread_mutex_unlock( &mlt_sdl_mutex ); } static void sdl_fill_audio( void *udata, uint8_t *stream, int len ) { consumer_sdl self = udata; // Get the volume double volume = mlt_properties_get_double( self->properties, "volume" ); pthread_mutex_lock( &self->audio_mutex ); // Block until audio received while ( self->running && len > self->audio_avail ) pthread_cond_wait( &self->audio_cond, &self->audio_mutex ); if ( self->audio_avail >= len ) { // Place in the audio buffer if ( volume != 1.0 ) SDL_MixAudio( stream, self->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); else memcpy( stream, self->audio_buffer, len ); // Remove len from the audio available self->audio_avail -= len; // Remove the samples memmove( self->audio_buffer, self->audio_buffer + len, self->audio_avail ); } else { // Just to be safe, wipe the stream first memset( stream, 0, len ); // Mix the audio SDL_MixAudio( stream, self->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); // No audio left self->audio_avail = 0; } // We're definitely playing now self->playing = 1; pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); } static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int *duration ) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int( properties, "channels" ); int dest_channels = channels; int frequency = mlt_properties_get_int( properties, "frequency" ); int scrub = mlt_properties_get_int( properties, "scrub_audio" ); static int counter = 0; int samples = mlt_sample_calculator( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ ); int16_t *pcm; mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples ); *duration = ( ( samples * 1000 ) / frequency ); pcm += mlt_properties_get_int( properties, "audio_offset" ); if ( mlt_properties_get_int( properties, "audio_off" ) ) { self->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 ) ); self->playing = 0; request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = dest_channels; request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *)self; if ( SDL_OpenAudio( &request, &got ) != 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE( self ), "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 ); int samples_copied = 0; int dst_stride = dest_channels * sizeof( *pcm ); pthread_mutex_lock( &self->audio_mutex ); while ( self->running && samples_copied < samples ) { int sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; while ( self->running && sample_space == 0 ) { pthread_cond_wait( &self->audio_cond, &self->audio_mutex ); sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; } if ( self->running ) { int samples_to_copy = samples - samples_copied; if ( samples_to_copy > sample_space ) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 ) { if ( channels == dest_channels ) { memcpy( &self->audio_buffer[ self->audio_avail ], pcm, dst_bytes ); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ]; int i = samples_to_copy + 1; while ( --i ) { memcpy( dest, pcm, dst_stride ); pcm += channels; dest += dest_channels; } } } else { memset( &self->audio_buffer[ self->audio_avail ], 0, dst_bytes ); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast( &self->audio_cond ); } pthread_mutex_unlock( &self->audio_mutex ); } else { self->playing = 1; } return init_audio; } static int consumer_play_video( consumer_sdl self, mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = self->properties; mlt_image_format vfmt = mlt_properties_get_int( properties, "mlt_image_format" ); int width = self->width, height = self->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 ( self->running && display_off == 0 ) { // Get the image, width and height mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); void *pool = mlt_cocoa_autorelease_init(); // Handle events if ( SDL_GetVideoSurface() ) { SDL_Event event; sdl_lock_display( ); pthread_mutex_lock( &mlt_sdl_mutex ); changed = consumer_get_dimensions( &self->window_width, &self->window_height ); pthread_mutex_unlock( &mlt_sdl_mutex ); sdl_unlock_display( ); while ( SDL_PollEvent( &event ) ) { mlt_events_fire( self->properties, "consumer-sdl-event", &event, NULL ); switch( event.type ) { case SDL_VIDEORESIZE: self->window_width = event.resize.w; self->window_height = event.resize.h; changed = 1; break; case SDL_QUIT: self->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 != self->width || height != self->height ) { if ( self->sdl_overlay != NULL ) SDL_FreeYUVOverlay( self->sdl_overlay ); self->sdl_overlay = NULL; } if ( self->running && ( !SDL_GetVideoSurface() || changed ) ) { // Force an overlay recreation if ( self->sdl_overlay != NULL ) SDL_FreeYUVOverlay( self->sdl_overlay ); self->sdl_overlay = NULL; // open SDL window with video overlay, if possible pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Surface *screen = SDL_SetVideoMode( self->window_width, self->window_height, self->bpp, self->sdl_flags ); if ( consumer_get_dimensions( &self->window_width, &self->window_height ) ) screen = SDL_SetVideoMode( self->window_width, self->window_height, self->bpp, self->sdl_flags ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( screen ) { uint32_t color = mlt_properties_get_int( self->properties, "window_background" ); SDL_FillRect( screen, NULL, color >> 8 ); SDL_Flip( screen ); } } if ( self->running ) { // Determine window's new display aspect ratio double this_aspect = ( double )self->window_width / self->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 self->width = width; self->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 self->rect.w = frame_aspect / this_aspect * self->window_width; self->rect.h = self->window_height; if ( self->rect.w > self->window_width ) { self->rect.w = self->window_width; self->rect.h = this_aspect / frame_aspect * self->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 ) ) { self->rect.w = self->window_width; self->rect.h = self->window_height; } // Use hardware scaler to normalise sample aspect ratio else if ( self->window_height * display_ratio > self->window_width ) { self->rect.w = self->window_width; self->rect.h = self->window_width / display_ratio; } else { self->rect.w = self->window_height * display_ratio; self->rect.h = self->window_height; } self->rect.x = ( self->window_width - self->rect.w ) / 2; self->rect.y = ( self->window_height - self->rect.h ) / 2; self->rect.x -= self->rect.x % 2; mlt_properties_set_int( self->properties, "rect_x", self->rect.x ); mlt_properties_set_int( self->properties, "rect_y", self->rect.y ); mlt_properties_set_int( self->properties, "rect_w", self->rect.w ); mlt_properties_set_int( self->properties, "rect_h", self->rect.h ); SDL_SetClipRect( SDL_GetVideoSurface(), &self->rect ); } if ( self->running && SDL_GetVideoSurface() && self->sdl_overlay == NULL ) { SDL_SetClipRect( SDL_GetVideoSurface(), &self->rect ); self->sdl_overlay = SDL_CreateYUVOverlay( width, height, ( vfmt == mlt_image_yuv422 ? SDL_YUY2_OVERLAY : SDL_IYUV_OVERLAY ), SDL_GetVideoSurface() ); } if ( self->running && SDL_GetVideoSurface() && self->sdl_overlay != NULL ) { self->buffer = self->sdl_overlay->pixels[ 0 ]; if ( SDL_LockYUVOverlay( self->sdl_overlay ) >= 0 ) { // We use height-1 because mlt_image_format_size() uses height + 1. // XXX Remove -1 when mlt_image_format_size() is changed. int size = mlt_image_format_size( vfmt, width, height - 1, NULL ); if ( image != NULL ) memcpy( self->buffer, image, size ); SDL_UnlockYUVOverlay( self->sdl_overlay ); SDL_DisplayYUVOverlay( self->sdl_overlay, &SDL_GetVideoSurface()->clip_rect ); } } sdl_unlock_display(); mlt_cocoa_autorelease_close( pool ); mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } else if ( self->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_events_fire( properties, "consumer-frame-show", frame, NULL ); } return 0; } static void *video_thread( void *arg ) { // Identify the arg consumer_sdl self = 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( self->properties, "real_time" ); // Get the current time gettimeofday( &now, NULL ); // Determine start time start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec; while ( self->running ) { // Pop the next frame pthread_mutex_lock( &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); while ( next == NULL && self->running ) { pthread_cond_wait( &self->video_cond, &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); } pthread_mutex_unlock( &self->video_mutex ); if ( !self->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 && self->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( self->queue ) < 2 ) ) consumer_play_video( self, next ); // If the queue is empty, recalculate start to allow build up again if ( real_time && ( mlt_deque_count( self->queue ) == 0 && speed == 1.0 ) ) { gettimeofday( &now, NULL ); start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000; } } else { static int dropped = 0; mlt_log_info( MLT_CONSUMER_SERVICE(&self->parent), "dropped video frame %d\n", ++dropped ); } // This frame can now be closed mlt_frame_close( next ); next = NULL; } if ( next != NULL ) mlt_frame_close( next ); mlt_consumer_stopped( &self->parent ); return NULL; } /** Threaded wrapper for pipe. */ static void *consumer_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->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 initialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; int duration = 0; int64_t playtime = 0; struct timespec tm = { 0, 100000 }; // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = !terminated? mlt_consumer_rt_frame( consumer ) : NULL; // Check for termination if ( terminate_on_pause && frame ) terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0; // Ensure that we have a frame if ( frame ) { // Play audio init_audio = consumer_play_audio( self, frame, init_audio, &duration ); // Determine the start time now if ( self->playing && init_video ) { // Create the video thread pthread_create( &thread, NULL, video_thread, self ); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "playtime", playtime ); while ( self->running && mlt_deque_count( self->queue ) > 15 ) nanosleep( &tm, NULL ); // Push this frame to the back of the queue pthread_mutex_lock( &self->video_mutex ); if ( self->is_purge ) { mlt_frame_close( frame ); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back( self->queue, frame ); pthread_cond_broadcast( &self->video_cond ); } pthread_mutex_unlock( &self->video_mutex ); // Calculate the next playtime playtime += ( duration * 1000 ); } else if ( terminated ) { if ( init_video || mlt_deque_count( self->queue ) == 0 ) break; else nanosleep( &tm, NULL ); } } self->running = 0; // Unblock sdl_preview if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "put_mode" ) && mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "put_pending" ) ) { frame = mlt_consumer_get_frame( consumer ); if ( frame ) mlt_frame_close( frame ); frame = NULL; } // Kill the video thread if ( init_video == 0 ) { pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); pthread_join( thread, NULL ); } while( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); pthread_mutex_lock( &self->audio_mutex ); self->audio_avail = 0; pthread_mutex_unlock( &self->audio_mutex ); 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 __APPLE__ // Get the wm structure if ( SDL_GetWMInfo( &wm ) == 1 ) { #ifndef _WIN32 // 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 } #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 self = parent->child; // Stop the consumer ///mlt_consumer_stop( parent ); // Now clean up the rest mlt_consumer_close( parent ); // Close the queue mlt_deque_close( self->queue ); // Destroy mutexes pthread_mutex_destroy( &self->audio_mutex ); pthread_cond_destroy( &self->audio_cond ); // Finally clean up this free( self ); } mlt-6.20.0/src/modules/sdl/consumer_sdl.yml000066400000000000000000000024411362234133600206400ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: sdl title: SDL Fast YUV version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > Simple DirectMedia Layer audio and video output module. parameters: - identifier: argument title: Video Standard type: string description: The size of the window as WxH pixels. required: no - identifier: volume title: Volume type: float description: Audio level factor. mutable: yes - identifier: video_off title: Video off type: integer description: If 1, disable video output mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: audio_off title: Audio off type: integer description: If 1, disable audio output mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: audio_buffer title: Audio buffer type: integer description: Size of the sdl audio buffer. mutable: yes default: 2048 minimum: 128 - identifier: scrub_audio title: Audio scrubbing type: integer description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 1 widget: checkbox mlt-6.20.0/src/modules/sdl/consumer_sdl_audio.c000066400000000000000000000463551362234133600214560ustar00rootroot00000000000000/* * consumer_sdl_audio.c -- A Simple DirectMedia Layer audio-only consumer * Copyright (C) 2009-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include extern pthread_mutex_t mlt_sdl_mutex; /** 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; atomic_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; atomic_int playing; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; int is_purge; }; /** 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_purge( mlt_consumer parent ); static void consumer_close( mlt_consumer parent ); static void *consumer_thread( void * ); static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer self, char *name ); /** 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_audio_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the consumer object consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) ); // If no malloc'd and consumer init ok if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { // Create the queue self->queue = mlt_deque_init( ); // Get the parent consumer object mlt_consumer parent = &self->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 ); self->properties = MLT_SERVICE_PROPERTIES( service ); // Set the default volume mlt_properties_set_double( self->properties, "volume", 1.0 ); // This is the initialisation of the consumer pthread_mutex_init( &self->audio_mutex, NULL ); pthread_cond_init( &self->audio_cond, NULL); pthread_mutex_init( &self->video_mutex, NULL ); pthread_cond_init( &self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set( self->properties, "rescale", "nearest" ); mlt_properties_set( self->properties, "deinterlace_method", "onefield" ); mlt_properties_set_int( self->properties, "top_field_first", -1 ); // Default buffer for low latency mlt_properties_set_int( self->properties, "buffer", 1 ); // Default audio buffer mlt_properties_set_int( self->properties, "audio_buffer", 2048 ); #if defined(_WIN32) && SDL_MAJOR_VERSION == 2 mlt_properties_set( self->properties, "audio_driver", "DirectSound" ); #endif // Ensure we don't join on a non-running object self->joined = 1; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Initialize the refresh handler pthread_cond_init( &self->refresh_cond, NULL ); pthread_mutex_init( &self->refresh_mutex, NULL ); mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb ); // Return the consumer produced return parent; } // malloc or consumer init failed free( self ); // Indicate failure return NULL; } static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name ) { if ( !strcmp( name, "refresh" ) ) { consumer_sdl self = parent->child; pthread_mutex_lock( &self->refresh_mutex ); if ( self->refresh_count < 2 ) self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1; pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); } } int consumer_start( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( !self->running ) { consumer_stop( parent ); mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); char *audio_driver = mlt_properties_get( properties, "audio_driver" ); char *audio_device = mlt_properties_get( properties, "audio_device" ); if ( audio_driver && strcmp( audio_driver, "" ) ) setenv( "SDL_AUDIODRIVER", audio_driver, 1 ); if ( audio_device && strcmp( audio_device, "" ) ) setenv( "AUDIODEV", audio_device, 1 ); pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } self->running = 1; self->joined = 0; pthread_create( &self->thread, NULL, consumer_thread, self ); } return 0; } int consumer_stop( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; if ( self->running && !self->joined ) { // Kill the thread and clean up self->joined = 1; self->running = 0; // Unlatch the consumer thread pthread_mutex_lock( &self->refresh_mutex ); pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); // Cleanup the main thread #ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); // Unlatch the video thread pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); // Unlatch the audio callback pthread_mutex_lock( &self->audio_mutex ); pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); SDL_QuitSubSystem( SDL_INIT_AUDIO ); } return 0; } int consumer_is_stopped( mlt_consumer parent ) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( self->running ) { pthread_mutex_lock( &self->video_mutex ); mlt_frame frame = MLT_FRAME( mlt_deque_peek_back( self->queue ) ); // When playing rewind or fast forward then we need to keep one // frame in the queue to prevent playback stalling. double speed = frame? mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" ) : 0; int n = ( speed == 0.0 || speed == 1.0 ) ? 0 : 1; while ( mlt_deque_count( self->queue ) > n ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); self->is_purge = 1; pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); } } static void sdl_fill_audio( void *udata, uint8_t *stream, int len ) { consumer_sdl self = udata; // Get the volume double volume = mlt_properties_get_double( self->properties, "volume" ); // Wipe the stream first memset( stream, 0, len ); pthread_mutex_lock( &self->audio_mutex ); if ( self->audio_avail >= len ) { // Place in the audio buffer if ( volume != 1.0 ) SDL_MixAudio( stream, self->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); else memcpy( stream, self->audio_buffer, len ); // Remove len from the audio available self->audio_avail -= len; // Remove the samples memmove( self->audio_buffer, self->audio_buffer + len, self->audio_avail ); } else { // Mix the audio SDL_MixAudio( stream, self->audio_buffer, self->audio_avail, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); // No audio left self->audio_avail = 0; } // We're definitely playing now self->playing = 1; pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); } static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int *duration ) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // Set the preferred params of the test card signal int channels = mlt_properties_get_int( properties, "channels" ); int dest_channels = channels; int frequency = mlt_properties_get_int( properties, "frequency" ); int scrub = mlt_properties_get_int( properties, "scrub_audio" ); static int counter = 0; int samples = mlt_sample_calculator( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ ); int16_t *pcm; mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples ); *duration = ( ( samples * 1000 ) / frequency ); pcm += mlt_properties_get_int( properties, "audio_offset" ); if ( mlt_properties_get_int( properties, "audio_off" ) ) { self->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 ) ); self->playing = 0; request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = dest_channels; request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *)self; if ( SDL_OpenAudio( &request, &got ) != 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE( self ), "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 ); int samples_copied = 0; int dst_stride = dest_channels * sizeof( *pcm ); pthread_mutex_lock( &self->audio_mutex ); while ( self->running && samples_copied < samples ) { int sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; while ( self->running && sample_space == 0 ) { pthread_cond_wait( &self->audio_cond, &self->audio_mutex ); sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; } if ( self->running ) { int samples_to_copy = samples - samples_copied; if ( samples_to_copy > sample_space ) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 ) { if ( channels == dest_channels ) { memcpy( &self->audio_buffer[ self->audio_avail ], pcm, dst_bytes ); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ]; int i = samples_to_copy + 1; while ( --i ) { memcpy( dest, pcm, dst_stride ); pcm += channels; dest += dest_channels; } } } else { memset( &self->audio_buffer[ self->audio_avail ], 0, dst_bytes ); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast( &self->audio_cond ); } pthread_mutex_unlock( &self->audio_mutex ); } else { self->playing = 1; } return init_audio; } static int consumer_play_video( consumer_sdl self, mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = self->properties; mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); return 0; } static void *video_thread( void *arg ) { // Identify the arg consumer_sdl self = 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( self->properties, "real_time" ); // Get the current time gettimeofday( &now, NULL ); // Determine start time start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec; while ( self->running ) { // Pop the next frame pthread_mutex_lock( &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); while ( next == NULL && self->running ) { pthread_cond_wait( &self->video_cond, &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); } pthread_mutex_unlock( &self->video_mutex ); if ( !self->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 ) { // 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( self->queue ) < 2 ) ) consumer_play_video( self, next ); // If the queue is empty, recalculate start to allow build up again if ( real_time && ( mlt_deque_count( self->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; } // This consumer is stopping. But audio has already been played for all // the frames in the queue. Spit out all the frames so that the display has // the option to catch up with the audio. if ( next != NULL ) { consumer_play_video( self, next ); mlt_frame_close( next ); next = NULL; } while ( mlt_deque_count( self->queue ) > 0 ) { next = mlt_deque_pop_front( self->queue ); consumer_play_video( self, next ); mlt_frame_close( next ); next = NULL; } mlt_consumer_stopped( &self->parent ); return NULL; } /** Threaded wrapper for pipe. */ static void *consumer_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( consumer ); // Video thread pthread_t thread; // internal initialization 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 }; // int last_position = -1; pthread_mutex_lock( &self->refresh_mutex ); self->refresh_count = 0; pthread_mutex_unlock( &self->refresh_mutex ); // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame( consumer ); // Ensure that we have a frame if ( frame ) { // Get the frame properties properties = MLT_FRAME_PROPERTIES( frame ); // Get the speed of the frame double speed = mlt_properties_get_double( properties, "_speed" ); // Clear refresh mlt_events_block( consumer_props, consumer_props ); mlt_properties_set_int( consumer_props, "refresh", 0 ); mlt_events_unblock( consumer_props, consumer_props ); // Play audio init_audio = consumer_play_audio( self, frame, init_audio, &duration ); // Determine the start time now if ( self->playing && init_video ) { // Create the video thread pthread_create( &thread, NULL, video_thread, self ); // 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 ( self->running && speed != 0 && mlt_deque_count( self->queue ) > 15 ) nanosleep( &tm, NULL ); // Push this frame to the back of the queue if ( self->running && speed ) { pthread_mutex_lock( &self->video_mutex ); if ( self->is_purge && speed == 1.0 ) { mlt_frame_close( frame ); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back( self->queue, frame ); pthread_cond_broadcast( &self->video_cond ); } pthread_mutex_unlock( &self->video_mutex ); // Calculate the next playtime playtime += ( duration * 1000 ); } else if ( self->running ) { pthread_mutex_lock( &self->refresh_mutex ); consumer_play_video( self, frame ); mlt_frame_close( frame ); frame = NULL; self->refresh_count --; if ( self->refresh_count <= 0 ) { pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex ); } pthread_mutex_unlock( &self->refresh_mutex ); } // Optimisation to reduce latency if ( speed == 1.0 ) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else { mlt_consumer_purge( consumer ); // last_position = -1; } } } // Kill the video thread if ( init_video == 0 ) { pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); pthread_join( thread, NULL ); } if ( frame ) { // The video thread has cleared out the queue. But the audio was played // for this frame. So play the video before stopping so the display has // the option to catch up with the audio. consumer_play_video( self, frame ); mlt_frame_close( frame ); frame = NULL; } pthread_mutex_lock( &self->audio_mutex ); self->audio_avail = 0; pthread_mutex_unlock( &self->audio_mutex ); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer mlt_consumer_stop( parent ); // Now clean up the rest mlt_consumer_close( parent ); // Close the queue mlt_deque_close( self->queue ); // Destroy mutexes pthread_mutex_destroy( &self->audio_mutex ); pthread_cond_destroy( &self->audio_cond ); pthread_mutex_destroy( &self->video_mutex ); pthread_cond_destroy( &self->video_cond ); pthread_mutex_destroy( &self->refresh_mutex ); pthread_cond_destroy( &self->refresh_cond ); // Finally clean up this free( self ); } mlt-6.20.0/src/modules/sdl/consumer_sdl_audio.yml000066400000000000000000000017211362234133600220210ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: sdl_audio title: SDL Audio Only version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: > Simple DirectMedia Layer audio only output module. parameters: - identifier: volume title: Volume type: float description: Audio level factor. mutable: yes - identifier: audio_off title: Audio off type: integer description: If 1, disable audio output mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: audio_buffer title: Audio buffer type: integer description: Size of the sdl audio buffer. mutable: yes default: 2048 minimum: 128 - identifier: scrub_audio title: Audio scrubbing type: integer description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/sdl/consumer_sdl_osx.h000066400000000000000000000022371362234133600211620ustar00rootroot00000000000000/* * consumer_sdl_osx.m -- An OS X compatibility shim for SDL * Copyright (C) 2010-2014 Meltytech, LLC * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CONSUMER_SDL_OSX_H_ #define _CONSUMER_SDL_OSX_H_ #ifdef __APPLE__ void* mlt_cocoa_autorelease_init(); void mlt_cocoa_autorelease_close( void* ); #else static inline void *mlt_cocoa_autorelease_init() { return NULL; } static inline void mlt_cocoa_autorelease_close(void* p) { } #endif #endif mlt-6.20.0/src/modules/sdl/consumer_sdl_osx.m000066400000000000000000000056331362234133600211720ustar00rootroot00000000000000/* * consumer_sdl_osx.m -- An OS X compatibility shim for SDL * Copyright (C) 2010-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #import void* mlt_cocoa_autorelease_init() { return [[NSAutoreleasePool alloc] init]; } void mlt_cocoa_autorelease_close( void* p ) { NSAutoreleasePool* pool = ( NSAutoreleasePool* ) p; [pool release]; } #if 0 /* The code below is not used at this time - could not get it to work, but * it is based on code from gruntster on the avidemux project team. */ #import static NSWindow* nsWindow = nil; static NSQuickDrawView* nsView = nil; void sdl_cocoa_init(void* parent, int x, int y, int width, int height) { NSRect contentRect; contentRect = NSMakeRect(x, y, width, height); if (!nsWindow) { // initWithWindowRef always returns the same result for the same WindowRef nsWindow = [[NSWindow alloc] initWithWindowRef:(WindowRef)parent]; [nsWindow setContentView:[[[NSView alloc] initWithFrame:contentRect] autorelease]]; } if (!nsView) { nsView = [[NSQuickDrawView alloc] initWithFrame:contentRect]; [[nsWindow contentView] addSubview:nsView]; [nsView release]; [nsWindow orderOut:nil]; // very important, otherwise window won't be placed correctly on repeat showings [nsWindow orderFront:nil]; } else { [nsView setFrame:contentRect]; [[nsWindow contentView] setFrame:contentRect]; } // finally, set SDL environment variables with all this nonsense char SDL_windowhack[20]; sprintf(SDL_windowhack, "%d", (int)nsWindow); setenv("SDL_NSWindowPointer", SDL_windowhack, 1); sprintf(SDL_windowhack,"%d", (int)nsView); setenv("SDL_NSQuickDrawViewPointer", SDL_windowhack, 1); } void sdl_cocoa_close(void) { if (nsWindow) { // Reference count cannot fall below 2 because SDL releases the window when closing // and again when reinitialising (even though this is our own window...). if ([nsWindow retainCount] > 2) [nsWindow release]; // SDL takes care of all the destroying...a little too much, so make sure our Carbon // window is still displayed (via its Cocoa wrapper) [nsWindow makeKeyAndOrderFront:nil]; } } #endif mlt-6.20.0/src/modules/sdl/consumer_sdl_osx_hack.h000066400000000000000000000020751362234133600221500ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #import @interface DummyThread : NSObject - init; - (void)startThread:(id)arg; @end @implementation DummyThread - init { [super init]; return self; } - (void)startThread:(id)arg { return; } @end mlt-6.20.0/src/modules/sdl/consumer_sdl_preview.c000066400000000000000000000415271362234133600220320ustar00rootroot00000000000000/* * consumer_sdl_preview.c -- A Simple DirectMedia Layer consumer * Copyright (C) 2004-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include extern pthread_mutex_t mlt_sdl_mutex; 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; mlt_position last_position; 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_purge( 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 self, mlt_frame frame ); static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer self, SDL_Event *event ); static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer self, char *name ); mlt_consumer consumer_sdl_preview_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) ); if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { // Get the parent consumer object mlt_consumer parent = &self->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 self->play = mlt_factory_consumer( profile, "sdl", arg ); self->still = mlt_factory_consumer( profile, "sdl_still", arg ); mlt_properties_set( properties, "rescale", "nearest" ); mlt_properties_set( properties, "deinterlace_method", "onefield" ); mlt_properties_set_int( properties, "prefill", 1 ); mlt_properties_set_int( properties, "top_field_first", -1 ); parent->close = consumer_close; parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; self->joined = 1; mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->play ), self, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb ); mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->still ), self, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb ); mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->play ), self, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb ); mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->still ), self, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb ); pthread_cond_init( &self->refresh_cond, NULL ); pthread_mutex_init( &self->refresh_mutex, NULL ); mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb ); mlt_events_register( properties, "consumer-sdl-paused", NULL ); return parent; } free( self ); return NULL; } void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer parent, mlt_frame frame ) { consumer_sdl self = parent->child; self->last_speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ); self->last_position = mlt_frame_get_position( frame ); 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 self = parent->child; pthread_mutex_lock( &self->refresh_mutex ); self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1; pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); } } static int consumer_start( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( !self->running ) { // properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); mlt_properties play = MLT_CONSUMER_PROPERTIES( self->play ); mlt_properties still = MLT_CONSUMER_PROPERTIES( self->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 ); self->running = 1; self->joined = 0; self->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 ); pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 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, "deinterlace_method,resize,rescale,width,height,aspect_ratio,display_ratio,preview_off,preview_format,window_background" ",top_field_first,volume,real_time,buffer,prefill,audio_off,frequency,drop_max" ); mlt_properties_pass_list( still, properties, "deinterlace_method,resize,rescale,width,height,aspect_ratio,display_ratio,preview_off,preview_format,window_background" ",top_field_first"); 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 ); mlt_properties_set_int( play, "terminate_on_pause", 1 ); // Start the still producer just to initialise the gui mlt_consumer_start( self->still ); self->active = self->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( &self->thread, NULL, consumer_thread, self ); } return 0; } static int consumer_stop( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; if ( self->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 self->running = 0; pthread_mutex_lock( &self->refresh_mutex ); pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); #ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); self->joined = 1; if ( app_locked && lock ) lock( ); pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Quit( ); pthread_mutex_unlock( &mlt_sdl_mutex ); } return 0; } static int consumer_is_stopped( mlt_consumer parent ) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( self->running ) mlt_consumer_purge( self->play ); } static void *consumer_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // internal initialization mlt_frame frame = NULL; int last_position = -1; int eos = 0; int eos_threshold = 20; if ( self->play ) eos_threshold = eos_threshold + mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self->play ), "buffer" ); // Determine if the application is dealing with the preview int preview_off = mlt_properties_get_int( properties, "preview_off" ); pthread_mutex_lock( &self->refresh_mutex ); self->refresh_count = 0; pthread_mutex_unlock( &self->refresh_mutex ); // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = mlt_consumer_get_frame( consumer ); // Ensure that we have a frame if ( self->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( self->play ); last_position = mlt_frame_get_position( frame ); } else { //mlt_consumer_purge( self->play ); last_position = -1; } // If we aren't playing normally, then use the still if ( speed != 1 ) { mlt_producer producer = MLT_PRODUCER( mlt_service_get_producer( MLT_CONSUMER_SERVICE( consumer ) ) ); mlt_position duration = producer? mlt_producer_get_playtime( producer ) : -1; int pause = 0; #ifndef SKIP_WAIT_EOS if ( self->active == self->play ) { // Do not interrupt the play consumer near the end if ( duration - self->last_position > eos_threshold ) { // Get a new frame at the sought position mlt_frame_close( frame ); if ( producer ) mlt_producer_seek( producer, self->last_position ); frame = mlt_consumer_get_frame( consumer ); pause = 1; } else { // Send frame with speed 0 to stop it if ( frame && !mlt_consumer_is_stopped( self->play ) ) { mlt_consumer_put_frame( self->play, frame ); frame = NULL; eos = 1; } // Check for end of stream if ( mlt_consumer_is_stopped( self->play ) ) { // Stream has ended mlt_log_verbose( MLT_CONSUMER_SERVICE( consumer ), "END OF STREAM\n" ); pause = 1; eos = 0; // reset eos indicator } else { // Prevent a tight busy loop struct timespec tm = { 0, 100000L }; // 100 usec nanosleep( &tm, NULL ); } } } #else pause = self->active == self->play; #endif if ( pause ) { // Start the still consumer if ( !mlt_consumer_is_stopped( self->play ) ) mlt_consumer_stop( self->play ); self->last_speed = speed; self->active = self->still; self->ignore_change = 0; mlt_consumer_start( self->still ); } // Send the frame to the active child if ( frame && !eos ) { mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", 1 ); if ( self->active ) mlt_consumer_put_frame( self->active, frame ); } if ( pause && speed == 0.0 ) { mlt_events_fire( properties, "consumer-sdl-paused", NULL ); } } // Allow a little grace time before switching consumers on speed changes else if ( self->ignore_change -- > 0 && self->active != NULL && !mlt_consumer_is_stopped( self->active ) ) { mlt_consumer_put_frame( self->active, frame ); } // Otherwise use the normal player else { if ( !mlt_consumer_is_stopped( self->still ) ) mlt_consumer_stop( self->still ); if ( mlt_consumer_is_stopped( self->play ) ) { self->last_speed = speed; self->active = self->play; self->ignore_change = 0; mlt_consumer_start( self->play ); } if ( self->play ) mlt_consumer_put_frame( self->play, frame ); } // Copy the rectangle info from the active consumer if ( self->running && preview_off == 0 && self->active ) { mlt_properties active = MLT_CONSUMER_PROPERTIES( self->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 ( self->active == self->still ) { pthread_mutex_lock( &self->refresh_mutex ); if ( self->running && speed == 0 && self->refresh_count <= 0 ) { mlt_events_fire( properties, "consumer-sdl-paused", NULL ); pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex ); } self->refresh_count --; pthread_mutex_unlock( &self->refresh_mutex ); } } else { if ( frame ) mlt_frame_close( frame ); mlt_consumer_put_frame( self->active, NULL ); self->running = 0; } } if ( self->play ) mlt_consumer_stop( self->play ); if ( self->still ) mlt_consumer_stop( self->still ); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer mlt_consumer_stop( parent ); // Close the child consumers mlt_consumer_close( self->play ); mlt_consumer_close( self->still ); // Now clean up the rest mlt_consumer_close( parent ); // Finally clean up self free( self ); } mlt-6.20.0/src/modules/sdl/consumer_sdl_preview.yml000066400000000000000000000002631362234133600224010ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: sdl title: SDL version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio - Video mlt-6.20.0/src/modules/sdl/consumer_sdl_still.c000066400000000000000000000450601362234133600214740ustar00rootroot00000000000000/* * consumer_sdl_still.c -- A Simple DirectMedia Layer consumer * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "consumer_sdl_osx.h" extern pthread_mutex_t mlt_sdl_mutex; /** 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; atomic_int running; int window_width; int window_height; int width; int height; atomic_int playing; int sdl_flags; SDL_Rect rect; uint8_t *buffer; int last_position; mlt_producer last_producer; }; /** 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( 1, sizeof( struct consumer_sdl_s ) ); // 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" ); // 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" ); 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 + 0.5; this->window_height = this->height; if ( sdl_started == 0 && preview_off == 0 ) { pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 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 ); } pthread_mutex_lock( &mlt_sdl_mutex ); if ( !SDL_GetVideoSurface() && preview_off == 0 ) SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags ); pthread_mutex_unlock( &mlt_sdl_mutex ); 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 ) { pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Quit( ); pthread_mutex_unlock( &mlt_sdl_mutex ); } } return 0; } static int consumer_is_stopped( mlt_consumer parent ) { consumer_sdl this = parent->child; return !this->running; } static int sdl_lock_display( ) { pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Surface *screen = SDL_GetVideoSurface( ); int result = screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 ); pthread_mutex_unlock( &mlt_sdl_mutex ); return result; } static void sdl_unlock_display( ) { pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Surface *screen = SDL_GetVideoSurface( ); if ( screen != NULL && SDL_MUSTLOCK( screen ) ) SDL_UnlockSurface( screen ); pthread_mutex_unlock( &mlt_sdl_mutex ); } 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 * 4; 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 ) * 4 ); *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 * 4; 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 ) * 4 ); *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 * 4; 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 ) * 4 ); 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 * 4; 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 ) * 4 ); *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_rgb24a; 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( ); void *pool = mlt_cocoa_autorelease_init(); sdl_lock_display(); // Handle events if ( SDL_GetVideoSurface() ) { SDL_Event event; pthread_mutex_lock( &mlt_sdl_mutex ); changed = consumer_get_dimensions( &this->window_width, &this->window_height ); pthread_mutex_unlock( &mlt_sdl_mutex ); 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 ( !SDL_GetVideoSurface() || changed ) { // open SDL window pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Surface *screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags ); if ( consumer_get_dimensions( &this->window_width, &this->window_height ) ) screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags ); if ( screen ) { uint32_t color = mlt_properties_get_int( this->properties, "window_background" ); SDL_FillRect( screen, NULL, color >> 8 ); changed = 1; } pthread_mutex_unlock( &mlt_sdl_mutex ); } mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "test_audio", 1 ); if ( changed == 0 && this->last_position == mlt_frame_get_position( frame ) && this->last_producer == mlt_frame_get_original_producer( frame ) && !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "refresh" ) ) { sdl_unlock_display( ); mlt_cocoa_autorelease_close( pool ); if ( unlock != NULL ) unlock( ); struct timespec tm = { 0, 100000 }; nanosleep( &tm, NULL ); return 0; } // Update last frame shown info this->last_position = mlt_frame_get_position( frame ); this->last_producer = mlt_frame_get_original_producer( frame ); // Get the image, width and height mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); 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 ); } pthread_mutex_lock( &mlt_sdl_mutex ); SDL_Surface *screen = SDL_GetVideoSurface( ); if ( !mlt_consumer_is_stopped( &this->parent ) && screen && screen->pixels ) { switch( screen->format->BytesPerPixel ) { case 1: display_1( screen, this->rect, image, width, height ); break; case 2: display_2( screen, this->rect, image, width, height ); break; case 3: display_3( screen, this->rect, image, width, height ); break; case 4: display_4( screen, this->rect, image, width, height ); break; default: fprintf( stderr, "Unsupported video depth %d\n", screen->format->BytesPerPixel ); break; } // Flip it into sight SDL_Flip( screen ); } pthread_mutex_unlock( &mlt_sdl_mutex ); sdl_unlock_display(); mlt_cocoa_autorelease_close( pool ); if ( unlock != NULL ) unlock( ); mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); 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 ); mlt_frame frame = NULL; // Allow the hosting app to provide the preview int preview_off = mlt_properties_get_int( properties, "preview_off" ); // 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_image_format vfmt = mlt_image_rgb24a; int height = this->height; int width = this->width; uint8_t *image = NULL; 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; 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 ); #ifndef __APPLE__ // Get the wm structure if ( SDL_GetWMInfo( &wm ) == 1 ) { #ifndef _WIN32 // 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 } #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 ); } mlt-6.20.0/src/modules/sdl/consumer_sdl_still.yml000066400000000000000000000002631362234133600220470ustar00rootroot00000000000000schema_version: 0.1 type: consumer identifier: sdl_still title: SDL RGB version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Video mlt-6.20.0/src/modules/sdl/factory.c000066400000000000000000000051021362234133600172300ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #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_audio_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 static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/sdl/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "sdl", consumer_sdl_init ); MLT_REGISTER_METADATA( consumer_type, "sdl", metadata, "consumer_sdl.yml" ); MLT_REGISTER( consumer_type, "sdl_audio", consumer_sdl_audio_init ); MLT_REGISTER_METADATA( consumer_type, "sdl_audio", metadata, "consumer_sdl_audio.yml" ); MLT_REGISTER( consumer_type, "sdl_preview", consumer_sdl_preview_init ); MLT_REGISTER_METADATA( consumer_type, "sdl_preview", metadata, "consumer_sdl_preview.yml" ); MLT_REGISTER( consumer_type, "sdl_still", consumer_sdl_still_init ); MLT_REGISTER_METADATA( consumer_type, "sdl_still", metadata, "consumer_sdl_still.yml" ); #ifdef WITH_SDL_IMAGE MLT_REGISTER( producer_type, "sdl_image", producer_sdl_image_init ); MLT_REGISTER_METADATA( producer_type, "sdl_image", metadata, "consumer_sdl_image.yml" ); #endif } mlt-6.20.0/src/modules/sdl/producer_sdl_image.c000066400000000000000000000167311362234133600214220ustar00rootroot00000000000000/* * producer_sdl_image.c -- Image loader which wraps SDL_image * Copyright (C) 2005-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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; *width = surface->w; *height = surface->h; int image_size = *width * *height * 3; 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: *format = mlt_image_rgb24a; image_size = *width * *height * 4; *image = mlt_pool_alloc( image_size ); memcpy( *image, surface->pixels, image_size ); break; default: *format = mlt_image_rgb24; *image = mlt_pool_alloc( image_size ); memcpy( *image, surface->pixels, image_size ); break; } if ( converted ) SDL_FreeSurface( converted ); // Update the frame mlt_frame_set_image( frame, *image, image_size, mlt_pool_release ); 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, "_filenames", filenames, 0, ( mlt_destructor )mlt_properties_close, 0 ); 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, "meta.media.width", surface->w ); mlt_properties_set_int( properties, "meta.media.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 ); // 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; } mlt-6.20.0/src/modules/sdl/producer_sdl_image.yml000066400000000000000000000003071362234133600217710ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: sdl_image title: SDL Image version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en notes: DEPRECATED tags: - Video mlt-6.20.0/src/modules/sdl2/000077500000000000000000000000001362234133600155015ustar00rootroot00000000000000mlt-6.20.0/src/modules/sdl2/CMakeLists.txt000066400000000000000000000005231362234133600202410ustar00rootroot00000000000000if(TARGET sdl2) file(GLOB mltsdl2_src *.c) add_library(mltsdl2 MODULE ${mltsdl2_src}) target_link_libraries(mltsdl2 mlt m Threads::Threads sdl2) install(TARGETS mltsdl2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/sdl2) endif() mlt-6.20.0/src/modules/sdl2/Makefile000066400000000000000000000017671362234133600171540ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread -lm include ../../../config.mak include config.mak TARGET = ../libmltsdl2$(LIBSUF) OBJS = factory.o \ common.o \ consumer_sdl2.o \ consumer_sdl2_audio.o ifeq ($(targetos),Darwin) CFLAGS += -ObjC LDFLAGS += -lobjc -framework Foundation else ifneq ($(targetos), MinGW) LDFLAGS += -lX11 endif ifeq ($(USE_PKG_CONFIG), 1) CFLAGS += $(shell pkg-config --cflags sdl2) LDFLAGS += $(shell pkg-config --libs sdl2) else CFLAGS += $(shell sdl2-config --cflags) LDFLAGS += $(shell sdl2-config --libs) 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/sdl2" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/sdl2" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/sdl2/common.c000066400000000000000000000044231362234133600171400ustar00rootroot00000000000000/* * common.h * Copyright (C) 2018 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include SDL_AudioDeviceID sdl2_open_audio( const SDL_AudioSpec* desired, SDL_AudioSpec* obtained ) { SDL_AudioDeviceID dev = 0; // First try to open using default/user requested driver. dev = SDL_OpenAudioDevice( NULL, 0, desired, obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE ); if( dev == 0 ) { mlt_log_info( NULL, "Failed to open audio device: %s\n", SDL_GetError() ); // Try alternative drivers. int i = 0; int driver_count = SDL_GetNumAudioDrivers(); for( i = 0; i < driver_count; i++ ) { const char* driver = SDL_GetAudioDriver( i ); if( strcmp( driver, "disk" ) == 0 || strcmp( driver, "dummy" ) == 0 ) { continue; } if( SDL_AudioInit( driver ) != 0 ) { continue; } mlt_log_info( NULL, "[sdl2] Try alternative driver: %s\n", driver ); dev = SDL_OpenAudioDevice( NULL, 0, desired, obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE ); if( dev != 0 ) { break; } else { mlt_log_info( NULL, "[sdl2] Open failed: %s\n", SDL_GetError() ); } } } if( dev == 0 && desired->channels > 2 ) { // All drivers have failed to open with the provided spec. // Try stereo channels since all drivers support that. mlt_log_info( NULL, "Failed to open surround device. Try stereo instead\n" ); SDL_AudioSpec desired_copy = *desired; desired_copy.channels = 2; SDL_AudioInit( NULL ); dev = sdl2_open_audio( &desired_copy, obtained ); } return dev; }mlt-6.20.0/src/modules/sdl2/common.h000066400000000000000000000017711362234133600171500ustar00rootroot00000000000000/* * common.h * Copyright (C) 2018 Meltytech, LLC * Author: Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMON_H #define COMMON_H #include SDL_AudioDeviceID sdl2_open_audio( const SDL_AudioSpec* desired, SDL_AudioSpec* obtained ); #endif // COMMON_H mlt-6.20.0/src/modules/sdl2/configure000077500000000000000000000007321362234133600174120ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ]; then if pkg-config --exists sdl2 > /dev/null 2>&1 ; then echo " - using SDL version $(pkg-config --modversion sdl2)" echo "USE_PKG_CONFIG=1" > config.mak echo "HAVE_SDL2=1" >> config.mak elif sdl2-config --version > /dev/null 2>&1 ; then echo " - using SDL version $(sdl2-config --version)" echo "USE_PKG_CONFIG=0" > config.mak else echo "- SDL2 development libs not found: disabling" touch ../disable-sdl2 fi exit 0 fi mlt-6.20.0/src/modules/sdl2/consumer_sdl2.c000066400000000000000000000651151362234133600204340ustar00rootroot00000000000000/* * consumer_sdl.c -- A Simple DirectMedia Layer consumer * Copyright (C) 2017-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #undef MLT_IMAGE_FORMAT // only yuv422 working currently extern pthread_mutex_t mlt_sdl_mutex; /** 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; atomic_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 out_channels; atomic_int playing; SDL_Window *sdl_window; SDL_Renderer *sdl_renderer; SDL_Texture *sdl_texture; SDL_Rect sdl_rect; uint8_t *buffer; int is_purge; #ifdef _WIN32 int no_quit_subsystem; #endif }; /** 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_purge( mlt_consumer parent ); static void consumer_close( mlt_consumer parent ); static void *consumer_thread( void * ); static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ); static int setup_sdl_video( consumer_sdl self ); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_sdl2_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the consumer object consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) ); // If no malloc'd and consumer init ok if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { // Create the queue self->queue = mlt_deque_init( ); // Get the parent consumer object mlt_consumer parent = &self->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 ); self->properties = MLT_SERVICE_PROPERTIES( service ); // Set the default volume mlt_properties_set_double( self->properties, "volume", 1.0 ); // This is the initialisation of the consumer pthread_mutex_init( &self->audio_mutex, NULL ); pthread_cond_init( &self->audio_cond, NULL); pthread_mutex_init( &self->video_mutex, NULL ); pthread_cond_init( &self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set( self->properties, "rescale", "nearest" ); mlt_properties_set( self->properties, "deinterlace_method", "onefield" ); mlt_properties_set_int( self->properties, "top_field_first", -1 ); // Default buffer for low latency mlt_properties_set_int( self->properties, "buffer", 1 ); // Default audio buffer mlt_properties_set_int( self->properties, "audio_buffer", 2048 ); // Default scrub audio mlt_properties_set_int( self->properties, "scrub_audio", 1 ); // Ensure we don't join on a non-running object self->joined = 1; // process actual param if ( arg && sscanf( arg, "%dx%d", &self->width, &self->height ) ) { mlt_properties_set_int( self->properties, "resolution", 1 ); } else { self->width = mlt_properties_get_int( self->properties, "width" ); self->height = mlt_properties_get_int( self->properties, "height" ); } // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Register specific events mlt_events_register( self->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event ); // Return the consumer produced return parent; } // malloc or consumer init failed free( self ); // Indicate failure return NULL; } static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service self, void **args ) { if ( listener != NULL ) listener( owner, self, ( SDL_Event * )args[ 0 ] ); } int consumer_start( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( !self->running ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); int audio_off = mlt_properties_get_int( properties, "audio_off" ); char *output_display = mlt_properties_get( properties, "output_display" ); 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" ); consumer_stop( parent ); self->running = 1; self->joined = 0; 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 ( ! mlt_properties_get_int( self->properties, "resolution" ) ) { if ( mlt_properties_get_int( self->properties, "width" ) > 0 ) self->width = mlt_properties_get_int( self->properties, "width" ); if ( mlt_properties_get_int( self->properties, "height" ) > 0 ) self->height = mlt_properties_get_int( self->properties, "height" ); } if ( audio_off == 0 ) SDL_InitSubSystem( SDL_INIT_AUDIO ); // Default window size if ( mlt_properties_get_int( self->properties, "resolution" ) ) { self->window_width = self->width; self->window_height = self->height; } else { double display_ratio = mlt_properties_get_double( self->properties, "display_ratio" ); self->window_width = ( double )self->height * display_ratio + 0.5; self->window_height = self->height; } // Initialize SDL video if needed. if ( setup_sdl_video(self) ) return 1; pthread_create( &self->thread, NULL, consumer_thread, self ); } return 0; } int consumer_stop( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; if ( self->joined == 0 ) { // Kill the thread and clean up self->joined = 1; self->running = 0; if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "audio_off" ) ) { pthread_mutex_lock( &self->audio_mutex ); pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); } #ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); // cleanup SDL pthread_mutex_lock( &mlt_sdl_mutex ); if ( self->sdl_texture ) SDL_DestroyTexture( self->sdl_texture ); self->sdl_texture = NULL; if ( self->sdl_renderer ) SDL_DestroyRenderer( self->sdl_renderer ); self->sdl_renderer = NULL; if ( self->sdl_window ) SDL_DestroyWindow( self->sdl_window ); self->sdl_window = NULL; #ifdef _WIN32 if ( !self->no_quit_subsystem ) #endif if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "audio_off" ) ) SDL_QuitSubSystem( SDL_INIT_AUDIO ); if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" ) == 0 ) SDL_Quit( ); pthread_mutex_unlock( &mlt_sdl_mutex ); } return 0; } int consumer_is_stopped( mlt_consumer parent ) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( self->running ) { pthread_mutex_lock( &self->video_mutex ); while ( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); self->is_purge = 1; pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); } } static void sdl_fill_audio( void *udata, uint8_t *stream, int len ) { consumer_sdl self = udata; // Get the volume double volume = mlt_properties_get_double( self->properties, "volume" ); // Wipe the stream first memset( stream, 0, len ); pthread_mutex_lock( &self->audio_mutex ); // Block until audio received while ( self->running && len > self->audio_avail ) pthread_cond_wait( &self->audio_cond, &self->audio_mutex ); if ( self->audio_avail >= len ) { // Place in the audio buffer if ( volume != 1.0 ) SDL_MixAudio( stream, self->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); else memcpy( stream, self->audio_buffer, len ); // Remove len from the audio available self->audio_avail -= len; // Remove the samples memmove( self->audio_buffer, self->audio_buffer + len, self->audio_avail ); } else { // Mix the audio SDL_MixAudio( stream, self->audio_buffer, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) ); // No audio left self->audio_avail = 0; } // We're definitely playing now self->playing = 1; pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); } static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int *duration ) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // 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" ); int scrub = mlt_properties_get_int( properties, "scrub_audio" ); static int counter = 0; int samples = mlt_sample_calculator( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ ); int16_t *pcm; mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples ); *duration = ( ( samples * 1000 ) / frequency ); pcm += mlt_properties_get_int( properties, "audio_offset" ); if ( mlt_properties_get_int( properties, "audio_off" ) ) { self->playing = 1; init_audio = 1; return init_audio; } if ( init_audio == 1 ) { SDL_AudioSpec request; SDL_AudioSpec got; SDL_AudioDeviceID dev; int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" ); // specify audio format memset( &request, 0, sizeof( SDL_AudioSpec ) ); self->playing = 0; request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = mlt_properties_get_int( properties, "channels" ); request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *)self; dev = sdl2_open_audio( &request, &got ); if( dev == 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE( self ), "SDL failed to open audio\n" ); init_audio = 2; } else { if( got.channels != request.channels ) { mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Unable to output %d channels. Change to %d\n", request.channels, got.channels ); } mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Audio Opened: driver=%s channels=%d frequency=%d\n", SDL_GetCurrentAudioDriver(), got.channels, got.freq ); SDL_PauseAudioDevice( dev, 0 ); init_audio = 0; self->out_channels = got.channels; } } if ( init_audio == 0 ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int samples_copied = 0; int dst_stride = self->out_channels * sizeof( *pcm ); pthread_mutex_lock( &self->audio_mutex ); while ( self->running && samples_copied < samples ) { int sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; while ( self->running && sample_space == 0 ) { struct timeval now; struct timespec tm; gettimeofday( &now, NULL ); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait( &self->audio_cond, &self->audio_mutex, &tm ); sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; if ( sample_space == 0 && self->running ) { mlt_log_warning( MLT_CONSUMER_SERVICE(&self->parent), "audio timed out\n" ); pthread_mutex_unlock( &self->audio_mutex ); #ifdef _WIN32 self->no_quit_subsystem = 1; #endif return 1; } } if ( self->running ) { int samples_to_copy = samples - samples_copied; if ( samples_to_copy > sample_space ) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 ) { if ( channels == self->out_channels ) { memcpy( &self->audio_buffer[ self->audio_avail ], pcm, dst_bytes ); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ]; int i = samples_to_copy + 1; while ( --i ) { memcpy( dest, pcm, dst_stride ); pcm += channels; dest += self->out_channels; } } } else { memset( &self->audio_buffer[ self->audio_avail ], 0, dst_bytes ); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast( &self->audio_cond ); } pthread_mutex_unlock( &self->audio_mutex ); } else { self->playing = 1; } return init_audio; } static int setup_sdl_video( consumer_sdl self ) { int error = 0; int sdl_flags = SDL_WINDOW_RESIZABLE; int texture_format = SDL_PIXELFORMAT_YUY2; // Skip this if video is disabled. int video_off = mlt_properties_get_int( self->properties, "video_off" ); int preview_off = mlt_properties_get_int( self->properties, "preview_off" ); if ( video_off || preview_off ) return error; if (!SDL_WasInit(SDL_INIT_VIDEO)) { pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_VIDEO ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } } #ifdef MLT_IMAGE_FORMAT int image_format = mlt_properties_get_int( self->properties, "mlt_image_format" ); if ( image_format ) switch ( image_format ) { case mlt_image_rgb24: texture_format = SDL_PIXELFORMAT_RGB24; break; case mlt_image_rgb24a: texture_format = SDL_PIXELFORMAT_ABGR8888; break; case mlt_image_yuv420p: texture_format = SDL_PIXELFORMAT_IYUV; break; case mlt_image_yuv422: texture_format = SDL_PIXELFORMAT_YUY2; break; default: mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Invalid image format %s\n", mlt_image_format_name( image_format ) ); return -1; } #endif if ( mlt_properties_get_int( self->properties, "fullscreen" ) ) { self->window_width = self->width; self->window_height = self->height; sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; SDL_ShowCursor( SDL_DISABLE ); } pthread_mutex_lock( &mlt_sdl_mutex ); self->sdl_window = SDL_CreateWindow("MLT", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, self->window_width, self->window_height, sdl_flags); self->sdl_renderer = SDL_CreateRenderer(self->sdl_window, -1, SDL_RENDERER_ACCELERATED); if ( self->sdl_renderer ) { // Get texture width and height from the profile. int width = mlt_properties_get_int( self->properties, "width" ); int height = mlt_properties_get_int( self->properties, "height" ); self->sdl_texture = SDL_CreateTexture( self->sdl_renderer, texture_format, SDL_TEXTUREACCESS_STREAMING, width, height ); if ( self->sdl_texture ) { SDL_SetRenderDrawColor( self->sdl_renderer, 0, 0, 0, 255); } else { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL texture: %s\n", SDL_GetError() ); error = -1; } } else { mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL renderer: %s\n", SDL_GetError() ); error = -1; } pthread_mutex_unlock( &mlt_sdl_mutex ); return error; } static int consumer_play_video( consumer_sdl self, mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = self->properties; #ifdef MLT_IMAGE_FORMAT mlt_image_format vfmt = mlt_properties_get_int( properties, "mlt_image_format" ); #else mlt_image_format vfmt = mlt_image_yuv422; #endif int width = self->width, height = self->height; uint8_t *image; int video_off = mlt_properties_get_int( properties, "video_off" ); int preview_off = mlt_properties_get_int( properties, "preview_off" ); int display_off = video_off | preview_off; if ( self->running && !display_off ) { // Get the image, width and height mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); if ( self->running ) { // Determine window's new display aspect ratio int x = mlt_properties_get_int( properties, "window_width" ); if ( x && x != self->window_width ) self->window_width = x; x = mlt_properties_get_int( properties, "window_height" ); if ( x && x != self->window_height ) self->window_height = x; double this_aspect = ( double )self->window_width / self->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 self->width = width; self->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 self->sdl_rect.w = frame_aspect / this_aspect * self->window_width; self->sdl_rect.h = self->window_height; if ( self->sdl_rect.w > self->window_width ) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = this_aspect / frame_aspect * self->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 ) ) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = self->window_height; } // Use hardware scaler to normalise sample aspect ratio else if ( self->window_height * display_ratio > self->window_width ) { self->sdl_rect.w = self->window_width; self->sdl_rect.h = self->window_width / display_ratio; } else { self->sdl_rect.w = self->window_height * display_ratio; self->sdl_rect.h = self->window_height; } self->sdl_rect.x = ( self->window_width - self->sdl_rect.w ) / 2; self->sdl_rect.y = ( self->window_height - self->sdl_rect.h ) / 2; self->sdl_rect.x -= self->sdl_rect.x % 2; mlt_properties_set_int( self->properties, "rect_x", self->sdl_rect.x ); mlt_properties_set_int( self->properties, "rect_y", self->sdl_rect.y ); mlt_properties_set_int( self->properties, "rect_w", self->sdl_rect.w ); mlt_properties_set_int( self->properties, "rect_h", self->sdl_rect.h ); } if ( self->running && image ) { unsigned char* planes[4]; int strides[4]; // We use height-1 because mlt_image_format_size() uses height + 1. // XXX Remove -1 when mlt_image_format_size() is changed. mlt_image_format_planes( vfmt, width, height - 1, image, planes, strides ); if ( strides[1] ) { SDL_UpdateYUVTexture( self->sdl_texture, NULL, planes[0], strides[0], planes[1], strides[1], planes[2], strides[2] ); } else { SDL_UpdateTexture( self->sdl_texture, NULL, planes[0], strides[0] ); } SDL_RenderClear( self->sdl_renderer ); SDL_RenderCopy( self->sdl_renderer, self->sdl_texture, NULL, &self->sdl_rect ); SDL_RenderPresent( self->sdl_renderer ); } mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } else if ( self->running ) { if ( !video_off ) { mlt_image_format preview_format = mlt_properties_get_int( properties, "preview_format" ); vfmt = preview_format == mlt_image_none ? mlt_image_rgb24a : preview_format; mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); } mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); } return 0; } static void *video_thread( void *arg ) { // Identify the arg consumer_sdl self = 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( self->properties, "real_time" ); // Determine start time gettimeofday( &now, NULL ); start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec; while ( self->running ) { // Pop the next frame pthread_mutex_lock( &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); while ( next == NULL && self->running ) { pthread_cond_wait( &self->video_cond, &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); } pthread_mutex_unlock( &self->video_mutex ); if ( !self->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 && self->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( self->queue ) < 2 ) ) consumer_play_video( self, next ); // If the queue is empty, recalculate start to allow build up again if ( real_time && ( mlt_deque_count( self->queue ) == 0 && speed == 1.0 ) ) { gettimeofday( &now, NULL ); start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000; } } else { static int dropped = 0; mlt_log_info( MLT_CONSUMER_SERVICE(&self->parent), "dropped video frame %d\n", ++dropped ); } // This frame can now be closed mlt_frame_close( next ); next = NULL; } if ( next != NULL ) mlt_frame_close( next ); mlt_consumer_stopped( &self->parent ); return NULL; } static void *consumer_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->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 initialization int init_audio = 1; int init_video = 1; mlt_frame frame = NULL; int duration = 0; int64_t playtime = 0; struct timespec tm = { 0, 100000 }; // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = !terminated? mlt_consumer_rt_frame( consumer ) : NULL; // Check for termination if ( terminate_on_pause && frame ) terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0; // Ensure that we have a frame if ( frame ) { // Play audio init_audio = consumer_play_audio( self, frame, init_audio, &duration ); // Determine the start time now if ( self->playing && init_video ) { // Create the video thread pthread_create( &thread, NULL, video_thread, self ); // Video doesn't need to be initialised any more init_video = 0; } // Set playtime for this frame mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "playtime", playtime ); while ( self->running && mlt_deque_count( self->queue ) > 15 ) nanosleep( &tm, NULL ); // Push this frame to the back of the queue pthread_mutex_lock( &self->video_mutex ); if ( self->is_purge ) { mlt_frame_close( frame ); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back( self->queue, frame ); pthread_cond_broadcast( &self->video_cond ); } pthread_mutex_unlock( &self->video_mutex ); // Calculate the next playtime playtime += ( duration * 1000 ); } else if ( terminated ) { if ( init_video || mlt_deque_count( self->queue ) == 0 ) break; else nanosleep( &tm, NULL ); } } self->running = 0; // Unblock sdl_preview if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "put_mode" ) && mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "put_pending" ) ) { frame = mlt_consumer_get_frame( consumer ); if ( frame ) mlt_frame_close( frame ); frame = NULL; } // Kill the video thread if ( init_video == 0 ) { pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); pthread_join( thread, NULL ); } while( mlt_deque_count( self->queue ) ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); pthread_mutex_lock( &self->audio_mutex ); self->audio_avail = 0; pthread_mutex_unlock( &self->audio_mutex ); return NULL; } static void consumer_close( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer ///mlt_consumer_stop( parent ); // Now clean up the rest mlt_consumer_close( parent ); // Close the queue mlt_deque_close( self->queue ); // Destroy mutexes pthread_mutex_destroy( &self->audio_mutex ); pthread_cond_destroy( &self->audio_cond ); // Finally clean up this free( self ); } mlt-6.20.0/src/modules/sdl2/consumer_sdl2.yml000066400000000000000000000022631362234133600210060ustar00rootroot00000000000000schema_version: 0.3 type: consumer identifier: sdl2 title: SDL2 version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > Simple DirectMedia Layer audio and video output module parameters: - identifier: resolution title: Resolution type: string description: The size of the window as WxH pixels argument: yes required: no - identifier: volume title: Volume type: float description: Audio level factor mutable: yes - identifier: video_off title: Video off type: boolean description: Disable video output mutable: yes default: 0 widget: checkbox - identifier: audio_off title: Audio off type: boolean description: Disable audio output mutable: yes default: 0 widget: checkbox - identifier: audio_buffer title: Audio buffer type: integer description: Size of the SDL audio buffer mutable: yes default: 2048 minimum: 128 - identifier: scrub_audio title: Audio scrubbing type: boolean description: Play sound even when the speed is not normal. mutable: yes default: 1 widget: checkbox mlt-6.20.0/src/modules/sdl2/consumer_sdl2_audio.c000066400000000000000000000477661362234133600216310ustar00rootroot00000000000000/* * consumer_sdl2_audio.c -- A Simple DirectMedia Layer audio-only consumer * Copyright (C) 2009-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #include #include extern pthread_mutex_t mlt_sdl_mutex; /** 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; atomic_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 out_channels; atomic_int playing; pthread_cond_t refresh_cond; pthread_mutex_t refresh_mutex; int refresh_count; int is_purge; #ifdef _WIN32 int no_quit_subsystem; #endif }; /** 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_purge( mlt_consumer parent ); static void consumer_close( mlt_consumer parent ); static void *consumer_thread( void * ); static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer self, char *name ); /** This is what will be called by the factory - anything can be passed in via the argument, but keep it simple. */ mlt_consumer consumer_sdl2_audio_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the consumer object consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) ); // If no malloc'd and consumer init ok if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 ) { // Create the queue self->queue = mlt_deque_init( ); // Get the parent consumer object mlt_consumer parent = &self->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 ); self->properties = MLT_SERVICE_PROPERTIES( service ); // Set the default volume mlt_properties_set_double( self->properties, "volume", 1.0 ); // This is the initialisation of the consumer pthread_mutex_init( &self->audio_mutex, NULL ); pthread_cond_init( &self->audio_cond, NULL); pthread_mutex_init( &self->video_mutex, NULL ); pthread_cond_init( &self->video_cond, NULL); // Default scaler (for now we'll use nearest) mlt_properties_set( self->properties, "rescale", "nearest" ); mlt_properties_set( self->properties, "deinterlace_method", "onefield" ); mlt_properties_set_int( self->properties, "top_field_first", -1 ); // Default buffer for low latency mlt_properties_set_int( self->properties, "buffer", 1 ); // Default audio buffer mlt_properties_set_int( self->properties, "audio_buffer", 2048 ); // Ensure we don't join on a non-running object self->joined = 1; // Allow thread to be started/stopped parent->start = consumer_start; parent->stop = consumer_stop; parent->is_stopped = consumer_is_stopped; parent->purge = consumer_purge; // Initialize the refresh handler pthread_cond_init( &self->refresh_cond, NULL ); pthread_mutex_init( &self->refresh_mutex, NULL ); mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb ); // Return the consumer produced return parent; } // malloc or consumer init failed free( self ); // Indicate failure return NULL; } static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name ) { if ( !strcmp( name, "refresh" ) ) { consumer_sdl self = parent->child; pthread_mutex_lock( &self->refresh_mutex ); if ( self->refresh_count < 2 ) self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1; pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); } } int consumer_start( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( !self->running ) { consumer_stop( parent ); mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent ); char *audio_driver = mlt_properties_get( properties, "audio_driver" ); char *audio_device = mlt_properties_get( properties, "audio_device" ); if ( audio_driver && strcmp( audio_driver, "" ) ) setenv( "SDL_AUDIODRIVER", audio_driver, 1 ); if ( audio_device && strcmp( audio_device, "" ) ) setenv( "AUDIODEV", audio_device, 1 ); pthread_mutex_lock( &mlt_sdl_mutex ); int ret = SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ); pthread_mutex_unlock( &mlt_sdl_mutex ); if ( ret < 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError() ); return -1; } self->running = 1; self->joined = 0; pthread_create( &self->thread, NULL, consumer_thread, self ); } return 0; } int consumer_stop( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; if ( self->running && !self->joined ) { // Kill the thread and clean up self->joined = 1; self->running = 0; // Unlatch the consumer thread pthread_mutex_lock( &self->refresh_mutex ); pthread_cond_broadcast( &self->refresh_cond ); pthread_mutex_unlock( &self->refresh_mutex ); // Cleanup the main thread #ifndef _WIN32 if ( self->thread ) #endif pthread_join( self->thread, NULL ); // Unlatch the video thread pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); // Unlatch the audio callback pthread_mutex_lock( &self->audio_mutex ); pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); #ifdef _WIN32 if ( !self->no_quit_subsystem ) #endif SDL_QuitSubSystem( SDL_INIT_AUDIO ); } return 0; } int consumer_is_stopped( mlt_consumer parent ) { consumer_sdl self = parent->child; return !self->running; } void consumer_purge( mlt_consumer parent ) { consumer_sdl self = parent->child; if ( self->running ) { pthread_mutex_lock( &self->video_mutex ); mlt_frame frame = MLT_FRAME( mlt_deque_peek_back( self->queue ) ); // When playing rewind or fast forward then we need to keep one // frame in the queue to prevent playback stalling. double speed = frame? mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" ) : 0; int n = ( speed == 0.0 || speed == 1.0 ) ? 0 : 1; while ( mlt_deque_count( self->queue ) > n ) mlt_frame_close( mlt_deque_pop_back( self->queue ) ); self->is_purge = 1; pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); } } static void sdl_fill_audio( void *udata, uint8_t *stream, int len ) { consumer_sdl self = udata; // Get the volume double volume = mlt_properties_get_double( self->properties, "volume" ); // Wipe the stream first memset( stream, 0, len ); pthread_mutex_lock( &self->audio_mutex ); int bytes = MIN(len, self->audio_avail); // Place in the audio buffer if ( volume != 1.0 ) { // Adjust the volume while copying. int16_t *src = (int16_t*) self->audio_buffer; int16_t *dst = (int16_t*) stream; int i = bytes / sizeof(*dst) + 1; while (--i) { *dst++ = CLAMP(volume * src[0], -32768, 32767); src++; } } else { memcpy( stream, self->audio_buffer, bytes ); } // Remove len from the audio available self->audio_avail -= bytes; // Remove the samples memmove( self->audio_buffer, self->audio_buffer + bytes, self->audio_avail ); // We're definitely playing now self->playing = 1; pthread_cond_broadcast( &self->audio_cond ); pthread_mutex_unlock( &self->audio_mutex ); } static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int *duration ) { // Get the properties of self consumer mlt_properties properties = self->properties; mlt_audio_format afmt = mlt_audio_s16; // 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" ); int scrub = mlt_properties_get_int( properties, "scrub_audio" ); static int counter = 0; int samples = mlt_sample_calculator( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ ); int16_t *pcm; mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples ); *duration = ( ( samples * 1000 ) / frequency ); pcm += mlt_properties_get_int( properties, "audio_offset" ); if ( mlt_properties_get_int( properties, "audio_off" ) ) { self->playing = 1; init_audio = 1; return init_audio; } if ( init_audio == 1 ) { SDL_AudioSpec request; SDL_AudioSpec got; SDL_AudioDeviceID dev; int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" ); // specify audio format memset( &request, 0, sizeof( SDL_AudioSpec ) ); self->playing = 0; request.freq = frequency; request.format = AUDIO_S16SYS; request.channels = mlt_properties_get_int( properties, "channels" ); request.samples = audio_buffer; request.callback = sdl_fill_audio; request.userdata = (void *)self; dev = sdl2_open_audio( &request, &got ); if( dev == 0 ) { mlt_log_error( MLT_CONSUMER_SERVICE( self ), "SDL failed to open audio\n" ); init_audio = 2; } else { if( got.channels != request.channels ) { mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Unable to output %d channels. Change to %d\n", request.channels, got.channels ); } mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Audio Opened: driver=%s channels=%d frequency=%d\n", SDL_GetCurrentAudioDriver(), got.channels, got.freq ); SDL_PauseAudioDevice( dev, 0 ); init_audio = 0; self->out_channels = got.channels; } } if ( init_audio == 0 ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int samples_copied = 0; int dst_stride = self->out_channels * sizeof( *pcm ); pthread_mutex_lock( &self->audio_mutex ); while ( self->running && samples_copied < samples ) { int sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; while ( self->running && sample_space == 0 ) { struct timeval now; struct timespec tm; gettimeofday( &now, NULL ); tm.tv_sec = now.tv_sec + 1; tm.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait( &self->audio_cond, &self->audio_mutex, &tm ); sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride; if ( sample_space == 0 ) { mlt_log_warning( MLT_CONSUMER_SERVICE(&self->parent), "audio timed out\n" ); pthread_mutex_unlock( &self->audio_mutex ); #ifdef _WIN32 self->no_quit_subsystem = 1; #endif return 1; } } if ( self->running ) { int samples_to_copy = samples - samples_copied; if ( samples_to_copy > sample_space ) { samples_to_copy = sample_space; } int dst_bytes = samples_to_copy * dst_stride; if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 ) { if ( channels == self->out_channels ) { memcpy( &self->audio_buffer[ self->audio_avail ], pcm, dst_bytes ); pcm += samples_to_copy * channels; } else { int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ]; int i = samples_to_copy + 1; while ( --i ) { memcpy( dest, pcm, dst_stride ); pcm += channels; dest += self->out_channels; } } } else { memset( &self->audio_buffer[ self->audio_avail ], 0, dst_bytes ); pcm += samples_to_copy * channels; } self->audio_avail += dst_bytes; samples_copied += samples_to_copy; } pthread_cond_broadcast( &self->audio_cond ); } pthread_mutex_unlock( &self->audio_mutex ); } else { self->playing = 1; } return init_audio; } static int consumer_play_video( consumer_sdl self, mlt_frame frame ) { // Get the properties of this consumer mlt_properties properties = self->properties; mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); return 0; } static void *video_thread( void *arg ) { // Identify the arg consumer_sdl self = 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( self->properties, "real_time" ); // Get the current time gettimeofday( &now, NULL ); // Determine start time start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec; while ( self->running ) { // Pop the next frame pthread_mutex_lock( &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); while ( next == NULL && self->running ) { pthread_cond_wait( &self->video_cond, &self->video_mutex ); next = mlt_deque_pop_front( self->queue ); } pthread_mutex_unlock( &self->video_mutex ); if ( !self->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 ) { // 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( self->queue ) < 2 ) ) consumer_play_video( self, next ); // If the queue is empty, recalculate start to allow build up again if ( real_time && ( mlt_deque_count( self->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; } // This consumer is stopping. But audio has already been played for all // the frames in the queue. Spit out all the frames so that the display has // the option to catch up with the audio. if ( next != NULL ) { consumer_play_video( self, next ); mlt_frame_close( next ); next = NULL; } while ( mlt_deque_count( self->queue ) > 0 ) { next = mlt_deque_pop_front( self->queue ); consumer_play_video( self, next ); mlt_frame_close( next ); next = NULL; } mlt_consumer_stopped( &self->parent ); return NULL; } /** Threaded wrapper for pipe. */ static void *consumer_thread( void *arg ) { // Identify the arg consumer_sdl self = arg; // Get the consumer mlt_consumer consumer = &self->parent; // Get the properties mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( consumer ); // Video thread pthread_t thread; // internal initialization 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 }; // int last_position = -1; pthread_mutex_lock( &self->refresh_mutex ); self->refresh_count = 0; pthread_mutex_unlock( &self->refresh_mutex ); // Loop until told not to while( self->running ) { // Get a frame from the attached producer frame = mlt_consumer_rt_frame( consumer ); // Ensure that we have a frame if ( frame ) { // Get the frame properties properties = MLT_FRAME_PROPERTIES( frame ); // Get the speed of the frame double speed = mlt_properties_get_double( properties, "_speed" ); // Clear refresh mlt_events_block( consumer_props, consumer_props ); mlt_properties_set_int( consumer_props, "refresh", 0 ); mlt_events_unblock( consumer_props, consumer_props ); // Play audio init_audio = consumer_play_audio( self, frame, init_audio, &duration ); // Determine the start time now if ( self->playing && init_video ) { // Create the video thread pthread_create( &thread, NULL, video_thread, self ); // 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 ( self->running && speed != 0 && mlt_deque_count( self->queue ) > 15 ) nanosleep( &tm, NULL ); // Push this frame to the back of the queue if ( self->running && speed ) { pthread_mutex_lock( &self->video_mutex ); if ( self->is_purge && speed == 1.0 ) { mlt_frame_close( frame ); frame = NULL; self->is_purge = 0; } else { mlt_deque_push_back( self->queue, frame ); pthread_cond_broadcast( &self->video_cond ); } pthread_mutex_unlock( &self->video_mutex ); // Calculate the next playtime playtime += ( duration * 1000 ); } else if ( self->running ) { pthread_mutex_lock( &self->refresh_mutex ); consumer_play_video( self, frame ); mlt_frame_close( frame ); frame = NULL; self->refresh_count --; if ( self->refresh_count <= 0 ) { pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex ); } pthread_mutex_unlock( &self->refresh_mutex ); } // Optimisation to reduce latency if ( speed == 1.0 ) { // TODO: disabled due to misbehavior on parallel-consumer // if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) ) // mlt_consumer_purge( consumer ); // last_position = mlt_frame_get_position( frame ); } else { mlt_consumer_purge( consumer ); // last_position = -1; } } } // Kill the video thread if ( init_video == 0 ) { pthread_mutex_lock( &self->video_mutex ); pthread_cond_broadcast( &self->video_cond ); pthread_mutex_unlock( &self->video_mutex ); pthread_join( thread, NULL ); } if ( frame ) { // The video thread has cleared out the queue. But the audio was played // for this frame. So play the video before stopping so the display has // the option to catch up with the audio. consumer_play_video( self, frame ); mlt_frame_close( frame ); frame = NULL; } pthread_mutex_lock( &self->audio_mutex ); self->audio_avail = 0; pthread_mutex_unlock( &self->audio_mutex ); return NULL; } /** Callback to allow override of the close method. */ static void consumer_close( mlt_consumer parent ) { // Get the actual object consumer_sdl self = parent->child; // Stop the consumer mlt_consumer_stop( parent ); // Now clean up the rest mlt_consumer_close( parent ); // Close the queue mlt_deque_close( self->queue ); // Destroy mutexes pthread_mutex_destroy( &self->audio_mutex ); pthread_cond_destroy( &self->audio_cond ); pthread_mutex_destroy( &self->video_mutex ); pthread_cond_destroy( &self->video_cond ); pthread_mutex_destroy( &self->refresh_mutex ); pthread_cond_destroy( &self->refresh_cond ); // Finally clean up this free( self ); } mlt-6.20.0/src/modules/sdl2/consumer_sdl2_audio.yml000066400000000000000000000017231362234133600221670ustar00rootroot00000000000000schema_version: 0.3 type: consumer identifier: sdl2_audio title: SDL2 Audio Only version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio description: > Simple DirectMedia Layer audio only output module. parameters: - identifier: volume title: Volume type: float description: Audio level factor. mutable: yes - identifier: audio_off title: Audio off type: integer description: If 1, disable audio output mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: audio_buffer title: Audio buffer type: integer description: Size of the sdl audio buffer. mutable: yes default: 2048 minimum: 128 - identifier: scrub_audio title: Audio scrubbing type: integer description: If enabled, sound is played even when the speed is not normal. mutable: yes minimum: 0 maximum: 1 default: 0 widget: checkbox mlt-6.20.0/src/modules/sdl2/factory.c000066400000000000000000000032771362234133600173250ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2018 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include extern mlt_consumer consumer_sdl2_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_consumer consumer_sdl2_audio_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/sdl2/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "sdl2", consumer_sdl2_init ); MLT_REGISTER_METADATA( consumer_type, "sdl2", metadata, "consumer_sdl2.yml" ); MLT_REGISTER( consumer_type, "sdl2_audio", consumer_sdl2_audio_init ); MLT_REGISTER_METADATA( consumer_type, "sdl2_audio", metadata, "consumer_sdl_audio.yml" ); } mlt-6.20.0/src/modules/sox/000077500000000000000000000000001362234133600154465ustar00rootroot00000000000000mlt-6.20.0/src/modules/sox/Makefile000066400000000000000000000014271362234133600171120ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lm include ../../../config.mak 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/sox" install -m 644 filter_sox.yml "$(DESTDIR)$(mltdatadir)/sox" install -m 644 filter_sox_effect.yml "$(DESTDIR)$(mltdatadir)/sox" uninstall: rm -f "$(DESTDIR)$(moduledir)/libmltsox$(LIBSUF)" rm -rf "$(DESTDIR)$(mltdatadir)/sox" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/sox/configure000077500000000000000000000021231362234133600173530ustar00rootroot00000000000000#!/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|NetBSD) LDD="ldd" ;; *) ;; esac pkg-config sox if [ $? -eq 0 ] then disable_sox=0 echo "CFLAGS += $(pkg-config --cflags 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 echo "- sox not found: disabling" touch ../disable-sox fi fi exit 0 fi mlt-6.20.0/src/modules/sox/factory.c000066400000000000000000000053511362234133600172650ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #ifdef SOX14 #include #endif extern mlt_filter filter_sox_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; mlt_properties result = NULL; // Load the yaml file snprintf( file, PATH_MAX, "%s/sox/filter_%s.yml", mlt_environment( "MLT_DATA" ), strcmp( id, "sox" ) ? "sox_effect" : "sox" ); result = mlt_properties_parse_yaml( file ); #ifdef SOX14 if ( result && ( type == filter_type ) && strcmp( id, "sox" ) ) { // Annotate the yaml properties with sox effect usage. mlt_properties params = mlt_properties_get_data( result, "parameters", NULL ); const sox_effect_handler_t *e; int i; for ( i = 0; sox_effect_fns[i]; i++ ) { e = sox_effect_fns[i](); if ( e && e->name && !strcmp( e->name, id + 4 ) ) { mlt_properties p = mlt_properties_get_data( params, "0", NULL ); mlt_properties_set( result, "identifier", e->name ); mlt_properties_set( result, "title", e->name ); mlt_properties_set( p, "type", "string" ); mlt_properties_set( p, "title", "Options" ); if ( e->usage ) mlt_properties_set( p, "format", e->usage ); break; } } } #endif return result; } MLT_REPOSITORY { MLT_REGISTER( filter_type, "sox", filter_sox_init ); MLT_REGISTER_METADATA( filter_type, "sox", metadata, NULL ); #ifdef SOX14 int i; const sox_effect_handler_t *e; char name[64] = "sox."; for ( i = 0; sox_effect_fns[i]; i++ ) { e = sox_effect_fns[i](); if ( e && e->name && !( e->flags & SOX_EFF_DEPRECATED ) #if (SOX_LIB_VERSION_CODE >= SOX_LIB_VERSION(14,3,0)) && !( e->flags & SOX_EFF_INTERNAL ) #endif ) { strcpy( name + 4, e->name ); MLT_REGISTER( filter_type, name, filter_sox_init ); MLT_REGISTER_METADATA( filter_type, name, metadata, NULL ); } } #endif } mlt-6.20.0/src/modules/sox/filter_sox.c000066400000000000000000000363161362234133600200010ustar00rootroot00000000000000/* * filter_sox.c -- apply any number of SOX effects using libst * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #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 #define DBFSTOAMP(x) pow(10,(x)/20.0) /** 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 ) { mlt_tokeniser_close( tokeniser ); 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 (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,2,0)) if ( sox_effect_options( eff, opt_count, &tokeniser->tokens[ tokeniser->count > 1 ? 1 : 0 ] ) == ST_SUCCESS ) #else if ( ( * eff->handler.getopts )( eff, opt_count, &tokeniser->tokens[ tokeniser->count > 1 ? 1 : 0 ] ) == ST_SUCCESS ) #endif #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, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { #if (ST_LIB_VERSION_CODE >= ST_LIB_VERSION(14,3,0)) SOX_SAMPLE_LOCALS; #endif // Get the filter service mlt_filter filter = mlt_frame_pop_audio( frame ); // Get the filter properties mlt_properties filter_properties = MLT_FILTER_PROPERTIES( filter ); mlt_service_lock( MLT_FILTER_SERVICE( 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 i; // channel int count = mlt_properties_get_int( filter_properties, "_effect_count" ); int analysis = mlt_properties_get( filter_properties, "effect" ) && !strcmp( mlt_properties_get( filter_properties, "effect" ), "analysis" ); // Get the producer's audio *format = mlt_audio_s32; mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); // 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_value( filter_properties, j ); // 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 || analysis ) ) { input_buffer = (st_sample_t*) *buffer + i * *samples; st_sample_t *p = input_buffer; st_size_t isamp = *samples; st_size_t osamp = *samples; int j = *samples + 1; char *normalise = mlt_properties_get( filter_properties, "normalise" ); double normalised_gain = 1.0; if ( analysis ) { // Run analysis to compute a gain level to normalize the audio across entire filter duration double max_power = mlt_properties_get_double( filter_properties, "_max_power" ); double peak = mlt_properties_get_double( filter_properties, "_max_peak" ); double use_peak = mlt_properties_get_int( filter_properties, "use_peak" ); double power = 0; int n = *samples + 1; // Compute power level of samples in this channel of this frame while ( --n ) { double s = abs( *p++ ); // Track peak if ( s > peak ) { peak = s; mlt_properties_set_double( filter_properties, "_max_peak", peak ); } power += s * s; } power /= *samples; // Track maximum power if ( power > max_power ) { max_power = power; mlt_properties_set_double( filter_properties, "_max_power", max_power ); } // Complete analysis the last channel of the last frame. if ( i + 1 == *channels && mlt_filter_get_position( filter, frame ) + 1 == mlt_filter_get_length2( filter, frame ) ) { double rms = sqrt( max_power / ST_SSIZE_MIN / ST_SSIZE_MIN ); char effect[32]; // Convert RMS or peak to gain if ( use_peak ) normalised_gain = ST_SSIZE_MIN / -peak; else { double gain = DBFSTOAMP(-12); // default -12 dBFS char *p = mlt_properties_get( filter_properties, "analysis_level" ); if (p) { gain = mlt_properties_get_double( filter_properties, "analysis_level" ); if ( strstr( p, "dB" ) ) gain = DBFSTOAMP( gain ); } normalised_gain = gain / rms; } // Set properties for serialization snprintf( effect, sizeof(effect), "vol %f", normalised_gain ); effect[31] = 0; mlt_properties_set( filter_properties, "effect", effect ); mlt_properties_set( filter_properties, "analyze", NULL ); // Show output comparable to normalize --no-adjust --fractions mlt_properties_set_double( filter_properties, "level", rms ); mlt_properties_set_double( filter_properties, "gain", normalised_gain ); mlt_properties_set_double( filter_properties, "peak", -peak / ST_SSIZE_MIN ); } // restore some variables p = input_buffer; } 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" ); double rms = 0; // Default the maximum gain factor to 20dBFS if ( max_gain == 0 ) max_gain = 10.0; // Compute rms amplitude while( --j ) { rms += ( double )*p * ( double )*p; p ++; } rms = sqrt( rms / *samples / ST_SSIZE_MIN / ST_SSIZE_MIN ); // 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 { mlt_log_warning( MLT_FILTER_SERVICE(filter), "effect processing failed\n" ); } // 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; } } } // Write back memcpy( input_buffer, output_buffer, *samples * sizeof(st_sample_t) ); } } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); 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 ( !strncmp( id, "sox.", 4 ) ) { char *s = malloc( strlen( id ) + ( arg? strlen( arg ) + 2 : 1 ) ); strcpy( s, id + 4 ); if ( arg ) { strcat( s, " " ); strcat( s, arg ); } mlt_properties_set( properties, "effect", s ); free( s ); } else if ( arg ) 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 ); mlt_properties_set( properties, "version", sox_version() ); } return this; } // What to do when a libst internal failure occurs void cleanup(void){} mlt-6.20.0/src/modules/sox/filter_sox.yml000066400000000000000000000047501362234133600203550ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: sox title: SoX version: 2 copyright: Meltytech, LLC license: LGPL language: en url: http://sox.sourceforge.net/ creator: Dan Dennedy tags: - Audio description: Process audio using a SoX effect. bugs: - Some effects are stereo only, but MLT processes each channel separately. - Some effects have a temporal side-effect that do not work well. parameters: - identifier: effect argument: yes title: Effect name and options type: string format: effect [options] description: > If the effect name is "analysis" then it does not run any effect. Instead, it analyzes the audio to determine a normalized gain level. The results are put into the level, peak, and gain properties as well as this effect property as the parameter to the vol effect. - identifier: analysis_level title: Normalization level type: string default: -12dBFS description: > Normalize the volume to the specified amplitude. The normalization may be indicated as a floating point value of the relative volume with 1.0 being maximum. The normalization may also be indicated as a numeric value with the suffix "dB" to set the amplitude in decibels. - identifier: level title: Signal power level (RMS) type: float readonly: yes - identifier: peak title: Peak signal level type: float readonly: yes - identifier: gain title: Gain to normalize type: float readonly: yes - identifier: use_peak title: Use peak description: > Use peak signal level instead of RMS (root mean square) power level to compute gain for normalization. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: normalise title: Dynamic normalization description: > This computes the gain for normalization dynamically per frame, but it uses a sliding smoothing window to prevent the gain from fluctuating wildly. Currently, this must be used in combination with some SoX effect. type: integer minimum: 0 maximum: 1 default: 0 widget: checkbox - identifier: window title: Smoothing window size type: integer minimum: 0 default: 75 unit: frames widget: spinner - identifier: max_gain title: Maximum gain description: > With dynamic normalization, this puts a maximum limit on the amount of gain. type: float minimum: 0 maximum: 20 default: 10 mlt-6.20.0/src/modules/sox/filter_sox_effect.yml000066400000000000000000000007771362234133600216760ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: sox title: sox version: 1 copyright: Meltytech, LLC license: LGPL language: en url: http://sox.sourceforge.net/ creator: Dan Dennedy tags: - Audio description: Process audio using a SoX effect. bugs: - Some effects are stereo only, but MLT processes each channel separately. - Some effects have a temporal side-effect that do not work well. parameters: - identifier: argument title: Effect name and options type: string format: effect [options] mlt-6.20.0/src/modules/swfdec/000077500000000000000000000000001362234133600161105ustar00rootroot00000000000000mlt-6.20.0/src/modules/swfdec/Makefile000066400000000000000000000012501362234133600175460ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lm include ../../../config.mak include config.mak TARGET = ../libmltswfdec$(LIBSUF) OBJS = producer_swfdec.o ifeq ($(targetos), MinGW) LDFLAGS += -Wl,enable-auto-import -lz 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/swfdec" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/swfdec" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/swfdec/configure000077500000000000000000000015501362234133600200200ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ] then pkg-config swfdec-0.9 2> /dev/null disable_swfdec=$? echo > config.mak if [ "$disable_swfdec" = "0" ] then echo "CFLAGS += $(pkg-config --cflags swfdec-0.9)" >> config.mak echo "LDFLAGS += $(pkg-config --libs swfdec-0.9)" >> config.mak else pkg-config swfdec-0.8 2> /dev/null disable_swfdec=$? if [ "$disable_swfdec" = "0" ] then echo "CFLAGS += $(pkg-config --cflags swfdec-0.8)" >> config.mak echo "LDFLAGS += $(pkg-config --libs swfdec-0.8)" >> config.mak else pkg-config swfdec-0.7 2> /dev/null disable_swfdec=$? if [ "$disable_swfdec" = "0" ] then echo "CFLAGS += $(pkg-config --cflags swfdec-0.7)" >> config.mak echo "LDFLAGS += $(pkg-config --libs swfdec-0.7)" >> config.mak else echo "- swfdec not found: disabling" touch ../disable-swfdec exit 0 fi fi fi fi mlt-6.20.0/src/modules/swfdec/producer_swfdec.c000066400000000000000000000216351362234133600214410ustar00rootroot00000000000000/* * producer_swfdec.c -- swfdec producer for Flash files * Copyright (C) 2010 Dan Dennedy * * swfdec library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * swfdec library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with swfdec library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include typedef struct { struct mlt_producer_s parent; SwfdecPlayer *player; SwfdecURL *url; cairo_surface_t *surface; cairo_t *cairo; mlt_position last_position; guint width; guint height; } *producer_swfdec; void swfdec_open( producer_swfdec swfdec, mlt_profile profile ) { mlt_properties properties = MLT_PRODUCER_PROPERTIES( &swfdec->parent ); // Setup the swfdec player swfdec->player = swfdec_player_new( NULL ); if ( mlt_properties_get( properties, "variables") ) swfdec_player_set_variables( swfdec->player, mlt_properties_get( properties, "variables" ) ); swfdec_player_set_url( swfdec->player, swfdec->url ); swfdec_player_set_maximum_runtime( swfdec->player, 10000 ); // Setup size swfdec_player_get_default_size( swfdec->player, &swfdec->width, &swfdec->height ); if ( swfdec->width == 0 || swfdec->height == 0 ) { swfdec_player_set_size( swfdec->player, profile->width, profile->height ); swfdec->width = profile->width; swfdec->height = profile->height; } // Setup scaling double scale = 1.0; if ( swfdec->width > 2 * swfdec->height ) scale = 0.5 * profile->width / swfdec->height; else if ( swfdec->height > 2 * swfdec->width ) scale = 0.5 * profile->height / swfdec->width; else scale = (double) profile->width / MAX( swfdec->width, swfdec->height ); swfdec->width = ceil( scale * swfdec->width ); swfdec->height = ceil( scale * swfdec->height ); // Compute the centering translation double x = swfdec->width > profile->width ? (swfdec->width - profile->width) / 2 : 0; double y = swfdec->height > profile->height ? (swfdec->height - profile->height) / 2 : 0; // Setup cairo swfdec->surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, MIN(profile->width, swfdec->width), MIN(profile->height, swfdec->height) ); swfdec->cairo = cairo_create( swfdec->surface ); cairo_translate( swfdec->cairo, -x, -y ); cairo_scale( swfdec->cairo, scale, scale ); } void swfdec_close( producer_swfdec swfdec ) { if ( swfdec->cairo ) cairo_destroy( swfdec->cairo ); swfdec->cairo = NULL; if ( swfdec->player ) g_object_unref( swfdec->player ); swfdec->player = NULL; if ( swfdec->surface ) cairo_surface_destroy( swfdec->surface ); swfdec->surface = NULL; } // Cairo uses 32 bit native endian ARGB static void bgra_to_rgba( uint8_t *src, uint8_t* dst, int width, int height ) { int n = width * height + 1; while ( --n ) { *dst++ = src[2]; *dst++ = src[1]; *dst++ = src[0]; *dst++ = src[3]; src += 4; } } static int get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { producer_swfdec swfdec = mlt_frame_pop_service( frame ); mlt_service service = MLT_PRODUCER_SERVICE( &swfdec->parent ); mlt_profile profile = mlt_service_profile( service ); mlt_service_lock( service ); if ( !swfdec->player ) swfdec_open( swfdec, profile ); // Set width and height *width = swfdec->width; *height = swfdec->height; *format = mlt_image_rgb24a; int size = mlt_image_format_size( *format, *width, *height, NULL ); *buffer = mlt_pool_alloc( size ); mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); // Seek mlt_position pos = mlt_frame_original_position( frame ); if ( pos > swfdec->last_position ) { gulong msec = 1000UL * ( pos - swfdec->last_position ) * profile->frame_rate_den / profile->frame_rate_num; while ( msec > 0 ) msec -= swfdec_player_advance( swfdec->player, msec ); } else if ( pos < swfdec->last_position ) { swfdec_close( swfdec ); swfdec_open( swfdec, mlt_service_profile( service ) ); gulong msec = 1000UL * pos * profile->frame_rate_den / profile->frame_rate_num; while ( msec > 0 ) msec -= swfdec_player_advance( swfdec->player, msec ); } swfdec->last_position = pos; // Render cairo_save( swfdec->cairo ); //cairo_set_source_rgba( swfdec->cairo, r, g, b, a ); cairo_set_operator( swfdec->cairo, CAIRO_OPERATOR_CLEAR ); cairo_paint( swfdec->cairo ); cairo_restore( swfdec->cairo ); swfdec_player_render( swfdec->player, swfdec->cairo ); // Get image from surface uint8_t *image = cairo_image_surface_get_data( swfdec->surface ); mlt_service_unlock( service ); // Convert to RGBA bgra_to_rgba( image, *buffer, swfdec->width, swfdec->height ); return 0; } static int get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ) { // Access the private data producer_swfdec swfdec = producer->child; // Create an empty frame *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) ); // Get the frames properties mlt_properties properties = MLT_FRAME_PROPERTIES( *frame ); // Update other info on the frame mlt_properties_set_int( properties, "test_image", 0 ); mlt_properties_set_int( properties, "width", swfdec->width ); mlt_properties_set_int( properties, "height", swfdec->height ); mlt_properties_set_int( properties, "progressive", 1 ); mlt_properties_set_double( properties, "aspect_ratio", 1.0 ); // Push the get_image method on to the stack mlt_frame_push_service( *frame, swfdec ); mlt_frame_push_get_image( *frame, 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 ) { // Obtain swfdec producer_swfdec swfdec = parent->child; // Close the file swfdec_close( swfdec ); if ( swfdec->url ) swfdec_url_free( swfdec->url ); // Close the parent parent->close = NULL; mlt_producer_close( parent ); // Free the memory free( swfdec ); } mlt_producer producer_swfdec_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename ) { if ( !filename ) return NULL; producer_swfdec swfdec = calloc( 1, sizeof( *swfdec ) ); mlt_producer producer = NULL; if ( swfdec && mlt_producer_init( &swfdec->parent, swfdec ) == 0 ) { // Initialize swfdec and try to open the file swfdec->url = swfdec_url_new_from_input( filename ); if ( swfdec->url ) { // Set the return value producer = &swfdec->parent; // Set the resource property (required for all producers) mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); mlt_properties_set( properties, "resource", filename ); // Set the callbacks producer->close = (mlt_destructor) producer_close; producer->get_frame = get_frame; // Set the meta media attributes swfdec->width = profile->width; swfdec->height = profile->height; mlt_properties_set_int( properties, "meta.media.nb_streams", 1 ); mlt_properties_set( properties, "meta.media.0.stream.type", "video" ); mlt_properties_set( properties, "meta.media.0.codec.name", "swf" ); mlt_properties_set( properties, "meta.media.0.codec.long_name", "Adobe Flash" ); mlt_properties_set( properties, "meta.media.0.codec.pix_fmt", "bgra" ); mlt_properties_set_int( properties, "meta.media.width", profile->width ); mlt_properties_set_int( properties, "meta.media.height", profile->height ); mlt_properties_set_double( properties, "meta.media.sample_aspect_num", 1.0 ); mlt_properties_set_double( properties, "meta.media.sample_aspect_den", 1.0 ); mlt_properties_set_int( properties, "meta.media.frame_rate_num", profile->frame_rate_num ); mlt_properties_set_int( properties, "meta.media.frame_rate_den", profile->frame_rate_den ); mlt_properties_set_int( properties, "meta.media.progressive", 1 ); } else { g_object_unref( swfdec->player ); mlt_producer_close( &swfdec->parent ); free( swfdec ); } } else { free( swfdec ); } return producer; } static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/swfdec/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { swfdec_init(); MLT_REGISTER( producer_type, "swfdec", producer_swfdec_init ); MLT_REGISTER_METADATA( producer_type, "swfdec", metadata, "producer_swfdec.yml" ); } mlt-6.20.0/src/modules/swfdec/producer_swfdec.yml000066400000000000000000000002511362234133600220070ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: swfdec title: Flash version: 1 copyright: Dan Dennedy creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Video mlt-6.20.0/src/modules/vid.stab/000077500000000000000000000000001362234133600163475ustar00rootroot00000000000000mlt-6.20.0/src/modules/vid.stab/CMakeLists.txt000066400000000000000000000007521362234133600211130ustar00rootroot00000000000000if(GPL) pkg_check_modules(vidstab IMPORTED_TARGET GLOBAL vidstab) if(TARGET PkgConfig::vidstab) file(GLOB mltvidstab_src *.c *.cpp) add_library(mltvidstab MODULE ${mltvidstab_src}) target_link_libraries(mltvidstab mlt m mlt++ PkgConfig::vidstab) install(TARGETS mltvidstab LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/vidstab) endif() endif() mlt-6.20.0/src/modules/vid.stab/Makefile000066400000000000000000000016131362234133600200100ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lm include ../../../config.mak TARGET = ../libmltvidstab$(LIBSUF) OBJS = factory.o \ common.o CPPOBJS = filter_deshake.o CPPOBJS += filter_vidstab.o CFLAGS += -Wno-deprecated CFLAGS += $(shell pkg-config --cflags vidstab) CXXFLAGS += $(CFLAGS) LDFLAGS += -L../../mlt++ -lmlt++ LDFLAGS += $(shell pkg-config --libs vidstab) SRCS := $(OBJS:.o=.c) $(CPPOBJS:.o=.cpp) all: $(TARGET) $(TARGET): $(OBJS) $(CPPOBJS) $(CXX) $(SHFLAGS) -o $@ $(OBJS) $(CPPOBJS) $(LDFLAGS) depend: $(SRCS) $(CXX) -MM $(CXXFLAGS) $^ 1>.depend distclean: clean rm -f .depend config.h config.mak clean: rm -f $(OBJS) $(TARGET) $(CPPOBJS) install: all install -m 755 $(TARGET) "$(DESTDIR)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/vid.stab" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/vid.stab" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/vid.stab/common.c000066400000000000000000000122261362234133600200060ustar00rootroot00000000000000/* * common.c * Copyright (C) 2014 Brian Matherly * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "common.h" mlt_image_format validate_format( mlt_image_format format ) { switch( format ) { case mlt_image_yuv420p: return mlt_image_yuv420p; default: return mlt_image_yuv422; } } /** Convert an MLT image to one that can be used by VS. * Use free_vsimage() when done with the resulting image. */ VSPixelFormat mltimage_to_vsimage( mlt_image_format mlt_format, int width, int height, uint8_t* mlt_img, uint8_t** vs_img ) { switch( mlt_format ) { case mlt_image_yuv420p: // This format maps with no conversion { *vs_img = mlt_img; return PF_YUV420P; } case mlt_image_yuv422: // Convert packed YUV422 to planar YUV444 // Note: vid.stab 0.98 seems to suffer chroma bleeding // when using PF_YUV422P - which is why PF_YUV444P is used. { *vs_img = mlt_pool_alloc( width * height * 3 ); uint8_t* yp = *vs_img; uint8_t* up = yp + ( width * height ); uint8_t* vp = up + ( width * height ); int i, j, n = width / 2 + 1; for ( i = 0; i < height; i++ ) { j = n; while ( --j ) { *yp++ = mlt_img[0]; *up++ = mlt_img[1]; *vp++ = mlt_img[3]; *yp++ = mlt_img[2]; *up++ = mlt_img[1]; *vp++ = mlt_img[3]; mlt_img += 4; } if ( width % 2 ) { *yp++ = mlt_img[0]; *up++ = mlt_img[1]; *vp++ = (mlt_img - 4)[3]; mlt_img += 2; } } return PF_YUV444P; } default: return PF_NONE; } } /** Convert a VS image back to the MLT image it originally came from in mltimage_to_vsimage(). */ void vsimage_to_mltimage( uint8_t* vs_img, uint8_t* mlt_img, mlt_image_format mlt_format, int width, int height ) { switch( mlt_format ) { case mlt_image_yuv420p: // This format was never converted break; case mlt_image_yuv422: // Convert planar YUV444 to packed YUV422 { uint8_t* yp = vs_img; uint8_t* up = yp + ( width * height ); uint8_t* vp = up + ( width * height ); int i, j, n = width / 2 + 1; for ( i = 0; i < height; i++ ) { j = n; while ( --j ) { *mlt_img++ = yp[0]; *mlt_img++ = ( up[0] + up[1] ) >> 1; *mlt_img++ = yp[1]; *mlt_img++ = ( vp[0] + vp[1] ) >> 1; yp += 2; up += 2; vp += 2; } if ( width % 2 ) { *mlt_img++ = yp[0]; *mlt_img++ = up[0]; yp += 1; up += 1; vp += 1; } } } break; default: break; } } /** Free an image allocated by mltimage_to_vsimage(). */ void free_vsimage( uint8_t* vs_img, VSPixelFormat format ) { if( format != PF_YUV420P ) { mlt_pool_release( vs_img ); } } /** Compare two VSMotionDetectConfig structures. * Return 1 if they are different. 0 if they are the same. */ int compare_motion_config( VSMotionDetectConfig* a, VSMotionDetectConfig* b ) { if( a->shakiness != b->shakiness || a->accuracy != b->accuracy || a->stepSize != b->stepSize || // Skip: Deprecated // a->algo != b->algo || a->virtualTripod != b->virtualTripod || a->show != b->show || // Skip: inconsequential? // a->modName != b->modName || a->contrastThreshold != b->contrastThreshold ) { return 1; } return 0; } /** Compare two VSTransformConfig structures. * Return 1 if they are different. 0 if they are the same. */ int compare_transform_config( VSTransformConfig* a, VSTransformConfig* b ) { if( a->relative != b->relative || a->smoothing != b->smoothing || a->crop != b->crop || a->invert != b->invert || a->zoom != b->zoom || a->optZoom != b->optZoom || a->zoomSpeed != b->zoomSpeed || a->interpolType != b->interpolType || a->maxShift != b->maxShift || a->maxAngle != b->maxAngle || // Skip: inconsequential? // a->modName != b->modName || // Skip: unused? // a->verbose != b->verbose || a->simpleMotionCalculation != b->simpleMotionCalculation || // Skip: unused? // a->storeTransforms != b->storeTransforms || a->smoothZoom != b->smoothZoom || a->camPathAlgo != b->camPathAlgo ) { return 1; } return 0; } static int vs_log_wrapper( int type, const char *tag, const char *format, ... ) { va_list vl; if ( type > mlt_log_get_level() ) return VS_OK; va_start( vl, format ); fprintf( stderr, "[%s] ", tag ); vfprintf( stderr, format, vl ); va_end( vl ); return VS_OK; } void init_vslog() { VS_ERROR_TYPE = MLT_LOG_ERROR; VS_WARN_TYPE = MLT_LOG_WARNING; VS_INFO_TYPE = MLT_LOG_INFO; VS_MSG_TYPE = MLT_LOG_VERBOSE; vs_log = vs_log_wrapper; } mlt-6.20.0/src/modules/vid.stab/common.h000066400000000000000000000030121362234133600200040ustar00rootroot00000000000000/* * common.h * Copyright (C) 2013 Jakub Ksiezniak * Copyright (C) 2014 Brian Matherly * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VIDSTAB_COMMON_H_ #define VIDSTAB_COMMON_H_ #include #include mlt_image_format validate_format( mlt_image_format format ); VSPixelFormat mltimage_to_vsimage( mlt_image_format mlt_format, int width, int height, uint8_t* mlt_img, uint8_t** vs_img ); void vsimage_to_mltimage( uint8_t* vs_img, uint8_t* mlt_img, mlt_image_format mlt_format, int width, int height ); void free_vsimage( uint8_t* vs_img, VSPixelFormat format ); int compare_motion_config( VSMotionDetectConfig* a, VSMotionDetectConfig* b ); int compare_transform_config( VSTransformConfig* a, VSTransformConfig* b ); void init_vslog(); #endif /* VIDSTAB_COMMON_H_ */ mlt-6.20.0/src/modules/vid.stab/configure000077500000000000000000000007541362234133600202640ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ] then if ! $(pkg-config vidstab) then echo "- vid.stab not found: disabling" touch ../disable-vid.stab exit 0 fi minver="0.98" modver=$(pkg-config --modversion vidstab) pkg-config --exists "vidstab >= $minver" if [ $? -ne 0 ] then echo "- vid.stab $modver found, but $minver or newer is required: disabling" touch ../disable-vid.stab exit 0 fi echo > config.mak case $targetos in Darwin) ;; MinGW) ;; *) ;; esac exit 0 fi mlt-6.20.0/src/modules/vid.stab/factory.c000066400000000000000000000032171362234133600201650ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include extern mlt_filter filter_deshake_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/vid.stab/filter_%s.yml", mlt_environment( "MLT_DATA" ), id ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( filter_type, "deshake", filter_deshake_init ); MLT_REGISTER( filter_type, "vidstab", filter_vidstab_init ); MLT_REGISTER_METADATA( filter_type, "deshake", metadata, "filter_deshake.yml" ); MLT_REGISTER_METADATA( filter_type, "vidstab", metadata, "filter_vidstab.yml" ); } mlt-6.20.0/src/modules/vid.stab/filter_deshake.cpp000066400000000000000000000171471362234133600220360ustar00rootroot00000000000000/* * filter_deshake.cpp * Copyright (C) 2013 Marco Gittler * Copyright (C) 2013 Jakub Ksiezniak * Copyright (C) 2014 Brian Matherly * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern "C" { #include #include "common.h" } #include #include #include typedef struct _deshake_data { bool initialized; VSMotionDetect md; VSTransformData td; VSSlidingAvgTrans avg; VSMotionDetectConfig mconf; VSTransformConfig tconf; mlt_position lastFrame; } DeshakeData; static void get_config( VSTransformConfig* tconf, VSMotionDetectConfig* mconf, mlt_filter filter, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); const char* filterName = mlt_properties_get( properties, "mlt_service" ); memset( mconf, 0, sizeof(VSMotionDetectConfig) ); *mconf = vsMotionDetectGetDefaultConfig( filterName ); mconf->shakiness = mlt_properties_get_int( properties, "shakiness" ); mconf->accuracy = mlt_properties_get_int(properties, "accuracy"); mconf->stepSize = mlt_properties_get_int(properties, "stepsize"); mconf->contrastThreshold = mlt_properties_get_double(properties, "mincontrast"); memset( tconf, 0, sizeof(VSTransformConfig) ); *tconf = vsTransformGetDefaultConfig( filterName ); tconf->smoothing = mlt_properties_get_int(properties, "smoothing"); tconf->maxShift = mlt_properties_get_int(properties, "maxshift"); tconf->maxAngle = mlt_properties_get_double(properties, "maxangle"); tconf->crop = (VSBorderType) mlt_properties_get_int(properties, "crop"); tconf->zoom = mlt_properties_get_int(properties, "zoom"); tconf->optZoom = mlt_properties_get_int(properties, "optzoom"); tconf->zoomSpeed = mlt_properties_get_double(properties, "zoomspeed"); tconf->relative = 1; // by default a bicubic interpolation is selected const char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" ); tconf->interpolType = VS_BiCubic; if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 ) tconf->interpolType = VS_Zero; else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 ) tconf->interpolType = VS_Linear; else if ( strcmp( interps, "bilinear" ) == 0 ) tconf->interpolType = VS_BiLinear; } static int check_config( mlt_filter filter, mlt_frame frame ) { DeshakeData *data = static_cast( filter->child ); VSTransformConfig new_tconf; VSMotionDetectConfig new_mconf; get_config( &new_tconf, &new_mconf, filter, frame ); if( compare_transform_config( &data->tconf, &new_tconf ) || compare_motion_config( &data->mconf, &new_mconf ) ) { return 1; } return 0; } static void init_deshake( DeshakeData *data, mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int *width, int *height ) { VSFrameInfo fiIn, fiOut; vsFrameInfoInit( &fiIn, *width, *height, vs_format ); vsFrameInfoInit( &fiOut, *width, *height, vs_format ); get_config( &data->tconf, &data->mconf, filter, frame ); vsMotionDetectInit( &data->md, &data->mconf, &fiIn ); vsTransformDataInit(&data->td, &data->tconf, &fiIn, &fiOut); data->avg.initialized = 0; } static void clear_deshake(DeshakeData *data) { if (data->initialized) { vsMotionDetectionCleanup(&data->md); vsTransformDataCleanup(&data->td); } } static int get_image(mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable) { mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); uint8_t* vs_image = NULL; VSPixelFormat vs_format = PF_NONE; // VS only works on progressive frames mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); *format = validate_format( *format ); DeshakeData *data = static_cast(filter->child); int error = mlt_frame_get_image(frame, image, format, width, height, 1); // Convert the received image to a format vid.stab can handle if ( !error ) { vs_format = mltimage_to_vsimage( *format, *width, *height, *image, &vs_image ); } if ( vs_image ) { // Service locks are for concurrency control mlt_service_lock(MLT_FILTER_SERVICE(filter)); // clear deshake data, when seeking or dropping frames mlt_position pos = mlt_filter_get_position( filter, frame ); if( pos != data->lastFrame + 1 || check_config( filter, frame) == 1 ) { clear_deshake( data ); data->initialized = false; } data->lastFrame = pos; if ( !data->initialized ) { init_deshake( data, filter, frame, vs_format, width, height ); data->initialized = true; } VSMotionDetect* md = &data->md; VSTransformData* td = &data->td; LocalMotions localmotions; VSTransform motion; VSFrame vsFrame; vsFrameFillFromBuffer(&vsFrame, vs_image, &md->fi); vsMotionDetection(md, &localmotions, &vsFrame); const char* filterName = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "mlt_service" ); motion = vsSimpleMotionsToTransform(md->fi, filterName, &localmotions); vs_vector_del(&localmotions); vsTransformPrepare(td, &vsFrame, &vsFrame); VSTransform t = vsLowPassTransforms(td, &data->avg, &motion); // mlt_log_warning(filter, "Trans: det: %f %f %f \n\t\t act: %f %f %f %f", // motion.x, motion.y, motion.alpha, // t.x, t.y, t.alpha, t.zoom); vsDoTransform(td, t); vsTransformFinish(td); vsimage_to_mltimage( vs_image, *image, *format, *width, *height ); mlt_service_unlock(MLT_FILTER_SERVICE(filter)); free_vsimage( vs_image, vs_format ); } return error; } static mlt_frame process_filter(mlt_filter filter, mlt_frame frame) { mlt_frame_push_service(frame, filter); mlt_frame_push_get_image(frame, get_image); return frame; } static void close_filter(mlt_filter filter) { DeshakeData *data = static_cast(filter->child); if (data) { clear_deshake(data); delete data; filter->child = NULL; } } extern "C" { mlt_filter filter_deshake_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = NULL; DeshakeData *data = new DeshakeData; memset(data, 0, sizeof(DeshakeData)); if ((filter = mlt_filter_new())) { filter->process = process_filter; filter->close = close_filter; filter->child = data; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); //properties for stabilize mlt_properties_set(properties, "shakiness", "4"); mlt_properties_set(properties, "accuracy", "4"); mlt_properties_set(properties, "stepsize", "6"); mlt_properties_set_double(properties, "mincontrast", 0.3); //properties for transform mlt_properties_set(properties, "smoothing", "15"); mlt_properties_set(properties, "maxshift", "-1"); mlt_properties_set(properties, "maxangle", "-1"); mlt_properties_set(properties, "crop", "0"); mlt_properties_set(properties, "zoom", "0"); mlt_properties_set(properties, "optzoom", "1"); mlt_properties_set_double(properties, "zoomspeed", 0.25); init_vslog(); return filter; } delete data; return NULL; } } mlt-6.20.0/src/modules/vid.stab/filter_deshake.yml000066400000000000000000000062371362234133600220530ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: deshake title: Vid.Stab Deshake copyright: Jakub Ksiezniak creator: Georg Martius version: 1 license: GPLv2 language: en url: http://public.hronopik.de/vid.stab/ tags: - Video description: Stabilize Video (for wiggly/rolling video) notes: > Deshakes a video clip by extracting relative transformations of subsequent frames and transforms the high-frequency away. This is a single pass version of the vidstab filter. parameters: - identifier: shakiness title: Shakiness type: integer description: How shaky the video is. readonly: no required: no minimum: 1 maximum: 10 default: 4 mutable: yes widget: spinner - identifier: accuracy title: Accuracy type: integer description: The accuracy of shakiness detection. readonly: no required: no minimum: 1 maximum: 15 default: 4 mutable: yes widget: spinner - identifier: stepsize title: Stepsize type: integer description: The step size of the search process. readonly: no required: no minimum: 0 maximum: 100 default: 6 mutable: yes widget: spinner - identifier: mincontrast title: Minimum Contrast type: float description: Below this contrast, a field is discarded. readonly: no required: no minimum: 0 maximum: 1 default: 0.3 mutable: yes widget: spinner - identifier: smoothing title: Smoothing type: integer description: Number of frames for lowpass filtering (2N + 1 frames) readonly: no required: no minimum: 0 maximum: 100 default: 15 mutable: yes widget: spinner - identifier: maxshift title: Maxshift type: integer description: Maximum number of pixels to transform the image. -1 = no limit unit: pixels readonly: no required: no minimum: -1 maximum: 1000 default: -1 mutable: yes widget: spinner - identifier: maxangle title: Maxangle type: float description: Maximum angle to rotate, -1 = no limit unit: radians readonly: no required: no minimum: -1 maximum: 3.142 default: -1 mutable: yes widget: spinner - identifier: crop title: Crop type: integer description: 0 = keep border, 1 = black background readonly: no required: no minimum: 0 maximum: 1 default: 0 mutable: yes widget: spinner - identifier: zoom title: Zoom type: integer description: Additional zoom amount unit: percent readonly: no required: no minimum: -500 maximum: 500 default: 0 mutable: yes widget: spinner - identifier: optzoom title: Optimal Zoom type: integer description: Automatically determine optimal zoom. 1 - static zoom, 2 - adaptive zoom readonly: no required: no minimum: 0 maximum: 2 default: 1 mutable: yes widget: spinner - identifier: zoomspeed title: Optimal Zoom Speed type: float description: Zoom per frame (used when optzoom = 2) unit: percent readonly: no required: no minimum: 0 maximum: 1 default: 0.25 mutable: yes widget: spinner mlt-6.20.0/src/modules/vid.stab/filter_vidstab.cpp000066400000000000000000000345761362234133600220730ustar00rootroot00000000000000/* * filter_vidstab.cpp * Copyright (C) 2013 Marco Gittler * Copyright (C) 2013 Jakub Ksiezniak * Copyright (C) 2014 Brian Matherly * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern "C" { #include #include #include #include "common.h" } #include #include #include typedef struct { VSMotionDetect md; FILE* results; mlt_position last_position; } vs_analyze; typedef struct { VSTransformData td; VSTransformConfig conf; VSTransformations trans; } vs_apply; typedef struct { vs_analyze* analyze_data; vs_apply* apply_data; } vs_data; static void get_transform_config( VSTransformConfig* conf, mlt_filter filter, mlt_frame frame ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); const char* filterName = mlt_properties_get( properties, "mlt_service" ); *conf = vsTransformGetDefaultConfig( filterName ); conf->smoothing = mlt_properties_get_int( properties, "smoothing" ); conf->maxShift = mlt_properties_get_int( properties, "maxshift" ); conf->maxAngle = mlt_properties_get_double( properties, "maxangle" ); conf->crop = (VSBorderType)mlt_properties_get_int( properties, "crop" ); conf->zoom = mlt_properties_get_int( properties, "zoom" ); conf->optZoom = mlt_properties_get_int( properties, "optzoom" ); conf->zoomSpeed = mlt_properties_get_double( properties, "zoomspeed" ); conf->relative = mlt_properties_get_int( properties, "relative" ); conf->invert = mlt_properties_get_int( properties, "invert" ); if ( mlt_properties_get_int( properties, "tripod" ) != 0 ) { // Virtual tripod mode: relative=False, smoothing=0 conf->relative = 0; conf->smoothing = 0; } // by default a bicubic interpolation is selected const char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" ); conf->interpolType = VS_BiCubic; if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 ) conf->interpolType = VS_Zero; else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 ) conf->interpolType = VS_Linear; else if ( strcmp( interps, "bilinear" ) == 0 ) conf->interpolType = VS_BiLinear; } static int check_apply_config( mlt_filter filter, mlt_frame frame ) { vs_apply* apply_data = ((vs_data*)filter->child)->apply_data; if( apply_data ) { VSTransformConfig new_conf; memset( &new_conf, 0, sizeof(VSTransformConfig) ); get_transform_config( &new_conf, filter, frame ); return compare_transform_config( &apply_data->conf, &new_conf ); } return 0; } static void destory_apply_data( vs_apply* apply_data ) { if ( apply_data ) { vsTransformDataCleanup( &apply_data->td ); vsTransformationsCleanup( &apply_data->trans ); free( apply_data ); } } static void init_apply_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); vs_data* data = (vs_data*)filter->child; vs_apply* apply_data = (vs_apply*)calloc( 1, sizeof(vs_apply) ); char* results = mlt_properties_get( properties, "results" ); char* filename = mlt_properties_get( properties, "filename" ); // The XML producer can convert "filename" from relative to absolute, but // it does not do the same for "results". Therefore, if both exist and // "filename" ends with "results", then use "filename" instead. if ( results && filename && strlen(filename) >= strlen(results) && !strcmp( &filename[strlen(filename) - strlen(results)], results ) ) { filename = mlt_properties_get( properties, "filename" ); } else { filename = mlt_properties_get( properties, "results" ); } mlt_log_info( MLT_FILTER_SERVICE(filter), "Load results from %s\n", filename ); // Initialize the VSTransformConfig memset( apply_data, 0, sizeof( vs_apply ) ); get_transform_config( &apply_data->conf, filter, frame ); // Initialize VSTransformData VSFrameInfo fi_src, fi_dst; vsFrameInfoInit( &fi_src, width, height, vs_format ); vsFrameInfoInit( &fi_dst, width, height, vs_format ); vsTransformDataInit( &apply_data->td, &apply_data->conf, &fi_src, &fi_dst ); // Initialize VSTransformations vsTransformationsInit( &apply_data->trans ); // Load the motions from the analyze step and convert them to VSTransformations FILE* f = mlt_fopen( filename, "r" ); VSManyLocalMotions mlms; if( vsReadLocalMotionsFile( f, &mlms ) == VS_OK ) { int i = 0; mlt_log_info( MLT_FILTER_SERVICE(filter), "Successfully loaded %d motions\n", vs_vector_size( &mlms ) ); vsLocalmotions2Transforms( &apply_data->td, &mlms, &apply_data->trans ); vsPreprocessTransforms( &apply_data->td, &apply_data->trans ); // Free the MultipleLocalMotions for( i = 0; i < vs_vector_size( &mlms ); i++ ) { LocalMotions* lms = (LocalMotions*)vs_vector_get( &mlms, i ); if( lms ) { vs_vector_del( lms ); } } vs_vector_del( &mlms ); data->apply_data = apply_data; } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Can not read results file: %s\n", filename ); destory_apply_data( apply_data ); data->apply_data = NULL; } if( f ) { fclose( f ); } } void destory_analyze_data( vs_analyze* analyze_data ) { if ( analyze_data ) { vsMotionDetectionCleanup( &analyze_data->md ); if( analyze_data->results ) { fclose( analyze_data->results ); analyze_data->results = NULL; } free( analyze_data ); } } static void init_analyze_data( mlt_filter filter, mlt_frame frame, VSPixelFormat vs_format, int width, int height ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); vs_data* data = (vs_data*)filter->child; vs_analyze* analyze_data = (vs_analyze*)calloc( 1, sizeof(vs_analyze) ); memset( analyze_data, 0, sizeof(vs_analyze) ); // Initialize a VSMotionDetectConfig const char* filterName = mlt_properties_get( properties, "mlt_service" ); VSMotionDetectConfig conf = vsMotionDetectGetDefaultConfig( filterName ); conf.shakiness = mlt_properties_get_int( properties, "shakiness" ); conf.accuracy = mlt_properties_get_int( properties, "accuracy" ); conf.stepSize = mlt_properties_get_int( properties, "stepsize" ); conf.contrastThreshold = mlt_properties_get_double( properties, "mincontrast" ); conf.show = mlt_properties_get_int( properties, "show" ); conf.virtualTripod = mlt_properties_get_int( properties, "tripod" ); // Initialize a VSFrameInfo VSFrameInfo fi; vsFrameInfoInit( &fi, width, height, vs_format ); // Initialize the saved VSMotionDetect vsMotionDetectInit( &analyze_data->md, &conf, &fi ); // Initialize the file to save results to char* filename = mlt_properties_get( properties, "filename" ); analyze_data->results = mlt_fopen( filename, "w" ); if ( vsPrepareFile( &analyze_data->md, analyze_data->results ) != VS_OK ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "Can not write to results file: %s\n", filename ); destory_analyze_data( analyze_data ); data->analyze_data = NULL; } else { data->analyze_data = analyze_data; } } static int apply_results( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height ) { int error = 0; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); vs_data* data = (vs_data*)filter->child; if ( check_apply_config( filter, frame ) || mlt_properties_get_int( properties, "reload" ) ) { mlt_properties_set_int( properties, "reload", 0 ); destory_apply_data( data->apply_data ); data->apply_data = NULL; } // Init transform data if necessary (first time) if ( !data->apply_data ) { init_apply_data( filter, frame, vs_format, width, height ); } if( data->apply_data ) { // Apply transformations to this image VSTransformData* td = &data->apply_data->td; VSTransformations* trans = &data->apply_data->trans; VSFrame vsFrame; vsFrameFillFromBuffer( &vsFrame, vs_image, vsTransformGetSrcFrameInfo( td ) ); trans->current = mlt_filter_get_position( filter, frame ); vsTransformPrepare( td, &vsFrame, &vsFrame ); VSTransform t = vsGetNextTransform( td, trans ); vsDoTransform( td, t ); vsTransformFinish( td ); } return error; } static void analyze_image( mlt_filter filter, mlt_frame frame, uint8_t* vs_image, VSPixelFormat vs_format, int width, int height ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); vs_data* data = (vs_data*)filter->child; mlt_position pos = mlt_filter_get_position( filter, frame ); // If any frames are skipped, analysis data will be incomplete. if( data->analyze_data && pos != data->analyze_data->last_position + 1 ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "Bad frame sequence\n" ); destory_analyze_data( data->analyze_data ); data->analyze_data = NULL; } if ( !data->analyze_data && pos == 0 ) { // Analysis must start on the first frame init_analyze_data( filter, frame, vs_format, width, height ); } if( data->analyze_data ) { // Initialize the VSFrame to be analyzed. VSMotionDetect* md = &data->analyze_data->md; LocalMotions localmotions; VSFrame vsFrame; vsFrameFillFromBuffer( &vsFrame, vs_image, &md->fi ); // Detect and save motions. if( vsMotionDetection( md, &localmotions, &vsFrame ) == VS_OK ) { vsWriteToFile( md, data->analyze_data->results, &localmotions); vs_vector_del( &localmotions ); } else { mlt_log_error( MLT_FILTER_SERVICE(filter), "Motion detection failed\n" ); destory_analyze_data( data->analyze_data ); data->analyze_data = NULL; } // Publish the motions if this is the last frame. if ( pos + 1 == mlt_filter_get_length2( filter, frame ) ) { mlt_log_info( MLT_FILTER_SERVICE(filter), "Analysis complete\n" ); destory_analyze_data( data->analyze_data ); data->analyze_data = NULL; mlt_properties_set( properties, "results", mlt_properties_get( properties, "filename" ) ); } else if ( data->analyze_data ) { data->analyze_data->last_position = pos; } } } static int get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = (mlt_filter)mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); uint8_t* vs_image = NULL; VSPixelFormat vs_format = PF_NONE; mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } // VS only works on progressive frames mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); *format = validate_format( *format ); int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); // Convert the received image to a format vid.stab can handle if ( !error ) { vs_format = mltimage_to_vsimage( *format, *width, *height, *image, &vs_image ); } if( vs_image ) { mlt_service_lock( MLT_FILTER_SERVICE(filter) ); char* results = mlt_properties_get( properties, "results" ); if( results && strcmp( results, "" ) ) { apply_results( filter, frame, vs_image, vs_format, *width, *height ); vsimage_to_mltimage( vs_image, *image, *format, *width, *height ); } else if (!mlt_properties_get(properties, "analyze") || mlt_properties_get_int(properties, "analyze")) { analyze_image( filter, frame, vs_image, vs_format, *width, *height ); if( mlt_properties_get_int( properties, "show" ) == 1 ) { vsimage_to_mltimage( vs_image, *image, *format, *width, *height ); } } mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); free_vsimage( vs_image, vs_format ); } return error; } static mlt_frame process_filter( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, get_image ); return frame; } static void filter_close( mlt_filter filter ) { vs_data* data = (vs_data*)filter->child; if ( data ) { if ( data->analyze_data ) destory_analyze_data( data->analyze_data ); if ( data->apply_data ) destory_apply_data( data->apply_data ); free( data ); } filter->close = NULL; filter->child = NULL; filter->parent.close = NULL; mlt_service_close( &filter->parent ); } extern "C" { mlt_filter filter_vidstab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new(); vs_data* data = (vs_data*)calloc( 1, sizeof(vs_data) ); if ( filter && data ) { data->analyze_data = NULL; data->apply_data = NULL; filter->close = filter_close; filter->child = data; filter->process = process_filter; mlt_properties properties = MLT_FILTER_PROPERTIES(filter); //properties for analyze mlt_properties_set( properties, "filename", "vidstab.trf" ); mlt_properties_set( properties, "shakiness", "4" ); mlt_properties_set( properties, "accuracy", "4" ); mlt_properties_set( properties, "stepsize", "6" ); mlt_properties_set( properties, "algo", "1" ); mlt_properties_set_double( properties, "mincontrast", 0.3 ); mlt_properties_set( properties, "show", "0" ); mlt_properties_set( properties, "tripod", "0" ); // properties for apply mlt_properties_set( properties, "smoothing", "15" ); mlt_properties_set( properties, "maxshift", "-1" ); mlt_properties_set( properties, "maxangle", "-1" ); mlt_properties_set( properties, "crop", "0" ); mlt_properties_set( properties, "invert", "0" ); mlt_properties_set( properties, "relative", "1" ); mlt_properties_set( properties, "zoom", "0" ); mlt_properties_set( properties, "optzoom", "1" ); mlt_properties_set_double( properties, "zoomspeed", 0.25 ); mlt_properties_set( properties, "reload", "0" ); mlt_properties_set( properties, "vid.stab.version", LIBVIDSTAB_VERSION ); init_vslog(); } else { if( filter ) { mlt_filter_close( filter ); } if( data ) { free( data ); } filter = NULL; } return filter; } } mlt-6.20.0/src/modules/vid.stab/filter_vidstab.yml000066400000000000000000000143671362234133600221060ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: vidstab title: Vid.Stab Detect and Transform copyright: Jakub Ksiezniak creator: Marco Gittler version: 2 license: GPL language: en url: http://public.hronopik.de/vid.stab/ tags: - Video description: Stabilize Video (for wiggly/rolling video) notes: > This filter requires two passes. The first pass performs analysis and stores the result in a file. Upon successful completion of the analysis, the "results" property is updated with the name of the file storing the results. The second pass applies the results to the image. To use with melt, use 'melt ... -consumer xml:output.mlt all=1 real_time=-1' for the first pass. Parallel processing (real_time < -1 or > 1) is not supported for the first pass. For the second pass, use output.mlt as the input. parameters: - identifier: results title: Analysis Results type: string description: > Set after analysis. Used during application. A set of image motion information that describes the analyzed clip. When results are not supplied, the filter computes the results and stores them in a file. This property is updated with the name of that file when the last frame has been processed. mutable: no - identifier: filename title: Target File Name type: string description: > Used during analysis. The name of the file to store the analysis results in. required: no readonly: no default: vidstab.trf widget: fileopen - identifier: shakiness title: Shakiness type: integer description: > Used during analysis. How shaky the video is. readonly: no required: no minimum: 1 maximum: 10 default: 4 mutable: no widget: spinner - identifier: accuracy title: Accuracy type: integer description: > Used during analysis. The accuracy of shakiness detection. readonly: no required: no minimum: 1 maximum: 15 default: 4 mutable: no widget: spinner - identifier: stepsize title: Stepsize type: integer description: > Used during analysis. The step size of the search process. readonly: no required: no minimum: 0 maximum: 100 default: 6 mutable: no widget: spinner - identifier: mincontrast title: Minimum Contrast type: float description: > Used during analysis. Below this contrast, a field is discarded. readonly: no required: no minimum: 0 maximum: 1 default: 0.3 mutable: no widget: spinner - identifier: show title: Show type: integer description: > Used during analysis. 0 = draw nothing 1 or 2 = show fields and transforms readonly: no required: no minimum: 0 maximum: 2 default: 0 mutable: no widget: spinner - identifier: tripod title: Tripod type: integer description: > Used during analysis and application. if 0, tripod mode is disabled. if > 0, specifies the frame to be used as a reference frame for tripod mode During application, relative and smoothing properties are both ignored if tripod mode is in use. readonly: no required: no minimum: 0 maximum: 100000 default: 0 mutable: no widget: spinner - identifier: smoothing title: Smoothing type: integer description: > Used during application. Number of frames for lowpass filtering (2N + 1 frames) readonly: no required: no minimum: 0 maximum: 100 default: 15 mutable: yes widget: spinner - identifier: maxshift title: Maxshift type: integer description: > Used during application. Maximum number of pixels to transform the image. -1 = no limit unit: pixels readonly: no required: no minimum: -1 maximum: 1000 default: -1 mutable: yes widget: spinner - identifier: maxangle title: Maxangle type: float description: > Used during application. Maximum angle to rotate, -1 = no limit unit: radians readonly: no required: no minimum: -1 maximum: 3.142 default: -1 mutable: yes widget: spinner - identifier: crop title: Crop type: boolean description: > Used during application. 0 = keep border, 1 = black background readonly: no required: no default: 0 mutable: yes - identifier: invert title: Invert type: boolean description: > Used during application. Invert transforms readonly: no required: no default: 0 mutable: yes - identifier: relative title: Relative type: boolean description: > Used during application. Consider transforms as absolute (0) or relative (1) readonly: no required: no default: 1 mutable: yes - identifier: zoom title: Zoom type: integer description: > Used during application. Additional zoom amount unit: percent readonly: no required: no minimum: -500 maximum: 500 default: 0 mutable: yes widget: spinner - identifier: optzoom title: Optimal Zoom type: integer description: > Used during application. Automatically determine optimal zoom. 1 - static zoom, 2 - adaptive zoom readonly: no required: no minimum: 0 maximum: 2 default: 1 mutable: yes widget: spinner - identifier: zoomspeed title: Optimal Zoom Speed type: float description: > Used during application. Zoom per frame (used when optzoom = 2) unit: percent readonly: no required: no minimum: 0 maximum: 1 default: 0.25 mutable: yes widget: spinner - identifier: reload title: Reload Results description: > The application should set this to 1 when it updates the results property to indicate that the results should be reloaded. type: boolean mutable: yes - identifier: analyze title: Commence analysis description: > This is optional, but it can help applications avoid race conditions writing to the file. When not set, it does not affect the start of analysis. When set, analysis only starts and the file written if true. type: boolean mlt-6.20.0/src/modules/vid.stab/gpl000066400000000000000000000000001362234133600170420ustar00rootroot00000000000000mlt-6.20.0/src/modules/videostab/000077500000000000000000000000001362234133600166155ustar00rootroot00000000000000mlt-6.20.0/src/modules/videostab/Makefile000066400000000000000000000017651362234133600202660ustar00rootroot00000000000000include ../../../config.mak CFLAGS += -I../.. ifdef SSE2_FLAGS CFLAGS += -msse2 endif LDFLAGS += -L../../framework -lmlt -lm TARGET = ../libmltvideostab$(LIBSUF) OBJS = factory.o \ filter_videostab.o filter_videostab2.o \ stabilize.o transform.o transform_image.o tlist.o\ stab/klt/convolve.o stab/klt/klt.o stab/klt/pyramid.o stab/klt/trackFeatures.o \ stab/klt/error.o stab/klt/klt_util.o stab/klt/selectGoodFeatures.o \ stab/estimate.o stab/resample.o stab/utils.o stab/vector.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)$(moduledir)" install -d $(DESTDIR)$(mltdatadir)/videostab install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/videostab" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/videostab/factory.c000066400000000000000000000033001362234133600204240ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (c) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include extern mlt_filter filter_videostab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_videostab2_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties videostab_metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/videostab/filter_%s.yml", mlt_environment( "MLT_DATA" ), id ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( filter_type, "videostab", filter_videostab_init ); MLT_REGISTER_METADATA( filter_type, "videostab", videostab_metadata, NULL ); MLT_REGISTER( filter_type, "videostab2", filter_videostab2_init ); MLT_REGISTER_METADATA( filter_type, "videostab2", videostab_metadata, NULL ); } mlt-6.20.0/src/modules/videostab/filter_videostab.c000066400000000000000000000143521362234133600223130ustar00rootroot00000000000000/* * filter_imagestab.c -- video stabilization with code from http://vstab.sourceforge.net/ * Copyright (c) 2011 Marco Gittler * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "stab/vector.h" #include "stab/utils.h" #include "stab/estimate.h" #include "stab/resample.h" typedef struct { mlt_filter parent; int initialized; int* lanc_kernels; es_ctx *es; vc *pos_i; vc *pos_h; vc *pos_y; rs_ctx *rs; } *videostab; static void serialize_vectors( videostab self, mlt_position length ) { mlt_geometry g = mlt_geometry_init(); if ( g ) { struct mlt_geometry_item_s item; int i; // Initialize geometry item item.key = item.f[0] = item.f[1] = 1; item.f[2] = item.f[3] = item.f[4] = 0; for ( i = 0; i < length; i++ ) { // Set the geometry item item.frame = i; item.x = self->pos_h[i].x; item.y = self->pos_h[i].y; // Add the geometry item mlt_geometry_insert( g, &item ); } // Put the analysis results in a property mlt_geometry_set_length( g, length ); mlt_properties_set_data( MLT_FILTER_PROPERTIES( self->parent ), "vectors", g, 0, (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise ); } } static void deserialize_vectors( videostab self, char *vectors, mlt_position length ) { mlt_geometry g = mlt_geometry_init(); // Parse the property as a geometry if ( g && !mlt_geometry_parse( g, vectors, length, -1, -1 ) ) { struct mlt_geometry_item_s item; int i; // Copy the geometry items to a vc array for interp() for ( i = 0; i < length; i++ ) { mlt_geometry_fetch( g, &item, i ); self->pos_h[i].x = item.x; self->pos_h[i].y = item.y; } } else { mlt_log_warning( MLT_FILTER_SERVICE(self->parent), "failed to parse vectors\n" ); } // We are done with this mlt_geometry if ( g ) mlt_geometry_close( g ); } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = mlt_frame_pop_service( frame ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } *format = mlt_image_rgb24; mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "consumer_deinterlace", 1 ); int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( !error && *image ) { videostab self = filter->child; mlt_position length = mlt_filter_get_length2( filter, frame ); int h = *height; int w = *width; // Service locks are for concurrency control mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); if ( !self->initialized ) { // Initialize our context self->initialized = 1; self->es = es_init( w, h ); self->pos_i = (vc*) malloc( length * sizeof(vc) ); self->pos_h = (vc*) malloc( length * sizeof(vc) ); self->pos_y = (vc*) malloc( h * sizeof(vc) ); self->rs = rs_init( w, h ); } char *vectors = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "vectors" ); if ( !vectors ) { // Analyse int pos = (int) mlt_filter_get_position( filter, frame ); self->pos_i[pos] = vc_add( pos == 0 ? vc_zero() : self->pos_i[pos - 1], es_estimate( self->es, *image ) ); // On last frame if ( pos == length - 1 ) { mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); double fps = mlt_profile_fps( profile ); // Filter and store the results hipass( self->pos_i, self->pos_h, length, fps ); serialize_vectors( self, length ); } } else { // Apply if ( self->initialized != 2 ) { // Load analysis results from property self->initialized = 2; deserialize_vectors( self, vectors, length ); } if ( self->initialized == 2 ) { // Stabilize float shutter_angle = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame) , "shutterangle" ); float pos = mlt_filter_get_position( filter, frame ); int i; for (i = 0; i < h; i ++) self->pos_y[i] = interp( self->lanc_kernels,self->pos_h, length, pos + (i - h / 2.0) * shutter_angle / (h * 360.0) ); rs_resample( self->lanc_kernels,self->rs, *image, self->pos_y ); } } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } void filter_close( mlt_filter parent ) { videostab self = parent->child; if ( self->es ) es_free( self->es ); free( self->pos_i ); free( self->pos_h ); free( self->pos_y ); if ( self->rs ) rs_free( self->rs ); if ( self->lanc_kernels) free_lanc_kernels(self->lanc_kernels); free( self ); parent->close = NULL; parent->child = NULL; } mlt_filter filter_videostab_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { videostab self = calloc( 1, sizeof(*self) ); if ( self ) { mlt_filter parent = mlt_filter_new(); if ( !parent ) { free( self ); return NULL; } parent->child = self; parent->close = filter_close; parent->process = filter_process; self->parent = parent; mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "shutterangle", "0" ); // 0 - 180 , default 0 self->lanc_kernels=prepare_lanc_kernels(); return parent; } return NULL; } mlt-6.20.0/src/modules/videostab/filter_videostab.yml000066400000000000000000000025261362234133600226720ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: videostab title: Videostab (*deprecated*) copyright: Copyright (C) 2011 Marco Gittler creator: Marco Gittler < contributor: - Dan Dennedy version: 0.1 license: GPL language: en url: http://vstab.sourceforge.net/ creator: Marco Gittler tags: - Video description: Stabilize Video (for wiggly video) notes: > This filter is deprecated and will eventually be removed; use the vidstab filter instead. This filter requires two passes. The first pass performs analysis and stores the result in the vectors property. The second pass applies the vectors to the image. To use with melt, use 'melt ... -consumer xml:output.mlt all=1' for the first pass. For the second pass, use output.mlt as the input. parameters: - identifier: shutterangle title: Shutterangle type: integer description: Angle that Images could be maximum rotated readonly: no required: no minimum: 0 maximum: 180 default: 0 mutable: yes widget: spinner - identifier: vectors title: Vectors type: geometry description: > A set of X/Y coordinates by which to adjust the image. When this is not supplied, the filter computes the vectors and stores them in this property when the last frame has been processed. mlt-6.20.0/src/modules/videostab/filter_videostab2.c000066400000000000000000000236661362234133600224050ustar00rootroot00000000000000/* * filter_imagestab.c -- video stabilization with code from http://vstab.sourceforge.net/ * Copyright (c) 2011 Marco Gittler * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "stabilize.h" #include "transform_image.h" typedef struct { StabData* stab; TransformData* trans; int initialized; void* parent; } videostab2_data; static void serialize_vectors( videostab2_data* self, mlt_position length ) { mlt_geometry g = mlt_geometry_init(); if ( g ) { struct mlt_geometry_item_s item; mlt_position i; // Initialize geometry item item.key = item.f[0] = item.f[1] = item.f[2] = item.f[3] = 1; item.f[4] = 0; tlist* transform_data =self->stab->transs; for ( i = 0; i < length; i++ ) { // Set the geometry item item.frame = i; if (transform_data){ if ( transform_data->data){ Transform* t=transform_data->data; item.x=t->x; item.y=t->y; item.w=t->alpha; item.h=t->zoom; transform_data=transform_data->next; } } // Add the geometry item mlt_geometry_insert( g, &item ); } // Put the analysis results in a property mlt_geometry_set_length( g, length ); mlt_properties_set_data( MLT_FILTER_PROPERTIES( (mlt_filter) self->parent ), "vectors", g, 0, (mlt_destructor) mlt_geometry_close, (mlt_serialiser) mlt_geometry_serialise ); } } // scale zoom implements the factor that the vetcors must be scaled since the vector is calculated for real with, now we need it for (scaled)width Transform* deserialize_vectors( char *vectors, mlt_position length ,float scale_zoom ) { mlt_geometry g = mlt_geometry_init(); Transform* tx=NULL; // Parse the property as a geometry if ( g && !mlt_geometry_parse( g, vectors, length, -1, -1 ) ) { struct mlt_geometry_item_s item; int i; tx=calloc(1,sizeof(Transform)*length); // Copy the geometry items to a vc array for interp() for ( i = 0; i < length; i++ ) { mlt_geometry_fetch( g, &item, i ); Transform t; t.x=scale_zoom*item.x; t.y=scale_zoom*item.y; t.alpha=item.w; t.zoom=scale_zoom*item.h; t.extra=0; tx[i]=t; } } else { //mlt_log_warning( NULL, "failed to parse vectors\n" ); } // We are done with this mlt_geometry if ( g ) mlt_geometry_close( g ); return tx; } static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = mlt_frame_pop_service( frame ); char *vectors = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "vectors" ); mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); // Disable consumer scaling if (profile && profile->width && profile->height) { *width = profile->width; *height = profile->height; } *format = mlt_image_yuv422; if (vectors) *format= mlt_image_rgb24; mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "consumer_deinterlace", 1 ); int error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( !error && *image ) { videostab2_data* data = filter->child; if ( data==NULL ) { // big error, abort return 1; } mlt_position length = mlt_filter_get_length2( filter, frame ); int h = *height; int w = *width; // Service locks are for concurrency control mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); // Handle signal from app to re-init data if ( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "refresh" ) ) { mlt_properties_set( MLT_FILTER_PROPERTIES(filter) , "refresh", NULL ); data->initialized = 0; } if ( !vectors) { if ( !data->initialized ) { // Initialize our context data->initialized = 1; data->stab->width=w; data->stab->height=h; if (*format==mlt_image_yuv420p) data->stab->framesize=w*h* 3/2;//( mlt_image_format_size ( *format, w,h , 0) ; // 3/2 =1 too small if (*format==mlt_image_yuv422) data->stab->framesize=w*h; data->stab->shakiness = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "shakiness" ); data->stab->accuracy = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "accuracy" ); data->stab->stepsize = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "stepsize" ); data->stab->algo = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "algo" ); data->stab->show = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter) , "show" ); data->stab->contrast_threshold = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter) , "mincontrast" ); stabilize_configure(data->stab); } // Analyse mlt_position pos = mlt_filter_get_position( filter, frame ); stabilize_filter_video ( data->stab , *image, *format ); // On last frame if ( pos == length - 1 ) { serialize_vectors( data , length ); } } else { if ( data->initialized!=1 ) { char *interps = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "rescale.interp" ); if ( data->initialized != 2 ) { // Load analysis results from property data->initialized = 2; int interp = 2; // default to bilinear float scale_zoom=1.0; if ( *width != mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.media.width" ) ) scale_zoom = (float) *width / (float) mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.media.width" ); if ( strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 ) interp = 0; else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 ) interp = 1; data->trans->interpoltype = interp; data->trans->smoothing = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "smoothing" ); data->trans->maxshift = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "maxshift" ); data->trans->maxangle = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter), "maxangle" ); data->trans->crop = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "crop" ); data->trans->invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "invert" ); data->trans->relative = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "relative" ); data->trans->zoom = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "zoom" ); data->trans->optzoom = mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "optzoom" ); data->trans->sharpen = mlt_properties_get_double( MLT_FILTER_PROPERTIES(filter), "sharpen" ); transform_configure(data->trans,w,h,*format ,*image, deserialize_vectors( vectors, length , scale_zoom ),length); } if ( data->initialized == 2 ) { // Stabilize float pos = mlt_filter_get_position( filter, frame ); data->trans->current_trans=pos; transform_filter_video(data->trans, *image, *format ); } } } mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); } return error; } static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service( frame, filter ); mlt_frame_push_get_image( frame, filter_get_image ); return frame; } static void filter_close( mlt_filter parent ) { videostab2_data* data = parent->child; if (data){ if (data->stab) stabilize_stop(data->stab); if (data->trans){ free(data->trans->src); free (data->trans); } free( data ); } parent->close = NULL; parent->child = NULL; } mlt_filter filter_videostab2_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { videostab2_data* data= calloc( 1, sizeof(videostab2_data)); if ( data ) { data->stab = calloc( 1, sizeof(StabData) ); if ( !data->stab ) { free( data ); return NULL; } data->trans = calloc( 1, sizeof (TransformData) ) ; if ( !data->trans ) { free( data->stab ); free( data ); return NULL; } mlt_filter parent = mlt_filter_new(); if ( !parent ) { free( data->trans ); free( data->stab ); free( data ); return NULL; } parent->child = data; parent->close = filter_close; parent->process = filter_process; data->parent = parent; //properties for stabilize mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "shakiness", "4" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "accuracy", "4" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "stepsize", "6" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "algo", "1" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "mincontrast", "0.3" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "show", "0" ); //properties for transform mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "smoothing", "10" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "maxshift", "-1" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "maxangle", "-1" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "crop", "0" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "invert", "0" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "relative", "1" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "zoom", "0" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "optzoom", "1" ); mlt_properties_set( MLT_FILTER_PROPERTIES(parent), "sharpen", "0.8" ); return parent; } return NULL; } mlt-6.20.0/src/modules/videostab/filter_videostab2.yml000066400000000000000000000115321362234133600227510ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: videostab2 title: Videostab2 (*deprecated*) copyright: Copyright (C) 2011 Marco Gittler creator: Marco Gittler version: 0.1 license: GPL language: en url: http://public.hronopik.de/vid.stab/ tags: - Video description: Stabilize Video (for wiggly/rolling video) notes: > This filter is deprecated and will eventually be removed; use the vidstab filter instead. This filter requires two passes. The first pass performs analysis and stores the result in the vectors property. The second pass applies the vectors to the image. To use with melt, use 'melt ... -consumer xml:output.mlt all=1' for the first pass. For the second pass, use output.mlt as the input. parameters: - identifier: vectors (transform) title: Vectors type: geometry description: > A set of X/Y coordinates by which to adjust the image. When this is not supplied, the filter computes the vectors and stores them in this property when the last frame has been processed. - identifier: shakiness title: Shakiness type: integer description: How shaky is the video (analysis) readonly: no required: no minimum: 1 maximum: 10 default: 4 mutable: yes widget: spinner - identifier: accuracy title: Accuracy type: integer description: Accuracy of shakiness detection (analysis) readonly: no required: no minimum: 1 maximum: 15 default: 4 mutable: yes widget: spinner - identifier: stepsize title: Stepsize type: integer description: Step size of search process (analysis) readonly: no required: no minimum: 0 maximum: 100 default: 6 mutable: yes widget: spinner - identifier: algo title: Algorithm type: integer description: 0 = brute force (translation only), 1 = small measurement fields (analysis) readonly: no required: no minimum: 0 maximum: 1 default: 1 mutable: yes widget: spinner - identifier: mincontrast title: Minimum Contrast type: float description: Below this contrast, a field is discarded (analysis) readonly: no required: no minimum: 0 maximum: 1 default: 0.3 mutable: yes widget: spinner - identifier: show title: Show type: integer description: 0 = draw nothing, 1 or 2 = show fields and transforms (analysis) readonly: no required: no minimum: 0 maximum: 2 default: 0 mutable: yes widget: spinner - identifier: smoothing title: Smoothing type: integer description: number of frames for lowpass filtering (2N + 1 frames) (transform) readonly: no required: no minimum: 0 maximum: 100 default: 10 mutable: yes widget: spinner - identifier: maxshift title: Maxshift type: integer description: maximum translation, -1 = no limit (transform) unit: pixels readonly: no required: no minimum: -1 maximum: 1000 default: -1 mutable: yes widget: spinner - identifier: maxangle title: Maxangle type: float description: max angle to rotate, -1 = no limit (transform) unit: radians readonly: no required: no minimum: -1 maximum: 3.142 default: -1 mutable: yes widget: spinner - identifier: crop title: Crop type: integer description: 0 = keep border, 1 = black background (transform) readonly: no required: no minimum: 0 maximum: 1 default: 0 mutable: yes widget: spinner - identifier: invert title: Invert type: integer description: Invert transforms (transform) readonly: no required: no minimum: 0 maximum: 1 default: 0 mutable: yes widget: spinner - identifier: relative title: Relative Transform type: integer description: 0 = absolute, 1 = relative (transform) readonly: no required: no minimum: 0 maximum: 1 default: 1 mutable: yes widget: spinner - identifier: zoom title: Zoom type: integer description: additional zoom amount (transform) unit: percent readonly: no required: no minimum: -500 maximum: 500 default: 0 mutable: yes widget: spinner - identifier: optzoom title: Optimal Zoom type: integer description: automatically determine optimal zoom (transform) readonly: no required: no minimum: 0 maximum: 1 default: 1 mutable: yes widget: spinner - identifier: sharpen title: Sharpen Image type: float description: amount of sharpening (transform) readonly: no required: no minimum: 0 maximum: 10 default: 0.8 mutable: yes widget: spinner - identifier: refresh description: > Applications should set this when it updates a transform parameter. type: integer minimum: 0 maximum: 1 mlt-6.20.0/src/modules/videostab/gpl000066400000000000000000000000001362234133600173100ustar00rootroot00000000000000mlt-6.20.0/src/modules/videostab/stab/000077500000000000000000000000001362234133600175465ustar00rootroot00000000000000mlt-6.20.0/src/modules/videostab/stab/estimate.c000066400000000000000000000063051362234133600215310ustar00rootroot00000000000000/* * Video stabilizer * * Copyright (c) 2008 Lenny * * 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. * */ #include #include #include #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(_WIN32) && !defined(__NetBSD__) && !defined(__OpenBSD__) #include #endif #include "estimate.h" #include "vector.h" #include "utils.h" #if !defined(MAXFLOAT) #define MAXFLOAT HUGE_VAL #endif es_ctx *es_init(int nc, int nr) { es_ctx *es = (es_ctx *)malloc(sizeof(es_ctx)); es->tc = KLTCreateTrackingContext(); es->tc->sequentialMode = TRUE; es->tc->min_eigenvalue = 8; es->tc->verbose = FALSE; KLTChangeTCPyramid(es->tc, 31); KLTUpdateTCBorder(es->tc); es->fr[0] = (KLT_PixelType *)malloc(nc * nr * sizeof(KLT_PixelType)); es->fr[1] = (KLT_PixelType *)malloc(nc * nr * sizeof(KLT_PixelType)); es->fl = KLTCreateFeatureList(64); es->dv = (vc *)malloc(64 * sizeof(vc)); es->nv = 0; es->nc = nc; es->nr = nr; es->ff = FALSE; return es; } vc es_estimate(es_ctx *es, unsigned char *fr) { KLT_PixelType *t; int is, id; t = es->fr[0]; es->fr[0] = es->fr[1]; es->fr[1] = t; for (is = 0, id = 0; id < es->nc * es->nr; is += 3, id ++) es->fr[1][id] = (fr[is + 0] * 30 + fr[is + 1] * 59 + fr[is + 2] * 11) / 100; if (es->ff == FALSE) { es->ff = TRUE; } else { vc bv = vc_set(0.0, 0.0); float be = MAXFLOAT; int i, i2; KLTSelectGoodFeatures( es->tc, es->fr[0], es->nc, es->nr, es->fl ); for (i = 0; i < es->fl->nFeatures; i ++) es->dv[i] = vc_set(es->fl->feature[i]->x, es->fl->feature[i]->y); KLTTrackFeatures( es->tc, es->fr[0], es->fr[1], es->nc, es->nr, es->fl ); es->nv = 0; for (i = 0; i < es->fl->nFeatures; i ++) { if (es->fl->feature[i]->val == KLT_TRACKED) { es->dv[es->nv] = vc_set( es->fl->feature[i]->x - es->dv[i].x, es->fl->feature[i]->y - es->dv[i].y ); es->nv ++; } } for (i = 0; i < es->nv; i ++) { float ce = 0.0; for (i2 = 0; i2 < es->nv; i2 ++) ce += vc_len(vc_sub(es->dv[i2], es->dv[i])); if (ce < be) { bv = es->dv[i]; be = ce; } } return bv; } return vc_zero(); } void es_free(es_ctx *es) { free(es->dv); free(es->fr[0]); free(es->fr[1]); KLTFreeFeatureList(es->fl); KLTFreeTrackingContext(es->tc); free(es); } mlt-6.20.0/src/modules/videostab/stab/estimate.h000066400000000000000000000005361362234133600215360ustar00rootroot00000000000000#ifndef ESTIMATE_H #define ESTIMATE_H #include "klt/klt.h" #include "vector.h" typedef struct { KLT_TrackingContext tc; KLT_PixelType *fr[2]; KLT_FeatureList fl; vc *dv; int nv; int nc, nr; int ff; } es_ctx; es_ctx *es_init(int, int); vc es_estimate(es_ctx *, unsigned char *); void es_free(es_ctx *); #endif mlt-6.20.0/src/modules/videostab/stab/klt/000077500000000000000000000000001362234133600203405ustar00rootroot00000000000000mlt-6.20.0/src/modules/videostab/stab/klt/base.h000066400000000000000000000012661362234133600214300ustar00rootroot00000000000000/********************************************************************* * base.h *********************************************************************/ #ifndef _BASE_H_ #define _BASE_H_ #ifndef uchar #define uchar unsigned char #endif #ifndef schar #define schar signed char #endif #ifndef uint #define uint unsigned int #endif #ifndef ushort #define ushort unsigned short #endif #ifndef ulong #define ulong unsigned long #endif #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif #ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) #endif #define max3(a,b,c) ((a) > (b) ? max((a),(c)) : max((b),(c))) #define min3(a,b,c) ((a) < (b) ? min((a),(c)) : min((b),(c))) #endif mlt-6.20.0/src/modules/videostab/stab/klt/convolve.c000066400000000000000000000154221362234133600223430ustar00rootroot00000000000000/********************************************************************* * convolve.c *********************************************************************/ /* Standard includes */ #include #include /* malloc(), realloc() */ /* Our includes */ #include "base.h" #include "error.h" #include "convolve.h" #include "klt_util.h" /* printing */ #define MAX_KERNEL_WIDTH 71 typedef struct { int width; float data[MAX_KERNEL_WIDTH]; } ConvolutionKernel; /* Kernels */ ConvolutionKernel gauss_kernel; ConvolutionKernel gaussderiv_kernel; float sigma_last = -10.0; /********************************************************************* * _KLTToFloatImage * * Given a pointer to image data (probably unsigned chars), copy * data to a float image. */ void _KLTToFloatImage( KLT_PixelType *img, int ncols, int nrows, _KLT_FloatImage floatimg) { KLT_PixelType *ptrend = img + ncols*nrows; float *ptrout = floatimg->data; floatimg->ncols = ncols; floatimg->nrows = nrows; while (img < ptrend) *ptrout++ = (float) *img++; } /********************************************************************* * _computeKernels */ void _computeKernels( float sigma, ConvolutionKernel *gauss, ConvolutionKernel *gaussderiv) { const float factor = 0.01f; /* for truncating tail */ int i; /* Compute kernels, and automatically determine widths */ { const int hw = MAX_KERNEL_WIDTH / 2; float max_gauss = 1.0f, max_gaussderiv = (float) (sigma*exp(-0.5f)); /* Compute gauss and deriv */ for (i = -hw ; i <= hw ; i++) { gauss->data[i+hw] = (float) exp(-i*i / (2*sigma*sigma)); gaussderiv->data[i+hw] = -i * gauss->data[i+hw]; } /* Compute widths */ gauss->width = MAX_KERNEL_WIDTH; for (i = -hw ; fabs(gauss->data[i+hw] / max_gauss) < factor ; i++, gauss->width -= 2); gaussderiv->width = MAX_KERNEL_WIDTH; for (i = -hw ; fabs(gaussderiv->data[i+hw] / max_gaussderiv) < factor ; i++, gaussderiv->width -= 2); if (gauss->width == MAX_KERNEL_WIDTH || gaussderiv->width == MAX_KERNEL_WIDTH) KLTError("(_computeKernels) MAX_KERNEL_WIDTH %d is too small for " "a sigma of %f", MAX_KERNEL_WIDTH, sigma); } /* Shift if width less than MAX_KERNEL_WIDTH */ for (i = 0 ; i < gauss->width ; i++) gauss->data[i] = gauss->data[i+(MAX_KERNEL_WIDTH-gauss->width)/2]; for (i = 0 ; i < gaussderiv->width ; i++) gaussderiv->data[i] = gaussderiv->data[i+(MAX_KERNEL_WIDTH-gaussderiv->width)/2]; /* Normalize gauss and deriv */ { const int hw = gaussderiv->width / 2; float den; den = 0.0; for (i = 0 ; i < gauss->width ; i++) den += gauss->data[i]; for (i = 0 ; i < gauss->width ; i++) gauss->data[i] /= den; den = 0.0; for (i = -hw ; i <= hw ; i++) den -= i*gaussderiv->data[i+hw]; for (i = -hw ; i <= hw ; i++) gaussderiv->data[i+hw] /= den; } sigma_last = sigma; } /********************************************************************* * _KLTGetKernelWidths * */ void _KLTGetKernelWidths( float sigma, int *gauss_width, int *gaussderiv_width) { _computeKernels(sigma, &gauss_kernel, &gaussderiv_kernel); *gauss_width = gauss_kernel.width; *gaussderiv_width = gaussderiv_kernel.width; } /********************************************************************* * _convolveImageHoriz */ void _convolveImageHoriz( _KLT_FloatImage imgin, ConvolutionKernel kernel, _KLT_FloatImage imgout) { float *ptrrow = imgin->data; /* Points to row's first pixel */ float *ptrout = imgout->data, /* Points to next output pixel */ *ppp; float sum; int radius = kernel.width / 2; int ncols = imgin->ncols, nrows = imgin->nrows; int i, j, k; /* For each row, do ... */ for (j = 0 ; j < nrows ; j++) { /* Zero leftmost columns */ for (i = 0 ; i < radius ; i++) *ptrout++ = 0.0; /* Convolve middle columns with kernel */ for ( ; i < ncols - radius ; i++) { ppp = ptrrow + i - radius; sum = 0.0; for (k = kernel.width-1 ; k >= 0 ; k--) sum += *ppp++ * kernel.data[k]; *ptrout++ = sum; } /* Zero rightmost columns */ for ( ; i < ncols ; i++) *ptrout++ = 0.0; ptrrow += ncols; } } /********************************************************************* * _convolveImageVert */ void _convolveImageVert( _KLT_FloatImage imgin, ConvolutionKernel kernel, _KLT_FloatImage imgout) { float *ptrcol = imgin->data; /* Points to row's first pixel */ float *ptrout = imgout->data, /* Points to next output pixel */ *ppp; float sum; int radius = kernel.width / 2; int ncols = imgin->ncols, nrows = imgin->nrows; int i, j, k; /* For each column, do ... */ for (i = 0 ; i < ncols ; i++) { /* Zero topmost rows */ for (j = 0 ; j < radius ; j++) { *ptrout = 0.0; ptrout += ncols; } /* Convolve middle rows with kernel */ for ( ; j < nrows - radius ; j++) { ppp = ptrcol + ncols * (j - radius); sum = 0.0; for (k = kernel.width-1 ; k >= 0 ; k--) { sum += *ppp * kernel.data[k]; ppp += ncols; } *ptrout = sum; ptrout += ncols; } /* Zero bottommost rows */ for ( ; j < nrows ; j++) { *ptrout = 0.0; ptrout += ncols; } ptrcol++; ptrout -= nrows * ncols - 1; } } /********************************************************************* * _convolveSeparate */ void _convolveSeparate( _KLT_FloatImage imgin, ConvolutionKernel horiz_kernel, ConvolutionKernel vert_kernel, _KLT_FloatImage imgout) { /* Create temporary image */ _KLT_FloatImage tmpimg; tmpimg = _KLTCreateFloatImage(imgin->ncols, imgin->nrows); /* Do convolution */ _convolveImageHoriz(imgin, horiz_kernel, tmpimg); _convolveImageVert(tmpimg, vert_kernel, imgout); /* Free memory */ _KLTFreeFloatImage(tmpimg); } /********************************************************************* * _KLTComputeGradients */ void _KLTComputeGradients( _KLT_FloatImage img, float sigma, _KLT_FloatImage gradx, _KLT_FloatImage grady) { /* Compute kernels, if necessary */ if (fabs(sigma - sigma_last) > 0.05) _computeKernels(sigma, &gauss_kernel, &gaussderiv_kernel); _convolveSeparate(img, gaussderiv_kernel, gauss_kernel, gradx); _convolveSeparate(img, gauss_kernel, gaussderiv_kernel, grady); } /********************************************************************* * _KLTComputeSmoothedImage */ void _KLTComputeSmoothedImage( _KLT_FloatImage img, float sigma, _KLT_FloatImage smooth) { /* Compute kernel, if necessary; gauss_deriv is not used */ if (fabs(sigma - sigma_last) > 0.05) _computeKernels(sigma, &gauss_kernel, &gaussderiv_kernel); _convolveSeparate(img, gauss_kernel, gauss_kernel, smooth); } mlt-6.20.0/src/modules/videostab/stab/klt/convolve.h000066400000000000000000000012111362234133600223370ustar00rootroot00000000000000/********************************************************************* * convolve.h *********************************************************************/ #ifndef _CONVOLVE_H_ #define _CONVOLVE_H_ #include "klt.h" #include "klt_util.h" void _KLTToFloatImage( KLT_PixelType *img, int ncols, int nrows, _KLT_FloatImage floatimg); void _KLTComputeGradients( _KLT_FloatImage img, float sigma, _KLT_FloatImage gradx, _KLT_FloatImage grady); void _KLTGetKernelWidths( float sigma, int *gauss_width, int *gaussderiv_width); void _KLTComputeSmoothedImage( _KLT_FloatImage img, float sigma, _KLT_FloatImage smooth); #endif mlt-6.20.0/src/modules/videostab/stab/klt/error.c000066400000000000000000000020301362234133600216300ustar00rootroot00000000000000/********************************************************************* * error.c * * Error and warning messages, and system commands. *********************************************************************/ /* Standard includes */ #include #include #include #include /********************************************************************* * KLTError * * Prints an error message and dies. * * INPUTS * exactly like printf */ void KLTError(char *fmt, ...) { va_list args; va_start(args, fmt); mlt_log_error(NULL, "KLT Error: "); mlt_log_error(NULL, fmt, args); mlt_log_error(NULL, "\n"); va_end(args); } /********************************************************************* * KLTWarning * * Prints a warning message. * * INPUTS * exactly like printf */ void KLTWarning(char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "KLT Warning: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); fflush(stderr); va_end(args); } mlt-6.20.0/src/modules/videostab/stab/klt/error.h000066400000000000000000000004611362234133600216430ustar00rootroot00000000000000/********************************************************************* * error.h *********************************************************************/ #ifndef _ERROR_H_ #define _ERROR_H_ #include #include void KLTError(char *fmt, ...); void KLTWarning(char *fmt, ...); #endif mlt-6.20.0/src/modules/videostab/stab/klt/klt.c000066400000000000000000000240321362234133600212770ustar00rootroot00000000000000/********************************************************************* * klt.c * * Kanade-Lucas-Tomasi tracker *********************************************************************/ /* Standard includes */ #include /* logf() */ #include /* malloc() */ /* Our includes */ #include "base.h" #include "convolve.h" #include "error.h" #include "klt.h" #include "pyramid.h" /********************************************************************* * KLTCreateTrackingContext * */ KLT_TrackingContext KLTCreateTrackingContext() { KLT_TrackingContext tc = NULL; /* Allocate memory */ tc = (KLT_TrackingContext) calloc(1, sizeof(KLT_TrackingContextRec)); /* Set values to default values */ tc->mindist = 10; tc->window_width = 7; tc->window_height = 7; tc->sequentialMode = FALSE; tc->smoothBeforeSelecting = TRUE; tc->min_eigenvalue = 1; tc->min_determinant = 0.01; tc->max_iterations = 10; tc->min_displacement = 0.1; tc->max_residue = 10.0; tc->grad_sigma = 1.0; tc->smooth_sigma_fact = 0.1; tc->pyramid_sigma_fact = 0.9; tc->step_factor = 1.0; tc->nSkippedPixels = 0; tc->pyramid_last = NULL; tc->pyramid_last_gradx = NULL; tc->pyramid_last_grady = NULL; tc->verbose = TRUE; /* Change nPyramidLevels and subsampling */ KLTChangeTCPyramid(tc, 31); /* Update border, which is dependent upon */ /* smooth_sigma_fact, pyramid_sigma_fact, window_size, and subsampling */ KLTUpdateTCBorder(tc); return(tc); } /********************************************************************* * KLTCreateFeatureList * */ KLT_FeatureList KLTCreateFeatureList( int nFeatures) { KLT_FeatureList fl; KLT_Feature first; int nbytes = sizeof(KLT_FeatureListRec) + nFeatures * sizeof(KLT_Feature) + nFeatures * sizeof(KLT_FeatureRec); int i; /* Allocate memory for feature list */ fl = (KLT_FeatureList) malloc(nbytes); /* Set parameters */ fl->nFeatures = nFeatures; /* Set pointers */ fl->feature = (KLT_Feature *) (fl + 1); first = (KLT_Feature) (fl->feature + nFeatures); for (i = 0 ; i < nFeatures ; i++) fl->feature[i] = first + i; /* Return feature list */ return(fl); } /********************************************************************* * KLTPrintTrackingContext */ void KLTPrintTrackingContext( KLT_TrackingContext tc) { fprintf(stderr, "\n\nTracking context:\n\n"); fprintf(stderr, "\tmindist = %d\n", tc->mindist); fprintf(stderr, "\twindow_width = %d\n", tc->window_width); fprintf(stderr, "\twindow_height = %d\n", tc->window_height); fprintf(stderr, "\tsequentialMode = %s\n", tc->sequentialMode ? "TRUE" : "FALSE"); fprintf(stderr, "\tsmoothBeforeSelecting = %s\n", tc->smoothBeforeSelecting ? "TRUE" : "FALSE"); fprintf(stderr, "\tmin_eigenvalue = %d\n", tc->min_eigenvalue); fprintf(stderr, "\tmin_determinant = %f\n", tc->min_determinant); fprintf(stderr, "\tmin_displacement = %f\n", tc->min_displacement); fprintf(stderr, "\tmax_iterations = %d\n", tc->max_iterations); fprintf(stderr, "\tmax_residue = %f\n", tc->max_residue); fprintf(stderr, "\tgrad_sigma = %f\n", tc->grad_sigma); fprintf(stderr, "\tsmooth_sigma_fact = %f\n", tc->smooth_sigma_fact); fprintf(stderr, "\tpyramid_sigma_fact = %f\n", tc->pyramid_sigma_fact); fprintf(stderr, "\tnSkippedPixels = %d\n", tc->nSkippedPixels); fprintf(stderr, "\tborderx = %d\n", tc->borderx); fprintf(stderr, "\tbordery = %d\n", tc->bordery); fprintf(stderr, "\tnPyramidLevels = %d\n", tc->nPyramidLevels); fprintf(stderr, "\tsubsampling = %d\n", tc->subsampling); fprintf(stderr, "\n\tpyramid_last = %s\n", (tc->pyramid_last!=NULL) ? "points to old image" : "NULL"); fprintf(stderr, "\tpyramid_last_gradx = %s\n", (tc->pyramid_last_gradx!=NULL) ? "points to old image" : "NULL"); fprintf(stderr, "\tpyramid_last_grady = %s\n", (tc->pyramid_last_grady!=NULL) ? "points to old image" : "NULL"); fprintf(stderr, "\n\n"); } /********************************************************************* * KLTChangeTCPyramid * */ void KLTChangeTCPyramid( KLT_TrackingContext tc, int search_range) { float window_halfwidth; float subsampling; /* Check window size (and correct if necessary) */ if (tc->window_width % 2 != 1) { tc->window_width = tc->window_width+1; KLTWarning("(KLTChangeTCPyramid) Window width must be odd. " "Changing to %d.\n", tc->window_width); } if (tc->window_height % 2 != 1) { tc->window_height = tc->window_height+1; KLTWarning("(KLTChangeTCPyramid) Window height must be odd. " "Changing to %d.\n", tc->window_height); } if (tc->window_width < 3) { tc->window_width = 3; KLTWarning("(KLTChangeTCPyramid) Window width must be at least three. \n" "Changing to %d.\n", tc->window_width); } if (tc->window_height < 3) { tc->window_height = 3; KLTWarning("(KLTChangeTCPyramid) Window height must be at least three. \n" "Changing to %d.\n", tc->window_height); } window_halfwidth = min(tc->window_width,tc->window_height)/2.0f; subsampling = ((float) search_range) / window_halfwidth; if (subsampling < 1.0) { /* 1.0 = 0+1 */ tc->nPyramidLevels = 1; } else if (subsampling <= 3.0) { /* 3.0 = 2+1 */ tc->nPyramidLevels = 2; tc->subsampling = 2; } else if (subsampling <= 5.0) { /* 5.0 = 4+1 */ tc->nPyramidLevels = 2; tc->subsampling = 4; } else if (subsampling <= 9.0) { /* 9.0 = 8+1 */ tc->nPyramidLevels = 2; tc->subsampling = 8; } else { /* The following lines are derived from the formula: search_range = window_halfwidth * \sum_{i=0}^{nPyramidLevels-1} 8^i, which is the same as: search_range = window_halfwidth * (8^nPyramidLevels - 1)/(8 - 1). Then, the value is rounded up to the nearest integer. */ float val = (float) (log(7.0*subsampling+1.0)/log(8.0)); tc->nPyramidLevels = (int) (val + 0.99); tc->subsampling = 8; } } /********************************************************************* * NOTE: Manually must ensure consistency with _KLTComputePyramid() */ float _pyramidSigma( KLT_TrackingContext tc) { return (tc->pyramid_sigma_fact * tc->subsampling); } /********************************************************************* * Updates border, which is dependent upon * smooth_sigma_fact, pyramid_sigma_fact, window_size, and subsampling */ void KLTUpdateTCBorder( KLT_TrackingContext tc) { float val; int pyramid_gauss_hw; int smooth_gauss_hw; int gauss_width, gaussderiv_width; int num_levels = tc->nPyramidLevels; int n_invalid_pixels; int window_hw; int ss = tc->subsampling; int ss_power; int border; int i; /* Check window size (and correct if necessary) */ if (tc->window_width % 2 != 1) { tc->window_width = tc->window_width+1; KLTWarning("(KLTUpdateTCBorder) Window width must be odd. " "Changing to %d.\n", tc->window_width); } if (tc->window_height % 2 != 1) { tc->window_height = tc->window_height+1; KLTWarning("(KLTUpdateTCBorder) Window height must be odd. " "Changing to %d.\n", tc->window_height); } if (tc->window_width < 3) { tc->window_width = 3; KLTWarning("(KLTUpdateTCBorder) Window width must be at least three. \n" "Changing to %d.\n", tc->window_width); } if (tc->window_height < 3) { tc->window_height = 3; KLTWarning("(KLTUpdateTCBorder) Window height must be at least three. \n" "Changing to %d.\n", tc->window_height); } window_hw = max(tc->window_width, tc->window_height)/2; /* Find widths of convolution windows */ _KLTGetKernelWidths(_KLTComputeSmoothSigma(tc), &gauss_width, &gaussderiv_width); smooth_gauss_hw = gauss_width/2; _KLTGetKernelWidths(_pyramidSigma(tc), &gauss_width, &gaussderiv_width); pyramid_gauss_hw = gauss_width/2; /* Compute the # of invalid pixels at each level of the pyramid. n_invalid_pixels is computed with respect to the ith level of the pyramid. So, e.g., if n_invalid_pixels = 5 after the first iteration, then there are 5 invalid pixels in level 1, which translated means 5*subsampling invalid pixels in the original level 0. */ n_invalid_pixels = smooth_gauss_hw; for (i = 1 ; i < num_levels ; i++) { val = ((float) n_invalid_pixels + pyramid_gauss_hw) / ss; n_invalid_pixels = (int) (val + 0.99); /* Round up */ } /* ss_power = ss^(num_levels-1) */ ss_power = 1; for (i = 1 ; i < num_levels ; i++) ss_power *= ss; /* Compute border by translating invalid pixels back into */ /* original image */ border = (n_invalid_pixels + window_hw) * ss_power; tc->borderx = border; tc->bordery = border; } /********************************************************************* * KLTFreeTrackingContext * KLTFreeFeatureList * KLTFreeFeatureHistory * KLTFreeFeatureTable */ void KLTFreeTrackingContext( KLT_TrackingContext tc) { if (tc->pyramid_last) _KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last); if (tc->pyramid_last_gradx) _KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last_gradx); if (tc->pyramid_last_grady) _KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last_grady); free(tc); } void KLTFreeFeatureList(KLT_FeatureList fl) { free(fl); } /********************************************************************* * KLTStopSequentialMode */ void KLTStopSequentialMode( KLT_TrackingContext tc) { tc->sequentialMode = FALSE; _KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last); _KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last_gradx); _KLTFreePyramid((_KLT_Pyramid) tc->pyramid_last_grady); tc->pyramid_last = NULL; tc->pyramid_last_gradx = NULL; tc->pyramid_last_grady = NULL; } /********************************************************************* * KLTCountRemainingFeatures */ int KLTCountRemainingFeatures( KLT_FeatureList fl) { int count = 0; int i; for (i = 0 ; i < fl->nFeatures ; i++) if (fl->feature[i]->val >= 0) count++; return count; } mlt-6.20.0/src/modules/videostab/stab/klt/klt.h000066400000000000000000000071441362234133600213110ustar00rootroot00000000000000/********************************************************************* * klt.h * * Kanade-Lucas-Tomasi tracker *********************************************************************/ #ifndef _KLT_H_ #define _KLT_H_ typedef float KLT_locType; typedef unsigned char KLT_PixelType; #define KLT_BOOL int #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #ifndef NULL #define NULL 0 #endif #define KLT_TRACKED 0 #define KLT_NOT_FOUND -1 #define KLT_SMALL_DET -2 #define KLT_MAX_ITERATIONS -3 #define KLT_OOB -4 #define KLT_LARGE_RESIDUE -5 #include "klt_util.h" /* for affine mapping */ /******************* * Structures */ typedef struct { /* Available to user */ int mindist; /* min distance b/w features */ int window_width, window_height; KLT_BOOL sequentialMode; /* whether to save most recent image to save time */ /* can set to TRUE manually, but don't set to */ /* FALSE manually */ KLT_BOOL smoothBeforeSelecting; /* whether to smooth image before */ /* selecting features */ /* Available, but hopefully can ignore */ int min_eigenvalue; /* smallest eigenvalue allowed for selecting */ float min_determinant; /* th for determining lost */ float min_displacement; /* th for stopping tracking when pixel changes little */ int max_iterations; /* th for stopping tracking when too many iterations */ float max_residue; /* th for stopping tracking when residue is large */ float grad_sigma; float smooth_sigma_fact; float pyramid_sigma_fact; float step_factor; /* size of Newton steps; 2.0 comes from equations, 1.0 seems to avoid overshooting */ int nSkippedPixels; /* # of pixels skipped when finding features */ int borderx; /* border in which features will not be found */ int bordery; int nPyramidLevels; /* computed from search_ranges */ int subsampling; /* " */ /* User must not touch these */ void *pyramid_last; void *pyramid_last_gradx; void *pyramid_last_grady; int verbose; } KLT_TrackingContextRec, *KLT_TrackingContext; typedef struct { KLT_locType x; KLT_locType y; int val; } KLT_FeatureRec, *KLT_Feature; typedef struct { int nFeatures; KLT_Feature *feature; } KLT_FeatureListRec, *KLT_FeatureList; typedef struct { int nFrames; KLT_Feature *feature; } KLT_FeatureHistoryRec, *KLT_FeatureHistory; typedef struct { int nFrames; int nFeatures; KLT_Feature **feature; } KLT_FeatureTableRec, *KLT_FeatureTable; /******************* * Functions */ /* Create */ KLT_TrackingContext KLTCreateTrackingContext(); KLT_FeatureList KLTCreateFeatureList(int); /* Free */ void KLTFreeTrackingContext(KLT_TrackingContext); void KLTFreeFeatureList(KLT_FeatureList); /* Processing */ void KLTSelectGoodFeatures( KLT_TrackingContext tc, KLT_PixelType *img, int ncols, int nrows, KLT_FeatureList fl); void KLTTrackFeatures( KLT_TrackingContext tc, KLT_PixelType *img1, KLT_PixelType *img2, int ncols, int nrows, KLT_FeatureList fl); void KLTReplaceLostFeatures( KLT_TrackingContext tc, KLT_PixelType *img, int ncols, int nrows, KLT_FeatureList fl); /* Utilities */ int KLTCountRemainingFeatures( KLT_FeatureList fl); void KLTPrintTrackingContext( KLT_TrackingContext tc); void KLTChangeTCPyramid( KLT_TrackingContext tc, int search_range); void KLTUpdateTCBorder( KLT_TrackingContext tc); void KLTStopSequentialMode( KLT_TrackingContext tc); float _KLTComputeSmoothSigma( KLT_TrackingContext tc); #endif mlt-6.20.0/src/modules/videostab/stab/klt/klt_util.c000066400000000000000000000021631362234133600223350ustar00rootroot00000000000000/********************************************************************* * klt_util.c *********************************************************************/ /* Standard includes */ #include #include /* Our includes */ #include "base.h" #include "error.h" #include "klt.h" #include "klt_util.h" /*********************************************************************/ float _KLTComputeSmoothSigma( KLT_TrackingContext tc) { return (tc->smooth_sigma_fact * max(tc->window_width, tc->window_height)); } /********************************************************************* * _KLTCreateFloatImage */ _KLT_FloatImage _KLTCreateFloatImage(int ncols, int nrows) { int nbytes = sizeof(_KLT_FloatImageRec) + ncols * nrows * sizeof(float); _KLT_FloatImage floatimg; floatimg = (_KLT_FloatImage)malloc(nbytes); floatimg->ncols = ncols; floatimg->nrows = nrows; floatimg->data = (float *)(floatimg + 1); return(floatimg); } /********************************************************************* * _KLTFreeFloatImage */ void _KLTFreeFloatImage( _KLT_FloatImage floatimg) { free(floatimg); } mlt-6.20.0/src/modules/videostab/stab/klt/klt_util.h000066400000000000000000000010171362234133600223370ustar00rootroot00000000000000/********************************************************************* * klt_util.h *********************************************************************/ #ifndef _KLT_UTIL_H_ #define _KLT_UTIL_H_ typedef struct { int ncols; int nrows; float *data; } _KLT_FloatImageRec, *_KLT_FloatImage; _KLT_FloatImage _KLTCreateFloatImage( int ncols, int nrows); void _KLTFreeFloatImage( _KLT_FloatImage); void _KLTPrintSubFloatImage( _KLT_FloatImage floatimg, int x0, int y0, int width, int height); #endif mlt-6.20.0/src/modules/videostab/stab/klt/pyramid.c000066400000000000000000000063731362234133600221620ustar00rootroot00000000000000/********************************************************************* * pyramid.c * *********************************************************************/ /* Standard includes */ #include /* malloc() ? */ #include /* memset() ? */ #include /* */ /* Our includes */ #include "base.h" #include "error.h" #include "convolve.h" /* for computing pyramid */ #include "pyramid.h" /********************************************************************* * */ _KLT_Pyramid _KLTCreatePyramid( int ncols, int nrows, int subsampling, int nlevels) { _KLT_Pyramid pyramid; int nbytes = sizeof(_KLT_PyramidRec) + nlevels * sizeof(_KLT_FloatImage *) + nlevels * sizeof(int) + nlevels * sizeof(int); int i; if (subsampling != 2 && subsampling != 4 && subsampling != 8 && subsampling != 16 && subsampling != 32) KLTError("(_KLTCreatePyramid) Pyramid's subsampling must " "be either 2, 4, 8, 16, or 32"); /* Allocate memory for structure and set parameters */ pyramid = (_KLT_Pyramid)malloc(nbytes); /* Set parameters */ pyramid->subsampling = subsampling; pyramid->nLevels = nlevels; pyramid->img = (_KLT_FloatImage *) (pyramid + 1); pyramid->ncols = (int *) (pyramid->img + nlevels); pyramid->nrows = (int *) (pyramid->ncols + nlevels); /* Allocate memory for each level of pyramid and assign pointers */ for (i = 0 ; i < nlevels ; i++) { pyramid->img[i] = _KLTCreateFloatImage(ncols, nrows); pyramid->ncols[i] = ncols; pyramid->nrows[i] = nrows; ncols /= subsampling; nrows /= subsampling; } return pyramid; } /********************************************************************* * */ void _KLTFreePyramid( _KLT_Pyramid pyramid) { int i; /* Free images */ for (i = 0 ; i < pyramid->nLevels ; i++) _KLTFreeFloatImage(pyramid->img[i]); /* Free structure */ free(pyramid); } /********************************************************************* * */ void _KLTComputePyramid( _KLT_FloatImage img, _KLT_Pyramid pyramid, float sigma_fact) { _KLT_FloatImage currimg, tmpimg; int ncols = img->ncols, nrows = img->nrows; int subsampling = pyramid->subsampling; int subhalf = subsampling / 2; float sigma = subsampling * sigma_fact; /* empirically determined */ int oldncols; int i, x, y; if (subsampling != 2 && subsampling != 4 && subsampling != 8 && subsampling != 16 && subsampling != 32) KLTError("(_KLTComputePyramid) Pyramid's subsampling must " "be either 2, 4, 8, 16, or 32"); /* Copy original image to level 0 of pyramid */ memcpy(pyramid->img[0]->data, img->data, ncols*nrows*sizeof(float)); currimg = img; for (i = 1 ; i < pyramid->nLevels ; i++) { tmpimg = _KLTCreateFloatImage(ncols, nrows); _KLTComputeSmoothedImage(currimg, sigma, tmpimg); /* Subsample */ oldncols = ncols; ncols /= subsampling; nrows /= subsampling; for (y = 0 ; y < nrows ; y++) for (x = 0 ; x < ncols ; x++) pyramid->img[i]->data[y*ncols+x] = tmpimg->data[(subsampling*y+subhalf)*oldncols + (subsampling*x+subhalf)]; /* Reassign current image */ currimg = pyramid->img[i]; _KLTFreeFloatImage(tmpimg); } } mlt-6.20.0/src/modules/videostab/stab/klt/pyramid.h000066400000000000000000000011351362234133600221560ustar00rootroot00000000000000/********************************************************************* * pyramid.h *********************************************************************/ #ifndef _PYRAMID_H_ #define _PYRAMID_H_ #include "klt_util.h" typedef struct { int subsampling; int nLevels; _KLT_FloatImage *img; int *ncols, *nrows; } _KLT_PyramidRec, *_KLT_Pyramid; _KLT_Pyramid _KLTCreatePyramid( int ncols, int nrows, int subsampling, int nlevels); void _KLTComputePyramid( _KLT_FloatImage floatimg, _KLT_Pyramid pyramid, float sigma_fact); void _KLTFreePyramid( _KLT_Pyramid pyramid); #endif mlt-6.20.0/src/modules/videostab/stab/klt/selectGoodFeatures.c000066400000000000000000000306341362234133600243010ustar00rootroot00000000000000/********************************************************************* * selectGoodFeatures.c * *********************************************************************/ /* Standard includes */ #include /* malloc(), qsort() */ #include /* fflush() */ #include /* memset() */ #include /* sqrt() */ /* Our includes */ #include "base.h" #include "error.h" #include "convolve.h" #include "klt.h" #include "klt_util.h" #include "pyramid.h" typedef enum {SELECTING_ALL, REPLACING_SOME} selectionMode; /*********************************************************************/ void _fillFeaturemap( int x, int y, uchar *featuremap, int mindist, int ncols, int nrows) { int ix, iy; for (iy = y - mindist ; iy <= y + mindist ; iy++) for (ix = x - mindist ; ix <= x + mindist ; ix++) if (ix >= 0 && ix < ncols && iy >= 0 && iy < nrows) featuremap[iy*ncols+ix] = 1; } /********************************************************************* * _enforceMinimumDistance * * Removes features that are within close proximity to better features. * * INPUTS * featurelist: A list of features. The nFeatures property * is used. * * OUTPUTS * featurelist: Is overwritten. Nearby "redundant" features are removed. * Writes -1's into the remaining elements. * * RETURNS * The number of remaining features. */ void _enforceMinimumDistance( int *pointlist, /* featurepoints */ int npoints, /* number of featurepoints */ KLT_FeatureList featurelist, /* features */ int ncols, int nrows, /* size of images */ int mindist, /* min. dist b/w features */ int min_eigenvalue, /* min. eigenvalue */ KLT_BOOL overwriteAllFeatures) { int indx; /* Index into features */ int x, y, val; /* Location and trackability of pixel under consideration */ uchar *featuremap; /* Boolean array recording proximity of features */ int *ptr; /* Cannot add features with an eigenvalue less than one */ if (min_eigenvalue < 1) min_eigenvalue = 1; /* Allocate memory for feature map and clear it */ featuremap = (uchar *) malloc(ncols * nrows * sizeof(uchar)); memset(featuremap, 0, ncols*nrows); /* Necessary because code below works with (mindist-1) */ mindist--; /* If we are keeping all old good features, then add them to the featuremap */ if (!overwriteAllFeatures) for (indx = 0 ; indx < featurelist->nFeatures ; indx++) if (featurelist->feature[indx]->val >= 0) { x = (int) featurelist->feature[indx]->x; y = (int) featurelist->feature[indx]->y; _fillFeaturemap(x, y, featuremap, mindist, ncols, nrows); } /* For each feature point, in descending order of importance, do ... */ ptr = pointlist; indx = 0; while (1) { /* If we can't add all the points, then fill in the rest of the featurelist with -1's */ if (ptr >= pointlist + 3*npoints) { while (indx < featurelist->nFeatures) { if (overwriteAllFeatures || featurelist->feature[indx]->val < 0) { featurelist->feature[indx]->x = -1; featurelist->feature[indx]->y = -1; featurelist->feature[indx]->val = KLT_NOT_FOUND; } indx++; } break; } x = *ptr++; y = *ptr++; val = *ptr++; while (!overwriteAllFeatures && indx < featurelist->nFeatures && featurelist->feature[indx]->val >= 0) indx++; if (indx >= featurelist->nFeatures) break; /* If no neighbor has been selected, and if the minimum eigenvalue is large enough, then add feature to the current list */ if (!featuremap[y*ncols+x] && val >= min_eigenvalue) { featurelist->feature[indx]->x = (KLT_locType) x; featurelist->feature[indx]->y = (KLT_locType) y; featurelist->feature[indx]->val = (int) val; indx++; /* Fill in surrounding region of feature map, but make sure that pixels are in-bounds */ _fillFeaturemap(x, y, featuremap, mindist, ncols, nrows); } } /* Free feature map */ free(featuremap); } /********************************************************************* * _comparePoints * * Used by qsort (in _KLTSelectGoodFeatures) to determine * which feature is better. * By switching the '>' with the '<', qsort is fooled into sorting * in descending order. */ int _comparePoints(const void *a, const void *b) { int v1 = *(((int *) a) + 2); int v2 = *(((int *) b) + 2); if (v1 > v2) return(-1); else if (v1 < v2) return(1); else return(0); } /********************************************************************* * _sortPointList */ void _sortPointList( int *pointlist, int npoints) { qsort(pointlist, npoints, 3*sizeof(int), _comparePoints); } /********************************************************************* * _minEigenvalue * * Given the three distinct elements of the symmetric 2x2 matrix * [gxx gxy] * [gxy gyy], * Returns the minimum eigenvalue of the matrix. */ float _minEigenvalue(float gxx, float gxy, float gyy) { return (float) ((gxx + gyy - sqrt((gxx - gyy)*(gxx - gyy) + 4*gxy*gxy)) / 2.0); } /*********************************************************************/ void _KLTSelectGoodFeatures( KLT_TrackingContext tc, KLT_PixelType *img, int ncols, int nrows, KLT_FeatureList featurelist, selectionMode mode) { _KLT_FloatImage floatimg, gradx, grady; int window_hw, window_hh; int *pointlist; int npoints = 0; KLT_BOOL overwriteAllFeatures = (mode == SELECTING_ALL) ? TRUE : FALSE; KLT_BOOL floatimages_created = FALSE; /* Check window size (and correct if necessary) */ if (tc->window_width % 2 != 1) { tc->window_width = tc->window_width+1; KLTWarning("Tracking context's window width must be odd. " "Changing to %d.\n", tc->window_width); } if (tc->window_height % 2 != 1) { tc->window_height = tc->window_height+1; KLTWarning("Tracking context's window height must be odd. " "Changing to %d.\n", tc->window_height); } if (tc->window_width < 3) { tc->window_width = 3; KLTWarning("Tracking context's window width must be at least three. \n" "Changing to %d.\n", tc->window_width); } if (tc->window_height < 3) { tc->window_height = 3; KLTWarning("Tracking context's window height must be at least three. \n" "Changing to %d.\n", tc->window_height); } window_hw = tc->window_width/2; window_hh = tc->window_height/2; /* Create pointlist, which is a simplified version of a featurelist, */ /* for speed. Contains only integer locations and values. */ pointlist = (int *) malloc(ncols * nrows * 3 * sizeof(int)); /* Create temporary images, etc. */ if (mode == REPLACING_SOME && tc->sequentialMode && tc->pyramid_last != NULL) { floatimg = ((_KLT_Pyramid) tc->pyramid_last)->img[0]; gradx = ((_KLT_Pyramid) tc->pyramid_last_gradx)->img[0]; grady = ((_KLT_Pyramid) tc->pyramid_last_grady)->img[0]; } else { floatimages_created = TRUE; floatimg = _KLTCreateFloatImage(ncols, nrows); gradx = _KLTCreateFloatImage(ncols, nrows); grady = _KLTCreateFloatImage(ncols, nrows); if (tc->smoothBeforeSelecting) { _KLT_FloatImage tmpimg; tmpimg = _KLTCreateFloatImage(ncols, nrows); _KLTToFloatImage(img, ncols, nrows, tmpimg); _KLTComputeSmoothedImage(tmpimg, _KLTComputeSmoothSigma(tc), floatimg); _KLTFreeFloatImage(tmpimg); } else { _KLTToFloatImage(img, ncols, nrows, floatimg); } /* Compute gradient of image in x and y direction */ _KLTComputeGradients(floatimg, tc->grad_sigma, gradx, grady); } /* Compute trackability of each image pixel as the minimum of the two eigenvalues of the Z matrix */ { float gx, gy; float gxx, gxy, gyy; int xx, yy; int *ptr; float val; unsigned int limit = 1; int borderx = tc->borderx; /* Must not touch cols */ int bordery = tc->bordery; /* lost by convolution */ int x, y; int i; if (borderx < window_hw) borderx = window_hw; if (bordery < window_hh) bordery = window_hh; /* Find largest value of an int */ for (i = 0 ; i < sizeof(int) ; i++) limit *= 256; limit = limit/2 - 1; /* For most of the pixels in the image, do ... */ ptr = pointlist; for (y = bordery ; y < nrows - bordery ; y += tc->nSkippedPixels + 1) for (x = borderx ; x < ncols - borderx ; x += tc->nSkippedPixels + 1) { /* Sum the gradients in the surrounding window */ gxx = 0; gxy = 0; gyy = 0; for (yy = y-window_hh ; yy <= y+window_hh ; yy++) for (xx = x-window_hw ; xx <= x+window_hw ; xx++) { gx = *(gradx->data + ncols*yy+xx); gy = *(grady->data + ncols*yy+xx); gxx += gx * gx; gxy += gx * gy; gyy += gy * gy; } /* Store the trackability of the pixel as the minimum of the two eigenvalues */ *ptr++ = x; *ptr++ = y; val = _minEigenvalue(gxx, gxy, gyy); if (val > limit) { KLTWarning("(_KLTSelectGoodFeatures) minimum eigenvalue %f is " "greater than the capacity of an int; setting " "to maximum value", val); val = (float) limit; } *ptr++ = (int) val; npoints++; } } /* Sort the features */ _sortPointList(pointlist, npoints); /* Check tc->mindist */ if (tc->mindist < 0) { KLTWarning("(_KLTSelectGoodFeatures) Tracking context field tc->mindist " "is negative (%d); setting to zero", tc->mindist); tc->mindist = 0; } /* Enforce minimum distance between features */ _enforceMinimumDistance( pointlist, npoints, featurelist, ncols, nrows, tc->mindist, tc->min_eigenvalue, overwriteAllFeatures); /* Free memory */ free(pointlist); if (floatimages_created) { _KLTFreeFloatImage(floatimg); _KLTFreeFloatImage(gradx); _KLTFreeFloatImage(grady); } } /********************************************************************* * KLTSelectGoodFeatures * * Main routine, visible to the outside. Finds the good features in * an image. * * INPUTS * tc: Contains parameters used in computation (size of image, * size of window, min distance b/w features, sigma to compute * image gradients, # of features desired). * img: Pointer to the data of an image (probably unsigned chars). * * OUTPUTS * features: List of features. The member nFeatures is computed. */ void KLTSelectGoodFeatures( KLT_TrackingContext tc, KLT_PixelType *img, int ncols, int nrows, KLT_FeatureList fl) { if (tc->verbose >= 1) { fprintf(stderr, "(KLT) Selecting the %d best features " "from a %d by %d image... ", fl->nFeatures, ncols, nrows); fflush(stderr); } _KLTSelectGoodFeatures(tc, img, ncols, nrows, fl, SELECTING_ALL); if (tc->verbose >= 1) fprintf( stderr, "\n\t%d features found.\n", KLTCountRemainingFeatures(fl) ); } /********************************************************************* * KLTReplaceLostFeatures * * Main routine, visible to the outside. Replaces the lost features * in an image. * * INPUTS * tc: Contains parameters used in computation (size of image, * size of window, min distance b/w features, sigma to compute * image gradients, # of features desired). * img: Pointer to the data of an image (probably unsigned chars). * * OUTPUTS * features: List of features. The member nFeatures is computed. */ void KLTReplaceLostFeatures( KLT_TrackingContext tc, KLT_PixelType *img, int ncols, int nrows, KLT_FeatureList fl) { int nLostFeatures = fl->nFeatures - KLTCountRemainingFeatures(fl); if (tc->verbose >= 1) { fprintf(stderr, "(KLT) Attempting to replace %d features " "in a %d by %d image... ", nLostFeatures, ncols, nrows); fflush(stderr); } /* If there are any lost features, replace them */ if (nLostFeatures > 0) _KLTSelectGoodFeatures(tc, img, ncols, nrows, fl, REPLACING_SOME); if (tc->verbose >= 1) fprintf( stderr, "\n\t%d features replaced.\n", nLostFeatures - fl->nFeatures + KLTCountRemainingFeatures(fl) ); } mlt-6.20.0/src/modules/videostab/stab/klt/trackFeatures.c000066400000000000000000000377651362234133600233310ustar00rootroot00000000000000/********************************************************************* * trackFeatures.c * *********************************************************************/ /* Standard includes */ #include /* fabs() */ #include /* malloc() */ #include /* fflush() */ /* Our includes */ #include "base.h" #include "error.h" #include "convolve.h" /* for computing pyramid */ #include "klt.h" #include "klt_util.h" /* _KLT_FloatImage */ #include "pyramid.h" /* _KLT_Pyramid */ typedef float *_FloatWindow; /********************************************************************* * _interpolate * * Given a point (x,y) in an image, computes the bilinear interpolated * gray-level value of the point in the image. */ float _interpolate(float x, float y, _KLT_FloatImage img) { int xt = x; int yt = y; float ax = x - xt; float ay = y - yt; float *ptr = img->data + (img->ncols * yt) + xt; return ( (1 - ax) * (1 - ay) * (*ptr) + ax * (1 - ay) * (*(ptr + 1)) + (1 - ax) * ay * (*(ptr + img->ncols)) + ax * ay * (*(ptr + img->ncols + 1)) ); } /********************************************************************* * _computeIntensityDifference * * Given two images and the window center in both images, * aligns the images wrt the window and computes the difference * between the two overlaid images. */ void _computeIntensityDifference( _KLT_FloatImage img1, /* images */ _KLT_FloatImage img2, float x1, float y1, /* center of window in 1st img */ float x2, float y2, /* center of window in 2nd img */ int width, int height, /* size of window */ _FloatWindow imgdiff) /* output */ { int hw = width/2, hh = height/2; float g1, g2; int i, j; /* Compute values */ for (j = -hh ; j <= hh ; j++) for (i = -hw ; i <= hw ; i++) { g1 = _interpolate(x1+i, y1+j, img1); g2 = _interpolate(x2+i, y2+j, img2); *imgdiff++ = g1 - g2; } } /********************************************************************* * _computeGradientSum * * Given two gradients and the window center in both images, * aligns the gradients wrt the window and computes the sum of the two * overlaid gradients. */ void _computeGradientSum( _KLT_FloatImage gradx1, /* gradient images */ _KLT_FloatImage grady1, _KLT_FloatImage gradx2, _KLT_FloatImage grady2, float x1, float y1, /* center of window in 1st img */ float x2, float y2, /* center of window in 2nd img */ int width, int height, /* size of window */ _FloatWindow gradx, /* output */ _FloatWindow grady) /* " */ { int hw = width/2, hh = height/2; float g1, g2; int i, j; /* Compute values */ for (j = -hh ; j <= hh ; j++) for (i = -hw ; i <= hw ; i++) { g1 = _interpolate(x1+i, y1+j, gradx1); g2 = _interpolate(x2+i, y2+j, gradx2); *gradx++ = g1 + g2; g1 = _interpolate(x1+i, y1+j, grady1); g2 = _interpolate(x2+i, y2+j, grady2); *grady++ = g1 + g2; } } /********************************************************************* * _compute2by2GradientMatrix * */ void _compute2by2GradientMatrix( _FloatWindow gradx, _FloatWindow grady, int width, /* size of window */ int height, float *gxx, /* return values */ float *gxy, float *gyy) { float gx, gy; int i; /* Compute values */ *gxx = 0.0; *gxy = 0.0; *gyy = 0.0; for (i = 0 ; i < width * height ; i++) { gx = *gradx++; gy = *grady++; *gxx += gx*gx; *gxy += gx*gy; *gyy += gy*gy; } } /********************************************************************* * _compute2by1ErrorVector * */ void _compute2by1ErrorVector( _FloatWindow imgdiff, _FloatWindow gradx, _FloatWindow grady, int width, /* size of window */ int height, float step_factor, /* 2.0 comes from equations, 1.0 seems to avoid overshooting */ float *ex, /* return values */ float *ey) { float diff; int i; /* Compute values */ *ex = 0; *ey = 0; for (i = 0 ; i < width * height ; i++) { diff = *imgdiff++; *ex += diff * (*gradx++); *ey += diff * (*grady++); } *ex *= step_factor; *ey *= step_factor; } /********************************************************************* * _solveEquation * * Solves the 2x2 matrix equation * [gxx gxy] [dx] = [ex] * [gxy gyy] [dy] = [ey] * for dx and dy. * * Returns KLT_TRACKED on success and KLT_SMALL_DET on failure */ int _solveEquation( float gxx, float gxy, float gyy, float ex, float ey, float small, float *dx, float *dy) { float det = gxx*gyy - gxy*gxy; if (det < small) return KLT_SMALL_DET; *dx = (gyy*ex - gxy*ey)/det; *dy = (gxx*ey - gxy*ex)/det; return KLT_TRACKED; } /********************************************************************* * _allocateFloatWindow */ _FloatWindow _allocateFloatWindow(int width, int height) { return (_FloatWindow)malloc(width * height * sizeof(float)); } /********************************************************************* * _sumAbsFloatWindow */ float _sumAbsFloatWindow( _FloatWindow fw, int width, int height) { float sum = 0.0; int w; for ( ; height > 0 ; height--) for (w=0 ; w < width ; w++) sum += (float) fabs(*fw++); return sum; } /********************************************************************* * _trackFeature * * Tracks a feature point from one image to the next. * * RETURNS * KLT_SMALL_DET if feature is lost, * KLT_MAX_ITERATIONS if tracking stopped because iterations timed out, * KLT_TRACKED otherwise. */ int _trackFeature( float x1, /* location of window in first image */ float y1, float *x2, /* starting location of search in second image */ float *y2, _KLT_FloatImage img1, _KLT_FloatImage gradx1, _KLT_FloatImage grady1, _KLT_FloatImage img2, _KLT_FloatImage gradx2, _KLT_FloatImage grady2, int width, /* size of window */ int height, float step_factor, /* 2.0 comes from equations, 1.0 seems to avoid overshooting */ int max_iterations, float small, /* determinant threshold for declaring KLT_SMALL_DET */ float th, /* displacement threshold for stopping */ float max_residue) /* residue threshold for declaring KLT_LARGE_RESIDUE */ { _FloatWindow imgdiff, gradx, grady; float gxx, gxy, gyy, ex, ey, dx, dy; int iteration = 0; int status; int hw = width / 2; int hh = height / 2; int nc = img1->ncols; int nr = img1->nrows; float one_plus_eps = 1.001f; /* To prevent rounding errors */ /* Allocate memory for windows */ imgdiff = _allocateFloatWindow(width, height); gradx = _allocateFloatWindow(width, height); grady = _allocateFloatWindow(width, height); /* Iteratively update the window position */ do { /* If out of bounds, exit loop */ if ( x1-hw < 0.0f || nc-( x1+hw) < one_plus_eps || *x2-hw < 0.0f || nc-(*x2+hw) < one_plus_eps || y1-hh < 0.0f || nr-( y1+hh) < one_plus_eps || *y2-hh < 0.0f || nr-(*y2+hh) < one_plus_eps) { status = KLT_OOB; break; } /* Compute gradient and difference windows */ _computeIntensityDifference(img1, img2, x1, y1, *x2, *y2, width, height, imgdiff); _computeGradientSum(gradx1, grady1, gradx2, grady2, x1, y1, *x2, *y2, width, height, gradx, grady); /* Use these windows to construct matrices */ _compute2by2GradientMatrix(gradx, grady, width, height, &gxx, &gxy, &gyy); _compute2by1ErrorVector(imgdiff, gradx, grady, width, height, step_factor, &ex, &ey); /* Using matrices, solve equation for new displacement */ status = _solveEquation(gxx, gxy, gyy, ex, ey, small, &dx, &dy); if (status == KLT_SMALL_DET) break; *x2 += dx; *y2 += dy; iteration++; } while ((fabs(dx)>=th || fabs(dy)>=th) && iteration < max_iterations); /* Check whether window is out of bounds */ if (*x2-hw < 0.0f || nc-(*x2+hw) < one_plus_eps || *y2-hh < 0.0f || nr-(*y2+hh) < one_plus_eps) status = KLT_OOB; /* Check whether residue is too large */ if (status == KLT_TRACKED) { _computeIntensityDifference( img1, img2, x1, y1, *x2, *y2, width, height, imgdiff); if (_sumAbsFloatWindow(imgdiff, width, height)/(width*height) > max_residue) status = KLT_LARGE_RESIDUE; } /* Free memory */ free(imgdiff); free(gradx); free(grady); /* Return appropriate value */ if (status == KLT_SMALL_DET) return KLT_SMALL_DET; else if (status == KLT_OOB) return KLT_OOB; else if (status == KLT_LARGE_RESIDUE) return KLT_LARGE_RESIDUE; else if (iteration >= max_iterations) return KLT_MAX_ITERATIONS; else return KLT_TRACKED; } /*********************************************************************/ KLT_BOOL _outOfBounds( float x, float y, int ncols, int nrows, int borderx, int bordery) { return (x < borderx || x > ncols-1-borderx || y < bordery || y > nrows-1-bordery ); } /********************************************************************* * KLTTrackFeatures * * Tracks feature points from one image to the next. */ void KLTTrackFeatures( KLT_TrackingContext tc, KLT_PixelType *img1, KLT_PixelType *img2, int ncols, int nrows, KLT_FeatureList featurelist) { _KLT_FloatImage tmpimg = NULL; _KLT_FloatImage floatimg1 = NULL; _KLT_FloatImage floatimg2 = NULL; _KLT_Pyramid pyramid1, pyramid1_gradx, pyramid1_grady; _KLT_Pyramid pyramid2, pyramid2_gradx, pyramid2_grady; int val = 0; KLT_BOOL floatimg1_created = FALSE; float subsampling = (float)tc->subsampling; float xloc, yloc, xlocout, ylocout; int i, indx, r; if (tc->verbose >= 1) { fprintf(stderr, "(KLT) Tracking %d features in a %d by %d image... ", KLTCountRemainingFeatures(featurelist), ncols, nrows); fflush(stderr); } /* Check window size (and correct if necessary) */ if (tc->window_width % 2 != 1) { tc->window_width = tc->window_width+1; KLTWarning("Tracking context's window width must be odd. " "Changing to %d.\n", tc->window_width); } if (tc->window_height % 2 != 1) { tc->window_height = tc->window_height+1; KLTWarning("Tracking context's window height must be odd. " "Changing to %d.\n", tc->window_height); } if (tc->window_width < 3) { tc->window_width = 3; KLTWarning("Tracking context's window width must be at least three. \n" "Changing to %d.\n", tc->window_width); } if (tc->window_height < 3) { tc->window_height = 3; KLTWarning("Tracking context's window height must be at least three. \n" "Changing to %d.\n", tc->window_height); } /* Create temporary image */ tmpimg = _KLTCreateFloatImage(ncols, nrows); /* Process first image by converting to float, smoothing, computing */ /* pyramid, and computing gradient pyramids */ if (tc->sequentialMode && tc->pyramid_last != NULL) { pyramid1 = (_KLT_Pyramid) tc->pyramid_last; pyramid1_gradx = (_KLT_Pyramid) tc->pyramid_last_gradx; pyramid1_grady = (_KLT_Pyramid) tc->pyramid_last_grady; if (pyramid1->ncols[0] != ncols || pyramid1->nrows[0] != nrows) KLTError("(KLTTrackFeatures) Size of incoming image (%d by %d) " "is different from size of previous image (%d by %d)\n", ncols, nrows, pyramid1->ncols[0], pyramid1->nrows[0]); } else { floatimg1_created = TRUE; floatimg1 = _KLTCreateFloatImage(ncols, nrows); _KLTToFloatImage(img1, ncols, nrows, tmpimg); _KLTComputeSmoothedImage(tmpimg, _KLTComputeSmoothSigma(tc), floatimg1); pyramid1 = _KLTCreatePyramid(ncols, nrows, (int) subsampling, tc->nPyramidLevels); _KLTComputePyramid(floatimg1, pyramid1, tc->pyramid_sigma_fact); pyramid1_gradx = _KLTCreatePyramid(ncols, nrows, (int) subsampling, tc->nPyramidLevels); pyramid1_grady = _KLTCreatePyramid(ncols, nrows, (int) subsampling, tc->nPyramidLevels); for (i = 0 ; i < tc->nPyramidLevels ; i++) _KLTComputeGradients(pyramid1->img[i], tc->grad_sigma, pyramid1_gradx->img[i], pyramid1_grady->img[i]); } /* Do the same thing with second image */ floatimg2 = _KLTCreateFloatImage(ncols, nrows); _KLTToFloatImage(img2, ncols, nrows, tmpimg); _KLTComputeSmoothedImage(tmpimg, _KLTComputeSmoothSigma(tc), floatimg2); pyramid2 = _KLTCreatePyramid(ncols, nrows, (int) subsampling, tc->nPyramidLevels); _KLTComputePyramid(floatimg2, pyramid2, tc->pyramid_sigma_fact); pyramid2_gradx = _KLTCreatePyramid(ncols, nrows, (int) subsampling, tc->nPyramidLevels); pyramid2_grady = _KLTCreatePyramid(ncols, nrows, (int) subsampling, tc->nPyramidLevels); for (i = 0 ; i < tc->nPyramidLevels ; i++) _KLTComputeGradients(pyramid2->img[i], tc->grad_sigma, pyramid2_gradx->img[i], pyramid2_grady->img[i]); /* For each feature, do ... */ for (indx = 0 ; indx < featurelist->nFeatures ; indx++) { /* Only track features that are not lost */ if (featurelist->feature[indx]->val >= 0) { xloc = featurelist->feature[indx]->x; yloc = featurelist->feature[indx]->y; /* Transform location to coarsest resolution */ for (r = tc->nPyramidLevels - 1 ; r >= 0 ; r--) { xloc /= subsampling; yloc /= subsampling; } xlocout = xloc; ylocout = yloc; /* Beginning with coarsest resolution, do ... */ for (r = tc->nPyramidLevels - 1 ; r >= 0 ; r--) { /* Track feature at current resolution */ xloc *= subsampling; yloc *= subsampling; xlocout *= subsampling; ylocout *= subsampling; val = _trackFeature(xloc, yloc, &xlocout, &ylocout, pyramid1->img[r], pyramid1_gradx->img[r], pyramid1_grady->img[r], pyramid2->img[r], pyramid2_gradx->img[r], pyramid2_grady->img[r], tc->window_width, tc->window_height, tc->step_factor, tc->max_iterations, tc->min_determinant, tc->min_displacement, tc->max_residue); if (val==KLT_SMALL_DET || val==KLT_OOB) break; } /* Record feature */ if (val == KLT_OOB) { featurelist->feature[indx]->x = -1.0; featurelist->feature[indx]->y = -1.0; featurelist->feature[indx]->val = KLT_OOB; } else if (_outOfBounds(xlocout, ylocout, ncols, nrows, tc->borderx, tc->bordery)) { featurelist->feature[indx]->x = -1.0; featurelist->feature[indx]->y = -1.0; featurelist->feature[indx]->val = KLT_OOB; } else if (val == KLT_SMALL_DET) { featurelist->feature[indx]->x = -1.0; featurelist->feature[indx]->y = -1.0; featurelist->feature[indx]->val = KLT_SMALL_DET; } else if (val == KLT_LARGE_RESIDUE) { featurelist->feature[indx]->x = -1.0; featurelist->feature[indx]->y = -1.0; featurelist->feature[indx]->val = KLT_LARGE_RESIDUE; } else if (val == KLT_MAX_ITERATIONS) { featurelist->feature[indx]->x = -1.0; featurelist->feature[indx]->y = -1.0; featurelist->feature[indx]->val = KLT_MAX_ITERATIONS; } else { featurelist->feature[indx]->x = xlocout; featurelist->feature[indx]->y = ylocout; featurelist->feature[indx]->val = KLT_TRACKED; } } } if (tc->sequentialMode) { tc->pyramid_last = pyramid2; tc->pyramid_last_gradx = pyramid2_gradx; tc->pyramid_last_grady = pyramid2_grady; } else { _KLTFreePyramid(pyramid2); _KLTFreePyramid(pyramid2_gradx); _KLTFreePyramid(pyramid2_grady); } /* Free memory */ _KLTFreeFloatImage(tmpimg); if (floatimg1_created) _KLTFreeFloatImage(floatimg1); _KLTFreeFloatImage(floatimg2); _KLTFreePyramid(pyramid1); _KLTFreePyramid(pyramid1_gradx); _KLTFreePyramid(pyramid1_grady); if (tc->verbose >= 1) fprintf( stderr, "\n\t%d features successfully tracked.\n", KLTCountRemainingFeatures(featurelist) ); } mlt-6.20.0/src/modules/videostab/stab/main.c000066400000000000000000000115501362234133600206400ustar00rootroot00000000000000/* * Video stabilizer * * Copyright (c) 2008 Lenny * * 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. * */ #include #include #include #include #include #include //#include #include "avi/avi.h" #include "vector.h" #include "utils.h" #include "estimate.h" #include "resample.h" AviMovie mv_in, mv_out; void print_help(char *argv[]) { printf( " \n" "Video stabilizer \n" " \n" "Usage: \n" " %s [options] \n" " \n" "Options: \n" " -r # | Rolling shutter angle | default: 0 | range: 0 - 180 \n" " -q # | Output MJPEG quality | default: 100 | range: 50 - 100 \n" " \n", argv[0] ); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int opt_shutter_angle = 0; int opt_mjpeg_quality = 100; int nf, i, nc, nr; int tfs, fps; vc *pos_i, *pos_h, *pos_y; es_ctx *es; rs_ctx *rs; opterr = 0; while ((i = getopt(argc, argv, "r:q:")) != -1) { switch (i) { case 'r': opt_shutter_angle = atoi(optarg); break; case 'q': opt_mjpeg_quality = atoi(optarg); break; default: print_help(argv); } } if (argc < optind + 2) print_help(argv); if (AVI_open_movie(argv[optind], &mv_in) != AVI_ERROR_NONE) { printf("error: can't read from %s\n", argv[optind]); return EXIT_FAILURE; } if (mv_in.header->Streams < 1 || mv_in.streams[0].sh.Type != AVIST_VIDEO) { printf("error: video stream not found on %s\n", argv[optind]); return EXIT_FAILURE; } if (AVI_open_compress(argv[optind + 1], &mv_out, 1, AVI_FORMAT_MJPEG) != AVI_ERROR_NONE) { printf("error: can't write to %s\n", argv[optind + 1]); return EXIT_FAILURE; } printf("status: setup\n"); prepare_lanc_kernels(); nc = mv_in.header->Width; nr = mv_in.header->Height; tfs = mv_in.header->TotalFrames; fps = 1000000 / mv_in.header->MicroSecPerFrame; pos_i = (vc *)malloc(tfs * sizeof(vc)); pos_h = (vc *)malloc(tfs * sizeof(vc)); pos_y = (vc *)malloc(nr * sizeof(vc)); AVI_set_compress_option(&mv_out, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_WIDTH, &nc); AVI_set_compress_option(&mv_out, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_HEIGHT, &nr); AVI_set_compress_option(&mv_out, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_FRAMERATE, &fps); AVI_set_compress_option(&mv_out, AVI_OPTION_TYPE_MAIN, 0, AVI_OPTION_QUALITY, &opt_mjpeg_quality); es = es_init(nc, nr); rs = rs_init(nc, nr); printf("status: estimating\n"); for (nf = 0; nf < tfs; nf ++) { unsigned char *fr = (unsigned char *)AVI_read_frame(&mv_in, AVI_FORMAT_RGB24, nf, 0); pos_i[nf] = vc_add( nf > 0 ? pos_i[nf - 1] : vc_set(0.0, 0.0), es_estimate(es, fr) ); free(fr); if ((nf + 1) % 10 == 0) { printf("."); fflush(stdout); } } printf("\nstatus: filtering\n"); hipass(pos_i, pos_h, tfs, fps / 2); printf("status: resampling\n"); for (nf = 0; nf < tfs; nf ++) { unsigned char *fr = (unsigned char *)AVI_read_frame(&mv_in, AVI_FORMAT_RGB24, nf, 0); for (i = 0; i < nr; i ++) { pos_y[i] = interp( pos_h, tfs, nf + (i - nr / 2.0) * opt_shutter_angle / (nr * 360.0) ); } rs_resample(rs, fr, pos_y); AVI_write_frame(&mv_out, nf, AVI_FORMAT_RGB24, fr, nc * nr * 3 * sizeof(unsigned char)); if ((nf + 1) % 10 == 0) { printf("."); fflush(stdout); } } printf("\nstatus: closing\n"); es_free(es); rs_free(rs); free_lanc_kernels(); AVI_close(&mv_in); AVI_close_compress(&mv_out); return EXIT_SUCCESS; } mlt-6.20.0/src/modules/videostab/stab/resample.c000066400000000000000000000045051362234133600215260ustar00rootroot00000000000000/* * Video stabilizer * * Copyright (c) 2008 Lenny * * 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. * */ #include #include #include "resample.h" #include "utils.h" rs_ctx *rs_init(int nc, int nr) { rs_ctx *rs = (rs_ctx *)malloc(sizeof(rs_ctx)); rs->tf = (unsigned char *)malloc(nc * nr * 3 * sizeof(unsigned char)); rs->nc = nc; rs->nr = nr; return rs; } void rs_resample(int* lanc_kernels,rs_ctx *rs, unsigned char *f, vc *p) { int i, x, y, c; for (y = 0; y < rs->nr; y ++) { int yp = y * rs->nc; int xd = floor(p[y].x); int *lk = select_lanc_kernel(lanc_kernels,p[y].x); for (x = 0; x < rs->nc; x ++) { int pd = (yp + x) * 3; int a[3]; for (c = 0; c < 3; c ++) a[c] = 0; for (i = -3; i < 5; i ++) { int ps = (yp + clamp(x + xd + i, 0, rs->nc - 1)) * 3; for (c = 0; c < 3; c ++) a[c] += f[ps + c] * lk[i + 3]; } for (c = 0; c < 3; c ++) rs->tf[pd + c] = clamp(a[c] / 1024, 0, 255); } } for (y = 0; y < rs->nr; y ++) { int yp = y * rs->nc; int yd = floor(p[y].y); int *lk = select_lanc_kernel(lanc_kernels,p[y].y); for (x = 0; x < rs->nc; x ++) { int pd = (yp + x) * 3; int a[3]; for (c = 0; c < 3; c ++) a[c] = 0; for (i = -3; i < 5; i ++) { int ps = (clamp(y + yd + i, 0, rs->nr - 1) * rs->nc + x) * 3; for (c = 0; c < 3; c ++) a[c] += rs->tf[ps + c] * lk[i + 3]; } for (c = 0; c < 3; c ++) f[pd + c] = clamp(a[c] / 1024, 0, 255); } } } void rs_free(rs_ctx *rs) { free(rs->tf); free(rs); } mlt-6.20.0/src/modules/videostab/stab/resample.h000066400000000000000000000003671362234133600215350ustar00rootroot00000000000000#ifndef RESAMPLE_H #define RESAMPLE_H #include "vector.h" typedef struct { unsigned char *tf; int nc, nr; } rs_ctx; rs_ctx *rs_init(int, int); void rs_resample(int*,rs_ctx *, unsigned char *, vc *); void rs_free(rs_ctx *); #endif mlt-6.20.0/src/modules/videostab/stab/utils.c000066400000000000000000000047701362234133600210620ustar00rootroot00000000000000/* * Video stabilizer * * Copyright (c) 2008 Lenny * * 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. * */ #include #include #include #include "utils.h" float lanc(float x, float r) { float t = x * M_PI; if (x == 0.0) return 1.0; if (x <= -r || x >= r) return 0.0; return r * sin(t) * sin(t / r) / (t * t); } float hann(float x, float d) { if (x < 0.0 || x > d) return 0.0; return 0.5 * (1.0 - cos((M_PI * 2.0 * x) / (d - 1.0))); } int clamp(int a, int b, int c) { if (a < b) a = b; if (a > c) a = c; return a; } void lopass(vc *vi, vc *vo, int l, int r) { int d = r * 2 + 1; float *ck = (float *)malloc(d * sizeof(float)); float cw = 0.0; int i, j; for (i = 0; i < d; i ++) cw += ck[i] = hann(i, d - 1); for (i = 0; i < l; i ++) { vc a = vc_zero(); for (j = i - r; j <= i + r; j ++) { int jc = clamp(j, 0, l - 1); vc_mul_acc(&a, vi[jc], ck[j - i + r]); } vo[i] = vc_div(a, cw); } free(ck); } void hipass(vc *vi, vc *vo, int l, int r) { int i; lopass(vi, vo, l, r); for (i = 0; i < l; i ++) vo[i] = vc_sub(vi[i], vo[i]); } int* prepare_lanc_kernels() { int i, j; int* lanc_kernels = (int *)malloc(256 * 8 * sizeof(int)); for (i = 0; i < 256; i ++) for (j = -3; j < 5; j ++) lanc_kernels[i * 8 + j + 3] = lanc(j - i / 256.0, 4) * 1024.0; return lanc_kernels; } int *select_lanc_kernel(int* lanc_kernels,float x) { return lanc_kernels + (int)((x - floor(x)) * 256.0) * 8; } void free_lanc_kernels(int *lanc_kernels) { free(lanc_kernels); } vc interp(int* lanc_kernels, vc *vi, int l, float x) { vc a = vc_zero(); int xd = floor(x); int *lk = select_lanc_kernel(lanc_kernels,x); int i; for (i = -3; i < 5; i ++) { int ic = clamp(xd + i, 0, l - 1); vc_mul_acc(&a, vi[ic], lk[i + 3]); } return vc_div(a, 1024.0); } mlt-6.20.0/src/modules/videostab/stab/utils.h000066400000000000000000000007351362234133600210640ustar00rootroot00000000000000#ifndef UTILS_H #define UTILS_H #include "vector.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif float lanc(float, float); float hann(float, float); int clamp(int, int, int); void lopass(vc *, vc *, int, int); void hipass(vc *, vc *, int, int); int* prepare_lanc_kernels(); int *select_lanc_kernel(int*,float); void free_lanc_kernels(int*); vc interp(int*,vc *, int, float); #endif mlt-6.20.0/src/modules/videostab/stab/vector.c000066400000000000000000000035521362234133600212210ustar00rootroot00000000000000/* * Video stabilizer * * Copyright (c) 2008 Lenny * * 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. * */ #include #include "vector.h" vc vc_zero() { vc v3; v3.x = 0.0; v3.y = 0.0; return v3; } vc vc_one() { vc v3; v3.x = 1.0; v3.y = 1.0; return v3; } vc vc_set(float x, float y) { vc v3; v3.x = x; v3.y = y; return v3; } vc vc_add(vc v1, vc v2) { vc v3; v3.x = v1.x + v2.x; v3.y = v1.y + v2.y; return v3; } vc vc_sub(vc v1, vc v2) { vc v3; v3.x = v1.x - v2.x; v3.y = v1.y - v2.y; return v3; } vc vc_mul(vc v1, float v2) { vc v3; v3.x = v1.x * v2; v3.y = v1.y * v2; return v3; } vc vc_div(vc v1, float v2) { vc v3; v3.x = v1.x / v2; v3.y = v1.y / v2; return v3; } void vc_acc(vc *v1, vc v2) { v1->x += v2.x; v1->y += v2.y; } void vc_mul_acc(vc *v1, vc v2, float v3) { v1->x += v2.x * v3; v1->y += v2.y * v3; } float vc_len(vc v) { return sqrt(v.x * v.x + v.y * v.y); } float vc_ang(vc v1, vc v2) { float c = v1.x * v2.y - v1.y * v2.x; if (fabs(c) > 0.0) { float l = vc_len(v1) * vc_len(v2); float d = acos((v1.x * v2.x + v1.y * v2.y) / l); if (c > 0.0) return d; else return -d; } return 0.0; } mlt-6.20.0/src/modules/videostab/stab/vector.h000066400000000000000000000005011362234133600212150ustar00rootroot00000000000000#ifndef VECTOR_H #define VECTOR_H typedef struct { float x, y; } vc; vc vc_zero(); vc vc_one(); vc vc_set(float, float); vc vc_add(vc, vc); vc vc_sub(vc, vc); vc vc_mul(vc, float); vc vc_div(vc, float); void vc_acc(vc *, vc); void vc_mul_acc(vc *, vc, float); float vc_len(vc); float vc_ang(vc, vc); #endif mlt-6.20.0/src/modules/videostab/stabilize.c000066400000000000000000000746541362234133600207670ustar00rootroot00000000000000/* * filter_stabilize.c * * Copyright (C) Georg Martius - June 2007 * georg dot martius at web dot de * mlt adaption by Marco Gittler marco at gitma dot de 2011 * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode is distributed in the hope that 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* Typical call: * transcode -V -J stabilize=shakiness=5:show=1,preview * -i inp.mpeg -y null,null -o dummy * all parameters are optional */ #define MOD_NAME "filter_stabilize.so" #define MOD_VERSION "v0.75 (2010-04-07)" #define MOD_CAP "extracts relative transformations of \n\ subsequent frames (used for stabilization together with the\n\ transform filter in a second pass)" #define MOD_AUTHOR "Georg Martius" /* Ideas: - Try OpenCL/Cuda, this should work great - use smoothing on the frames and then use gradient decent! - stepsize could be adapted (maybe to check only one field with large stepsize and use the maximally required for the other fields */ #define MOD_FEATURES \ TC_MODULE_FEATURE_FILTER|TC_MODULE_FEATURE_VIDEO #define MOD_FLAGS \ TC_MODULE_FLAG_RECONFIGURABLE | TC_MODULE_FLAG_DELAY #define MAX(a,b) ((a < b) ? (b) : (a)) #define MIN(a,b) ((a < b) ? (a) : (b)) #include "stabilize.h" #include #include #include #include #ifdef USE_SSE2 #include #endif void addTrans(StabData* sd, Transform sl) { if (!sd->transs) { sd->transs = tlist_new(0); } tlist_append(sd->transs, &sl,sizeof(Transform) ); } /** initialise measurement fields on the frame. The size of the fields and the maxshift is used to calculate an optimal distribution in the frame. */ int initFields(StabData* sd) { int size = sd->field_size; int rows = MAX(3,(sd->height - sd->maxshift*2)/size-1); int cols = MAX(3,(sd->width - sd->maxshift*2)/size-1); // make sure that the remaining rows have the same length sd->field_num = rows*cols; sd->field_rows = rows; mlt_log_debug (NULL,"field setup: rows: %i cols: %i Total: %i fields", rows, cols, sd->field_num); if (!(sd->fields = malloc(sizeof(Field) * sd->field_num))) { mlt_log_error ( NULL, "malloc failed!\n"); return 0; } else { int i, j; // the border is the amount by which the field centers // have to be away from the image boundary // (stepsize is added in case shift is increased through stepsize) int border = size/2 + sd->maxshift + sd->stepsize; int step_x = (sd->width - 2*border)/MAX(cols-1,1); int step_y = (sd->height - 2*border) / MAX(rows-1,1); for (j = 0; j < rows; j++) { for (i = 0; i < cols; i++) { int idx = j*cols+i; sd->fields[idx].x = border + i*step_x; sd->fields[idx].y = border + j*step_y; sd->fields[idx].size = size; } } } return 1; } /** compares the two given images and returns the average absolute difference \param d_x shift in x direction \param d_y shift in y direction */ double compareImg(unsigned char* I1, unsigned char* I2, int width, int height, int bytesPerPixel, int d_x, int d_y) { int i, j; unsigned char* p1 = NULL; unsigned char* p2 = NULL; long int sum = 0; int effectWidth = width - abs(d_x); int effectHeight = height - abs(d_y); /* DEBUGGING code to export single frames */ /* char buffer[100]; */ /* sprintf(buffer, "pic_%02ix%02i_1.ppm", d_x, d_y); */ /* FILE *pic1 = fopen(buffer, "w"); */ /* sprintf(buffer, "pic_%02ix%02i_2.ppm", d_x, d_y); */ /* FILE *pic2 = fopen(buffer, "w"); */ /* fprintf(pic1, "P6\n%i %i\n255\n", effectWidth, effectHeight); */ /* fprintf(pic2, "P6\n%i %i\n255\n", effectWidth, effectHeight); */ for (i = 0; i < effectHeight; i++) { p1 = I1; p2 = I2; if (d_y > 0 ){ p1 += (i + d_y) * width * bytesPerPixel; p2 += i * width * bytesPerPixel; } else { p1 += i * width * bytesPerPixel; p2 += (i - d_y) * width * bytesPerPixel; } if (d_x > 0) { p1 += d_x * bytesPerPixel; } else { p2 -= d_x * bytesPerPixel; } #ifdef USE_SSE2 __m128i A,B,C,D,E; for (j = 0; j < effectWidth * bytesPerPixel - 16; j++) { #else for (j = 0; j < effectWidth * bytesPerPixel; j++) { #endif /* fwrite(p1,1,1,pic1);fwrite(p1,1,1,pic1);fwrite(p1,1,1,pic1); fwrite(p2,1,1,pic2);fwrite(p2,1,1,pic2);fwrite(p2,1,1,pic2); */ #ifdef USE_SSE2 A= _mm_loadu_si128((__m128i*)p1); //load unaligned data B= _mm_loadu_si128((__m128i*)p2); C= _mm_sad_epu8(A, B);//diff per 8 bit pixel stored in 2 64 byte values D = _mm_srli_si128(C, 8); // shift first 64 byte value to align at the same as C E = _mm_add_epi32(C, D); // add the 2 values (sum of all diffs) sum+= _mm_cvtsi128_si32(E); //convert _m128i to int p1+=16; p2+=16; j+=15; #else sum += abs((int)*p1 - (int)*p2); p1++; p2++; #endif } } /* fclose(pic1); fclose(pic2); */ return sum/((double) effectWidth * effectHeight * bytesPerPixel); } /** compares a small part of two given images and returns the average absolute difference. Field center, size and shift have to be chosen, so that no clipping is required \param field Field specifies position(center) and size of subimage \param d_x shift in x direction \param d_y shift in y direction */ double compareSubImg(unsigned char* const I1, unsigned char* const I2, const Field* field, int width, int height, int bytesPerPixel, int d_x, int d_y) { int k, j; unsigned char* p1 = NULL; unsigned char* p2 = NULL; int s2 = field->size / 2; double sum=0.0; p1=I1 + ((field->x - s2) + (field->y - s2)*width)*bytesPerPixel; p2=I2 + ((field->x - s2 + d_x) + (field->y - s2 + d_y)*width)*bytesPerPixel; // TODO: use some mmx or sse stuff here #ifdef USE_SSE2 __m128i A,B,C,D,E; #endif for (j = 0; j < field->size; j++){ #ifdef USE_SSE2 for (k = 0; k < (field->size * bytesPerPixel) - 16 ; k++) { A= _mm_loadu_si128((__m128i*)p1); //load unaligned data B= _mm_loadu_si128((__m128i*)p2); C= _mm_sad_epu8(A, B);//abd diff stored in 2 64 byte values D = _mm_srli_si128(C, 8); // shift value 8 byte right E = _mm_add_epi32(C, D); // add the 2 values (sum of all diffs) sum+= _mm_cvtsi128_si32(E); //convert _m128i to int p1+=16; p2+=16; k+=15; #else for (k = 0; k < (field->size * bytesPerPixel); k++) { sum += abs((int)*p1 - (int)*p2); p1++; p2++; #endif } p1 += ((width - field->size) * bytesPerPixel); p2 += ((width - field->size) * bytesPerPixel); } return sum/((double) field->size *field->size* bytesPerPixel); } /** \see contrastSubImg called with bytesPerPixel=1*/ double contrastSubImgYUV(StabData* sd, const Field* field){ return contrastSubImg(sd->curr,field,sd->width,sd->height,1); } /** \see contrastSubImg three times called with bytesPerPixel=3 for all channels */ double contrastSubImgRGB(StabData* sd, const Field* field){ unsigned char* const I = sd->curr; return ( contrastSubImg(I, field,sd->width,sd->height,3) + contrastSubImg(I+1,field,sd->width,sd->height,3) + contrastSubImg(I+2,field,sd->width,sd->height,3))/3; } /** calculates Michelson-contrast in the given small part of the given image \param I pointer to framebuffer \param field Field specifies position(center) and size of subimage \param width width of frame \param height height of frame \param bytesPerPixel calc contrast for only for first channel */ double contrastSubImg(unsigned char* const I, const Field* field, int width, int height, int bytesPerPixel) { #if USE_SSE2 int k, j; int s2 = field->size / 2; __m128i mini = _mm_set_epi64(_mm_set1_pi8(255), _mm_set1_pi8(255)); __m128i maxi = _mm_set_epi64(_mm_set1_pi8(0) , _mm_set1_pi8(0)); unsigned char* p = I + ((field->x - s2) + (field->y - s2)*width)*bytesPerPixel; __m128i A; for (j = 0; j < field->size; j++){ for (k = 0; k < (field->size * bytesPerPixel)-16 ; k++) { A= _mm_loadu_si128((__m128i*)p); //load unaligned data maxi = _mm_max_epu8(A,maxi); mini = _mm_min_epu8(A,mini); p+=16; k+=15; } p += (width - field->size) * bytesPerPixel; } int max=0; union { __m128i m; uint8_t t[16]; } m; m.m=maxi; for (j=0;j<16;j++) { if (m.t[j] >max) max=m.t[j]; } int min=255; m.m=mini; for (j=0;j<16;j++) { if (m.t[j] size / 2; unsigned char mini = 255; unsigned char maxi = 0; p = I + ((field->x - s2) + (field->y - s2)*width)*bytesPerPixel; // TODO: use some mmx or sse stuff here for (j = 0; j < field->size; j++){ for (k = 0; k < field->size * bytesPerPixel; k++) { mini = (mini < *p) ? mini : *p; maxi = (maxi > *p) ? maxi : *p; p += bytesPerPixel; } p += (width - field->size) * bytesPerPixel; } return (maxi-mini)/(maxi+mini+0.1); // +0.1 to avoid division by 0 #endif } /** tries to register current frame onto previous frame. This is the most simple algorithm: shift images to all possible positions and calc summed error Shift with minimal error is selected. */ Transform calcShiftRGBSimple(StabData* sd) { int x = 0, y = 0; int i, j; double minerror = 1e20; for (i = -sd->maxshift; i <= sd->maxshift; i++) { for (j = -sd->maxshift; j <= sd->maxshift; j++) { double error = compareImg(sd->curr, sd->prev, sd->width, sd->height, 3, i, j); if (error < minerror) { minerror = error; x = i; y = j; } } } return new_transform(x, y, 0, 0, 0); } /** tries to register current frame onto previous frame. (only the luminance is used) This is the most simple algorithm: shift images to all possible positions and calc summed error Shift with minimal error is selected. */ Transform calcShiftYUVSimple(StabData* sd) { int x = 0, y = 0; int i, j; unsigned char *Y_c, *Y_p;// , *Cb, *Cr; #ifdef STABVERBOSE FILE *f = NULL; char buffer[32]; tc_snprintf(buffer, sizeof(buffer), "f%04i.dat", sd->t); f = fopen(buffer, "w"); fprintf(f, "# splot \"%s\"\n", buffer); #endif // we only use the luminance part of the image Y_c = sd->curr; // Cb_c = sd->curr + sd->width*sd->height; //Cr_c = sd->curr + 5*sd->width*sd->height/4; Y_p = sd->prev; //Cb_p = sd->prev + sd->width*sd->height; //Cr_p = sd->prev + 5*sd->width*sd->height/4; double minerror = 1e20; for (i = -sd->maxshift; i <= sd->maxshift; i++) { for (j = -sd->maxshift; j <= sd->maxshift; j++) { double error = compareImg(Y_c, Y_p, sd->width, sd->height, 1, i, j); #ifdef STABVERBOSE fprintf(f, "%i %i %f\n", i, j, error); #endif if (error < minerror) { minerror = error; x = i; y = j; } } } #ifdef STABVERBOSE fclose(f); tc_log_msg(MOD_NAME, "Minerror: %f\n", minerror); #endif return new_transform(x, y, 0, 0, 0); } /* calculates rotation angle for the given transform and * field with respect to the given center-point */ double calcAngle(StabData* sd, Field* field, Transform* t, int center_x, int center_y) { // we better ignore fields that are to close to the rotation center if (abs(field->x - center_x) + abs(field->y - center_y) < sd->maxshift) { return 0; } else { // double r = sqrt(field->x*field->x + field->y*field->y); double a1 = atan2(field->y - center_y, field->x - center_x); double a2 = atan2(field->y - center_y + t->y, field->x - center_x + t->x); double diff = a2 - a1; return (diff>M_PI) ? diff - 2*M_PI : ( (diff<-M_PI) ? diff + 2*M_PI : diff); } } /* calculates the optimal transformation for one field in YUV frames * (only luminance) */ Transform calcFieldTransYUV(StabData* sd, const Field* field, int fieldnum) { Transform t = null_transform(); unsigned char *Y_c = sd->curr, *Y_p = sd->prev; // we only use the luminance part of the image int i, j; /* // check contrast in sub image */ /* double contr = contrastSubImg(Y_c, field, sd->width, sd->height, 1); */ /* if(contr < sd->contrast_threshold) { */ /* t.extra=-1; */ /* return t; */ /* } */ #ifdef STABVERBOSE // printf("%i %i %f\n", sd->t, fieldnum, contr); FILE *f = NULL; char buffer[32]; snprintf(buffer, sizeof(buffer), "f%04i_%02i.dat", sd->t, fieldnum); f = fopen(buffer, "w"); fprintf(f, "# splot \"%s\"\n", buffer); #endif double minerror = 1e10; double error = 1e10; for (i = -sd->maxshift; i <= sd->maxshift; i += sd->stepsize) { for (j = -sd->maxshift; j <= sd->maxshift; j += sd->stepsize) { error = compareSubImg(Y_c, Y_p, field, sd->width, sd->height, 1, i, j); #ifdef STABVERBOSE fprintf(f, "%i %i %f\n", i, j, error); #endif if (error < minerror) { minerror = error; t.x = i; t.y = j; } } } if (sd->stepsize > 1) { // make fine grain check around the best match int r = sd->stepsize - 1; for (i = t.x - r; i <= t.x + r; i += 1) { for (j = -t.y - r; j <= t.y + r; j += 1) { if (i == t.x && j == t.y) continue; //no need to check this since already done error = compareSubImg(Y_c, Y_p, field, sd->width, sd->height, 1, i, j); #ifdef STABVERBOSE fprintf(f, "%i %i %f\n", i, j, error); #endif if (error < minerror){ minerror = error; t.x = i; t.y = j; } } } } #ifdef STABVERBOSE fclose(f); mlt_log_debug ( "Minerror: %f\n", minerror); #endif if (!sd->allowmax && fabs(t.x) == sd->maxshift) { #ifdef STABVERBOSE mlt_log_debug ( "maximal x shift "); #endif t.x = 0; } if (!sd->allowmax && fabs(t.y) == sd->maxshift) { #ifdef STABVERBOSE mlt_log_debug ("maximal y shift "); #endif t.y = 0; } return t; } /* calculates the optimal transformation for one field in RGB * slower than the YUV version because it uses all three color channels */ Transform calcFieldTransRGB(StabData* sd, const Field* field, int fieldnum) { Transform t = null_transform(); unsigned char *I_c = sd->curr, *I_p = sd->prev; int i, j; double minerror = 1e20; for (i = -sd->maxshift; i <= sd->maxshift; i += 2) { for (j=-sd->maxshift; j <= sd->maxshift; j += 2) { double error = compareSubImg(I_c, I_p, field, sd->width, sd->height, 3, i, j); if (error < minerror) { minerror = error; t.x = i; t.y = j; } } } for (i = t.x - 1; i <= t.x + 1; i += 2) { for (j = -t.y - 1; j <= t.y + 1; j += 2) { double error = compareSubImg(I_c, I_p, field, sd->width, sd->height, 3, i, j); if (error < minerror) { minerror = error; t.x = i; t.y = j; } } } if (!sd->allowmax && fabs(t.x) == sd->maxshift) { t.x = 0; } if (!sd->allowmax && fabs(t.y) == sd->maxshift) { t.y = 0; } return t; } /* compares contrast_idx structures respect to the contrast (for sort function) */ int cmp_contrast_idx(const void *ci1, const void* ci2) { double a = ((contrast_idx*)ci1)->contrast; double b = ((contrast_idx*)ci2)->contrast; return a < b ? 1 : ( a > b ? -1 : 0 ); } /* select only the best 'maxfields' fields first calc contrasts then select from each part of the frame a some fields */ tlist* selectfields(StabData* sd, contrastSubImgFunc contrastfunc) { int i,j; tlist* goodflds = tlist_new(0); contrast_idx *ci = malloc(sizeof(contrast_idx) * sd->field_num); // we split all fields into row+1 segments and take from each segment // the best fields int numsegms = (sd->field_rows+1); int segmlen = sd->field_num/(sd->field_rows+1)+1; // split the frame list into rows+1 segments contrast_idx *ci_segms = malloc(sizeof(contrast_idx) * sd->field_num); int remaining = 0; // calculate contrast for each field for (i = 0; i < sd->field_num; i++) { ci[i].contrast = contrastfunc(sd, &sd->fields[i]); ci[i].index=i; if(ci[i].contrast < sd->contrast_threshold) ci[i].contrast = 0; // else printf("%i %lf\n", ci[i].index, ci[i].contrast); } memcpy(ci_segms, ci, sizeof(contrast_idx) * sd->field_num); // get best fields from each segment for(i=0; i sd->field_num ? sd->field_num : endindex; //printf("Segment: %i: %i-%i\n", i, startindex, endindex); // sort within segment qsort(ci_segms+startindex, endindex-startindex, sizeof(contrast_idx), cmp_contrast_idx); // take maxfields/numsegms for(j=0; jmaxfields/numsegms; j++){ if(startindex+j >= endindex) continue; // printf("%i %lf\n", ci_segms[startindex+j].index, // ci_segms[startindex+j].contrast); if(ci_segms[startindex+j].contrast > 0){ tlist_append(goodflds, &ci[ci_segms[startindex+j].index],sizeof(contrast_idx)); // don't consider them in the later selection process ci_segms[startindex+j].contrast=0; } } } // check whether enough fields are selected // printf("Phase2: %i\n", tc_list_size(goodflds)); remaining = sd->maxfields - tlist_size(goodflds); if(remaining > 0){ // take the remaining from the leftovers qsort(ci_segms, sd->field_num, sizeof(contrast_idx), cmp_contrast_idx); for(j=0; j < remaining; j++){ if(ci_segms[j].contrast > 0){ tlist_append(goodflds, &ci_segms[j], sizeof(contrast_idx)); } } } // printf("Ende: %i\n", tc_list_size(goodflds)); free(ci); free(ci_segms); return goodflds; } /* tries to register current frame onto previous frame. * Algorithm: * check all fields for vertical and horizontal transformation * use minimal difference of all possible positions * discards fields with low contrast * select maxfields field according to their contrast * calculate shift as cleaned mean of all remaining fields * calculate rotation angle of each field in respect to center of fields * after shift removal * calculate rotation angle as cleaned mean of all angles * compensate for possibly off-center rotation */ Transform calcTransFields(StabData* sd, calcFieldTransFunc fieldfunc, contrastSubImgFunc contrastfunc) { Transform* ts = malloc(sizeof(Transform) * sd->field_num); Field** fs = malloc(sizeof(Field*) * sd->field_num); double *angles = malloc(sizeof(double) * sd->field_num); int i, index=0, num_trans; Transform t; #ifdef STABVERBOSE FILE *f = NULL; char buffer[32]; tc_snprintf(buffer, sizeof(buffer), "k%04i.dat", sd->t); f = fopen(buffer, "w"); fprintf(f, "# plot \"%s\" w l, \"\" every 2:1:0\n", buffer); #endif tlist* goodflds = selectfields(sd, contrastfunc); // use all "good" fields and calculate optimal match to previous frame contrast_idx* f; while((f = (contrast_idx*)tlist_pop(goodflds,0) ) != 0){ int i = f->index; t = fieldfunc(sd, &sd->fields[i], i); // e.g. calcFieldTransYUV #ifdef STABVERBOSE fprintf(f, "%i %i\n%f %f %i\n \n\n", sd->fields[i].x, sd->fields[i].y, sd->fields[i].x + t.x, sd->fields[i].y + t.y, t.extra); #endif if (t.extra != -1){ // ignore if extra == -1 (unused at the moment) ts[index] = t; fs[index] = sd->fields+i; index++; } } tlist_fini(goodflds); t = null_transform(); num_trans = index; // amount of transforms we actually have if (num_trans < 1) { printf( "too low contrast! No field remains.\n" "(no translations are detected in frame %i)", sd->t); free(ts); free(fs); free(angles); return t; } int center_x = 0; int center_y = 0; // calc center point of all remaining fields for (i = 0; i < num_trans; i++) { center_x += fs[i]->x; center_y += fs[i]->y; } center_x /= num_trans; center_y /= num_trans; if (sd->show){ // draw fields and transforms into frame. // this has to be done one after another to handle possible overlap if (sd->show > 1) { for (i = 0; i < num_trans; i++) drawFieldScanArea(sd, fs[i], &ts[i]); } for (i = 0; i < num_trans; i++) drawField(sd, fs[i], &ts[i]); for (i = 0; i < num_trans; i++) drawFieldTrans(sd, fs[i], &ts[i]); } /* median over all transforms t= median_xy_transform(ts, sd->field_num);*/ // cleaned mean t = cleanmean_xy_transform(ts, num_trans); // subtract avg for (i = 0; i < num_trans; i++) { ts[i] = sub_transforms(&ts[i], &t); } // figure out angle if (sd->field_num < 6) { // the angle calculation is inaccurate for 5 and less fields t.alpha = 0; } else { for (i = 0; i < num_trans; i++) { angles[i] = calcAngle(sd, fs[i], &ts[i], center_x, center_y); } double min,max; t.alpha = -cleanmean(angles, num_trans, &min, &max); if(max-min>sd->maxanglevariation){ t.alpha=0; printf( "too large variation in angle(%f)\n", max-min); } } // compensate for off-center rotation double p_x = (center_x - sd->width/2); double p_y = (center_y - sd->height/2); t.x += (cos(t.alpha)-1)*p_x - sin(t.alpha)*p_y; t.y += sin(t.alpha)*p_x + (cos(t.alpha)-1)*p_y; #ifdef STABVERBOSE fclose(f); #endif free(ts); free(fs); free(angles); return t; } /** draws the field scanning area */ void drawFieldScanArea(StabData* sd, const Field* field, const Transform* t) { if (sd->pixelformat != mlt_image_yuv420p) { mlt_log_warning (NULL, "format not usable\n"); return; } drawBox(sd->curr, sd->width, sd->height, 1, field->x, field->y, field->size+2*sd->maxshift, field->size+2*sd->maxshift, 80); } /** draws the field */ void drawField(StabData* sd, const Field* field, const Transform* t) { if (sd->pixelformat != mlt_image_yuv420p){ mlt_log_warning (NULL, "format not usable\n"); return; } drawBox(sd->curr, sd->width, sd->height, 1, field->x, field->y, field->size, field->size, t->extra == -1 ? 100 : 40); } /** draws the transform data of this field */ void drawFieldTrans(StabData* sd, const Field* field, const Transform* t) { if (sd->pixelformat != mlt_image_yuv420p){ mlt_log_warning (NULL, "format not usable\n"); return; } drawBox(sd->curr, sd->width, sd->height, 1, field->x, field->y, 5, 5, 128); // draw center drawBox(sd->curr, sd->width, sd->height, 1, field->x + t->x, field->y + t->y, 8, 8, 250); // draw translation } /** * draws a box at the given position x,y (center) in the given color (the same for all channels) */ void drawBox(unsigned char* I, int width, int height, int bytesPerPixel, int x, int y, int sizex, int sizey, unsigned char color){ unsigned char* p = NULL; int j,k; p = I + ((x - sizex/2) + (y - sizey/2)*width)*bytesPerPixel; for (j = 0; j < sizey; j++){ for (k = 0; k < sizex * bytesPerPixel; k++) { *p = color; p++; } p += (width - sizex) * bytesPerPixel; } } struct iterdata { FILE *f; int counter; }; /*************************************************************************/ /* Module interface routines and data. */ /*************************************************************************/ /* * stabilize_configure: Configure this instance of the module. See * tcmodule-data.h for function details. */ int stabilize_configure(StabData* instance /*const char *options, int pixelfmt */ /*TCModuleExtraData *xdata[]*/) { StabData *sd = instance; /* sd->framesize = sd->vob->im_v_width * MAX_PLANES * sizeof(char) * 2 * sd->vob->im_v_height * 2; */ /*TODO sd->framesize = sd->vob->im_v_size; */ sd->prev = calloc(1,sd->framesize); sd->grayimage = calloc(1,sd->width*sd->height); if (!sd->prev || !sd->grayimage) { printf( "malloc failed"); return -1; } sd->currcopy = 0; sd->hasSeenOneFrame = 0; sd->transs = 0; sd->allowmax = 0; sd->field_size = MIN(sd->width, sd->height)/12; sd->maxanglevariation = 1; sd->shakiness = MIN(10,MAX(1,sd->shakiness)); sd->accuracy = MAX(sd->shakiness,MIN(15,MAX(1,sd->accuracy))); if (1) { mlt_log_debug (NULL, "Image Stabilization Settings:\n"); mlt_log_debug (NULL, " shakiness = %d\n", sd->shakiness); mlt_log_debug (NULL, " accuracy = %d\n", sd->accuracy); mlt_log_debug (NULL, " stepsize = %d\n", sd->stepsize); mlt_log_debug (NULL, " algo = %d\n", sd->algo); mlt_log_debug (NULL, " mincontrast = %f\n", sd->contrast_threshold); mlt_log_debug (NULL, " show = %d\n", sd->show); } #ifndef USE_SSE2 mlt_log_info(NULL,"No SSE2 support enabled, this will slow down a lot\n"); #endif // shift and size: shakiness 1: height/40; 10: height/4 sd->maxshift = MIN(sd->width, sd->height)*sd->shakiness/40; sd->field_size = MIN(sd->width, sd->height)*sd->shakiness/40; mlt_log_debug ( NULL, "Fieldsize: %i, Maximal translation: %i pixel\n", sd->field_size, sd->maxshift); if (sd->algo==1) { // initialize measurement fields. field_num is set here. if (!initFields(sd)) { return -1; } sd->maxfields = (sd->accuracy) * sd->field_num / 15; mlt_log_debug ( NULL, "Number of used measurement fields: %i out of %i\n", sd->maxfields, sd->field_num); } if (sd->show){ sd->currcopy = calloc(1,sd->framesize); } /* load unsharp filter to smooth the frames. This allows larger stepsize.*/ char unsharp_param[128]; int masksize = MIN(13,sd->stepsize*1.8); // only works up to 13. sprintf(unsharp_param,"luma=-1:luma_matrix=%ix%i:pre=1", masksize, masksize); return 0; } /** * stabilize_filter_video: performs the analysis of subsequent frames * See tcmodule-data.h for function details. */ int stabilize_filter_video(StabData* instance, unsigned char *frame,mlt_image_format pixelformat) { StabData *sd = instance; sd->pixelformat=pixelformat; int l=sd->width*sd->height; unsigned char* tmpgray=sd->grayimage; if (pixelformat == mlt_image_yuv422){ while(l--){ *tmpgray++=*frame++; frame++; }; } if(sd->show) { // save the buffer to restore at the end for prev if (pixelformat == mlt_image_yuv420p){ memcpy(sd->currcopy, sd->grayimage, sd->framesize); } } if (sd->hasSeenOneFrame) { sd->curr = sd->grayimage; if (pixelformat == mlt_image_rgb24) { if (sd->algo == 0) addTrans(sd, calcShiftRGBSimple(sd)); else if (sd->algo == 1) addTrans(sd, calcTransFields(sd, calcFieldTransRGB, contrastSubImgRGB)); } else if (pixelformat == mlt_image_yuv420p ) { if (sd->algo == 0) addTrans(sd, calcShiftYUVSimple(sd)); else if (sd->algo == 1) addTrans(sd, calcTransFields(sd, calcFieldTransYUV, contrastSubImgYUV)); } else if (pixelformat == mlt_image_yuv422 ) { if (sd->algo == 0) addTrans(sd, calcShiftYUVSimple(sd)); else if (sd->algo == 1) addTrans(sd, calcTransFields(sd, calcFieldTransYUV, contrastSubImgYUV)); } else { mlt_log_warning (NULL,"unsupported Codec: %i\n", pixelformat); return 0; } } else { sd->hasSeenOneFrame = 1; addTrans(sd, null_transform()); } if(!sd->show) { // copy current frame to prev for next frame comparison memcpy(sd->prev, sd->grayimage, sd->framesize); } else { // use the copy because we changed the original frame memcpy(sd->prev, sd->currcopy, sd->framesize); } sd->t++; return 0; } /** * stabilize_stop: Reset this instance of the module. See tcmodule-data.h * for function details. */ int stabilize_stop(StabData* instance) { StabData *sd = instance; free(sd->prev); sd->prev = NULL; free(sd->grayimage); sd->grayimage=NULL; return 0; } mlt-6.20.0/src/modules/videostab/stabilize.h000066400000000000000000000161661362234133600207660ustar00rootroot00000000000000/* * filter_stabilize.c * * Copyright (C) Georg Martius - June 2007 * georg dot martius at web dot de * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode is distributed in the hope that 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* Typical call: * transcode -V -J stabilize=shakiness=5:show=1,preview * -i inp.mpeg -y null,null -o dummy * all parameters are optional */ #define MOD_NAME "filter_stabilize.so" #define MOD_VERSION "v0.75 (2010-04-07)" #define MOD_CAP "extracts relative transformations of \n\ subsequent frames (used for stabilization together with the\n\ transform filter in a second pass)" #define MOD_AUTHOR "Georg Martius" /* Ideas: - Try OpenCL/Cuda, this should work great - use smoothing on the frames and then use gradient decent! - stepsize could be adapted (maybe to check only one field with large stepsize and use the maximally required for the other fields */ #define MOD_FEATURES \ TC_MODULE_FEATURE_FILTER|TC_MODULE_FEATURE_VIDEO #define MOD_FLAGS \ TC_MODULE_FLAG_RECONFIGURABLE | TC_MODULE_FLAG_DELAY #include "transform.h" #include #include #include #include "tlist.h" #include /* if defined we are very verbose and generate files to analyse * this is really just for debugging and development */ // #define STABVERBOSE typedef struct _field { int x; // middle position x int y; // middle position y int size; // size of field } Field; // structure that contains the contrast and the index of a field typedef struct _contrast_idx { double contrast; int index; } contrast_idx; /* private date structure of this filter*/ typedef struct _stab_data { int framesize; // size of frame buffer in bytes (prev) unsigned char* curr; // current frame buffer (only pointer) unsigned char* currcopy; // copy of the current frame needed for drawing unsigned char* prev; // frame buffer for last frame (copied) unsigned char* grayimage; // frame buffer for last frame (copied) short hasSeenOneFrame; // true if we have a valid previous frame int width, height; mlt_image_format pixelformat; /* list of transforms*/ //TCList* transs; tlist* transs; Field* fields; /* Options */ /* maximum number of pixels we expect the shift of subsequent frames */ int maxshift; int stepsize; // stepsize of field transformation detection int allowmax; // 1 if maximal shift is allowed int algo; // algorithm to use int field_num; // number of measurement fields int maxfields; // maximum number of fields used (selected by contrast) int field_size; // size = min(sd->width, sd->height)/10; int field_rows; // number of rows /* if 1 and 2 then the fields and transforms are shown in the frames */ int show; /* measurement fields with lower contrast are discarded */ double contrast_threshold; /* maximal difference in angles of fields */ double maxanglevariation; /* meta parameter for maxshift and fieldsize between 1 and 10 */ int shakiness; int accuracy; // meta parameter for number of fields between 1 and 10 int t; char conf_str[1024]; } StabData; /* type for a function that calculates the transformation of a certain field */ typedef Transform (*calcFieldTransFunc)(StabData*, const Field*, int); /* type for a function that calculates the contrast of a certain field */ typedef double (*contrastSubImgFunc)(StabData* sd, const Field* field); static const char stabilize_help[] = "" "Overview:\n" " Generates a file with relative transform information\n" " (translation, rotation) about subsequent frames." " See also transform.\n" "Options\n" " 'result' path to the file used to write the transforms\n" " (def:inputfile.stab)\n" " 'shakiness' how shaky is the video and how quick is the camera?\n" " 1: little (fast) 10: very strong/quick (slow) (def: 4)\n" " 'accuracy' accuracy of detection process (>=shakiness)\n" " 1: low (fast) 15: high (slow) (def: 4)\n" " 'stepsize' stepsize of search process, region around minimum \n" " is scanned with 1 pixel resolution (def: 6)\n" " 'algo' 0: brute force (translation only);\n" " 1: small measurement fields (def)\n" " 'mincontrast' below this contrast a field is discarded (0-1) (def: 0.3)\n" " 'show' 0: draw nothing (def); 1,2: show fields and transforms\n" " in the resulting frames. Consider the 'preview' filter\n" " 'help' print this help message\n"; int initFields(StabData* sd); double compareImg(unsigned char* I1, unsigned char* I2, int width, int height, int bytesPerPixel, int d_x, int d_y); double compareSubImg(unsigned char* const I1, unsigned char* const I2, const Field* field, int width, int height, int bytesPerPixel,int d_x,int d_y); double contrastSubImgYUV(StabData* sd, const Field* field); double contrastSubImgRGB(StabData* sd, const Field* field); double contrastSubImg(unsigned char* const I, const Field* field, int width, int height, int bytesPerPixel); int cmp_contrast_idx(const void *ci1, const void* ci2); tlist* selectfields(StabData* sd, contrastSubImgFunc contrastfunc); Transform calcShiftRGBSimple(StabData* sd); Transform calcShiftYUVSimple(StabData* sd); double calcAngle(StabData* sd, Field* field, Transform* t, int center_x, int center_y); Transform calcFieldTransYUV(StabData* sd, const Field* field, int fieldnum); Transform calcFieldTransRGB(StabData* sd, const Field* field, int fieldnum); Transform calcTransFields(StabData* sd, calcFieldTransFunc fieldfunc, contrastSubImgFunc contrastfunc); void drawFieldScanArea(StabData* sd, const Field* field, const Transform* t); void drawField(StabData* sd, const Field* field, const Transform* t); void drawFieldTrans(StabData* sd, const Field* field, const Transform* t); void drawBox(unsigned char* I, int width, int height, int bytesPerPixel, int x, int y, int sizex, int sizey, unsigned char color); void addTrans(StabData* sd, Transform sl); int stabilize_configure(StabData* instance); int stabilize_stop(StabData* instance); int stabilize_filter_video(StabData* instance, unsigned char *frame,mlt_image_format imageformat); mlt-6.20.0/src/modules/videostab/tlist.c000066400000000000000000000021011362234133600201120ustar00rootroot00000000000000#include "tlist.h" #include #include tlist* tlist_new(int size){ tlist* t=malloc(sizeof(tlist)); memset(t,0,sizeof(tlist)); return t; } void tlist_append(tlist* t,void* data,int size){ tlist* next=tlist_new(0); tlist* pos=t; while (pos && pos->next) { pos=pos->next; } pos->data=malloc(size); memcpy(pos->data,data,size); pos->next=next; } int tlist_size(tlist* t){ int ret=0; tlist* pos=t; while (pos && pos->next && pos->data) { pos=pos->next ; ret++; }; return ret; } void* tlist_pop(tlist* t,int at){ int ret=0; tlist* pos=t; /*if (pos && !pos->next ){ return pos->data; }*/ while (pos && pos->next) { if (ret==at){ tlist* n=pos->next; pos->data=n->data; pos->next=n->next; return pos->data; } pos=pos->next ; ret++; }; return NULL; } void tlist_fini(tlist* list){ tlist* head=list; while (head){ free(head->data); tlist *del=head; head=head->next; free(del); } } mlt-6.20.0/src/modules/videostab/tlist.h000066400000000000000000000004451362234133600201300ustar00rootroot00000000000000 #ifndef __TLIST__ #define __TLIST__ typedef struct _tlist { void* data; void* next; } tlist; tlist* tlist_new(int size); void tlist_append(tlist* t,void* data,int size); int tlist_size(tlist* t); void* tlist_pop(tlist* t,int at); void tlist_fini(tlist* ); #endif mlt-6.20.0/src/modules/videostab/transform.c000066400000000000000000000206551362234133600210040ustar00rootroot00000000000000/* * transform.c * * Copyright (C) Georg Martius - June 2007 * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode is distributed in the hope that 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "transform.h" /*********************************************************************** * helper functions to create and operate with transforms. * all functions are non-destructive */ /* create an initialized transform*/ Transform new_transform(double x, double y, double alpha, double zoom, int extra) { Transform t; t.x = x; t.y = y; t.alpha = alpha; t.zoom = zoom; t.extra = extra; return t; } /* create a zero initialized transform*/ Transform null_transform(void) { return new_transform(0, 0, 0, 0, 0); } /* adds two transforms */ Transform add_transforms(const Transform* t1, const Transform* t2) { Transform t; t.x = t1->x + t2->x; t.y = t1->y + t2->y; t.alpha = t1->alpha + t2->alpha; t.zoom = t1->zoom + t2->zoom; t.extra = 0; return t; } /* like add_transform but with non-pointer signature */ Transform add_transforms_(const Transform t1, const Transform t2) { return add_transforms(&t1, &t2); } /* subtracts two transforms */ Transform sub_transforms(const Transform* t1, const Transform* t2) { Transform t; t.x = t1->x - t2->x; t.y = t1->y - t2->y; t.alpha = t1->alpha - t2->alpha; t.zoom = t1->zoom - t2->zoom; t.extra = 0; return t; } /* multiplies a transforms with a scalar */ Transform mult_transform(const Transform* t1, double f) { Transform t; t.x = t1->x * f; t.y = t1->y * f; t.alpha = t1->alpha * f; t.zoom = t1->zoom * f; t.extra = 0; return t; } /* like mult_transform but with non-pointer signature */ Transform mult_transform_(const Transform t1, double f) { return mult_transform(&t1,f); } /* compares a transform with respect to x (for sort function) */ int cmp_trans_x(const void *t1, const void* t2) { double a = ((Transform*)t1)->x; double b = ((Transform*)t2)->x; return a < b ? -1 : ( a > b ? 1 : 0 ); } /* compares a transform with respect to y (for sort function) */ int cmp_trans_y(const void *t1, const void* t2) { double a = ((Transform*)t1)->y; double b = ((Transform*)t2)->y; return a < b ? -1 : ( a > b ? 1: 0 ); } /* static int cmp_trans_alpha(const void *t1, const void* t2){ */ /* double a = ((Transform*)t1)->alpha; */ /* double b = ((Transform*)t2)->alpha; */ /* return a < b ? -1 : ( a > b ? 1 : 0 ); */ /* } */ /* compares two double values (for sort function)*/ int cmp_double(const void *t1, const void* t2) { double a = *((double*)t1); double b = *((double*)t2); return a < b ? -1 : ( a > b ? 1 : 0 ); } /** * median_xy_transform: calulcates the median of an array * of transforms, considering only x and y * * Parameters: * transforms: array of transforms. * len: length of array * Return value: * A new transform with x and y being the median of * all transforms. alpha and other fields are 0. * Preconditions: * len>0 * Side effects: * None */ Transform median_xy_transform(const Transform* transforms, int len) { Transform* ts = malloc(sizeof(Transform) * len); Transform t; memcpy(ts,transforms, sizeof(Transform)*len ); int half = len/2; qsort(ts, len, sizeof(Transform), cmp_trans_x); t.x = len % 2 == 0 ? ts[half].x : (ts[half].x + ts[half+1].x)/2; qsort(ts, len, sizeof(Transform), cmp_trans_y); t.y = len % 2 == 0 ? ts[half].y : (ts[half].y + ts[half+1].y)/2; t.alpha = 0; t.zoom = 0; t.extra = 0; free(ts); return t; } /** * cleanmean_xy_transform: calulcates the cleaned mean of an array * of transforms, considering only x and y * * Parameters: * transforms: array of transforms. * len: length of array * Return value: * A new transform with x and y being the cleaned mean * (meaning upper and lower pentile are removed) of * all transforms. alpha and other fields are 0. * Preconditions: * len>0 * Side effects: * None */ Transform cleanmean_xy_transform(const Transform* transforms, int len) { Transform* ts = malloc(sizeof(Transform) * len); Transform t = null_transform(); int i, cut = len / 5; memcpy(ts, transforms, sizeof(Transform) * len); qsort(ts,len, sizeof(Transform), cmp_trans_x); for (i = cut; i < len - cut; i++){ // all but cutted t.x += ts[i].x; } qsort(ts, len, sizeof(Transform), cmp_trans_y); for (i = cut; i < len - cut; i++){ // all but cutted t.y += ts[i].y; } free(ts); return mult_transform(&t, 1.0 / (len - (2.0 * cut))); } /** * calulcates the cleaned maximum and minimum of an array of transforms, * considerung only x and y * It cuts off the upper and lower x-th percentil * * Parameters: * transforms: array of transforms. * len: length of array * percentil: the x-th percentil to cut off * min: pointer to min (return value) * max: pointer to max (return value) * Return value: * call by reference in min and max * Preconditions: * len>0, 0<=percentil<50 * Side effects: * only on min and max */ void cleanmaxmin_xy_transform(const Transform* transforms, int len, int percentil, Transform* min, Transform* max){ Transform* ts = malloc(sizeof(Transform) * len); int cut = len * percentil / 100; memcpy(ts, transforms, sizeof(Transform) * len); qsort(ts,len, sizeof(Transform), cmp_trans_x); min->x = ts[cut].x; max->x = ts[len-cut-1].x; qsort(ts, len, sizeof(Transform), cmp_trans_y); min->y = ts[cut].y; max->y = ts[len-cut-1].y; free(ts); } /** * media: median of a double array * * Parameters: * ds: array of values * len: length of array * Return value: * the median value of the array * Preconditions: len>0 * Side effects: ds will be sorted! */ double median(double* ds, int len) { int half=len/2; qsort(ds,len, sizeof(double), cmp_double); return len % 2 == 0 ? ds[half] : (ds[half] + ds[half+1])/2; } /** * mean: mean of a double array * * Parameters: * ds: array of values * len: length of array * Return value: the mean value of the array * Preconditions: len>0 * Side effects: None */ double mean(const double* ds, int len) { double sum=0; int i = 0; for (i = 0; i < len; i++) sum += ds[i]; return sum / len; } /** * cleanmean: mean with cutted upper and lower pentile * * Parameters: * ds: array of values * len: length of array * len: length of array * minimum: minimal value (after cleaning) if not NULL * maximum: maximal value (after cleaning) if not NULL * Return value: * the mean value of the array without the upper * and lower pentile (20% each) * and lower pentile (20% each) * and minimum and maximum without the pentiles * Preconditions: len>0 * Side effects: ds will be sorted! */ double cleanmean(double* ds, int len, double* minimum, double* maximum) { int cut = len / 5; double sum = 0; int i = 0; qsort(ds, len, sizeof(double), cmp_double); for (i = cut; i < len - cut; i++) { // all but first and last sum += ds[i]; } if (minimum) *minimum = ds[cut]; if (maximum) *maximum = ds[len-cut-1]; return sum / (len - (2.0 * cut)); } /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ mlt-6.20.0/src/modules/videostab/transform.h000066400000000000000000000100131362234133600207740ustar00rootroot00000000000000/* * transform.h * * Copyright (C) Georg Martius - June 2007 * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode is distributed in the hope that 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef __TRANSFORM_H #define __TRANSFORM_H #define DEFAULT_TRANS_FILE_NAME "transforms.dat" /* structure to hold information about frame transformations x,y are translations, alpha is a rotation around the center in RAD, zoom is a percentage to zoom in and extra is for additional information like scene cut (unused) */ typedef struct _transform { double x; double y; double alpha; double zoom; int extra; /* -1: ignore transform (only internal use); 0 for normal trans; 1 for inter scene cut (unused) */ } Transform; /* helper functions to create and operate with transforms. * all functions are non-destructive * the "_" version uses non-pointer Transforms. This is slower * but useful when cascading calculations like * add_transforms_(mult_transform(&t1, 5.0), &t2) */ Transform null_transform(void); Transform new_transform(double x, double y, double alpha, double zoom, int extra); Transform add_transforms(const Transform* t1, const Transform* t2); Transform add_transforms_(const Transform t1, const Transform t2); Transform sub_transforms(const Transform* t1, const Transform* t2); Transform mult_transform(const Transform* t1, double f); Transform mult_transform_(const Transform t1, double f); /* compares a transform with respect to x (for sort function) */ int cmp_trans_x(const void *t1, const void* t2); /* compares a transform with respect to y (for sort function) */ int cmp_trans_y(const void *t1, const void* t2); /* static int cmp_trans_alpha(const void *t1, const void* t2); */ /* compares two double values (for sort function)*/ int cmp_double(const void *t1, const void* t2); /* calculates the median of an array of transforms, * considering only x and y */ Transform median_xy_transform(const Transform* transforms, int len); /* median of a double array */ double median(double* ds, int len); /* mean of a double array */ double mean(const double* ds, int len); /* mean with cutted upper and lower pentile * (min and max are optionally returned) */ double cleanmean(double* ds, int len, double* minimum, double* maximum); /* calculates the cleaned mean of an array of transforms, * considering only x and y */ Transform cleanmean_xy_transform(const Transform* transforms, int len); /* calculates the cleaned (cutting of x-th percentil) * maximum and minimum of an array of transforms, * considerung only x and y */ void cleanmaxmin_xy_transform(const Transform* transforms, int len, int percentil, Transform* min, Transform* max); /* helper functions */ /* optimized round function */ inline static int myround(float x) { if(x>0) return x + 0.5; else return x - 0.5; } /* optimized floor function This does not give the correct value for negative integer values like -1.0. In this case it will produce -2.0. */ inline static int myfloor(float x) { if(x<0) return x - 1; else return x; } #endif /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ mlt-6.20.0/src/modules/videostab/transform_image.c000066400000000000000000000645441362234133600221530ustar00rootroot00000000000000/* * filter_transform.c * * Copyright (C) Georg Martius - June 2007 * georg dot martius at web dot de * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode is distributed in the hope that 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * Typical call: * transcode -J transform -i inp.mpeg -y xdiv,tcaud inp_stab.avi */ #include "transform_image.h" #include #include #include #define TC_MAX(a, b) (((a) > (b)) ?(a) :(b)) #define TC_MIN(a, b) (((a) < (b)) ?(a) :(b)) /* clamp x between a and b */ #define TC_CLAMP(x, a, b) TC_MIN(TC_MAX((a), (x)), (b)) static const char* interpoltypes[5] = {"No (0)", "Linear (1)", "Bi-Linear (2)", "Quadratic (3)", "Bi-Cubic (4)"}; void (*interpolate)(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel) = 0; /** interpolateBiLinBorder: bi-linear interpolation function that also works at the border. This is used by many other interpolation methods at and outsize the border, see interpolate */ void interpolateBiLinBorder(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel) { int x_f = myfloor(x); int x_c = x_f+1; int y_f = myfloor(y); int y_c = y_f+1; short v1 = PIXELN(img, x_c, y_c, width, height, N, channel, def); short v2 = PIXELN(img, x_c, y_f, width, height, N, channel, def); short v3 = PIXELN(img, x_f, y_c, width, height, N, channel, def); short v4 = PIXELN(img, x_f, y_f, width, height, N, channel, def); float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) + (v2*(x - x_f) + v4*(x_c - x))*(y_c - y); *rv = (unsigned char)s; } /* taken from http://en.wikipedia.org/wiki/Bicubic_interpolation for alpha=-0.5 in matrix notation: a0-a3 are the neigthboring points where the target point is between a1 and a2 t is the point of interpolation (position between a1 and a2) value between 0 and 1 | 0, 2, 0, 0 | |a0| |-1, 0, 1, 0 | |a1| (1,t,t^2,t^3) | 2,-5, 4,-1 | |a2| |-1, 3,-3, 1 | |a3| */ static short bicub_kernel(float t, short a0, short a1, short a2, short a3){ return (2*a1 + t*((-a0+a2) + t*((2*a0-5*a1+4*a2-a3) + t*(-a0+3*a1-3*a2+a3) )) ) / 2; } /** interpolateBiCub: bi-cubic interpolation function using 4x4 pixel, see interpolate */ void interpolateBiCub(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel) { // do a simple linear interpolation at the border if (x < 1 || x >= width-2 || y < 1 || y >= height - 2) { interpolateBiLinBorder(rv, x,y,img,width,height,def,N,channel); } else { int x_f = myfloor(x); int y_f = myfloor(y); float tx = x-x_f; short v1 = bicub_kernel(tx, PIXN(img, x_f-1, y_f-1, width, height, N, channel), PIXN(img, x_f, y_f-1, width, height, N, channel), PIXN(img, x_f+1, y_f-1, width, height, N, channel), PIXN(img, x_f+2, y_f-1, width, height, N, channel)); short v2 = bicub_kernel(tx, PIXN(img, x_f-1, y_f, width, height, N, channel), PIXN(img, x_f, y_f, width, height, N, channel), PIXN(img, x_f+1, y_f, width, height, N, channel), PIXN(img, x_f+2, y_f, width, height, N, channel)); short v3 = bicub_kernel(tx, PIXN(img, x_f-1, y_f+1, width, height, N, channel), PIXN(img, x_f, y_f+1, width, height, N, channel), PIXN(img, x_f+1, y_f+1, width, height, N, channel), PIXN(img, x_f+2, y_f+1, width, height, N, channel)); short v4 = bicub_kernel(tx, PIXN(img, x_f-1, y_f+2, width, height, N, channel), PIXN(img, x_f, y_f+2, width, height, N, channel), PIXN(img, x_f+1, y_f+2, width, height, N, channel), PIXN(img, x_f+2, y_f+2, width, height, N, channel)); *rv = (unsigned char)bicub_kernel(y-y_f, v1, v2, v3, v4); } } /** interpolateSqr: bi-quatratic interpolation function, see interpolate */ void interpolateSqr(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel) { if (x < 0 || x >= width-1 || y < 0 || y >= height-1) { interpolateBiLinBorder(rv, x, y, img, width, height, def,N,channel); } else { int x_f = myfloor(x); int x_c = x_f+1; int y_f = myfloor(y); int y_c = y_f+1; short v1 = PIXN(img, x_c, y_c, width, height, N, channel); short v2 = PIXN(img, x_c, y_f, width, height, N, channel); short v3 = PIXN(img, x_f, y_c, width, height, N, channel); short v4 = PIXN(img, x_f, y_f, width, height, N, channel); float f1 = 1 - sqrt((x_c - x) * (y_c - y)); float f2 = 1 - sqrt((x_c - x) * (y - y_f)); float f3 = 1 - sqrt((x - x_f) * (y_c - y)); float f4 = 1 - sqrt((x - x_f) * (y - y_f)); float s = (v1*f1 + v2*f2 + v3*f3+ v4*f4)/(f1 + f2 + f3 + f4); *rv = (unsigned char)s; } } /** interpolateBiLin: bi-linear interpolation function, see interpolate */ void interpolateBiLin(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel) { if (x < 0 || x >= width-1 || y < 0 || y >= height - 1) { interpolateBiLinBorder(rv, x, y, img, width, height, def,N,channel); } else { int x_f = myfloor(x); int x_c = x_f+1; int y_f = myfloor(y); int y_c = y_f+1; short v1 = PIXN(img, x_c, y_c, width, height, N, channel); short v2 = PIXN(img, x_c, y_f, width, height, N, channel); short v3 = PIXN(img, x_f, y_c, width, height, N, channel); short v4 = PIXN(img, x_f, y_f, width, height, N, channel); float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) + (v2*(x - x_f) + v4*(x_c - x))*(y_c - y); *rv = (unsigned char)s; } } /** interpolateLin: linear (only x) interpolation function, see interpolate */ void interpolateLin(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel) { int x_f = myfloor(x); int x_c = x_f+1; int y_n = myround(y); float v1 = PIXELN(img, x_c, y_n, width, height, N, channel,def); float v2 = PIXELN(img, x_f, y_n, width, height, N, channel,def); float s = v1*(x - x_f) + v2*(x_c - x); *rv = (unsigned char)s; } /** interpolateZero: nearest neighbor interpolation function, see interpolate */ void interpolateZero(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel) { int x_n = myround(x); int y_n = myround(y); *rv = (unsigned char) PIXELN(img, x_n, y_n, width, height, N,channel,def); } /** * interpolateN: Bi-linear interpolation function for N channel image. * * Parameters: * rv: destination pixel (call by reference) * x,y: the source coordinates in the image img. Note this * are real-value coordinates, that's why we interpolate * img: source image * width,height: dimension of image * N: number of channels * channel: channel number (0..N-1) * def: default value if coordinates are out of range * Return value: None */ void interpolateN(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char N, unsigned char channel, unsigned char def) { if (x < - 1 || x > width || y < -1 || y > height) { *rv = def; } else { int x_f = myfloor(x); int x_c = x_f+1; int y_f = myfloor(y); int y_c = y_f+1; short v1 = PIXELN(img, x_c, y_c, width, height, N, channel, def); short v2 = PIXELN(img, x_c, y_f, width, height, N, channel, def); short v3 = PIXELN(img, x_f, y_c, width, height, N, channel, def); short v4 = PIXELN(img, x_f, y_f, width, height, N, channel, def); float s = (v1*(x - x_f)+v3*(x_c - x))*(y - y_f) + (v2*(x - x_f) + v4*(x_c - x))*(y_c - y); *rv = (unsigned char)s; } } /** * transformRGB: applies current transformation to frame * Parameters: * td: private data structure of this filter * Return value: * 0 for failure, 1 for success * Preconditions: * The frame must be in RGB format */ int transformRGB(TransformData* td) { Transform t; int x = 0, y = 0, z = 0; unsigned char *D_1, *D_2; t = td->trans[td->current_trans]; D_1 = td->src; D_2 = td->dest; float zm = 1.0-t.zoom/100; float zcos_a = zm*cos(-t.alpha); // scaled cos float zsin_a = zm*sin(-t.alpha); // scaled sin float c_s_x = td->width_src/2.0; float c_s_y = td->height_src/2.0; float c_d_x = td->width_dest/2.0; float c_d_y = td->height_dest/2.0; /* for each pixel in the destination image we calc the source * coordinate and make an interpolation: * p_d = c_d + M(p_s - c_s) + t * where p are the points, c the center coordinate, * _s source and _d destination, * t the translation, and M the rotation matrix * p_s = M^{-1}(p_d - c_d - t) + c_s */ /* All 3 channels */ if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) { for (x = 0; x < td->width_dest; x++) { for (y = 0; y < td->height_dest; y++) { float x_d1 = (x - c_d_x); float y_d1 = (y - c_d_y); float x_s = zcos_a * x_d1 + zsin_a * y_d1 + c_s_x -t.x; float y_s = -zsin_a * x_d1 + zcos_a * y_d1 + c_s_y -t.y; for (z = 0; z < 3; z++) { // iterate over colors unsigned char* dest = &D_2[(x + y * td->width_dest)*3+z]; interpolate(dest, x_s, y_s, D_1, td->width_src, td->height_src, td->crop ? 16 : *dest,3,z); } } } }else { /* no rotation, just translation *(also no interpolation, since no size change (so far) */ int round_tx = myround(t.x); int round_ty = myround(t.y); for (x = 0; x < td->width_dest; x++) { for (y = 0; y < td->height_dest; y++) { for (z = 0; z < 3; z++) { // iterate over colors short p = PIXELN(D_1, x - round_tx, y - round_ty, td->width_src, td->height_src, 3, z, -1); if (p == -1) { if (td->crop == 1) D_2[(x + y * td->width_dest)*3+z] = 16; } else { D_2[(x + y * td->width_dest)*3+z] = (unsigned char)p; } } } } } return 1; } /** * transformYUV: applies current transformation to frame * * Parameters: * td: private data structure of this filter * Return value: * 0 for failure, 1 for success * Preconditions: * The frame must be in YUV format */ int transformYUV(TransformData* td) { Transform t; int x = 0, y = 0; unsigned char *Y_1, *Y_2, *Cb_1, *Cb_2, *Cr_1, *Cr_2; t = td->trans[td->current_trans]; Y_1 = td->src; Y_2 = td->dest; Cb_1 = td->src + td->width_src * td->height_src; Cb_2 = td->dest + td->width_dest * td->height_dest; Cr_1 = td->src + 5*td->width_src * td->height_src/4; Cr_2 = td->dest + 5*td->width_dest * td->height_dest/4; float c_s_x = td->width_src/2.0; float c_s_y = td->height_src/2.0; float c_d_x = td->width_dest/2.0; float c_d_y = td->height_dest/2.0; float z = 1.0-t.zoom/100; float zcos_a = z*cos(-t.alpha); // scaled cos float zsin_a = z*sin(-t.alpha); // scaled sin /* for each pixel in the destination image we calc the source * coordinate and make an interpolation: * p_d = c_d + M(p_s - c_s) + t * where p are the points, c the center coordinate, * _s source and _d destination, * t the translation, and M the rotation and scaling matrix * p_s = M^{-1}(p_d - c_d - t) + c_s */ /* Luminance channel */ if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) { for (x = 0; x < td->width_dest; x++) { for (y = 0; y < td->height_dest; y++) { float x_d1 = (x - c_d_x); float y_d1 = (y - c_d_y); float x_s = zcos_a * x_d1 + zsin_a * y_d1 + c_s_x -t.x; float y_s = -zsin_a * x_d1 + zcos_a * y_d1 + c_s_y -t.y; unsigned char* dest = &Y_2[x + y * td->width_dest]; interpolate(dest, x_s, y_s, Y_1, td->width_src, td->height_src, td->crop ? 16 : *dest,1,0); } } }else { /* no rotation, no zooming, just translation *(also no interpolation, since no size change) */ int round_tx = myround(t.x); int round_ty = myround(t.y); for (x = 0; x < td->width_dest; x++) { for (y = 0; y < td->height_dest; y++) { short p = PIXEL(Y_1, x - round_tx, y - round_ty, td->width_src, td->height_src, -1); if (p == -1) { if (td->crop == 1) Y_2[x + y * td->width_dest] = 16; } else { Y_2[x + y * td->width_dest] = (unsigned char)p; } } } } /* Color channels */ int ws2 = td->width_src/2; int wd2 = td->width_dest/2; int hs2 = td->height_src/2; int hd2 = td->height_dest/2; if (fabs(t.alpha) > td->rotation_threshhold || t.zoom != 0) { for (x = 0; x < wd2; x++) { for (y = 0; y < hd2; y++) { float x_d1 = x - (c_d_x)/2; float y_d1 = y - (c_d_y)/2; float x_s = zcos_a * x_d1 + zsin_a * y_d1 + (c_s_x -t.x)/2; float y_s = -zsin_a * x_d1 + zcos_a * y_d1 + (c_s_y -t.y)/2; unsigned char* dest = &Cr_2[x + y * wd2]; interpolate(dest, x_s, y_s, Cr_1, ws2, hs2, td->crop ? 128 : *dest,1,0); dest = &Cb_2[x + y * wd2]; interpolate(dest, x_s, y_s, Cb_1, ws2, hs2, td->crop ? 128 : *dest,1,0); } } } else { // no rotation, no zoom, no interpolation, just translation int round_tx2 = myround(t.x/2.0); int round_ty2 = myround(t.y/2.0); for (x = 0; x < wd2; x++) { for (y = 0; y < hd2; y++) { short cr = PIXEL(Cr_1, x - round_tx2, y - round_ty2, wd2, hd2, -1); short cb = PIXEL(Cb_1, x - round_tx2, y - round_ty2, wd2, hd2, -1); if (cr == -1) { if (td->crop==1) { Cr_2[x + y * wd2] = 128; Cb_2[x + y * wd2] = 128; } } else { Cr_2[x + y * wd2] = (unsigned char)cr; Cb_2[x + y * wd2] = (unsigned char)cb; } } } } return 1; } /** * preprocess_transforms: does smoothing, relative to absolute conversion, * and cropping of too large transforms. * This is actually the core algorithm for canceling the jiggle in the * movie. We perform a low-pass filter in terms of transformation size. * This enables still camera movement, but in a smooth fashion. * * Parameters: * td: transform private data structure * Return value: * 1 for success and 0 for failure * Preconditions: * None * Side effects: * td->trans will be modified */ int preprocess_transforms(TransformData* td) { Transform* ts = td->trans; int i; if (td->trans_len < 1) return 0; if (0) { mlt_log_debug(NULL,"Preprocess transforms:"); } if (td->smoothing>0) { /* smoothing */ Transform* ts2 = malloc(sizeof(Transform) * td->trans_len); memcpy(ts2, ts, sizeof(Transform) * td->trans_len); /* we will do a sliding average with minimal update * \hat x_{n/2} = x_1+x_2 + .. + x_n * \hat x_{n/2+1} = x_2+x_3 + .. + x_{n+1} = x_{n/2} - x_1 + x_{n+1} * avg = \hat x / n */ int s = td->smoothing * 2 + 1; Transform null = null_transform(); /* avg is the average over [-smoothing, smoothing] transforms around the current point */ Transform avg; /* avg2 is a sliding average over the filtered signal! (only to past) * with smoothing * 10 horizont to kill offsets */ Transform avg2 = null_transform(); double tau = 1.0/(3 * s); /* initialise sliding sum with hypothetic sum centered around * -1st element. We have two choices: * a) assume the camera is not moving at the beginning * b) assume that the camera moves and we use the first transforms */ Transform s_sum = null; for (i = 0; i < td->smoothing; i++){ s_sum = add_transforms(&s_sum, i < td->trans_len ? &ts2[i]:&null); } mult_transform(&s_sum, 2); // choice b (comment out for choice a) for (i = 0; i < td->trans_len; i++) { Transform* old = ((i - td->smoothing - 1) < 0) ? &null : &ts2[(i - td->smoothing - 1)]; Transform* new = ((i + td->smoothing) >= td->trans_len) ? &null : &ts2[(i + td->smoothing)]; s_sum = sub_transforms(&s_sum, old); s_sum = add_transforms(&s_sum, new); avg = mult_transform(&s_sum, 1.0/s); /* lowpass filter: * meaning high frequency must be transformed away */ ts[i] = sub_transforms(&ts2[i], &avg); /* kill accumulating offset in the filtered signal*/ avg2 = add_transforms_(mult_transform(&avg2, 1 - tau), mult_transform(&ts[i], tau)); ts[i] = sub_transforms(&ts[i], &avg2); if (0 /*verbose*/ ) { mlt_log_warning(NULL,"s_sum: %5lf %5lf %5lf, ts: %5lf, %5lf, %5lf\n", s_sum.x, s_sum.y, s_sum.alpha, ts[i].x, ts[i].y, ts[i].alpha); mlt_log_warning(NULL, " avg: %5lf, %5lf, %5lf avg2: %5lf, %5lf, %5lf", avg.x, avg.y, avg.alpha, avg2.x, avg2.y, avg2.alpha); } } free(ts2); } /* invert? */ if (td->invert) { for (i = 0; i < td->trans_len; i++) { ts[i] = mult_transform(&ts[i], -1); } } /* relative to absolute */ if (td->relative) { Transform t = ts[0]; for (i = 1; i < td->trans_len; i++) { if (0/*verbose*/ ) { mlt_log_warning(NULL, "shift: %5lf %5lf %lf \n", t.x, t.y, t.alpha *180/M_PI); } ts[i] = add_transforms(&ts[i], &t); t = ts[i]; } } /* crop at maximal shift */ if (td->maxshift != -1) for (i = 0; i < td->trans_len; i++) { ts[i].x = TC_CLAMP(ts[i].x, -td->maxshift, td->maxshift); ts[i].y = TC_CLAMP(ts[i].y, -td->maxshift, td->maxshift); } if (td->maxangle != - 1.0) for (i = 0; i < td->trans_len; i++) ts[i].alpha = TC_CLAMP(ts[i].alpha, -td->maxangle, td->maxangle); /* Calc optimal zoom * cheap algo is to only consider transformations * uses cleaned max and min */ if (td->optzoom != 0 && td->trans_len > 1){ Transform min_t, max_t; cleanmaxmin_xy_transform(ts, td->trans_len, 10, &min_t, &max_t); // the zoom value only for x double zx = 2*TC_MAX(max_t.x,fabs(min_t.x))/td->width_src; // the zoom value only for y double zy = 2*TC_MAX(max_t.y,fabs(min_t.y))/td->height_src; td->zoom += 100* TC_MAX(zx,zy); // use maximum mlt_log_debug(NULL,"Final zoom: %lf\n", td->zoom); } /* apply global zoom */ if (td->zoom != 0){ for (i = 0; i < td->trans_len; i++) ts[i].zoom += td->zoom; } return 1; } /** * transform_init: Initialize this instance of the module. See * tcmodule-data.h for function details. */ #if 0 static int transform_init(TCModuleInstance *self, uint32_t features) { TransformData* td = NULL; TC_MODULE_SELF_CHECK(self, "init"); TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features); td = tc_zalloc(sizeof(TransformData)); if (td == NULL) { tc_log_error(MOD_NAME, "init: out of memory!"); return TC_ERROR; } self->userdata = td; if (verbose) { tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP); } return T; } #endif /** * transform_configure: Configure this instance of the module. See * tcmodule-data.h for function details. */ int transform_configure(TransformData *self,int width,int height, mlt_image_format pixelformat, unsigned char* image ,Transform* tx,int trans_len) { TransformData *td = self; /**** Initialise private data structure */ /* td->framesize = td->vob->im_v_width * * MAX_PLANES * sizeof(char) * 2 * td->vob->im_v_height * 2; */ // rgb24 = w*h*3 , yuv420p = w* h* 3/2 td->framesize_src = width*height*(pixelformat==mlt_image_rgb24 ? 3 : (3.0/2.0)); td->src = malloc(td->framesize_src); /* FIXME */ if (td->src == NULL) { mlt_log_error(NULL,"tc_malloc failed\n"); return -1; } td->width_src = width; td->height_src = height; /* Todo: in case we can scale the images, calc new size later */ td->width_dest = width; td->height_dest = height; td->framesize_dest = td->framesize_src; td->dest = 0; td->trans = tx; td->trans_len = trans_len; td->current_trans = 0; td->warned_transform_end = 0; /* Options */ // set from filter td->maxshift = -1; // set from filter td->maxangle = -1; // set from filter td->crop = 0; // set from filter td->relative = 1; // set from filter td->invert = 0; // set from filter td->smoothing = 10; td->rotation_threshhold = 0.25/(180/M_PI); // set from filter td->zoom = 0; // set from filter td->optzoom = 1; // set from filter td->interpoltype = 2; // bi-linear // set from filter td->sharpen = 0.8; td->interpoltype = TC_MIN(td->interpoltype,4); if (1) { mlt_log_debug(NULL, "Image Transformation/Stabilization Settings:\n"); mlt_log_debug(NULL, " smoothing = %d\n", td->smoothing); mlt_log_debug(NULL, " maxshift = %d\n", td->maxshift); mlt_log_debug(NULL, " maxangle = %f\n", td->maxangle); mlt_log_debug(NULL, " crop = %s\n", td->crop ? "Black" : "Keep"); mlt_log_debug(NULL, " relative = %s\n", td->relative ? "True": "False"); mlt_log_debug(NULL, " invert = %s\n", td->invert ? "True" : "False"); mlt_log_debug(NULL, " zoom = %f\n", td->zoom); mlt_log_debug(NULL, " optzoom = %s\n", td->optzoom ? "On" : "Off"); mlt_log_debug(NULL, " interpol = %s\n", interpoltypes[td->interpoltype]); mlt_log_debug(NULL, " sharpen = %f\n", td->sharpen); } if (td->maxshift > td->width_dest/2 ) td->maxshift = td->width_dest/2; if (td->maxshift > td->height_dest/2) td->maxshift = td->height_dest/2; if (!preprocess_transforms(td)) { mlt_log_error(NULL,"error while preprocessing transforms!"); return -1; } switch(td->interpoltype){ case 0: interpolate = &interpolateZero; break; case 1: interpolate = &interpolateLin; break; case 2: interpolate = &interpolateBiLin; break; case 3: interpolate = &interpolateSqr; break; case 4: interpolate = &interpolateBiCub; break; default: interpolate = &interpolateBiLin; } return 0; } /** * transform_filter_video: performs the transformation of frames * See tcmodule-data.h for function details. */ int transform_filter_video(TransformData *self, unsigned char *frame,mlt_image_format pixelformat) { TransformData *td = self; td->dest = frame; memcpy(td->src, frame, td->framesize_src); if (td->current_trans >= td->trans_len) { td->current_trans = td->trans_len-1; if(!td->warned_transform_end) mlt_log_warning(NULL,"not enough transforms found, use last transformation!\n"); td->warned_transform_end = 1; } if (pixelformat == mlt_image_rgb24 ) { transformRGB(td); } else if (pixelformat == mlt_image_yuv420p) { transformYUV(td); } else { mlt_log_error(NULL,"unsupported Codec: %i\n", pixelformat); return 1; } td->current_trans++; return 0; } mlt-6.20.0/src/modules/videostab/transform_image.h000066400000000000000000000131331362234133600221440ustar00rootroot00000000000000/* * filter_transform.c * * Copyright (C) Georg Martius - June 2007 * georg dot martius at web dot de * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode is distributed in the hope that 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * Typical call: * transcode -J transform -i inp.mpeg -y xdiv,tcaud inp_stab.avi */ #include "transform.h" #include #include #include "tlist.h" #include #define DEFAULT_TRANS_FILE_NAME "transforms.dat" #define PIXEL(img, x, y, w, h, def) ((x) < 0 || (y) < 0) ? def \ : (((x) >=w || (y) >= h) ? def : img[(x) + (y) * w]) #define PIX(img, x, y, w, h) (img[(x) + (y) * w]) #define PIXN(img, x, y, w, h,N,channel) (img[((x) + (y) * w)*N+channel]) // gives Pixel in N-channel image. channel in {0..N-1} #define PIXELN(img, x, y, w, h, N,channel , def) ((x) < 0 || (y) < 0) ? def \ : (((x) >=w || (y) >= h) ? def : img[((x) + (y) * w)*N + channel]) typedef struct { int framesize_src; // size of frame buffer in bytes (src) int framesize_dest; // size of frame buffer in bytes (dest) unsigned char* src; // copy of the current frame buffer unsigned char* dest; // pointer to the current frame buffer (to overwrite) int pixelformat; int width_src, height_src; int width_dest, height_dest; Transform* trans; // array of transformations int current_trans; // index to current transformation int trans_len; // length of trans array short warned_transform_end; // whether we warned that there is no transform left /* Options */ int maxshift; // maximum number of pixels we will shift double maxangle; // maximum angle in rad /* whether to consider transforms as relative (to previous frame) * or absolute transforms */ int relative; /* number of frames (forward and backward) * to use for smoothing transforms */ int smoothing; int crop; // 1: black bg, 0: keep border from last frame(s) int invert; // 1: invert transforms, 0: nothing /* constants */ /* threshold below which no rotation is performed */ double rotation_threshhold; double zoom; // percentage to zoom: 0->no zooming 10:zoom in 10% int optzoom; // 1: determine optimal zoom, 0: nothing int interpoltype; // type of interpolation: 0->Zero,1->Lin,2->BiLin,3->Sqr double sharpen; // amount of sharpening char conf_str[1024]; } TransformData; /* forward declarations, please look below for documentation*/ void interpolateBiLinBorder(unsigned char *rv, float x, float y, unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel); void interpolateBiCub(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def,unsigned char N, unsigned char channel); void interpolateSqr(unsigned char *rv, float x, float y, unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel); void interpolateBiLin(unsigned char *rv, float x, float y, unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel); void interpolateLin(unsigned char *rv, float x, float y, unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel); void interpolateZero(unsigned char *rv, float x, float y, unsigned char* img, int w, int h, unsigned char def,unsigned char N, unsigned char channel); void interpolateN(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char N, unsigned char channel, unsigned char def); int transformRGB(TransformData* td); int transformYUV(TransformData* td); int read_input_file(TransformData* td,tlist* list); int preprocess_transforms(TransformData* td); /** * interpolate: general interpolation function pointer for one channel image data * * Parameters: * rv: destination pixel (call by reference) * x,y: the source coordinates in the image img. Note this * are real-value coordinates, that's why we interpolate * img: source image * width,height: dimension of image * def: default value if coordinates are out of range * Return value: None */ /*void (*interpolate)(unsigned char *rv, float x, float y, unsigned char* img, int width, int height, unsigned char def) = 0; */ /** interpolateBiLinBorder: bi-linear interpolation function that also works at the border. This is used by many other interpolation methods at and outsize the border, see interpolate */ int transform_configure(TransformData *self,int width,int height, mlt_image_format pixelformat, unsigned char* image,Transform* tx,int trans_len) ; int transform_filter_video(TransformData *self, unsigned char *frame,mlt_image_format pixelformat); mlt-6.20.0/src/modules/vmfx/000077500000000000000000000000001362234133600156155ustar00rootroot00000000000000mlt-6.20.0/src/modules/vmfx/CMakeLists.txt000066400000000000000000000004131362234133600203530ustar00rootroot00000000000000file(GLOB mltvmfx_src *.c) add_library(mltvmfx MODULE ${mltvmfx_src}) target_link_libraries(mltvmfx mlt) install(TARGETS mltvmfx LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/vmfx) mlt-6.20.0/src/modules/vmfx/Makefile000066400000000000000000000012541362234133600172570ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt 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 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/vmfx" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/vmfx" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/vmfx/factory.c000066400000000000000000000047001362234133600174310ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #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 ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/vmfx/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } 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 ); MLT_REGISTER_METADATA( filter_type, "chroma", metadata, "filter_chroma.yml" ); MLT_REGISTER_METADATA( filter_type, "chroma_hold", metadata, "filter_chroma_hold.yml" ); MLT_REGISTER_METADATA( filter_type, "threshold", metadata, "filter_mono.yml" ); MLT_REGISTER_METADATA( filter_type, "shape", metadata, "filter_shape.yml" ); MLT_REGISTER_METADATA( producer_type, "pgm", metadata, "producer_pgm.yml" ); } mlt-6.20.0/src/modules/vmfx/filter_chroma.c000066400000000000000000000061731362234133600206060ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #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 u, v; RGB2UV_601_SCALED( r, g, b, u, v ); *format = mlt_image_yuv422; 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; } mlt-6.20.0/src/modules/vmfx/filter_chroma.yml000066400000000000000000000002641362234133600211600ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: chroma title: Chroma Key version: 1 copyright: Visual Media FX ? creator: Charles Yates license: LGPLv2.1 language: en tags: - Video mlt-6.20.0/src/modules/vmfx/filter_chroma_hold.c000066400000000000000000000062121362234133600216060ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #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 u, v; RGB2UV_601_SCALED( r, g, b, u, v ); *format = mlt_image_yuv422; 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; } mlt-6.20.0/src/modules/vmfx/filter_chroma_hold.yml000066400000000000000000000002721362234133600221650ustar00rootroot00000000000000schema_version: 0.1 type: filter identifier: chroma_hold title: Chroma Hold version: 1 copyright: Visual Media FX ? creator: Charles Yates license: LGPLv2.1 language: en tags: - Video mlt-6.20.0/src/modules/vmfx/filter_mono.c000066400000000000000000000062051362234133600203010ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #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 frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { mlt_filter filter = mlt_frame_pop_service(frame); // Render the frame *format = mlt_image_yuv422; if ( mlt_frame_get_image( frame, image, format, width, height, writable ) == 0 ) { mlt_properties properties = mlt_filter_properties(filter); mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); int midpoint = mlt_properties_anim_get_int(properties, "midpoint", position, length); int use_alpha = mlt_properties_get_int(properties, "use_alpha"); int invert = mlt_properties_get_int(properties, "invert"); int full_luma = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), "full_luma"); uint8_t white = full_luma? 255 : 235; uint8_t black = full_luma? 0 : 16; uint8_t *p = *image; uint8_t A = invert? white : black; uint8_t B = invert? black : white; int size = *width * *height + 1; if ( !use_alpha ) { while (--size) { if ( *p < midpoint ) *p ++ = A; else *p ++ = B; *p ++ = 128; } } else { uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); while (--size) { if ( *alpha ++ < midpoint ) *p ++ = A; else *p ++ = B; *p ++ = 128; } } } return 0; } /** Filter processing. */ static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) { mlt_frame_push_service(frame, filter); 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 filter = mlt_filter_new( ); if ( filter != NULL ) { mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "midpoint", 128 ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "use_alpha", 0 ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "invert", 0 ); filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/vmfx/filter_mono.yml000066400000000000000000000013511362234133600206550ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: threshold title: Threshold version: 2 copyright: Visual Media FX ? creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: midpoint title: Threshold type: integer description: The value of the threshold mutable: yes minimum: 0 maximum: 255 default: 128 - identifier: use_alpha title: Use alpha channel type: boolean description: Whether to compare the midpoint with the alpha channel instead of luma mutable: yes default: 0 widget: checkbox - identifier: invert title: Invert type: integer description: Wether to swap black and white mutable: yes default: 0 widget: checkbox mlt-6.20.0/src/modules/vmfx/filter_shape.c000066400000000000000000000201061362234133600204250ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include static 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 frame, 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( frame ) ); mlt_frame mask = mlt_frame_pop_service( frame ); mlt_filter filter = mlt_frame_pop_service( frame ); // 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 use_mix = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "use_mix" ); int invert = mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "invert" ) * 255; if (mlt_properties_get_int(MLT_FILTER_PROPERTIES(filter), "reverse")) { mix = 1.0 - mix; invert = !mlt_properties_get_int( MLT_FILTER_PROPERTIES( filter ), "invert" ) * 255; } // Render the frame *format = mlt_image_yuv422; *width -= *width % 2; if ( mlt_frame_get_image( frame, image, format, width, height, writable ) == 0 && ( !use_luminance || !use_mix || (int) mix != 1 || invert == 255 ) ) { // Get the alpha mask of the source uint8_t *alpha = mlt_frame_get_alpha_mask( frame ); // 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( frame ), "consumer_deinterlace, deinterlace_method, rescale.interp, consumer_tff, consumer_color_trc" ); 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 ); if ( use_mix ) { while( size -- ) { a = ( double )*q ++ / 255.0; b = 1.0 - smoothstep( a, a + softness, mix ); *p = ( uint8_t )( *p * b ) ^ invert; p ++; } } else { while( size -- ) *p++ = *q++; } } else if ( !use_mix ) { // Do not apply threshold filter. uint8_t *q = mask_img; while( size -- ) { *p = *q; p++; q += 2; } } else if ( (int) mix != 1 || invert == 255 ) { int full_range = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "full_luma" ); double offset = full_range ? 0.0 : 16.0; double divisor = full_range ? 255.0 : 235.0; uint8_t *q = mask_img; // Ensure softness tends to zero as mix tends to 1 softness *= ( 1.0 - mix ); while( size -- ) { a = ( ( double ) *q - offset ) / divisor; 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 filter, mlt_frame frame ) { // Obtain the shape instance char *resource = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "resource" ); if ( !resource ) return frame; char *last_resource = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "_resource" ); mlt_producer producer = mlt_properties_get_data( MLT_FILTER_PROPERTIES( filter ), "instance", NULL ); // Calculate the position and length int position = mlt_filter_get_position( filter, frame ); mlt_position length = mlt_filter_get_length2( filter, frame ); // If we haven't created the instance or it's changed if ( producer == NULL || !last_resource || strcmp( resource, last_resource ) ) { mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE( filter ) ); char temp[ 512 ]; // Store the last resource now mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "_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_profile_lumas_dir(profile), strchr( resource, '%' ) + 1 ); test = mlt_fopen( temp, "r" ); if ( test == NULL ) { strcat( temp, ".png" ); test = mlt_fopen( temp, "r" ); } if ( test ) { fclose( test ); resource = temp; } } 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( filter ), "instance", producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); } // We may still not have a producer in which case, we do nothing if ( producer != NULL ) { mlt_frame mask = NULL; double alpha_mix = mlt_properties_anim_get_double( MLT_FILTER_PROPERTIES(filter), "mix", position, length ); mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), MLT_FILTER_PROPERTIES( filter ), "producer." ); mlt_producer_seek( producer, position ); if ( mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &mask, 0 ) == 0 ) { char name[64]; snprintf( name, sizeof(name), "shape %s", mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "_unique_id" ) ); mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), name, mask, 0, ( mlt_destructor )mlt_frame_close, NULL ); mlt_frame_push_service( frame, filter ); 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( filter ), "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 ); } mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "always_scale", 1 ); } } 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 filter = mlt_filter_new( ); if ( filter != NULL ) { mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "resource", arg ); mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "mix", "100" ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "use_mix", 1 ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "audio_match", 1 ); mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "invert", 0 ); mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "softness", 0.1 ); filter->process = filter_process; } return filter; } mlt-6.20.0/src/modules/vmfx/filter_shape.yml000066400000000000000000000042101362234133600210020ustar00rootroot00000000000000schema_version: 0.3 type: filter identifier: shape title: Shape Alpha version: 4 copyright: Visual Media FX ? creator: Charles Yates license: LGPLv2.1 language: en tags: - Video parameters: - identifier: resource argument: yes title: File type: string description: > The name of a file or MLT producer URL. To use a luma wipe from the lumas module, put % in front of the base name of the luma file e.g. %luma16.pgm required: true mutable: yes - identifier: mix title: Threshold type: float description: > Convert alpha or luma values below this level as opaque and above this level as transparent. This is mostly useful for luma wipe images. unit: '%' minimum: 0 maximum: 100 default: 100 mutable: yes - identifier: softness title: Softness type: float mutable: yes description: > When using mix (threshold) how soft to make the edge around the threshold. 0.0 = no softness, 1.0 = too soft minimum: 0 maximum: 1 default: 0.1 mutable: yes - identifier: invert title: Invert type: boolean description: Use the inverse of the alpha or luma value. default: no mutable: yes - identifier: reverse title: Reverse type: boolean description: Use the complement of the mix level. default: no mutable: yes - identifier: use_luminance title: Use Luma type: boolean description: Use the image luma instead of the alpha channel. default: no mutable: yes - identifier: use_mix title: Use Threshold type: boolean description: > Whether to apply a threshold filter to the luma or alpha or not. If not, luma or alpha value of the resource (File) is copied to the alpha channel. default: yes mutable: yes - identifier: audio_match title: Audio volume follows Threshold type: boolean description: > This controls whether to also apply a volume level adjstment corresponding to the current mix property. The default is retained for legacy reason, but it is generally not recommended to enable this. default: yes mutable: yes mlt-6.20.0/src/modules/vmfx/producer_pgm.c000066400000000000000000000144021362234133600204500ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #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)){ if (resource && strstr(resource, "%luma")) { // Failed to read file; generate it. mlt_luma_map luma = mlt_luma_map_new(resource); if (profile) { luma->w = profile->width; luma->h = profile->height; } uint16_t* map = mlt_luma_map_render(luma); if (map) { int n = luma->w * luma->h; image = mlt_pool_alloc(n * 2); width = luma->w; height = luma->h; for (int i = 0; i < n; i++) { image[2*i] = 16 + map[i] * 219 / USHRT_MAX; image[2*i+1] = 128; } mlt_pool_release(map); } free(luma); } } if (image) { 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, "meta.media.width", width ); mlt_properties_set_int( properties, "meta.media.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 = mlt_fopen( name, "rb" ); 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 ), "meta.media.width" ); int real_height = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "meta.media.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_frame_set_image( this, image, size * 2, mlt_pool_release ); *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_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 ); } mlt-6.20.0/src/modules/vmfx/producer_pgm.yml000066400000000000000000000002621362234133600210260ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: pgm title: PGM Image version: 1 copyright: Visual Media FX ? creator: Charles Yates license: LGPLv2.1 language: en tags: - Video mlt-6.20.0/src/modules/vorbis/000077500000000000000000000000001362234133600161415ustar00rootroot00000000000000mlt-6.20.0/src/modules/vorbis/Makefile000066400000000000000000000014331362234133600176020ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt include ../../../config.mak TARGET = ../libmltvorbis$(LIBSUF) OBJS = factory.o \ producer_vorbis.o CFLAGS += $(shell pkg-config --cflags vorbis) CFLAGS += $(shell pkg-config --cflags vorbisfile) LDFLAGS += $(shell pkg-config --libs vorbis) LDFLAGS += $(shell 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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/vorbis" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/vorbis" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/vorbis/configure000077500000000000000000000003331362234133600200470ustar00rootroot00000000000000#!/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 mlt-6.20.0/src/modules/vorbis/factory.c000066400000000000000000000026351362234133600177620ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include extern mlt_producer producer_vorbis_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/vorbis/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( producer_type, "vorbis", producer_vorbis_init ); MLT_REGISTER_METADATA( producer_type, "vorbis", metadata, "producer_vorbis.yml" ); } mlt-6.20.0/src/modules/vorbis/producer_vorbis.c000066400000000000000000000236711362234133600215250ustar00rootroot00000000000000/* * producer_vorbis.c -- vorbis producer * Copyright (C) 2003-2017 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 = mlt_fopen( file, "rb" ); // 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); free(metadata->name); free(metadata->content); free(metadata); } ++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, "audio_frequency", (int) vi->rate ); mlt_properties_set_int( properties, "audio_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, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Obtain the frame number of this frame mlt_position position = mlt_frame_original_position( frame ); // Get the producer mlt_producer this = mlt_frame_pop_audio( frame ); // Get the producer properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( this ); mlt_service_lock( MLT_PRODUCER_SERVICE( this ) ); // Get the ogg vorbis file OggVorbis_File *ov = mlt_properties_get_data( properties, "ogg_vorbis_file", NULL ); // Obtain the expected frame number 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 ) * *channels ); *samples = mlt_sample_calculator( fps, *frequency, expected ++ ); } } // Now handle the audio if we have enough if ( audio_used >= *samples ) { int size = *samples * *channels * sizeof( int16_t ); *format = mlt_audio_s16; *buffer = mlt_pool_alloc( size ); memcpy( *buffer, audio_buffer, size ); audio_used -= *samples; memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * *channels * sizeof( int16_t ) ); mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); } 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 ); mlt_service_unlock( MLT_PRODUCER_SERVICE( this ) ); 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 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( MLT_FRAME_PROPERTIES(*frame), MLT_PRODUCER_PROPERTIES( this ), "frequency, channels" ); // Calculate the next timecode mlt_producer_prepare_next( this ); return 0; } mlt-6.20.0/src/modules/vorbis/producer_vorbis.yml000066400000000000000000000006531362234133600220770ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: vorbis title: Ogg Vorbis version: 1 copyright: Meltytech, LLC creator: Charles Yates license: LGPLv2.1 language: en tags: - Audio description: | OGG Vorbis file reader. parameters: - identifier: argument title: File type: string description: File to use (only .ogg supported at the moment) readonly: no required: yes mutable: no widget: fileopen mlt-6.20.0/src/modules/xine/000077500000000000000000000000001362234133600156005ustar00rootroot00000000000000mlt-6.20.0/src/modules/xine/CMakeLists.txt000066400000000000000000000013711362234133600203420ustar00rootroot00000000000000if(GPL) set(mltxine_src factory.c deinterlace.c yadif.c filter_deinterlace.c) if(X86_64) list(APPEND mltxine_src cpu_accel.c) if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") # avoid crash in yadif filter_line_sse2 add_compile_options(-fno-tree-dominator-opts -fno-tree-pre) endif() endif() add_library(mltxine MODULE ${mltxine_src}) target_link_libraries(mltxine mlt) target_compile_definitions(mltxine PRIVATE PIC) set_property(TARGET mltxine PROPERTY POSITION_INDEPENDENT_CODE ON) install(TARGETS mltxine LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/xine) endif() mlt-6.20.0/src/modules/xine/Makefile000066400000000000000000000011331362234133600172360ustar00rootroot00000000000000CFLAGS += -I../../ LDFLAGS += -L../../framework -lmlt include ../../../config.mak TARGET = ../libmltxine$(LIBSUF) OBJS = factory.o \ deinterlace.o \ yadif.o \ filter_deinterlace.o ifdef MMX_FLAGS CFLAGS += -DARCH_X86 OBJS += cpu_accel.o 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)$(moduledir)" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/xine/attributes.h000066400000000000000000000027211362234133600201410ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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_ */ mlt-6.20.0/src/modules/xine/cpu_accel.c000066400000000000000000000126501362234133600176660ustar00rootroot00000000000000/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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; } mlt-6.20.0/src/modules/xine/deinterlace.c000066400000000000000000000661411362234133600202330ustar00rootroot00000000000000 /* * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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? */ deinterlace_linearblend_yuv(pdst,psrc,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? */ deinterlace_linearblend_yuv(pdst,psrc,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? */ deinterlace_linearblend_yuv(pdst,psrc,width,height); break; case DEINTERLACE_ONEFIELD: if( check_for_mmx() ) deinterlace_onefield_yuv_mmx(pdst,psrc,width,height); else /* FIXME: provide an alternative? */ deinterlace_linearblend_yuv(pdst,psrc,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 }; mlt-6.20.0/src/modules/xine/deinterlace.h000066400000000000000000000030631362234133600202320ustar00rootroot00000000000000 /* * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 #define DEINTERLACE_YADIF 7 #define DEINTERLACE_YADIF_NOSPATIAL 8 extern const char *deinterlace_methods[]; #endif mlt-6.20.0/src/modules/xine/factory.c000066400000000000000000000020601362234133600174110ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 ); } mlt-6.20.0/src/modules/xine/filter_deinterlace.c000066400000000000000000000320011362234133600215640ustar00rootroot00000000000000/* * filter_deinterlace.c -- deinterlace filter * Copyright (C) 2003-2014 Meltytech, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "deinterlace.h" #include "yadif.h" #include #include #include #define YADIF_MODE_TEMPORAL_SPATIAL (0) #define YADIF_MODE_TEMPORAL (2) static yadif_filter *init_yadif( int width, int height ) { yadif_filter *yadif = mlt_pool_alloc( sizeof( *yadif ) ); yadif->cpu = 0; // Pure C #ifdef USE_SSE yadif->cpu |= AVS_CPU_INTEGER_SSE; #endif #ifdef USE_SSE2 yadif->cpu |= AVS_CPU_SSE2; #endif // Create intermediate planar planes yadif->yheight = height; yadif->ywidth = width; yadif->uvwidth = yadif->ywidth / 2; yadif->ypitch = ( yadif->ywidth + 15 ) / 16 * 16; yadif->uvpitch = ( yadif->uvwidth + 15 ) / 16 * 16; yadif->ysrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); yadif->usrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch); yadif->vsrc = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); yadif->yprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); yadif->uprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); yadif->vprev = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); yadif->ynext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); yadif->unext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); yadif->vnext = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); yadif->ydest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->ypitch ); yadif->udest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); yadif->vdest = (unsigned char *) mlt_pool_alloc( yadif->yheight * yadif->uvpitch ); return yadif; } static void close_yadif(yadif_filter *yadif) { mlt_pool_release( yadif->ysrc ); mlt_pool_release( yadif->usrc ); mlt_pool_release( yadif->vsrc ); mlt_pool_release( yadif->yprev ); mlt_pool_release( yadif->uprev ); mlt_pool_release( yadif->vprev ); mlt_pool_release( yadif->ynext ); mlt_pool_release( yadif->unext ); mlt_pool_release( yadif->vnext ); mlt_pool_release( yadif->ydest ); mlt_pool_release( yadif->udest ); mlt_pool_release( yadif->vdest ); mlt_pool_release( yadif ); #if defined(__GNUC__) && !defined(PIC) // Set SSSE3 bit to cpu asm (\ "mov $1, %%eax \n\t"\ "push %%ebx \n\t"\ "cpuid \n\t"\ "pop %%ebx \n\t"\ "mov %%ecx, %%edx \n\t"\ "shr $9, %%edx \n\t"\ "and $1, %%edx \n\t"\ "shl $9, %%edx \n\t"\ "and $511, %%ebx \n\t"\ "or %%edx, %%ebx \n\t"\ : "=b"(yadif->cpu) : "p"(yadif->cpu) : "%eax", "%ecx", "%edx"); #endif } static int deinterlace_yadif( mlt_frame frame, mlt_filter filter, uint8_t **image, mlt_image_format *format, int *width, int *height, int mode ) { mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); mlt_frame previous_frame = mlt_properties_get_data( properties, "previous frame", NULL ); uint8_t* previous_image = NULL; int previous_width = *width; int previous_height = *height; mlt_frame next_frame = mlt_properties_get_data( properties, "next frame", NULL ); uint8_t* next_image = NULL; int next_width = *width; int next_height = *height; mlt_log_debug( MLT_FILTER_SERVICE(filter), "previous " MLT_POSITION_FMT " current " MLT_POSITION_FMT " next " MLT_POSITION_FMT "\n", previous_frame? mlt_frame_original_position(previous_frame) : -1, mlt_frame_original_position(frame), next_frame? mlt_frame_original_position(next_frame) : -1); if ( !previous_frame || !next_frame ) return 1; mlt_service_lock( MLT_FILTER_SERVICE(filter) ); // Get the preceding frame's image int error = mlt_frame_get_image( previous_frame, &previous_image, format, &previous_width, &previous_height, 0 ); int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( previous_frame ), "progressive" ); // Check that we aren't already progressive if ( !error && previous_image && !progressive ) { // OK, now we know we have work to do and can request the image in our format frame->convert_image( previous_frame, &previous_image, format, mlt_image_yuv422 ); mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); // Get the current frame's image *format = mlt_image_yuv422; error = mlt_frame_get_image( frame, image, format, width, height, 1 ); if ( !error && *image && *format == mlt_image_yuv422 ) { // Get the following frame's image error = mlt_frame_get_image( next_frame, &next_image, format, &next_width, &next_height, 0 ); if ( !error && next_image && *format == mlt_image_yuv422 ) { yadif_filter *yadif = init_yadif( *width, *height ); if ( yadif ) { const int order = mlt_properties_get_int( properties, "top_field_first" ); const int pitch = *width << 1; const int parity = 0; // Convert packed to planar YUY2ToPlanes( *image, pitch, *width, *height, yadif->ysrc, yadif->ypitch, yadif->usrc, yadif->vsrc, yadif->uvpitch, yadif->cpu ); YUY2ToPlanes( previous_image, pitch, *width, *height, yadif->yprev, yadif->ypitch, yadif->uprev, yadif->vprev, yadif->uvpitch, yadif->cpu ); YUY2ToPlanes( next_image, pitch, *width, *height, yadif->ynext, yadif->ypitch, yadif->unext, yadif->vnext, yadif->uvpitch, yadif->cpu ); // Deinterlace each plane filter_plane( mode, yadif->ydest, yadif->ypitch, yadif->yprev, yadif->ysrc, yadif->ynext, yadif->ypitch, *width, *height, parity, order, yadif->cpu); filter_plane( mode, yadif->udest, yadif->uvpitch,yadif->uprev, yadif->usrc, yadif->unext, yadif->uvpitch, *width >> 1, *height, parity, order, yadif->cpu); filter_plane( mode, yadif->vdest, yadif->uvpitch, yadif->vprev, yadif->vsrc, yadif->vnext, yadif->uvpitch, *width >> 1, *height, parity, order, yadif->cpu); // Convert planar to packed YUY2FromPlanes( *image, pitch, *width, *height, yadif->ydest, yadif->ypitch, yadif->udest, yadif->vdest, yadif->uvpitch, yadif->cpu); close_yadif( yadif ); } } } } else { mlt_service_unlock( MLT_FILTER_SERVICE(filter) ); // Get the current frame's image error = mlt_frame_get_image( frame, image, format, width, height, 0 ); } return error; } /** Do it :-). */ static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) { int error = 0; mlt_filter filter = mlt_frame_pop_service( frame ); mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); int deinterlace = mlt_properties_get_int( properties, "consumer_deinterlace" ); // The progressive var should only represent the frame's original state and not the state // as modified by this filter! int progressive = mlt_properties_get_int( properties, "progressive" ); // At this point - before image was requested - (progressive == 0) cannot be trusted because // some producers (avformat) do not yet know. if ( deinterlace && !mlt_properties_get_int( properties, "test_image" ) ) { // Determine deinterlace method char *method_str = mlt_properties_get( MLT_FILTER_PROPERTIES( filter ), "method" ); int method = DEINTERLACE_NONE; char *frame_method_str = mlt_properties_get( properties, "deinterlace_method" ); if ( frame_method_str ) method_str = frame_method_str; if ( !method_str || strcmp( method_str, "yadif" ) == 0 ) method = DEINTERLACE_YADIF; else if ( strcmp( method_str, "yadif-nospatial" ) == 0 ) method = DEINTERLACE_YADIF_NOSPATIAL; else if ( strcmp( method_str, "onefield" ) == 0 ) method = DEINTERLACE_ONEFIELD; else if ( strcmp( method_str, "linearblend" ) == 0 ) 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; // Some producers like pixbuf want rescale_width & _height, but will not get them if you request // the previous image first. So, on the first iteration, we use linearblend. if ( ( method == DEINTERLACE_YADIF || method == DEINTERLACE_YADIF_NOSPATIAL ) && !mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "_notfirst" ) ) { mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "_notfirst", 1 ); method = DEINTERLACE_LINEARBLEND; error = 1; } if ( method == DEINTERLACE_YADIF ) { error = deinterlace_yadif( frame, filter, image, format, width, height, YADIF_MODE_TEMPORAL_SPATIAL ); } else if ( method == DEINTERLACE_YADIF_NOSPATIAL ) { error = deinterlace_yadif( frame, filter, image, format, width, height, YADIF_MODE_TEMPORAL ); } if ( error || ( method > DEINTERLACE_NONE && method < DEINTERLACE_YADIF ) ) { mlt_service service = mlt_properties_get_data( MLT_FILTER_PROPERTIES(filter), "service", NULL ); // Get the current frame's image int error2 = mlt_frame_get_image( frame, image, format, width, height, writable ); progressive = mlt_properties_get_int( properties, "progressive" ); if ( error ) { method = DEINTERLACE_LINEARBLEND; // If YADIF requested, prev/next cancelled because some previous frames were progressive, // but new frames are interlaced, then turn prev/next frames back on. if ( !progressive ) mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 1 ); } else { // Signal that we no longer need previous and next frames mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 0 ); } error = error2; if ( !error && !progressive ) { // OK, now we know we have work to do and can request the image in our format error = frame->convert_image( frame, image, format, mlt_image_yuv422 ); // Check that we aren't already progressive if ( !error && *image && *format == mlt_image_yuv422 ) { // Deinterlace the image using one of the Xine deinterlacers int image_size = mlt_image_format_size( *format, *width, *height, NULL ); uint8_t *new_image = mlt_pool_alloc( image_size ); deinterlace_yuv( new_image, image, *width * 2, *height, method ); mlt_frame_set_image( frame, new_image, image_size, mlt_pool_release ); *image = new_image; } } } else if ( method == DEINTERLACE_NONE ) { error = mlt_frame_get_image( frame, image, format, width, height, writable ); } // update progressive flag after having obtained image progressive = mlt_properties_get_int( properties, "progressive" ); mlt_log_debug( MLT_FILTER_SERVICE( filter ), "error %d deint %d prog %d fmt %s method %s\n", error, deinterlace, progressive, mlt_image_format_name( *format ), method_str ? method_str : "yadif" ); if ( !error ) { // Make sure that others know the frame is deinterlaced mlt_properties_set_int( properties, "progressive", 1 ); } } else { // Pass through error = mlt_frame_get_image( frame, image, format, width, height, writable ); } if ( !deinterlace || progressive ) { // Signal that we no longer need previous and next frames mlt_service service = mlt_properties_get_data( MLT_FILTER_PROPERTIES(filter), "service", NULL ); if ( service ) mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 0 ); } return error; } /** Deinterlace filter processing - this should be lazy evaluation here... */ static mlt_frame deinterlace_process( mlt_filter filter, mlt_frame frame ) { // Push filter on to the service stack mlt_frame_push_service( frame, filter ); // Push the get_image method on to the stack mlt_frame_push_get_image( frame, filter_get_image ); return frame; } static void on_service_changed( mlt_service owner, mlt_service filter ) { mlt_service service = mlt_properties_get_data( MLT_SERVICE_PROPERTIES(filter), "service", NULL ); mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_need_previous_next", 1 ); } /** Constructor for the filter. */ mlt_filter filter_deinterlace_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { mlt_filter filter = mlt_filter_new( ); if ( filter != NULL ) { filter->process = deinterlace_process; mlt_properties_set( MLT_FILTER_PROPERTIES( filter ), "method", arg ); mlt_events_listen( MLT_FILTER_PROPERTIES( filter ), filter, "service-changed", (mlt_listener) on_service_changed ); } return filter; } mlt-6.20.0/src/modules/xine/gpl000066400000000000000000000000001362234133600162730ustar00rootroot00000000000000mlt-6.20.0/src/modules/xine/vf_yadif_template.h000066400000000000000000000252661362234133600214460ustar00rootroot00000000000000/* * Copyright (C) 2006 Michael Niedermayer * * SSE2/SSSE3 version (custom optimization) by h.yamagata * * Small fix by Alexander Balakhnin (fizick@avisynth.org.ru) * * MPlayer 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. * * MPlayer is distributed in the hope that 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 MPlayer; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #define LOAD8(mem,dst) \ "movq "mem", "#dst" \n\t"\ "punpcklbw %%xmm7, "#dst" \n\t" #define CHECK(pj,mj) \ "movdqu "#pj"(%[cur],%[mrefs]), %%xmm2 \n\t" /* cur[x-refs-1+j] */\ "movdqu "#mj"(%[cur],%[prefs]), %%xmm3 \n\t" /* cur[x+refs-1-j] */\ "movdqa %%xmm2, %%xmm4 \n\t"\ "movdqa %%xmm2, %%xmm5 \n\t"\ "pxor %%xmm3, %%xmm4 \n\t"\ "pavgb %%xmm3, %%xmm5 \n\t"\ "pand %[pb1], %%xmm4 \n\t"\ "psubusb %%xmm4, %%xmm5 \n\t"\ "psrldq $1, %%xmm5 \n\t"\ "punpcklbw %%xmm7, %%xmm5 \n\t" /* (cur[x-refs+j] + cur[x+refs-j])>>1 */\ "movdqa %%xmm2, %%xmm4 \n\t"\ "psubusb %%xmm3, %%xmm2 \n\t"\ "psubusb %%xmm4, %%xmm3 \n\t"\ "pmaxub %%xmm3, %%xmm2 \n\t"\ "movdqa %%xmm2, %%xmm3 \n\t"\ "movdqa %%xmm2, %%xmm4 \n\t" /* ABS(cur[x-refs-1+j] - cur[x+refs-1-j]) */\ "psrldq $1, %%xmm3 \n\t" /* ABS(cur[x-refs +j] - cur[x+refs -j]) */\ "psrldq $2, %%xmm4 \n\t" /* ABS(cur[x-refs+1+j] - cur[x+refs+1-j]) */\ "punpcklbw %%xmm7, %%xmm2 \n\t"\ "punpcklbw %%xmm7, %%xmm3 \n\t"\ "punpcklbw %%xmm7, %%xmm4 \n\t"\ "paddw %%xmm3, %%xmm2 \n\t"\ "paddw %%xmm4, %%xmm2 \n\t" /* score */ #define CHECK1 \ "movdqa %%xmm0, %%xmm3 \n\t"\ "pcmpgtw %%xmm2, %%xmm3 \n\t" /* if(score < spatial_score) */\ "pminsw %%xmm2, %%xmm0 \n\t" /* spatial_score= score; */\ "movdqa %%xmm3, %%xmm6 \n\t"\ "pand %%xmm3, %%xmm5 \n\t"\ "pandn %%xmm1, %%xmm3 \n\t"\ "por %%xmm5, %%xmm3 \n\t"\ "movdqa %%xmm3, %%xmm1 \n\t" /* spatial_pred= (cur[x-refs+j] + cur[x+refs-j])>>1; */ #define CHECK2 /* pretend not to have checked dir=2 if dir=1 was bad.\ hurts both quality and speed, but matches the C version. */\ "paddw %[pw1], %%xmm6 \n\t"\ "psllw $14, %%xmm6 \n\t"\ "paddsw %%xmm6, %%xmm2 \n\t"\ "movdqa %%xmm0, %%xmm3 \n\t"\ "pcmpgtw %%xmm2, %%xmm3 \n\t"\ "pminsw %%xmm2, %%xmm0 \n\t"\ "pand %%xmm3, %%xmm5 \n\t"\ "pandn %%xmm1, %%xmm3 \n\t"\ "por %%xmm5, %%xmm3 \n\t"\ "movdqa %%xmm3, %%xmm1 \n\t" /* mode argument mod - Fizick */ /* static attribute_align_arg void FILTER_LINE_FUNC_NAME(YadifContext *yadctx, uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int refs, int parity){ const int mode = yadctx->mode; */ static attribute_align_arg void FILTER_LINE_FUNC_NAME(int mode, uint8_t *dst, const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w, int refs, int parity){ DECLARE_ALIGNED(16, uint8_t, tmp0[16]); DECLARE_ALIGNED(16, uint8_t, tmp1[16]); DECLARE_ALIGNED(16, uint8_t, tmp2[16]); DECLARE_ALIGNED(16, uint8_t, tmp3[16]); int x; static DECLARE_ALIGNED(16, const unsigned short, pw_1[]) = { 0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001 }; static DECLARE_ALIGNED(16, const unsigned short, pb_1[]) = { 0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101 }; #define FILTER\ for(x=0; x>1 */\ "movdqa %%xmm0, %[tmp0] \n\t" /* c */\ "movdqa %%xmm3, %[tmp1] \n\t" /* d */\ "movdqa %%xmm1, %[tmp2] \n\t" /* e */\ "psubw %%xmm4, %%xmm2 \n\t"\ PABS( %%xmm4, %%xmm2) /* temporal_diff0 */\ LOAD8("(%[prev],%[mrefs])", %%xmm3) /* prev[x-refs] */\ LOAD8("(%[prev],%[prefs])", %%xmm4) /* prev[x+refs] */\ "psubw %%xmm0, %%xmm3 \n\t"\ "psubw %%xmm1, %%xmm4 \n\t"\ PABS( %%xmm5, %%xmm3)\ PABS( %%xmm5, %%xmm4)\ "paddw %%xmm4, %%xmm3 \n\t" /* temporal_diff1 */\ "psrlw $1, %%xmm2 \n\t"\ "psrlw $1, %%xmm3 \n\t"\ "pmaxsw %%xmm3, %%xmm2 \n\t"\ LOAD8("(%[next],%[mrefs])", %%xmm3) /* next[x-refs] */\ LOAD8("(%[next],%[prefs])", %%xmm4) /* next[x+refs] */\ "psubw %%xmm0, %%xmm3 \n\t"\ "psubw %%xmm1, %%xmm4 \n\t"\ PABS( %%xmm5, %%xmm3)\ PABS( %%xmm5, %%xmm4)\ "paddw %%xmm4, %%xmm3 \n\t" /* temporal_diff2 */\ "psrlw $1, %%xmm3 \n\t"\ "pmaxsw %%xmm3, %%xmm2 \n\t"\ "movdqa %%xmm2, %[tmp3] \n\t" /* diff */\ \ "paddw %%xmm0, %%xmm1 \n\t"\ "paddw %%xmm0, %%xmm0 \n\t"\ "psubw %%xmm1, %%xmm0 \n\t"\ "psrlw $1, %%xmm1 \n\t" /* spatial_pred */\ PABS( %%xmm2, %%xmm0) /* ABS(c-e) */\ \ "movdqu -1(%[cur],%[mrefs]), %%xmm2 \n\t" /* cur[x-refs-1] */\ "movdqu -1(%[cur],%[prefs]), %%xmm3 \n\t" /* cur[x+refs-1] */\ "movdqa %%xmm2, %%xmm4 \n\t"\ "psubusb %%xmm3, %%xmm2 \n\t"\ "psubusb %%xmm4, %%xmm3 \n\t"\ "pmaxub %%xmm3, %%xmm2 \n\t"\ /*"pshuflw $9,%%xmm2, %%xmm3 \n\t"*/\ /*"pshufhw $9,%%xmm2, %%xmm3 \n\t"*/\ "movdqa %%xmm2, %%xmm3 \n\t" /* correct replacement (here) */\ "psrldq $2, %%xmm3 \n\t"/* for "pshufw $9,%%mm2, %%mm3" - fix by Fizick */\ "punpcklbw %%xmm7, %%xmm2 \n\t" /* ABS(cur[x-refs-1] - cur[x+refs-1]) */\ "punpcklbw %%xmm7, %%xmm3 \n\t" /* ABS(cur[x-refs+1] - cur[x+refs+1]) */\ "paddw %%xmm2, %%xmm0 \n\t"\ "paddw %%xmm3, %%xmm0 \n\t"\ "psubw %[pw1], %%xmm0 \n\t" /* spatial_score */\ \ CHECK(-2,0)\ CHECK1\ CHECK(-3,1)\ CHECK2\ CHECK(0,-2)\ CHECK1\ CHECK(1,-3)\ CHECK2\ \ /* if(yadctx->mode<2) ... */\ "movdqa %[tmp3], %%xmm6 \n\t" /* diff */\ "cmpl $2, %[mode] \n\t"\ "jge 1f \n\t"\ LOAD8("(%["prev2"],%[mrefs],2)", %%xmm2) /* prev2[x-2*refs] */\ LOAD8("(%["next2"],%[mrefs],2)", %%xmm4) /* next2[x-2*refs] */\ LOAD8("(%["prev2"],%[prefs],2)", %%xmm3) /* prev2[x+2*refs] */\ LOAD8("(%["next2"],%[prefs],2)", %%xmm5) /* next2[x+2*refs] */\ "paddw %%xmm4, %%xmm2 \n\t"\ "paddw %%xmm5, %%xmm3 \n\t"\ "psrlw $1, %%xmm2 \n\t" /* b */\ "psrlw $1, %%xmm3 \n\t" /* f */\ "movdqa %[tmp0], %%xmm4 \n\t" /* c */\ "movdqa %[tmp1], %%xmm5 \n\t" /* d */\ "movdqa %[tmp2], %%xmm7 \n\t" /* e */\ "psubw %%xmm4, %%xmm2 \n\t" /* b-c */\ "psubw %%xmm7, %%xmm3 \n\t" /* f-e */\ "movdqa %%xmm5, %%xmm0 \n\t"\ "psubw %%xmm4, %%xmm5 \n\t" /* d-c */\ "psubw %%xmm7, %%xmm0 \n\t" /* d-e */\ "movdqa %%xmm2, %%xmm4 \n\t"\ "pminsw %%xmm3, %%xmm2 \n\t"\ "pmaxsw %%xmm4, %%xmm3 \n\t"\ "pmaxsw %%xmm5, %%xmm2 \n\t"\ "pminsw %%xmm5, %%xmm3 \n\t"\ "pmaxsw %%xmm0, %%xmm2 \n\t" /* max */\ "pminsw %%xmm0, %%xmm3 \n\t" /* min */\ "pxor %%xmm4, %%xmm4 \n\t"\ "pmaxsw %%xmm3, %%xmm6 \n\t"\ "psubw %%xmm2, %%xmm4 \n\t" /* -max */\ "pmaxsw %%xmm4, %%xmm6 \n\t" /* diff= MAX3(diff, min, -max); */\ "1: \n\t"\ \ "movdqa %[tmp1], %%xmm2 \n\t" /* d */\ "movdqa %%xmm2, %%xmm3 \n\t"\ "psubw %%xmm6, %%xmm2 \n\t" /* d-diff */\ "paddw %%xmm6, %%xmm3 \n\t" /* d+diff */\ "pmaxsw %%xmm2, %%xmm1 \n\t"\ "pminsw %%xmm3, %%xmm1 \n\t" /* d = clip(spatial_pred, d-diff, d+diff); */\ "packuswb %%xmm1, %%xmm1 \n\t"\ \ :[tmp0]"=m"(tmp0),\ [tmp1]"=m"(tmp1),\ [tmp2]"=m"(tmp2),\ [tmp3]"=m"(tmp3)\ :[prev] "r"(prev),\ [cur] "r"(cur),\ [next] "r"(next),\ [prefs]"r"((intptr_t)refs),\ [mrefs]"r"((intptr_t)-refs),\ [pw1] "m"(*pw_1),\ [pb1] "m"(*pb_1),\ [mode] "g"(mode)\ );\ __asm__ volatile("movq %%xmm1, %0" :"=m"(*dst));\ dst += 8;\ prev+= 8;\ cur += 8;\ next+= 8;\ } if(parity){ #define prev2 "prev" #define next2 "cur" FILTER #undef prev2 #undef next2 }else{ #define prev2 "cur" #define next2 "next" FILTER #undef prev2 #undef next2 } } #undef LOAD8 #undef PABS #undef CHECK #undef CHECK1 #undef CHECK2 #undef FILTER #undef FILTER_LINE_FUNC_NAME mlt-6.20.0/src/modules/xine/xineutils.h000066400000000000000000001112101362234133600177710ustar00rootroot00000000000000/* * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #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 char *_private_strpbrk(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 mlt-6.20.0/src/modules/xine/yadif.c000066400000000000000000000504421362234133600170450ustar00rootroot00000000000000/* Yadif C-plugin for Avisynth 2.5 - Yet Another DeInterlacing Filter Copyright (C)2007 Alexander G. Balakhnin aka Fizick http://avisynth.org.ru Port of YADIF filter from MPlayer Copyright (C) 2006 Michael Niedermayer 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. 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. Avisynth_C plugin Assembler optimized for GNU C compiler */ #include "yadif.h" #include #include #include #define MIN(a,b) ((a) > (b) ? (b) : (a)) #define MAX(a,b) ((a) < (b) ? (b) : (a)) #define ABS(a) ((a) > 0 ? (a) : (-(a))) #define MIN3(a,b,c) MIN(MIN(a,b),c) #define MAX3(a,b,c) MAX(MAX(a,b),c) static void (*filter_line)(int mode, uint8_t *dst, const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w, int refs, int parity); #if defined(__GNUC__) && defined(USE_SSE) #define LOAD4(mem,dst) \ "movd "mem", "#dst" \n\t"\ "punpcklbw %%mm7, "#dst" \n\t" #define PABS(tmp,dst) \ "pxor "#tmp", "#tmp" \n\t"\ "psubw "#dst", "#tmp" \n\t"\ "pmaxsw "#tmp", "#dst" \n\t" #define CHECK(pj,mj) \ "movq "#pj"(%[cur],%[mrefs]), %%mm2 \n\t" /* cur[x-refs-1+j] */\ "movq "#mj"(%[cur],%[prefs]), %%mm3 \n\t" /* cur[x+refs-1-j] */\ "movq %%mm2, %%mm4 \n\t"\ "movq %%mm2, %%mm5 \n\t"\ "pxor %%mm3, %%mm4 \n\t"\ "pavgb %%mm3, %%mm5 \n\t"\ "pand %[pb1], %%mm4 \n\t"\ "psubusb %%mm4, %%mm5 \n\t"\ "psrlq $8, %%mm5 \n\t"\ "punpcklbw %%mm7, %%mm5 \n\t" /* (cur[x-refs+j] + cur[x+refs-j])>>1 */\ "movq %%mm2, %%mm4 \n\t"\ "psubusb %%mm3, %%mm2 \n\t"\ "psubusb %%mm4, %%mm3 \n\t"\ "pmaxub %%mm3, %%mm2 \n\t"\ "movq %%mm2, %%mm3 \n\t"\ "movq %%mm2, %%mm4 \n\t" /* ABS(cur[x-refs-1+j] - cur[x+refs-1-j]) */\ "psrlq $8, %%mm3 \n\t" /* ABS(cur[x-refs +j] - cur[x+refs -j]) */\ "psrlq $16, %%mm4 \n\t" /* ABS(cur[x-refs+1+j] - cur[x+refs+1-j]) */\ "punpcklbw %%mm7, %%mm2 \n\t"\ "punpcklbw %%mm7, %%mm3 \n\t"\ "punpcklbw %%mm7, %%mm4 \n\t"\ "paddw %%mm3, %%mm2 \n\t"\ "paddw %%mm4, %%mm2 \n\t" /* score */ #define CHECK1 \ "movq %%mm0, %%mm3 \n\t"\ "pcmpgtw %%mm2, %%mm3 \n\t" /* if(score < spatial_score) */\ "pminsw %%mm2, %%mm0 \n\t" /* spatial_score= score; */\ "movq %%mm3, %%mm6 \n\t"\ "pand %%mm3, %%mm5 \n\t"\ "pandn %%mm1, %%mm3 \n\t"\ "por %%mm5, %%mm3 \n\t"\ "movq %%mm3, %%mm1 \n\t" /* spatial_pred= (cur[x-refs+j] + cur[x+refs-j])>>1; */ #define CHECK2 /* pretend not to have checked dir=2 if dir=1 was bad.\ hurts both quality and speed, but matches the C version. */\ "paddw %[pw1], %%mm6 \n\t"\ "psllw $14, %%mm6 \n\t"\ "paddsw %%mm6, %%mm2 \n\t"\ "movq %%mm0, %%mm3 \n\t"\ "pcmpgtw %%mm2, %%mm3 \n\t"\ "pminsw %%mm2, %%mm0 \n\t"\ "pand %%mm3, %%mm5 \n\t"\ "pandn %%mm1, %%mm3 \n\t"\ "por %%mm5, %%mm3 \n\t"\ "movq %%mm3, %%mm1 \n\t" static void filter_line_mmx2(int mode, uint8_t *dst, const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w, int refs, int parity){ static const uint64_t pw_1 = 0x0001000100010001ULL; static const uint64_t pb_1 = 0x0101010101010101ULL; // const int mode = p->mode; uint64_t tmp0, tmp1, tmp2, tmp3; int x; #define FILTER\ for(x=0; x>1 */\ "movq %%mm0, %[tmp0] \n\t" /* c */\ "movq %%mm3, %[tmp1] \n\t" /* d */\ "movq %%mm1, %[tmp2] \n\t" /* e */\ "psubw %%mm4, %%mm2 \n\t"\ PABS( %%mm4, %%mm2) /* temporal_diff0 */\ LOAD4("(%[prev],%[mrefs])", %%mm3) /* prev[x-refs] */\ LOAD4("(%[prev],%[prefs])", %%mm4) /* prev[x+refs] */\ "psubw %%mm0, %%mm3 \n\t"\ "psubw %%mm1, %%mm4 \n\t"\ PABS( %%mm5, %%mm3)\ PABS( %%mm5, %%mm4)\ "paddw %%mm4, %%mm3 \n\t" /* temporal_diff1 */\ "psrlw $1, %%mm2 \n\t"\ "psrlw $1, %%mm3 \n\t"\ "pmaxsw %%mm3, %%mm2 \n\t"\ LOAD4("(%[next],%[mrefs])", %%mm3) /* next[x-refs] */\ LOAD4("(%[next],%[prefs])", %%mm4) /* next[x+refs] */\ "psubw %%mm0, %%mm3 \n\t"\ "psubw %%mm1, %%mm4 \n\t"\ PABS( %%mm5, %%mm3)\ PABS( %%mm5, %%mm4)\ "paddw %%mm4, %%mm3 \n\t" /* temporal_diff2 */\ "psrlw $1, %%mm3 \n\t"\ "pmaxsw %%mm3, %%mm2 \n\t"\ "movq %%mm2, %[tmp3] \n\t" /* diff */\ \ "paddw %%mm0, %%mm1 \n\t"\ "paddw %%mm0, %%mm0 \n\t"\ "psubw %%mm1, %%mm0 \n\t"\ "psrlw $1, %%mm1 \n\t" /* spatial_pred */\ PABS( %%mm2, %%mm0) /* ABS(c-e) */\ \ "movq -1(%[cur],%[mrefs]), %%mm2 \n\t" /* cur[x-refs-1] */\ "movq -1(%[cur],%[prefs]), %%mm3 \n\t" /* cur[x+refs-1] */\ "movq %%mm2, %%mm4 \n\t"\ "psubusb %%mm3, %%mm2 \n\t"\ "psubusb %%mm4, %%mm3 \n\t"\ "pmaxub %%mm3, %%mm2 \n\t"\ /*"pshufw $9,%%mm2, %%mm3 \n\t"*/\ "movq %%mm2, %%mm3 \n\t" /* replace for "pshufw $9,%%mm2, %%mm3" - Fizick */\ "psrlq $16, %%mm3 \n\t"/* replace for "pshufw $9,%%mm2, %%mm3" - Fizick*/\ "punpcklbw %%mm7, %%mm2 \n\t" /* ABS(cur[x-refs-1] - cur[x+refs-1]) */\ "punpcklbw %%mm7, %%mm3 \n\t" /* ABS(cur[x-refs+1] - cur[x+refs+1]) */\ "paddw %%mm2, %%mm0 \n\t"\ "paddw %%mm3, %%mm0 \n\t"\ "psubw %[pw1], %%mm0 \n\t" /* spatial_score */\ \ CHECK(-2,0)\ CHECK1\ CHECK(-3,1)\ CHECK2\ CHECK(0,-2)\ CHECK1\ CHECK(1,-3)\ CHECK2\ \ /* if(p->mode<2) ... */\ "movq %[tmp3], %%mm6 \n\t" /* diff */\ "cmpl $2, %[mode] \n\t"\ "jge 1f \n\t"\ LOAD4("(%["prev2"],%[mrefs],2)", %%mm2) /* prev2[x-2*refs] */\ LOAD4("(%["next2"],%[mrefs],2)", %%mm4) /* next2[x-2*refs] */\ LOAD4("(%["prev2"],%[prefs],2)", %%mm3) /* prev2[x+2*refs] */\ LOAD4("(%["next2"],%[prefs],2)", %%mm5) /* next2[x+2*refs] */\ "paddw %%mm4, %%mm2 \n\t"\ "paddw %%mm5, %%mm3 \n\t"\ "psrlw $1, %%mm2 \n\t" /* b */\ "psrlw $1, %%mm3 \n\t" /* f */\ "movq %[tmp0], %%mm4 \n\t" /* c */\ "movq %[tmp1], %%mm5 \n\t" /* d */\ "movq %[tmp2], %%mm7 \n\t" /* e */\ "psubw %%mm4, %%mm2 \n\t" /* b-c */\ "psubw %%mm7, %%mm3 \n\t" /* f-e */\ "movq %%mm5, %%mm0 \n\t"\ "psubw %%mm4, %%mm5 \n\t" /* d-c */\ "psubw %%mm7, %%mm0 \n\t" /* d-e */\ "movq %%mm2, %%mm4 \n\t"\ "pminsw %%mm3, %%mm2 \n\t"\ "pmaxsw %%mm4, %%mm3 \n\t"\ "pmaxsw %%mm5, %%mm2 \n\t"\ "pminsw %%mm5, %%mm3 \n\t"\ "pmaxsw %%mm0, %%mm2 \n\t" /* max */\ "pminsw %%mm0, %%mm3 \n\t" /* min */\ "pxor %%mm4, %%mm4 \n\t"\ "pmaxsw %%mm3, %%mm6 \n\t"\ "psubw %%mm2, %%mm4 \n\t" /* -max */\ "pmaxsw %%mm4, %%mm6 \n\t" /* diff= MAX3(diff, min, -max); */\ "1: \n\t"\ \ "movq %[tmp1], %%mm2 \n\t" /* d */\ "movq %%mm2, %%mm3 \n\t"\ "psubw %%mm6, %%mm2 \n\t" /* d-diff */\ "paddw %%mm6, %%mm3 \n\t" /* d+diff */\ "pmaxsw %%mm2, %%mm1 \n\t"\ "pminsw %%mm3, %%mm1 \n\t" /* d = clip(spatial_pred, d-diff, d+diff); */\ "packuswb %%mm1, %%mm1 \n\t"\ \ :[tmp0]"=m"(tmp0),\ [tmp1]"=m"(tmp1),\ [tmp2]"=m"(tmp2),\ [tmp3]"=m"(tmp3)\ :[prev] "r"(prev),\ [cur] "r"(cur),\ [next] "r"(next),\ [prefs]"r"((intptr_t)refs),\ [mrefs]"r"((intptr_t)-refs),\ [pw1] "m"(pw_1),\ [pb1] "m"(pb_1),\ [mode] "g"(mode)\ );\ asm volatile("movd %%mm1, %0" :"=m"(*dst));\ dst += 4;\ prev+= 4;\ cur += 4;\ next+= 4;\ } if(parity){ #define prev2 "prev" #define next2 "cur" FILTER #undef prev2 #undef next2 }else{ #define prev2 "cur" #define next2 "next" FILTER #undef prev2 #undef next2 } } #undef LOAD4 #undef PABS #undef CHECK #undef CHECK1 #undef CHECK2 #undef FILTER #ifndef attribute_align_arg #if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__>1) # define attribute_align_arg __attribute__((force_align_arg_pointer)) #else # define attribute_align_arg #endif #endif // for proper alignment SSE2 we need in GCC 4.2 and above #if (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__>1) #ifndef DECLARE_ALIGNED #define DECLARE_ALIGNED(n,t,v) t v __attribute__ ((aligned (n))) #endif // ================= SSE2 ================= #if defined(USE_SSE2) && defined(ARCH_X86_64) #define PABS(tmp,dst) \ "pxor "#tmp", "#tmp" \n\t"\ "psubw "#dst", "#tmp" \n\t"\ "pmaxsw "#tmp", "#dst" \n\t" #define FILTER_LINE_FUNC_NAME filter_line_sse2 #include "vf_yadif_template.h" #endif // ================ SSSE3 ================= #ifdef USE_SSE3 #define PABS(tmp,dst) \ "pabsw "#dst", "#dst" \n\t" #define FILTER_LINE_FUNC_NAME filter_line_ssse3 #include "vf_yadif_template.h" #endif #endif // GCC 4.2+ #endif // GNUC, USE_SSE static void filter_line_c(int mode, uint8_t *dst, const uint8_t *prev, const uint8_t *cur, const uint8_t *next, int w, int refs, int parity){ int x; const uint8_t *prev2= parity ? prev : cur ; const uint8_t *next2= parity ? cur : next; for(x=0; x>1; int e= cur[+refs]; int temporal_diff0= ABS(prev2[0] - next2[0]); int temporal_diff1=( ABS(prev[-refs] - c) + ABS(prev[+refs] - e) )>>1; int temporal_diff2=( ABS(next[-refs] - c) + ABS(next[+refs] - e) )>>1; int diff= MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2); int spatial_pred= (c+e)>>1; int spatial_score= ABS(cur[-refs-1] - cur[+refs-1]) + ABS(c-e) + ABS(cur[-refs+1] - cur[+refs+1]) - 1; #define CHECK(j)\ { int score= ABS(cur[-refs-1+ j] - cur[+refs-1- j])\ + ABS(cur[-refs + j] - cur[+refs - j])\ + ABS(cur[-refs+1+ j] - cur[+refs+1- j]);\ if(score < spatial_score){\ spatial_score= score;\ spatial_pred= (cur[-refs + j] + cur[+refs - j])>>1;\ CHECK(-1) CHECK(-2) }} }} CHECK( 1) CHECK( 2) }} }} if(mode<2){ int b= (prev2[-2*refs] + next2[-2*refs])>>1; int f= (prev2[+2*refs] + next2[+2*refs])>>1; #if 0 int a= cur[-3*refs]; int g= cur[+3*refs]; int max= MAX3(d-e, d-c, MIN3(MAX(b-c,f-e),MAX(b-c,b-a),MAX(f-g,f-e)) ); int min= MIN3(d-e, d-c, MAX3(MIN(b-c,f-e),MIN(b-c,b-a),MIN(f-g,f-e)) ); #else int max= MAX3(d-e, d-c, MIN(b-c, f-e)); int min= MIN3(d-e, d-c, MAX(b-c, f-e)); #endif diff= MAX3(diff, min, -max); } if(spatial_pred > d + diff) spatial_pred = d + diff; else if(spatial_pred < d - diff) spatial_pred = d - diff; dst[0] = spatial_pred; dst++; cur++; prev++; next++; prev2++; next2++; } } static void interpolate(uint8_t *dst, const uint8_t *cur0, const uint8_t *cur2, int w) { int x; for (x=0; x>1; // simple average } } void filter_plane(int mode, uint8_t *dst, int dst_stride, const uint8_t *prev0, const uint8_t *cur0, const uint8_t *next0, int refs, int w, int h, int parity, int tff, int cpu){ int y; filter_line = filter_line_c; #ifdef __GNUC__ #if (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__>1) #ifdef USE_SSE3 if (cpu & AVS_CPU_SSSE3) filter_line = filter_line_ssse3; else #endif #if defined(USE_SSE2) && defined(ARCH_X86_64) if (cpu & AVS_CPU_SSE2) filter_line = filter_line_sse2; else #endif #endif // GCC 4.2+ #ifdef USE_SSE if (cpu & AVS_CPU_INTEGER_SSE) filter_line = filter_line_mmx2; #endif #endif // GNUC y=0; if(((y ^ parity) & 1)){ memcpy(dst, cur0 + refs, w);// duplicate 1 }else{ memcpy(dst, cur0, w); } y=1; if(((y ^ parity) & 1)){ interpolate(dst + dst_stride, cur0, cur0 + refs*2, w); // interpolate 0 and 2 }else{ memcpy(dst + dst_stride, cur0 + refs, w); // copy original } for(y=2; y= AVS_CPU_INTEGER_SSE) asm volatile("emms"); #endif } #if defined(__GNUC__) && defined(USE_SSE) && !defined(PIC) static attribute_align_arg void YUY2ToPlanes_mmx(const unsigned char *srcYUY2, int pitch_yuy2, int width, int height, unsigned char *py, int pitch_y, unsigned char *pu, unsigned char *pv, int pitch_uv) { /* process by 16 bytes (8 pixels), so width is assumed mod 8 */ int widthdiv2 = width>>1; // static unsigned __int64 Ymask = 0x00FF00FF00FF00FFULL; int h; for (h=0; h> 1; int h; for (h=0; h>1)] = pSrcYUY2[w2+1]; pSrcV[(w>>1)] = pSrcYUY2[w2+3]; } pSrcY += srcPitchY; pSrcU += srcPitchUV; pSrcV += srcPitchUV; pSrcYUY2 += nSrcPitchYUY2; } } //---------------------------------------------------------------------------------------------- void YUY2FromPlanes(unsigned char *pSrcYUY2, int nSrcPitchYUY2, int nWidth, int nHeight, const unsigned char * pSrcY, int srcPitchY, const unsigned char * pSrcU, const unsigned char * pSrcV, int srcPitchUV, int cpu) { int h,w; int w0 = 0; #if defined(__GNUC__) && defined(USE_SSE) && !defined(PIC) if (cpu & AVS_CPU_INTEGER_SSE) { w0 = (nWidth/8)*8; YUY2FromPlanes_mmx(pSrcYUY2, nSrcPitchYUY2, w0, nHeight, pSrcY, srcPitchY, pSrcU, pSrcV, srcPitchUV); } #endif for (h=0; h>1)]; pSrcYUY2[w2+2] = pSrcY[w+1]; pSrcYUY2[w2+3] = pSrcV[(w>>1)]; } pSrcY += srcPitchY; pSrcU += srcPitchUV; pSrcV += srcPitchUV; pSrcYUY2 += nSrcPitchYUY2; } } mlt-6.20.0/src/modules/xine/yadif.h000066400000000000000000000041031362234133600170430ustar00rootroot00000000000000/* Yadif C-plugin for Avisynth 2.5 - Yet Another DeInterlacing Filter Copyright (C)2007 Alexander G. Balakhnin aka Fizick http://avisynth.org.ru Port of YADIF filter from MPlayer Copyright (C) 2006 Michael Niedermayer 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. 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. Avisynth_C plugin Assembler optimized for GNU C compiler */ #ifndef YADIF_H_ #define YADIF_H_ #include #define AVS_CPU_INTEGER_SSE 0x1 #define AVS_CPU_SSE2 0x2 #define AVS_CPU_SSSE3 0x4 typedef struct yadif_filter { int cpu; // optimization int yheight; int ypitch; int uvpitch; int ywidth; int uvwidth; unsigned char *ysrc; unsigned char *usrc; unsigned char *vsrc; unsigned char *yprev; unsigned char *uprev; unsigned char *vprev; unsigned char *ynext; unsigned char *unext; unsigned char *vnext; unsigned char *ydest; unsigned char *udest; unsigned char *vdest; } yadif_filter; void filter_plane(int mode, uint8_t *dst, int dst_stride, const uint8_t *prev0, const uint8_t *cur0, const uint8_t *next0, int refs, int w, int h, int parity, int tff, int cpu); void YUY2ToPlanes(const unsigned char *pSrcYUY2, int nSrcPitchYUY2, int nWidth, int nHeight, unsigned char * pSrcY, int srcPitchY, unsigned char * pSrcU, unsigned char * pSrcV, int srcPitchUV, int cpu); void YUY2FromPlanes(unsigned char *pSrcYUY2, int nSrcPitchYUY2, int nWidth, int nHeight, const unsigned char * pSrcY, int srcPitchY, const unsigned char * pSrcU, const unsigned char * pSrcV, int srcPitchUV, int cpu); #endif mlt-6.20.0/src/modules/xml/000077500000000000000000000000001362234133600154355ustar00rootroot00000000000000mlt-6.20.0/src/modules/xml/CMakeLists.txt000066400000000000000000000006351362234133600202010ustar00rootroot00000000000000pkg_check_modules(xml IMPORTED_TARGET libxml-2.0) if(TARGET PkgConfig::xml) file(GLOB mltxml_src *.c) add_library(mltxml MODULE ${mltxml_src}) target_link_libraries(mltxml mlt Threads::Threads PkgConfig::xml) install(TARGETS mltxml LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mlt) file(GLOB yml *.yml) install(FILES ${yml} mlt-xml.dtd DESTINATION ${CMAKE_INSTALL_DATADIR}/mlt/xml) endif() mlt-6.20.0/src/modules/xml/Makefile000066400000000000000000000014341362234133600170770ustar00rootroot00000000000000CFLAGS += -I../.. LDFLAGS += -L../../framework -lmlt -lpthread include ../../../config.mak TARGET = ../libmltxml$(LIBSUF) OBJS = factory.o \ consumer_xml.o \ producer_xml.o \ common.o CFLAGS += $(shell pkg-config libxml-2.0 --cflags) LDFLAGS += $(shell pkg-config libxml-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)$(moduledir)" install -d "$(DESTDIR)$(mltdatadir)/xml" install -m 644 mlt-xml.dtd "$(DESTDIR)$(mltdatadir)/xml" install -m 644 *.yml "$(DESTDIR)$(mltdatadir)/xml" ifneq ($(wildcard .depend),) include .depend endif mlt-6.20.0/src/modules/xml/common.c000066400000000000000000000033371362234133600170770ustar00rootroot00000000000000/* * Copyright (C) 2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include // Returns string length of "plain:" prefix if webvx or speed parameter if timewarp. // Otherwise, returns 0. size_t mlt_xml_prefix_size( mlt_properties properties, const char *name, const char *value ) { size_t result = 0; if ( !strcmp( "resource", name ) ) { const char *mlt_service = mlt_properties_get( properties, "mlt_service" ); const char *plain = "plain:"; size_t plain_len = strlen( plain ); if ( mlt_service && !strcmp( "timewarp", mlt_service ) ) { const char *delimiter = strchr( value, ':' ); if ( delimiter ) result = delimiter - value; if ( result && ( value[result - 1] == '.' || value[result - 1] == ',' || isdigit(value[result - 1]) ) ) ++result; // include the delimiter else result = 0; // invalid } else if ( !strncmp( value, plain, plain_len ) ) { result = plain_len; } } return result; } mlt-6.20.0/src/modules/xml/common.h000066400000000000000000000020051362234133600170730ustar00rootroot00000000000000/* * Copyright (C) 2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MLT_XML_COMMON_H #define MLT_XML_COMMON_H #include size_t mlt_xml_prefix_size( mlt_properties properties, const char *name, const char *value ); #endif // MLT_XML_COMMON_H mlt-6.20.0/src/modules/xml/configure000077500000000000000000000003341362234133600173440ustar00rootroot00000000000000#!/bin/sh if [ "$help" != "1" ] then pkg-config libxml-2.0 > /dev/null 2>&1 disable_xml2=$? if [ "$disable_xml2" != "0" ] then echo "- xml2 not found: disabling xml module" touch ../disable-xml fi exit 0 fi mlt-6.20.0/src/modules/xml/consumer_xml.c000066400000000000000000001042351362234133600203210ustar00rootroot00000000000000/* * consumer_xml.c -- a libxml2 serialiser of mlt service networks * Copyright (C) 2003-2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "common.h" #include #include #include #include #include #include #include #include #define ID_SIZE 128 #define TIME_PROPERTY "_consumer_xml" #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; int no_meta; mlt_profile profile; mlt_time_format time_format; }; typedef struct serialise_context_s* serialise_context; /** 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 consumer ); static void consumer_close( mlt_consumer parent ); static void *consumer_thread( void *arg ); static void serialise_service( serialise_context context, mlt_service service, xmlNode *node ); typedef enum { xml_existing, xml_producer, xml_multitrack, xml_playlist, xml_tractor, xml_filter, xml_transition } xml_type; /** Create or retrieve an id associated to this service. */ static char *xml_get_id( serialise_context context, mlt_service service, xml_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 != xml_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 xml_producer: sprintf( temp, "producer%d", context->producer_count ++ ); break; case xml_multitrack: sprintf( temp, "multitrack%d", context->multitrack_count ++ ); break; case xml_playlist: sprintf( temp, "playlist%d", context->playlist_count ++ ); break; case xml_tractor: sprintf( temp, "tractor%d", context->tractor_count ++ ); break; case xml_filter: sprintf( temp, "filter%d", context->filter_count ++ ); break; case xml_transition: sprintf( temp, "transition%d", context->transition_count ++ ); break; case xml_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 == xml_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_xml_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) { // Create the consumer object mlt_consumer consumer = calloc( 1, sizeof( struct mlt_consumer_s ) ); // If no malloc'd and consumer init ok if ( consumer != NULL && mlt_consumer_init( consumer, NULL, profile ) == 0 ) { // Allow thread to be started/stopped consumer->start = consumer_start; consumer->stop = consumer_stop; consumer->is_stopped = consumer_is_stopped; // Assign close callback consumer->close = consumer_close; mlt_properties_set( MLT_CONSUMER_PROPERTIES( consumer ), "resource", arg ); mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "real_time", 0 ); mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "prefill", 1 ); mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( consumer ), "terminate_on_pause", 1 ); // Return the consumer produced return consumer; } // malloc or consumer init failed free( consumer ); // 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 && ( !context->no_meta || strncmp( name, "meta.", 5 ) ) && strcmp( name, "mlt" ) && strcmp( name, "mlt_type" ) && strcmp( name, "in" ) && strcmp( name, "out" ) && strcmp( name, "id" ) && strcmp( name, "title" ) && strcmp( name, "root" ) && strcmp( name, "width" ) && strcmp( name, "height" ) ) { char *value = mlt_properties_get_value_tf( properties, i, context->time_format ); if ( value ) { int rootlen = strlen( context->root ); const char *value_orig = value; size_t prefix_size = mlt_xml_prefix_size( properties, name, value ); // Strip off prefix. if ( prefix_size ) value += prefix_size; // Ignore trailing slash on root. if ( rootlen && ( context->root[rootlen - 1] == '/' || context->root[rootlen - 1] == '\\') ) --rootlen; // convert absolute path to relative if ( rootlen && !strncmp( value, context->root, rootlen ) && ( value[rootlen] == '/' || value[rootlen] == '\\' ) ) { if ( prefix_size ) { char *s = calloc( 1, strlen( value_orig ) - rootlen + 1 ); strncat( s, value_orig, prefix_size ); strcat( s, value + rootlen + 1 ); p = xmlNewTextChild( node, NULL, _x("property"), _x(s) ); free( s ); } else { p = xmlNewTextChild( node, NULL, _x("property"), _x(value_orig + rootlen + 1) ); } } else p = xmlNewTextChild( node, NULL, _x("property"), _x(value_orig) ); 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_tf( properties, i, context->time_format ); if ( value ) { int rootlen = strlen( context->root ); // convert absolute path to relative if ( rootlen && !strncmp( value, context->root, rootlen ) && value[ rootlen ] == '/' ) p = xmlNewTextChild( node, NULL, _x("property"), _x(value + rootlen + 1) ); else 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, "_loader" ) == 0 ) { // Get a new id - if already allocated, do nothing char *id = xml_get_id( context, MLT_FILTER_SERVICE( filter ), xml_filter ); if ( id != NULL ) { 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 ( mlt_properties_get_position( properties, "in" ) ) xmlNewProp( p, _x("in"), _x( mlt_properties_get_time( properties, "in", context->time_format ) ) ); if ( mlt_properties_get_position( properties, "out" ) ) xmlNewProp( p, _x("out"), _x( mlt_properties_get_time( properties, "out", context->time_format ) ) ); 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 = xml_get_id( context, parent, xml_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_time( properties, "in", context->time_format )) ); xmlNewProp( child, _x("out"), _x(mlt_properties_get_time( properties, "out", context->time_format )) ); // If the xml producer fails to load a producer, it creates a text producer that says INVALID // and sets the xml_mlt_service property to the original service. const char *xml_mlt_service = mlt_properties_get(properties, "_xml_mlt_service"); if (xml_mlt_service) { // We should not serialize this as a text producer but using the original mlt_service. mlt_properties_set(properties, "mlt_service", xml_mlt_service); } 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 = xml_get_id( context, parent, xml_existing ); mlt_properties properties = MLT_SERVICE_PROPERTIES( service ); xmlNewProp( node, _x("parent"), _x(id) ); xmlNewProp( node, _x("in"), _x(mlt_properties_get_time( properties, "in", context->time_format )) ); xmlNewProp( node, _x("out"), _x(mlt_properties_get_time( properties, "out", context->time_format )) ); } } 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 = xml_get_id( context, service, xml_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 = xml_get_id( context, MLT_SERVICE( parent ), xml_existing ); xmlNewProp( track, _x("producer"), _x(id) ); if ( mlt_producer_is_cut( producer ) ) { xmlNewProp( track, _x("in"), _x( mlt_properties_get_time( properties, "in", context->time_format ) ) ); xmlNewProp( track, _x("out"), _x( mlt_properties_get_time( properties, "out", context->time_format ) ) ); serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, context->store ); serialise_store_properties( context, MLT_PRODUCER_PROPERTIES( producer ), track, "xml_" ); if ( !context->no_meta ) 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 = xml_get_id( context, service, xml_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, "xml_" ); if ( !context->no_meta ) 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 ); mlt_properties producer_props = MLT_PRODUCER_PROPERTIES( producer ); char *service_s = mlt_properties_get( producer_props, "mlt_service" ); if ( service_s != NULL && strcmp( service_s, "blank" ) == 0 ) { xmlNode *entry = xmlNewChild( child, NULL, _x("blank"), NULL ); mlt_properties_set_data( producer_props, "_profile", context->profile, 0, NULL, NULL ); mlt_properties_set_position( producer_props, TIME_PROPERTY, info.frame_count ); xmlNewProp( entry, _x("length"), _x( mlt_properties_get_time( producer_props, TIME_PROPERTY, context->time_format ) ) ); } else { char temp[ 20 ]; xmlNode *entry = xmlNewChild( child, NULL, _x("entry"), NULL ); id = xml_get_id( context, MLT_SERVICE( producer ), xml_existing ); xmlNewProp( entry, _x("producer"), _x(id) ); mlt_properties_set_position( producer_props, TIME_PROPERTY, info.frame_in ); xmlNewProp( entry, _x("in"), _x( mlt_properties_get_time( producer_props, TIME_PROPERTY, context->time_format ) ) ); mlt_properties_set_position( producer_props, TIME_PROPERTY, info.frame_out ); xmlNewProp( entry, _x("out"), _x( mlt_properties_get_time( producer_props, TIME_PROPERTY, context->time_format ) ) ); 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, "xml_" ); if ( !context->no_meta ) 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 = xml_get_id( context, service, xml_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 = xml_get_id( context, service, xml_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" )) ); if ( mlt_properties_get_position( properties, "in" ) >= 0 ) xmlNewProp( child, _x("in"), _x(mlt_properties_get_time( properties, "in", context->time_format )) ); if ( mlt_properties_get_position( properties, "out" ) >= 0 ) xmlNewProp( child, _x("out"), _x(mlt_properties_get_time( properties, "out", context->time_format )) ); // Store application specific properties serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, context->store ); serialise_store_properties( context, MLT_SERVICE_PROPERTIES( service ), child, "xml_" ); if ( !context->no_meta ) 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 = xml_get_id( context, service, xml_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" )) ); if ( mlt_properties_get_position( properties, "in" ) ) xmlNewProp( child, _x("in"), _x( mlt_properties_get_time( properties, "in", context->time_format ) ) ); if ( mlt_properties_get_position( properties, "out" ) ) xmlNewProp( child, _x("out"), _x( mlt_properties_get_time( properties, "out", context->time_format ) ) ); 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 = xml_get_id( context, service, xml_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" )) ); if ( mlt_properties_get_position( properties, "in" ) ) xmlNewProp( child, _x("in"), _x( mlt_properties_get_time( properties, "in", context->time_format ) ) ); if ( mlt_properties_get_position( properties, "out" ) ) xmlNewProp( child, _x("out"), _x( mlt_properties_get_time( properties, "out", context->time_format ) ) ); 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, "xml" ) == 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, "xml" ) != 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 ( resource && strcmp( resource, "" ) == 0 ) { serialise_multitrack( context, service, node ); break; } // Recurse on playlist's clips else if ( resource && strcmp( resource, "" ) == 0 ) { serialise_playlist( context, service, node ); } // Recurse on tractor's producer else if ( resource && 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 ); if ( mlt_properties_get( properties, "xml" ) != NULL ) break; } } // 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 ); } } static void serialise_other( mlt_properties properties, struct serialise_context_s *context, xmlNodePtr root ) { int i; for ( i = 0; i < mlt_properties_count( properties ); i++ ) { const char* name = mlt_properties_get_name( properties, i ); if ( strlen(name) > 10 && !strncmp( name, "xml_retain", 10 ) ) { mlt_service service = mlt_properties_get_data_at( properties, i, NULL ); if ( service ) { mlt_properties_set_int( MLT_SERVICE_PROPERTIES( service ), "xml_retain", 1 ); serialise_service( context, service, root ); } } } } xmlDocPtr xml_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("mlt") ); struct serialise_context_s *context = calloc( 1, sizeof( struct serialise_context_s ) ); mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( consumer ) ); char tmpstr[ 32 ]; xmlDocSetRootElement( doc, root ); // Indicate the numeric locale if ( mlt_properties_get_lcnumeric( properties ) ) xmlNewProp( root, _x("LC_NUMERIC"), _x( mlt_properties_get_lcnumeric( properties ) ) ); else #ifdef _WIN32 { char* lcnumeric = getlocale(); mlt_properties_set( properties, "_xml_lcnumeric_in", lcnumeric ); free( lcnumeric ); mlt_properties_to_utf8( properties, "_xml_lcnumeric_in", "_xml_lcnumeric_out" ); lcnumeric = mlt_properties_get( properties, "_xml_lcnumeric_out" ); xmlNewProp( root, _x("LC_NUMERIC"), _x( lcnumeric ) ); } #else xmlNewProp( root, _x("LC_NUMERIC"), _x( setlocale( LC_NUMERIC, NULL ) ) ); #endif // Indicate the version xmlNewProp( root, _x("version"), _x( mlt_version_get_string() ) ); // If we have root, then deal with it now if ( mlt_properties_get( properties, "root" ) != NULL ) { if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "no_root" ) ) 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" ); context->no_meta = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "no_meta" ); const char *time_format = mlt_properties_get( MLT_CONSUMER_PROPERTIES( consumer ), "time_format" ); if ( time_format && ( !strcmp( time_format, "smpte" ) || !strcmp( time_format, "SMPTE" ) || !strcmp( time_format, "timecode" ) || !strcmp( time_format, "smpte_df" ) ) ) context->time_format = mlt_time_smpte_df; else if ( time_format && ( !strcmp( time_format, "smpte_ndf" ) ) ) context->time_format = mlt_time_smpte_ndf; else if ( time_format && ( !strcmp( time_format, "clock" ) || !strcmp( time_format, "CLOCK" ) ) ) context->time_format = mlt_time_clock; // 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 ); // Add a profile child element if ( profile ) { if ( !mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( consumer ), "no_profile" ) ) { xmlNodePtr profile_node = xmlNewChild( root, NULL, _x("profile"), NULL ); if ( profile->description ) xmlNewProp( profile_node, _x("description"), _x(profile->description) ); sprintf( tmpstr, "%d", profile->width ); xmlNewProp( profile_node, _x("width"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->height ); xmlNewProp( profile_node, _x("height"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->progressive ); xmlNewProp( profile_node, _x("progressive"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->sample_aspect_num ); xmlNewProp( profile_node, _x("sample_aspect_num"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->sample_aspect_den ); xmlNewProp( profile_node, _x("sample_aspect_den"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->display_aspect_num ); xmlNewProp( profile_node, _x("display_aspect_num"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->display_aspect_den ); xmlNewProp( profile_node, _x("display_aspect_den"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->frame_rate_num ); xmlNewProp( profile_node, _x("frame_rate_num"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->frame_rate_den ); xmlNewProp( profile_node, _x("frame_rate_den"), _x(tmpstr) ); sprintf( tmpstr, "%d", profile->colorspace ); xmlNewProp( profile_node, _x("colorspace"), _x(tmpstr) ); } context->profile = profile; } // 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_other( MLT_SERVICE_PROPERTIES( service ), context, root ); serialise_service( context, service, root ); // In pass two, we serialise the tractor and reference the // producers and playlists context->pass++; serialise_other( MLT_SERVICE_PROPERTIES( service ), context, root ); 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 void output_xml( mlt_consumer consumer ) { // Get the producer service mlt_service service = mlt_service_producer( MLT_CONSUMER_SERVICE( consumer ) ); mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); char *resource = mlt_properties_get( properties, "resource" ); xmlDocPtr doc = NULL; if ( !service ) return; // 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 = xml_make_doc( consumer, 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) ); #ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif xmlFree( buffer ); } else { xmlSaveFormatFileEnc( resource, doc, "utf-8", 1 ); } // Close the document xmlFreeDoc( doc ); } static int consumer_start( mlt_consumer consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); if ( mlt_properties_get_int( properties, "all" ) ) { // 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, consumer ); } } else { output_xml( consumer ); mlt_consumer_stop( consumer ); mlt_consumer_stopped( consumer ); } return 0; } static int consumer_is_stopped( mlt_consumer consumer ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); return !mlt_properties_get_int( properties, "running" ); } static int consumer_stop( mlt_consumer consumer ) { // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // 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 if ( thread ) pthread_join( *thread, NULL ); } return 0; } static void *consumer_thread( void *arg ) { // Map the argument to the object mlt_consumer consumer = arg; // Get the properties mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); // Convenience functionality int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" ); int terminated = 0; // Frame and size mlt_frame frame = NULL; int video_off = mlt_properties_get_int( properties, "video_off" ); int audio_off = mlt_properties_get_int( properties, "audio_off" ); // Loop while running while( !terminated && mlt_properties_get_int( properties, "running" ) ) { // Get the frame 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; // Check that we have a frame to work with if ( frame ) { int width = 0, height = 0; int frequency = mlt_properties_get_int( properties, "frequency" ); int channels = mlt_properties_get_int( properties, "channels" ); int samples = 0; mlt_image_format iformat = mlt_image_yuv422; mlt_audio_format aformat = mlt_audio_s16; uint8_t *buffer; if ( !video_off ) mlt_frame_get_image( frame, &buffer, &iformat, &width, &height, 0 ); if ( !audio_off ) mlt_frame_get_audio( frame, (void**) &buffer, &aformat, &frequency, &channels, &samples ); // Close the frame mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); mlt_frame_close( frame ); } } output_xml( consumer ); // Indicate that the consumer is stopped mlt_properties_set_int( properties, "running", 0 ); mlt_consumer_stopped( consumer ); return NULL; } static void consumer_close( mlt_consumer consumer ) { // Stop the consumer mlt_consumer_stop( consumer ); // Close the parent mlt_consumer_close( consumer ); // Free the memory free( consumer ); } mlt-6.20.0/src/modules/xml/consumer_xml.yml000066400000000000000000000071561362234133600207040ustar00rootroot00000000000000schema_version: 0.3 type: consumer identifier: xml title: XML version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > Serialise the service network to XML. See docs/mlt-xml.txt for more information. notes: > If you set a data property beginning with (and longer than) "xml_retain" on the service connected to this consumer where the data is a mlt_service pointer, then the pointed at service will also be serialized before the connected service. This can be useful, for example, to save a playlist as a media bin along with a multitrack. You can serialize more than one of these additional services by setting more than property, each with a unique key beginning with "xml_retain". 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. parameters: - identifier: resource argument: yes title: File type: string description: > The name of a file in which to store the XML. If the value does not contain a period (to start an extension), then the value is interpreted as the name of a property in which to store the XML. This makes it easy for an application to use the consumer to serialize a service network and retrieve the XML in-memory. readonly: no required: no mutable: no default: stdout widget: fileopen - identifier: all title: Process all frames type: boolean description: > Without this option, the XML consumer does not process any frames and simply serializes the service network. However, some filters (.e.g, videostab) require two passes where the first pass performs some analysis and stores the result in a property. Therefore, set this property to 1 (true) to cause the consumer to process all frames before serializing to XML. default: 0 - identifier: title title: Title type: string description: > You can give the composition a friendly name that some applications may use. - identifier: root title: Base path type: string description: > If a file name in the XML is relative, but not relative to the current XML file's directory, then you can set the directory to which it is relative here. - identifier: no_meta title: Exclude meta properties type: boolean description: > Set this to disable the output of properties with the prefix "meta." default: 0 widget: checkbox - identifier: no_root title: No root attribute type: boolean description: > Set this to disable the output of the root attribute on the root element. default: 0 widget: checkbox - identifier: time_format title: Time format type: string description: Output time-based values as timecode or clock formats. values: - frames - smpte_df # SMPTE drop-frame timecode - smpte_ndf # SMPTE non-drop-frame timecode - smpte # or SMPTE - same as smpte_df - timecode # same as smpte_df - clock # or CLOCK default: frames widget: dropdown - identifier: store title: Include property prefix type: string description: > To save additional properties that MLT does not know about, supply an application-specific property name prefix that you are using. - identifier: no_profile title: No profile element type: boolean description: Set this to disable the output of the profile element. default: 0 widget: checkbox mlt-6.20.0/src/modules/xml/factory.c000066400000000000000000000036761362234133600172640ustar00rootroot00000000000000/* * factory.c -- the factory method interfaces * Copyright (C) 2003-2014 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include extern mlt_consumer consumer_xml_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); static mlt_properties metadata( mlt_service_type type, const char *id, void *data ) { char file[ PATH_MAX ]; snprintf( file, PATH_MAX, "%s/xml/%s", mlt_environment( "MLT_DATA" ), (char*) data ); return mlt_properties_parse_yaml( file ); } MLT_REPOSITORY { MLT_REGISTER( consumer_type, "xml", consumer_xml_init ); MLT_REGISTER( producer_type, "xml", producer_xml_init ); MLT_REGISTER( producer_type, "xml-string", producer_xml_init ); MLT_REGISTER( producer_type, "xml-nogl", producer_xml_init ); MLT_REGISTER_METADATA( consumer_type, "xml", metadata, "consumer_xml.yml" ); MLT_REGISTER_METADATA( producer_type, "xml", metadata, "producer_xml.yml" ); MLT_REGISTER_METADATA( producer_type, "xml-string", metadata, "producer_xml-string.yml" ); MLT_REGISTER_METADATA( producer_type, "xml-nogl", metadata, "producer_xml-nogl.yml" ); } mlt-6.20.0/src/modules/xml/mlt-xml.dtd000066400000000000000000000055241362234133600175320ustar00rootroot00000000000000 mlt-6.20.0/src/modules/xml/producer_xml-nogl.yml000066400000000000000000000006611362234133600216230ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: xml-nogl title: XML without OpenGL version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > This is the same as the regular "xml" producer except it prevents automatically creating the qglsl consumer when it detects the usage of OpenGL-based services within the XML. See ProducerXml for more information. mlt-6.20.0/src/modules/xml/producer_xml-string.yml000066400000000000000000000007261362234133600221740ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: xml-string title: XML String version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: > This is the same as the regular "xml" producer except it takes a pointer to a string as the constructor argument. That means it can only be used by applications and not directly exposed to users of those applications. See ProducerXml for more information. mlt-6.20.0/src/modules/xml/producer_xml.c000066400000000000000000002020501362234133600203030ustar00rootroot00000000000000/* * producer_xml.c -- a libxml2 parser of mlt service networks * Copyright (C) 2003-2020 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // TODO: destroy unreferenced producers (they are currently destroyed // when the returned producer is closed). #include "common.h" #include #include #include #include #include #include #include #include // for xmlCreateFileParserCtxt #include #define BRANCH_SIG_LEN 4000 #define _x (const xmlChar*) #define _s (const char*) #undef DEBUG #ifdef DEBUG extern xmlDocPtr xml_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, mlt_dummy_consumer_type }; struct deserialise_context_s { mlt_deque stack_types; mlt_deque stack_service; mlt_properties producer_map; mlt_properties destructors; char *property; int is_value; xmlDocPtr value_doc; mlt_deque stack_node; xmlDocPtr entity_doc; int entity_is_replace; mlt_deque stack_branch; const xmlChar *publicId; const xmlChar *systemId; mlt_properties params; mlt_profile profile; mlt_profile consumer_profile; int pass; char *lc_numeric; mlt_consumer consumer; int multi_consumer; int consumer_count; int seekable; mlt_consumer qglsl; }; typedef struct deserialise_context_s *deserialise_context; /** Trim the leading and trailing whitespace from a string in-place. */ static char* trim( char *s ) { int n; if ( s && ( n = strlen( s ) ) ) { int i = 0; while ( i < n && isspace( s[i] ) ) i++; while ( --n && isspace( s[n] ) ); n = n - i + 1; if ( n > 0 ) memmove( s, s + i, n ); s[ n ] = 0; } return s; } /** Convert the numerical current branch address to a dot-delimited string. */ static char *serialise_branch( deserialise_context context, char *s ) { int i, n = mlt_deque_count( context->stack_branch ); s[0] = 0; for ( i = 0; i < n - 1; i++ ) { int len = strlen( s ); snprintf( s + len, BRANCH_SIG_LEN - len, "%lu.", (unsigned long) mlt_deque_peek( context->stack_branch, i ) ); } return s; } /** Push a service. */ static void context_push_service( deserialise_context context, mlt_service that, enum service_type type ) { mlt_deque_push_back( context->stack_service, that ); mlt_deque_push_back_int( context->stack_types, type ); // Record the tree branch on which this service lives if ( that != NULL && mlt_properties_get( MLT_SERVICE_PROPERTIES( that ), "_xml_branch" ) == NULL ) { char s[ BRANCH_SIG_LEN ]; mlt_properties_set( MLT_SERVICE_PROPERTIES( that ), "_xml_branch", serialise_branch( context, s ) ); } } /** Pop a service. */ static mlt_service context_pop_service( deserialise_context context, enum service_type *type ) { mlt_service result = NULL; if ( type ) *type = mlt_invalid_type; if ( mlt_deque_count( context->stack_service ) > 0 ) { result = mlt_deque_pop_back( context->stack_service ); if ( type != NULL ) *type = mlt_deque_pop_back_int( context->stack_types ); // Set the service's profile and locale so mlt_property time-to-position conversions can get fps if ( result ) { mlt_properties_set_data( MLT_SERVICE_PROPERTIES( result ), "_profile", context->profile, 0, NULL, NULL ); mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( result ), context->lc_numeric ); } } return result; } /** Push a node. */ static void context_push_node( deserialise_context context, xmlNodePtr node ) { mlt_deque_push_back( context->stack_node, node ); } /** Pop a node. */ static xmlNodePtr context_pop_node( deserialise_context context ) { return mlt_deque_pop_back( context->stack_node ); } // 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 ); } static inline int is_known_prefix(const char* resource) { char *prefix = strchr(resource, ':'); if (prefix) { const char *whitelist[] = { "alsa", "avfoundation", "dshow", "fbdev", "gdigrab", "jack", "lavfi", "oss", "pulse", "sndio", "video4linux2", "v4l2", "x11grab", "async", "cache", "concat", "crypto", "data", "ffrtmphttp", "file", "ftp", "gopher", "hls", "http", "httpproxy", "mmsh", "mmst", "pipe", "rtmp", "rtmpt", "rtp", "srtp", "subfile", "tcp", "udp", "udplite", "unix" }; size_t i, n = prefix - resource; for (i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); ++i) { if (!strncmp(whitelist[i], resource, n)) return 1; } } return 0; } // Prepend the property value with the document root static inline void qualify_property( deserialise_context context, mlt_properties properties, const char *name ) { const char *resource_orig = mlt_properties_get( properties, name ); char *resource = mlt_properties_get( properties, name ); if ( resource != NULL && resource[0] ) { char *root = mlt_properties_get( context->producer_map, "root" ); int n = strlen( root ) + strlen( resource ) + 2; size_t prefix_size = mlt_xml_prefix_size( properties, name, resource ); // Strip off prefix. if ( prefix_size ) resource += prefix_size; // Qualify file name properties if ( root != NULL && strcmp( root, "" ) ) { char *full_resource = calloc( 1, n ); int drive_letter = strlen(resource) > 3 && resource[1] == ':' && (resource[2] == '/' || resource[2] == '\\'); if (resource[0] != '/' && resource[0] != '\\' && !drive_letter && !is_known_prefix(resource)) { if ( prefix_size ) strncat( full_resource, resource_orig, prefix_size ); strcat( full_resource, root ); strcat( full_resource, "/" ); strcat( full_resource, resource ); } else { strcpy( full_resource, resource_orig ); } 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 ), "_xml_branch" ); char *service_branch = mlt_properties_get( MLT_SERVICE_PROPERTIES( service ), "_xml_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; mlt_log_warning( NULL, "[producer_xml] 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 service, 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( service, filter ); attach_filters( MLT_FILTER_SERVICE( filter ), MLT_FILTER_SERVICE( filter ) ); } } } static void on_start_profile( deserialise_context context, const xmlChar *name, const xmlChar **atts) { mlt_profile p = context->profile; for ( ; atts != NULL && *atts != NULL; atts += 2 ) { if ( xmlStrcmp( atts[ 0 ], _x("name") ) == 0 || xmlStrcmp( atts[ 0 ], _x("profile") ) == 0 ) { mlt_profile my_profile = mlt_profile_init( _s(atts[ 1 ]) ); if ( my_profile ) { p->description = strdup( my_profile->description ); p->display_aspect_den = my_profile->display_aspect_den; p->display_aspect_num = my_profile->display_aspect_num; p->frame_rate_den = my_profile->frame_rate_den; p->frame_rate_num = my_profile->frame_rate_num; p->width = my_profile->width; p->height = my_profile->height; p->progressive = my_profile->progressive; p->sample_aspect_den = my_profile->sample_aspect_den; p->sample_aspect_num = my_profile->sample_aspect_num; p->colorspace = my_profile->colorspace; p->is_explicit = 1; mlt_profile_close( my_profile ); } } else if ( xmlStrcmp( atts[ 0 ], _x("description") ) == 0 ) { free( p->description ); p->description = strdup( _s(atts[ 1 ]) ); p->is_explicit = 1; } else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_den") ) == 0 ) p->display_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("display_aspect_num") ) == 0 ) p->display_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_num") ) == 0 ) p->sample_aspect_num = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("sample_aspect_den") ) == 0 ) p->sample_aspect_den = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("width") ) == 0 ) p->width = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("height") ) == 0 ) p->height = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("progressive") ) == 0 ) p->progressive = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_num") ) == 0 ) p->frame_rate_num = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("frame_rate_den") ) == 0 ) p->frame_rate_den = strtol( _s(atts[ 1 ]), NULL, 0 ); else if ( xmlStrcmp( atts[ 0 ], _x("colorspace") ) == 0 ) p->colorspace = strtol( _s(atts[ 1 ]), NULL, 0 ); } } 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 ); mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( service ), context->lc_numeric ); 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 { mlt_log_error( NULL, "[producer_xml] 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 ); mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( parent ), context->lc_numeric ); type = mlt_tractor_type; // Flag it as a synthesised tractor for clean up later mlt_properties_set_int( MLT_SERVICE_PROPERTIES( parent ), "loader_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 { mlt_log_error( NULL, "[producer_xml] 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 ) mlt_log_error( NULL, "[producer_xml] 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_new( context->profile ); 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, "_xml.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 = -1; mlt_position out = -1; if ( mlt_properties_get( properties, "in" ) ) in = mlt_properties_get_position( properties, "in" ); if ( mlt_properties_get( properties, "out" ) ) 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 { mlt_log_error( NULL, "[producer_xml] Invalid state of playlist end %d\n", type ); } } 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] ); } 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 *service_name = trim( mlt_properties_get( properties, "mlt_service" ) ); if ( resource ) { // If a document was saved as +INVALID.txt (see below), then ignore the mlt_service and // try to load it just from the resource. This is an attempt to recover the failed // producer in case, for example, a file returns. if (!strcmp("qtext", service_name)) { const char *text = mlt_properties_get( properties, "text" ); if (text && !strcmp("INVALID", text)) { service_name = NULL; } } else if (!strcmp("pango", service_name)) { const char *markup = mlt_properties_get( properties, "markup" ); if (markup && !strcmp("INVALID", markup)) { service_name = NULL; } } if (service_name) { char *temp = calloc( 1, strlen( service_name ) + strlen( resource ) + 2 ); strcat( temp, service_name ); strcat( temp, ":" ); strcat( temp, resource ); producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, temp ) ); free( temp ); } } else { producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, service_name ) ); } } // Just in case the plugin requested doesn't exist... if ( !producer && resource ) producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, resource ) ); if ( !producer ) { mlt_log_error( NULL, "[producer_xml] failed to load producer \"%s\"\n", resource ); producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "+INVALID.txt" ) ); if (producer) { // Save the original mlt_service for the consumer to serialize it as original. mlt_properties_set( MLT_SERVICE_PROPERTIES(producer), "_xml_mlt_service", mlt_properties_get( properties, "mlt_service" ) ); } } if ( !producer ) producer = MLT_SERVICE( mlt_factory_producer( context->profile, NULL, "colour:red" ) ); if ( !producer ) { mlt_service_close( service ); free( service ); return; } // Track this producer track_service( context->destructors, producer, (mlt_destructor) mlt_producer_close ); mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( producer ), context->lc_numeric ); if ( mlt_properties_get( MLT_SERVICE_PROPERTIES( producer ), "seekable" ) ) context->seekable &= mlt_properties_get_int( MLT_SERVICE_PROPERTIES( producer ), "seekable" ); // Propagate 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" ); qualify_property( context, properties, "argument" ); // timewarp producer // Handle in/out properties separately mlt_position in = -1; mlt_position out = -1; // Get in if ( mlt_properties_get( properties, "in" ) ) in = mlt_properties_get_position( properties, "in" ); // Let Kino-SMIL clipBegin be a synonym for in else if ( mlt_properties_get( properties, "clipBegin" ) ) in = mlt_properties_get_position( properties, "clipBegin" ); // Get out if ( mlt_properties_get( properties, "out" ) ) out = mlt_properties_get_position( properties, "out" ); // Let Kino-SMIL clipEnd be a synonym for out else if ( mlt_properties_get( properties, "clipEnd" ) ) out = mlt_properties_get_position( properties, "clipEnd" ); // Remove in and out mlt_properties_set( properties, "in", NULL ); mlt_properties_set( properties, "out", NULL ); // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set( properties, "mlt_type", NULL ); mlt_properties_set( properties, "mlt_service", 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 ) ) { if ( in > -1 ) mlt_properties_set_position( properties, "in", in ); if ( out > -1 ) 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 ); } } if ( service ) { mlt_service_close( service ); free( 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 ); 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 ) { // Append a blank to the playlist mlt_playlist_blank_time( MLT_PLAYLIST( service ), _s(atts[1]) ); break; } } // Push the playlist back onto the stack context_push_service( context, service, type ); } else { mlt_log_error( NULL, "[producer_xml] 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( ); mlt_properties_set_data( temp, "_profile", context->profile, 0, NULL, NULL ); mlt_properties_set_lcnumeric( temp, context->lc_numeric ); 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 = mlt_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 mlt_position in = -1; mlt_position out = -1; if ( mlt_properties_get( temp, "in" ) ) in = mlt_properties_get_position( temp, "in" ); if ( mlt_properties_get( temp, "out" ) ) out = mlt_properties_get_position( temp, "out" ); mlt_playlist_append_io( MLT_PLAYLIST( parent ), producer, in, out ); // 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 { mlt_log_error( NULL, "[producer_xml] 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 = mlt_invalid_type; mlt_service entry = context_pop_service( context, &entry_type ); if ( entry == NULL && entry_type != mlt_entry_type ) { mlt_log_error( NULL, "[producer_xml] 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 = mlt_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 mlt_log_error( NULL, "[producer_xml] 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_position in = -1; mlt_position out = -1; if ( mlt_properties_get( track_props, "in" ) ) in = mlt_properties_get_position( track_props, "in" ); if ( mlt_properties_get( track_props, "out" ) ) out = mlt_properties_get_position( track_props, "out" ); mlt_producer cut = mlt_producer_cut( MLT_PRODUCER( producer ), in, 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 ); } else { mlt_log_error( NULL, "[producer_xml] Invalid state at end of track\n" ); } if ( track ) { mlt_service_close( track ); free( track ); } } 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 = mlt_invalid_type; mlt_service parent = context_pop_service( context, &parent_type ); if ( service != NULL && type == mlt_dummy_filter_type ) { char *id = trim( mlt_properties_get( properties, "mlt_service" ) ); mlt_service filter = MLT_SERVICE( mlt_factory_filter( context->profile, id, NULL ) ); mlt_properties filter_props = MLT_SERVICE_PROPERTIES( filter ); if ( !filter ) { mlt_log_error( NULL, "[producer_xml] failed to load filter \"%s\"\n", id ); if ( parent ) context_push_service( context, parent, parent_type ); mlt_service_close( service ); free( service ); return; } track_service( context->destructors, filter, (mlt_destructor) mlt_filter_close ); mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( filter ), context->lc_numeric ); // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set( properties, "mlt_type", NULL ); mlt_properties_set( properties, "mlt_service", NULL ); // Propagate 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" ); qualify_property( context, properties, "filename" ); qualify_property( context, properties, "av.file" ); 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_properties_get( properties, "track" ) ) { 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 { mlt_log_error( NULL, "[producer_xml] filter closed with invalid parent...\n" ); } } else { mlt_log_error( NULL, "[producer_xml] Invalid top of stack on filter close\n" ); } if ( service ) { mlt_service_close( service ); free(service); } } 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 = mlt_invalid_type; mlt_service parent = context_pop_service( context, &parent_type ); if ( service != NULL && type == mlt_dummy_transition_type ) { char *id = trim( 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 ); if ( !effect ) { mlt_log_error( NULL, "[producer_xml] failed to load transition \"%s\"\n", id ); if ( parent ) context_push_service( context, parent, parent_type ); mlt_service_close( service ); free( service ); return; } track_service( context->destructors, effect, (mlt_destructor) mlt_transition_close ); mlt_properties_set_lcnumeric( MLT_SERVICE_PROPERTIES( effect ), context->lc_numeric ); // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set( properties, "mlt_type", NULL ); mlt_properties_set( properties, "mlt_service", NULL ); // Propagate 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 ) ); 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 { mlt_log_warning( NULL, "[producer_xml] Misplaced transition - ignoring\n" ); } // Put the parent back on the stack context_push_service( context, parent, parent_type ); } else { mlt_log_error( NULL, "[producer_xml] transition closed with invalid parent...\n" ); } } else { mlt_log_error( NULL, "[producer_xml] Invalid top of stack on transition close\n" ); } if ( service ) { mlt_service_close( service ); free( service ); } } static void on_start_consumer( deserialise_context context, const xmlChar *name, const xmlChar **atts) { if ( context->pass == 1 ) { mlt_properties properties = mlt_properties_new(); mlt_properties_set_lcnumeric( properties, context->lc_numeric ); context_push_service( context, (mlt_service) properties, mlt_dummy_consumer_type ); // Set the properties from attributes for ( ; atts != NULL && *atts != NULL; atts += 2 ) mlt_properties_set( properties, (const char*) atts[0], (const char*) atts[1] ); } } static void set_preview_scale(mlt_profile *consumer_profile, mlt_profile *profile, double scale) { *consumer_profile = mlt_profile_clone(*profile); if (*consumer_profile) { (*consumer_profile)->width *= scale; (*consumer_profile)->width -= (*consumer_profile)->width % 2; (*consumer_profile)->height *= scale; (*consumer_profile)->height -= (*consumer_profile)->height % 2; } } static void on_end_consumer( deserialise_context context, const xmlChar *name ) { if ( context->pass == 1 ) { // Get the consumer from the stack enum service_type type; mlt_properties properties = (mlt_properties) context_pop_service( context, &type ); if ( properties && type == mlt_dummy_consumer_type ) { qualify_property( context, properties, "resource" ); qualify_property( context, properties, "target" ); char *resource = mlt_properties_get( properties, "resource" ); if ( context->multi_consumer > 1 || context->qglsl || mlt_properties_get_int( context->params, "multi" ) ) { // Instantiate the multi consumer if ( !context->consumer ) { if ( context->qglsl ) context->consumer = context->qglsl; else context->consumer = mlt_factory_consumer( context->profile, "multi", NULL ); if ( context->consumer ) { // Track this consumer track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close ); mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric ); } } if ( context->consumer ) { // Set this properties object on multi consumer mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES(context->consumer); char key[20]; snprintf( key, sizeof(key), "%d", context->consumer_count++ ); mlt_properties_inc_ref( properties ); mlt_properties_set_data( consumer_properties, key, properties, 0, (mlt_destructor) mlt_properties_close, NULL ); // Pass in / out if provided mlt_properties_pass_list( consumer_properties, properties, "in, out" ); // Pass along quality and performance properties to the multi consumer and its render thread(s). if ( !context->qglsl ) { mlt_properties_pass_list( consumer_properties, properties, "real_time, deinterlace_method, rescale, progressive, top_field_first, channels, channel_layout" ); // We only really know how to optimize real_time for the avformat consumer. const char *service_name = mlt_properties_get( properties, "mlt_service" ); if ( service_name && !strcmp( "avformat", service_name ) ) mlt_properties_set_int( properties, "real_time", -1 ); } } } else { double scale = mlt_properties_get_double(properties, "scale"); if (scale > 0.0) { set_preview_scale(&context->consumer_profile, &context->profile, scale); } // Instantiate the consumer char *id = trim( mlt_properties_get( properties, "mlt_service" ) ); mlt_profile profile = context->consumer_profile? context->consumer_profile : context->profile; context->consumer = mlt_factory_consumer( profile, id, resource ); if ( context->consumer ) { // Track this consumer track_service( context->destructors, MLT_CONSUMER_SERVICE(context->consumer), (mlt_destructor) mlt_consumer_close ); mlt_properties_set_lcnumeric( MLT_CONSUMER_PROPERTIES(context->consumer), context->lc_numeric ); if (context->consumer_profile) { mlt_properties_set_data(MLT_CONSUMER_PROPERTIES(context->consumer), "_profile", context->consumer_profile, sizeof(*context->consumer_profile), (mlt_destructor) mlt_profile_close, NULL); } // Do not let XML overwrite these important properties set by mlt_factory. mlt_properties_set( properties, "mlt_type", NULL ); mlt_properties_set( properties, "mlt_service", NULL ); // Inherit the properties mlt_properties_inherit( MLT_CONSUMER_PROPERTIES(context->consumer), properties ); } } } // Close the dummy if ( properties ) mlt_properties_close( properties ); } } 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 { mlt_log_error( NULL, "[producer_xml] 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) ); #ifdef _WIN32 xmlFreeFunc xmlFree = NULL; xmlMemGet( &xmlFree, NULL, NULL, NULL); #endif 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 { mlt_log_error( NULL, "[producer_xml] 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 ); if ( context->pass == 0 ) { if ( xmlStrcmp( name, _x("mlt") ) == 0 || xmlStrcmp( name, _x("profile") ) == 0 || xmlStrcmp( name, _x("profileinfo") ) == 0 ) on_start_profile( context, name, atts ); if ( xmlStrcmp( name, _x("consumer") ) == 0 ) context->multi_consumer++; // Check for a service beginning with glsl. or movit. for ( ; atts != NULL && *atts != NULL; atts += 2 ) { if ( !xmlStrncmp( atts[1], _x("glsl."), 5 ) || !xmlStrncmp( atts[1], _x("movit."), 6 ) ) { mlt_properties_set_int( context->params, "qglsl", 1 ); break; } } return; } mlt_deque_push_back_int( context->stack_branch, mlt_deque_pop_back_int( context->stack_branch ) + 1 ); mlt_deque_push_back_int( context->stack_branch, 0 ); // Build a tree from nodes within a property value if ( context->is_value == 1 && context->pass == 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( mlt_deque_peek_back( context->stack_node ), 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("consumer") ) == 0 ) on_start_consumer( context, name, atts ); else if ( xmlStrcmp( name, _x("westley") ) == 0 || xmlStrcmp( name, _x("mlt") ) == 0 ) { for ( ; atts != NULL && *atts != NULL; atts += 2 ) { if ( xmlStrcmp( atts[0], _x("LC_NUMERIC") ) ) mlt_properties_set( context->producer_map, _s( atts[0] ), _s(atts[1] ) ); else if ( !context->lc_numeric ) context->lc_numeric = strdup( _s( 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 ); if ( context->is_value == 1 && context->pass == 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 ); else if ( xmlStrcmp( name, _x("consumer") ) == 0 ) on_end_consumer( context, name ); mlt_deque_pop_back_int( context->stack_branch ); } 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( 1, len + 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 ( mlt_deque_count( context->stack_node ) ) xmlNodeAddContent( mlt_deque_peek_back( context->stack_node ), ( 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( 1, strlen( s ) + len + 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; // Check for a service beginning with glsl. or movit. if ( !strncmp( value, "glsl.", 5 ) || !strncmp( value, "movit.", 6 ) ) mlt_properties_set_int( context->params, "qglsl", 1 ); 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 entity 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 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("mlt"), _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; } static void on_error( void * ctx, const char * msg, ... ) { struct _xmlError* err_ptr = xmlCtxtGetLastError( ctx ); switch( err_ptr->level ) { case XML_ERR_WARNING: mlt_log_warning( NULL, "[producer_xml] parse warning: %s\trow: %d\tcol: %d\n", err_ptr->message, err_ptr->line, err_ptr->int2 ); break; case XML_ERR_ERROR: mlt_log_error( NULL, "[producer_xml] parse error: %s\trow: %d\tcol: %d\n", err_ptr->message, err_ptr->line, err_ptr->int2 ); break; default: case XML_ERR_FATAL: mlt_log_fatal( NULL, "[producer_xml] parse fatal: %s\trow: %d\tcol: %d\n", err_ptr->message, err_ptr->line, err_ptr->int2 ); break; } } /** 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; int is_query = 0; for ( i = 0; i < n; i++ ) { switch ( url[ i ] ) { case '?': url[ i++ ] = '\0'; name = &url[ i ]; is_query = 1; break; case ':': #ifdef _WIN32 if ( url[i + 1] != '/' && url[i + 1] != '\\' ) #endif case '=': if ( is_query ) { url[ i++ ] = '\0'; value = &url[ i ]; } break; case '&': if ( is_query ) { 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 unnecessary libxml2 warnings static int file_exists( char *name ) { int exists = 0; if ( name != NULL ) { FILE *f = mlt_fopen( name, "r" ); exists = f != NULL; if ( exists ) fclose( f ); } return exists; } // This function will add remaining services in the context service stack marked // with a "xml_retain" property to a property named "xml_retain" on the returned // service. The property is a mlt_properties data property. static void retain_services( struct deserialise_context_s *context, mlt_service service ) { mlt_properties retain_list = mlt_properties_new(); enum service_type type; mlt_service retain_service = context_pop_service( context, &type ); while ( retain_service ) { mlt_properties retain_properties = MLT_SERVICE_PROPERTIES( retain_service ); if ( mlt_properties_get_int( retain_properties, "xml_retain" ) ) { // Remove the retained service from the destructors list. int i; for ( i = mlt_properties_count( context->destructors ) - 1; i >= 1; i -- ) { const char *name = mlt_properties_get_name( context->destructors, i ); if ( mlt_properties_get_data_at( context->destructors, i, NULL ) == retain_service ) { mlt_properties_set_data( context->destructors, name, retain_service, 0, NULL, NULL ); break; } } const char *name = mlt_properties_get( retain_properties, "id" ); if ( name ) mlt_properties_set_data( retain_list, name, retain_service, 0, (mlt_destructor) mlt_service_close, NULL ); } retain_service = context_pop_service( context, &type ); } if ( mlt_properties_count( retain_list ) > 0 ) { mlt_properties_set_data( MLT_SERVICE_PROPERTIES(service), "xml_retain", retain_list, 0, (mlt_destructor) mlt_properties_close, NULL ); } else { mlt_properties_close( retain_list ); } } static deserialise_context context_new( mlt_profile profile ) { deserialise_context context = calloc( 1, sizeof( struct deserialise_context_s ) ); if ( context ) { context->producer_map = mlt_properties_new(); context->destructors = mlt_properties_new(); context->params = mlt_properties_new(); context->profile = profile; context->seekable = 1; context->stack_service = mlt_deque_init(); context->stack_types = mlt_deque_init(); context->stack_node = mlt_deque_init(); context->stack_branch = mlt_deque_init(); mlt_deque_push_back_int( context->stack_branch, 0 ); } return context; } static void context_close( deserialise_context context ) { mlt_properties_close( context->producer_map ); mlt_properties_close( context->destructors ); mlt_properties_close( context->params ); mlt_deque_close( context->stack_service ); mlt_deque_close( context->stack_types ); mlt_deque_close( context->stack_node ); mlt_deque_close( context->stack_branch ); xmlFreeDoc( context->entity_doc ); free( context->lc_numeric ); free( context ); } mlt_producer producer_xml_init( mlt_profile profile, mlt_service_type servtype, const char *id, char *data ) { xmlSAXHandler *sax, *sax_orig; deserialise_context context; mlt_properties properties = NULL; int i = 0; struct _xmlParserCtxt *xmlcontext; int well_formed = 0; char *filename = NULL; int is_filename = strcmp( id, "xml-string" ); // Strip file:// prefix if ( data && strlen( data ) >= 7 && strncmp( data, "file://", 7 ) == 0 ) data += 7; if ( data == NULL || !strcmp( data, "" ) ) return NULL; context = context_new( profile ); if ( context == NULL ) return NULL; // Decode URL and parse parameters mlt_properties_set( context->producer_map, "root", "" ); if ( is_filename ) { mlt_properties_set( context->params, "_mlt_xml_resource", data ); filename = mlt_properties_get( context->params, "_mlt_xml_resource" ); parse_url( context->params, url_decode( filename, data ) ); // We need the directory prefix which was used for the xml if ( strchr( filename, '/' ) || strchr( filename, '\\' ) ) { char *root = NULL; mlt_properties_set( context->producer_map, "root", filename ); root = mlt_properties_get( context->producer_map, "root" ); if ( strchr( root, '/') ) *( strrchr( root, '/' ) ) = '\0'; else if ( strchr( root, '\\') ) *( strrchr( root, '\\' ) ) = '\0'; // If we don't have an absolute path here, we're heading for disaster... if ( root[ 0 ] != '/' && !strchr( root, ':' ) ) { 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 ); } } if ( !file_exists( filename ) ) { // Try the un-converted text encoding as a fallback. // Fixes launching melt as child process from Shotcut on Windows // when there are extended characters in the path. filename = mlt_properties_get( context->params, "_mlt_xml_resource" ); } if ( !file_exists( filename ) ) { context_close( context ); return NULL; } } // We need to track the number of registered filters mlt_properties_set_int( context->destructors, "registered", 0 ); // Setup SAX callbacks for first pass sax = calloc( 1, sizeof( xmlSAXHandler ) ); sax->startElement = on_start_element; sax->characters = on_characters; sax->warning = on_error; sax->error = on_error; sax->fatalError = on_error; // 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 ( is_filename ) xmlcontext = xmlCreateFileParserCtxt( filename ); else xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) ); // Invalid context - clean up and return NULL if ( xmlcontext == NULL ) { context_close( context ); free( sax ); return NULL; } // Parse sax_orig = xmlcontext->sax; xmlcontext->sax = sax; xmlcontext->_private = ( void* )context; xmlParseDocument( xmlcontext ); well_formed = xmlcontext->wellFormed; // Cleanup after parsing xmlcontext->sax = sax_orig; xmlcontext->_private = NULL; if ( xmlcontext->myDoc ) xmlFreeDoc( xmlcontext->myDoc ); xmlFreeParserCtxt( xmlcontext ); // Bad xml - clean up and return NULL if ( !well_formed ) { context_close( context ); free( sax ); return NULL; } // Setup the second pass context->pass ++; if ( is_filename ) xmlcontext = xmlCreateFileParserCtxt( filename ); else xmlcontext = xmlCreateMemoryParserCtxt( data, strlen( data ) ); // Invalid context - clean up and return NULL if ( xmlcontext == NULL ) { context_close( context ); free( sax ); return NULL; } // Reset the stack. mlt_deque_close( context->stack_service ); mlt_deque_close( context->stack_types ); mlt_deque_close( context->stack_node ); context->stack_service = mlt_deque_init(); context->stack_types = mlt_deque_init(); context->stack_node = mlt_deque_init(); // Create the qglsl consumer now, if requested, so that glsl.manager // may exist when trying to load glsl. or movit. services. // The "if requested" part can come from query string qglsl=1 or when // a service beginning with glsl. or movit. appears in the XML. if ( mlt_properties_get_int( context->params, "qglsl" ) && strcmp( id, "xml-nogl" ) // Only if glslManager does not yet exist. && !mlt_properties_get_data( mlt_global_properties(), "glslManager", NULL ) ) context->qglsl = mlt_factory_consumer( profile, "qglsl", NULL ); // Setup SAX callbacks for second pass sax->endElement = on_end_element; sax->cdataBlock = on_characters; sax->internalSubset = on_internal_subset; sax->entityDecl = on_entity_declaration; sax->getEntity = on_get_entity; // Parse sax_orig = xmlcontext->sax; xmlcontext->sax = sax; xmlcontext->_private = ( void* )context; xmlParseDocument( xmlcontext ); well_formed = xmlcontext->wellFormed; // Cleanup after parsing xmlFreeDoc( context->entity_doc ); context->entity_doc = NULL; free( sax ); xmlMemoryDump( ); // for debugging xmlcontext->sax = sax_orig; xmlcontext->_private = NULL; if ( xmlcontext->myDoc ) xmlFreeDoc( xmlcontext->myDoc ); xmlFreeParserCtxt( xmlcontext ); // 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 = xml_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_at( properties, i, 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_XML_DEEP" ) == NULL ) { // Now assign additional properties if ( is_filename && ( mlt_service_identify( service ) == tractor_type || mlt_service_identify( service ) == playlist_type || mlt_service_identify( service ) == multitrack_type ) ) { mlt_properties_set_int( properties, "_original_type", mlt_service_identify( service ) ); mlt_properties_set( properties, "_original_resource", mlt_properties_get( properties, "resource" ) ); mlt_properties_set( properties, "resource", data ); } // This tells consumer_xml not to deep copy mlt_properties_set( properties, "xml", "was here" ); } else { // Allow the project to be edited mlt_properties_set( properties, "_xml", "was here" ); mlt_properties_set_int( properties, "_mlt_service_hidden", 1 ); } // Make consumer available mlt_properties_inc_ref( MLT_CONSUMER_PROPERTIES( context->consumer ) ); mlt_properties_set_data( properties, "consumer", context->consumer, 0, (mlt_destructor) mlt_consumer_close, NULL ); mlt_properties_set_int( properties, "seekable", context->seekable ); retain_services( context, service ); } else { // Return null if not well formed service = NULL; } // Clean up if ( context->qglsl && context->consumer != context->qglsl ) mlt_consumer_close( context->qglsl ); context_close( context ); return MLT_PRODUCER( service ); } mlt-6.20.0/src/modules/xml/producer_xml.yml000066400000000000000000000020441362234133600206630ustar00rootroot00000000000000schema_version: 0.1 type: producer identifier: xml title: XML File version: 1 copyright: Meltytech, LLC creator: Dan Dennedy license: LGPLv2.1 language: en tags: - Audio - Video description: | Construct a service network from an XML description. See docs/mlt-xml.txt. notes: > If there is a service with a property "xml_retain=1" that is not the producer, and if it also has an "id" property; then the extra service is put into a properties list keyed on the id property. Then, that properties list is placed as a property on the returned service with the name "xml_retain". This lets an application retrieve additional deserialized services that are not the lastmost producer or anywhere in its graph. bugs: - This producer is not thread-safe during its construction because it may modify the mlt_profile, even if is_explicit is set. parameters: - identifier: argument title: File type: string description: An XML text file containing MLT XML. readonly: no required: yes mutable: no widget: fileopen mlt-6.20.0/src/swig/000077500000000000000000000000001362234133600141365ustar00rootroot00000000000000mlt-6.20.0/src/swig/Makefile000066400000000000000000000011401362234133600155720ustar00rootroot00000000000000include ../../config.mak include config.mak all clean: list='$(SUBDIRS)'; \ for subdir in $$list; do \ if [ -x $$subdir/build -a ! -f .$$subdir -o $@ = clean ] ; \ then echo -n Building $$subdir... ; \ cd $$subdir && CXXFLAGS="$(CXXFLAGS)" ./build $@ 2>&1; \ if [ $$? -eq 0 ] ; \ then echo OK && touch ../.$$subdir ; \ else exit 1 ; \ fi ; \ cd .. ; \ if [ -f $$subdir/Makefile -a -f .$$subdir ] ; \ then $(MAKE) -C $$subdir $@ || exit 1 ; \ fi ; \ if [ $@ = clean ] ; \ then rm -f .$$subdir ; \ fi ; \ fi \ done distclean: clean depend: install: uninstall: mlt-6.20.0/src/swig/configure000077500000000000000000000014001362234133600160400ustar00rootroot00000000000000#!/bin/sh if [ "$help" = "1" ] then cat << EOF SWIG options: --swig-languages=[all | [csharp | java | lua | perl | php | python | ruby | tcl]*] - High level language bindings (default: none) EOF else languages="" touch config.mak # Iterate through arguments for i in "$@" do case $i in --swig-languages=* ) languages=${i#--swig-languages=} [ "$languages" = "none" ] && languages="" if [ -z "$languages" ]; then echo SUBDIRS = $languages > config.mak continue fi which swig > /dev/null 2>&1 [ $? != 0 ] && echo "Please install swig" && exit 1 [ "$languages" = "all" ] && languages="csharp java lua perl php python ruby tcl" echo SUBDIRS = $languages > config.mak ;; esac done fi mlt-6.20.0/src/swig/csharp/000077500000000000000000000000001362234133600154165ustar00rootroot00000000000000mlt-6.20.0/src/swig/csharp/build000077500000000000000000000016651362234133600164530ustar00rootroot00000000000000#!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -rf *.cxx *.snk *.so *.o *.exe *.dll mlt.i ../.cs src_swig ) exit 0 fi path=`which mcs 2> /dev/null` if [ $? = 0 ] then ln -sf ../mlt.i # Invoke swig mkdir src_swig swig -c++ -I../../mlt++ -I../.. -csharp -dllimport libmltsharp -outdir src_swig -namespace Mlt mlt.i || exit $? # Compile the wrapper ${CXX} -fPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. mlt_wrap.cxx || exit $? # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o libmltsharp.so || exit $? # Compile the library assembly mcs -out:mlt-sharp.dll -target:library src_swig/*.cs # uncomment the below if you want to sign the assembly # sn -k mlt-sharp.snk # mcs -out:mlt-sharp.dll -target:library -keyfile:mlt-sharp.snk src_swig/*.cs # Compile the example mcs -r:mlt-sharp.dll play.cs else echo Mono C# compiler not installed. exit 1 fi mlt-6.20.0/src/swig/csharp/play.cs000066400000000000000000000007221362234133600167130ustar00rootroot00000000000000using System; using System.Threading; using Mlt; public class Play { public static void Main(String[] args) { Console.WriteLine("Welcome to MLT."); Factory.init(); Profile profile = new Profile(""); Producer p = new Producer(profile, args[0], null); if (p.is_valid()) { Consumer c = new Consumer(profile, "sdl", null); c.set("rescale", "none"); c.connect(p); c.start(); while (!c.is_stopped()) Thread.Sleep(300); c.stop(); } } } mlt-6.20.0/src/swig/csharp/play.sh000077500000000000000000000000351362234133600167200ustar00rootroot00000000000000#!/bin/sh mono play.exe "$@" mlt-6.20.0/src/swig/java/000077500000000000000000000000001362234133600150575ustar00rootroot00000000000000mlt-6.20.0/src/swig/java/Play.java000066400000000000000000000017361362234133600166360ustar00rootroot00000000000000import org.mltframework.*; public class Play { static { System.loadLibrary("mlt_java"); } public static void main (String[] args) { // Start the mlt system Factory.init( null ); // Set the output profile Profile profile = new Profile( "" ); // Create the producer Producer p = new Producer( profile, args[0], null ); if ( p.is_valid() ) { p.set ("eof", "loop"); // Create the consumer Consumer c = new Consumer( profile, "sdl", null); // Turn off the default rescaling c.set("rescale", "none"); // Connect the producer to the consumer c.connect(p); // Start the consumer c.start(); // Wait until the user stops the consumer Object o = new Object(); while ( !c.is_stopped() ) { synchronized (o) { try { o.wait(1000); } catch (InterruptedException e) { // ignored } } } // Stop it anyway c.stop(); } else { System.out.println ("Unable to open " + args[0]); } } } mlt-6.20.0/src/swig/java/Play.sh000077500000000000000000000000761362234133600163260ustar00rootroot00000000000000#!/bin/sh java -Djava.library.path=. -cp .:src_swig Play "$@" mlt-6.20.0/src/swig/java/build000077500000000000000000000017011362234133600161030ustar00rootroot00000000000000#!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -rf *.cxx *.so *.o mlt.i ../.java *.class src_swig ) exit 0 fi path=`which java 2> /dev/null` if [ $? = 0 ] then # Locate the path for the include path=`dirname $path` path=`dirname $path` # Change this as needed # export JAVA_INCLUDE="-I$path/include -I$path/include/linux" ln -sf ../mlt.i # Invoke swig mkdir -p src_swig/org/mltframework swig -c++ -I../../mlt++ -I../.. -java -outdir src_swig/org/mltframework -package org.mltframework mlt.i || exit $? # Compile the wrapper ${CXX} -fPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. mlt_wrap.cxx $JAVA_INCLUDE || exit $? # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o libmlt_java.so || exit $? # Compile the test javac `find src_swig -name '*.java'` || exit $? export CLASSPATH=`pwd`/src_swig javac Play.java else echo "Java command not found" exit 1 fi mlt-6.20.0/src/swig/lua/000077500000000000000000000000001362234133600147175ustar00rootroot00000000000000mlt-6.20.0/src/swig/lua/build000077500000000000000000000010231362234133600157400ustar00rootroot00000000000000#!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.lua ) exit 0 fi path=`which lua 2> /dev/null` if [ $? = 0 ] then ln -sf ../mlt.i # Invoke swig swig -c++ -I../../mlt++ -I../.. -lua mlt.i || exit $? # Compile the wrapper ${CXX} -fPIC -DPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. mlt_wrap.cxx || exit $? # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o mlt.so || exit $? else echo Lua not installed. exit 1 fi mlt-6.20.0/src/swig/lua/play.lua000077500000000000000000000007311362234133600163730ustar00rootroot00000000000000#!/usr/bin/env lua require("mlt") mlt.Factory_init() profile = mlt.Profile() producer = mlt.Producer( profile, arg[1] ) if producer:is_valid() then consumer = mlt.Consumer( profile, "sdl" ) consumer:set( "rescale", "none" ) consumer:set( "terminate_on_pause", 1 ) consumer:connect( producer ) event = consumer:setup_wait_for( "consumer-stopped" ) consumer:start() consumer:wait_for( event ) else print( "Unable to open "..arg[1] ) end mlt.Factory_close() mlt-6.20.0/src/swig/mlt.i000066400000000000000000000142171362234133600151110ustar00rootroot00000000000000/** * mlt.i - Swig Bindings for mlt++ * Copyright (C) 2004-2015 Meltytech, LLC * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ %module mlt %include "carrays.i" %array_class(unsigned char, UnsignedCharArray); %{ #include int mlt_log_get_level( void ); void mlt_log_set_level( int ); %} /** These methods return objects which should be gc'd. */ namespace Mlt { %newobject Factory::init( const char * ); %newobject Factory::producer( Profile &, char *, char * ); %newobject Factory::filter( Profile &, char *, char * ); %newobject Factory::transition( Profile &, char *, char * ); %newobject Factory::consumer( Profile &, char *, char * ); %newobject Properties::listen( const char *, void *, mlt_listener ); %newobject Properties::setup_wait_for( const char * ); %newobject Properties::parse_yaml( const char * ); %newobject Service::producer( ); %newobject Service::consumer( ); %newobject Service::get_frame( int ); %newobject Service::filter( int ); %newobject Producer::filter( int ); %newobject Producer::cut( int, int ); %newobject Playlist::current( ); %newobject Playlist::clip_info( int ); %newobject Playlist::get_clip( int ); %newobject Multitrack::track( int ); %newobject Tractor::multitrack( ); %newobject Tractor::field( ); %newobject Tractor::track( int ); %newobject Frame::get_original_producer( ); %newobject Repository::consumers( ); %newobject Repository::filters( ); %newobject Repository::producers( ); %newobject Repository::transitions( ); %newobject Repository::metadata( mlt_service_type, const char * ); %newobject Repository::languages( ); %newobject Profile::list(); %newobject Repository::presets(); %newobject Properties::get_anim(); %newobject Animation::Animation(); %rename(__assign__) Animation::operator=; %rename(__assign__) Frame::operator=; #if defined(SWIGPYTHON) %feature("shadow") Frame::get_waveform(int, int) %{ def get_waveform(*args): return _mlt.frame_get_waveform(*args) %} %feature("shadow") Frame::get_image(mlt_image_format&, int&, int&) %{ def get_image(*args): return _mlt.frame_get_image(*args) %} #endif } /** Classes to wrap. */ %include %include %include int mlt_log_get_level( void ); void mlt_log_set_level( int ); %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include %include #if defined(SWIGRUBY) %{ static void ruby_listener( mlt_properties owner, void *object ); class RubyListener { protected: VALUE callback; Mlt::Event *event; public: RubyListener( VALUE callback ) : callback( callback ) {} RubyListener( Mlt::Properties &properties, char *id, VALUE callback ) : callback( callback ) { event = properties.listen( id, this, ( mlt_listener )ruby_listener ); } virtual ~RubyListener( ) { delete event; } void mark( ) { ((void (*)(VALUE))(rb_gc_mark))( callback ); } void doit( ) { ID method = rb_intern( "call" ); rb_funcall( callback, method, 0 ); } }; static void ruby_listener( mlt_properties owner, void *object ) { RubyListener *o = static_cast< RubyListener * >( object ); o->doit( ); } void markRubyListener( void* p ) { RubyListener *o = static_cast( p ); o->mark( ); } static void on_playlist_next( mlt_properties owner, void *object, int i ); class PlaylistNextListener : RubyListener { private: Mlt::Event *event; public: PlaylistNextListener( Mlt::Properties *properties, VALUE proc ) : RubyListener( proc ) { event = properties->listen( "playlist-next", this, ( mlt_listener )on_playlist_next ); } ~PlaylistNextListener() { delete event; } void yield( int i ) { ID method = rb_intern( "call" ); rb_funcall( callback, method, 1, INT2FIX( i ) ); } }; static void on_playlist_next( mlt_properties owner, void *object, int i ) { PlaylistNextListener *o = static_cast< PlaylistNextListener * >( object ); o->yield( i ); } %} // Ruby wrapper %rename( Listener ) RubyListener; %markfunc RubyListener "markRubyListener"; %markfunc PlaylistNextListener "markRubyListener"; class RubyListener { public: RubyListener( Mlt::Properties &properties, char *id, VALUE callback ); }; class PlaylistNextListener { public: PlaylistNextListener( Mlt::Properties *properties, VALUE proc ); }; #endif // SWIGGRUBY #if defined(SWIGPYTHON) %{ typedef struct { int size; char* data; } binary_data; binary_data frame_get_waveform( Mlt::Frame &frame, int w, int h ) { binary_data result = { w * h, (char*) frame.get_waveform( w, h ) }; return result; } binary_data frame_get_image( Mlt::Frame &frame, mlt_image_format format, int w, int h ) { binary_data result = { mlt_image_format_size( format, w, h, NULL ), (char*) frame.get_image( format, w, h ) }; return result; } %} %typemap(out) binary_data { $result = %#if PY_MAJOR_VERSION < 3 PyString_FromStringAndSize( %#else PyByteArray_FromStringAndSize( %#endif $1.data, $1.size ); } binary_data frame_get_waveform(Mlt::Frame&, int, int); binary_data frame_get_image(Mlt::Frame&, mlt_image_format, int, int); #endif mlt-6.20.0/src/swig/nodejs/000077500000000000000000000000001362234133600154205ustar00rootroot00000000000000mlt-6.20.0/src/swig/nodejs/apply-filter.js000066400000000000000000000020031362234133600203610ustar00rootroot00000000000000#!/usr/bin/env node const path = require('path') const mlt = require('./node-gyp-build/Release/mlt.node') const { Factory, Profile, Producer, Filter, Consumer } = mlt; Factory.init('') let profile = new Profile('hdv_720_25p') let producer = new Producer(profile, 'avformat', process.argv[2]) if (!producer.is_valid()) { console.log(`Unable to open producer`) process.exit(1) } producer.set_in_and_out(100, 200) let videoOutputFilePath = path.join(__dirname, 'output.mp4') console.log('output:', videoOutputFilePath) let consumer = new Consumer(profile, 'avformat', videoOutputFilePath) consumer.set('rescale', 'none') consumer.set('vcodec', 'libx264') consumer.set('acodec', 'aac') let filter = new Filter(profile, 'charcoal') producer.attach(filter) consumer.connect(producer) consumer.start() check() function check() { process.stdout.write('.') if (consumer.is_stopped()) { consumer.stop() Factory.close() return } setTimeout(check, 1000) } mlt-6.20.0/src/swig/nodejs/binding.gyp000066400000000000000000000003051362234133600175510ustar00rootroot00000000000000{ "targets": [{ "target_name": "mlt", "sources": [ "mlt_wrap.cxx" ], "include_dirs": [ "../.." ], "libraries": [ "-L../../../mlt++ -lmlt++" ] }] } mlt-6.20.0/src/swig/nodejs/build000077500000000000000000000007261362234133600164520ustar00rootroot00000000000000#!/bin/sh CXX=${CXX:-g++} gypBuildDir="node-gyp-build" if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.nodejs; rm -r "$gypBuildDir" ) exit 0 fi which node-gyp 2> /dev/null if [ $? = 0 ] then ln -sf ../mlt.i # Invoke swig swig -c++ -I../../mlt++ -I../.. -javascript -node mlt.i || exit $? # Compile the wrapper NODE_GYP_BUILD_DIR="$gypBuildDir" node-gyp configure build || exit $? else echo node-gyp not installed. exit 1 fi mlt-6.20.0/src/swig/nodejs/list-members.js000066400000000000000000000011551362234133600203630ustar00rootroot00000000000000#!/usr/bin/env node const mlt = require('./node-gyp-build/Release/mlt.node') const members = Object.keys(mlt) const maxKeyLength = members.reduce((max, key) => key.length > max ? key.length : max, 0) const output = members.map(member => [member, mlt[member]]) .map(([key, val]) => `${padRight(key, maxKeyLength)}\t${isFunction(val) ? '[function]' : val}`) .join('\n') console.log(output) function padRight(str, len, char = ' ') { while (str.length < len) { str += char } return str; } function isFunction(val) { return typeof val === 'function' } function entries(obj) { return } mlt-6.20.0/src/swig/nodejs/play.js000066400000000000000000000014421362234133600167240ustar00rootroot00000000000000#!/usr/bin/env node const path = require('path') const mlt = require('./node-gyp-build/Release/mlt.node') const { Factory, Profile, Producer, Consumer } = mlt; const videoFile = process.argv[2] console.log(videoFile) Factory.init('') let profile = new Profile('hdv_720_25p') let producer = new Producer(profile, 'avformat', videoFile) if (!producer.is_valid()) { console.log(`Unable to open producer`) process.exit(1) } console.log('get_fps', producer.get_fps()) let consumer = new Consumer(profile, 'sdl') consumer.connect(producer) consumer.start() check() function check() { console.log('is_stopped', consumer.is_stopped()) if (consumer.is_stopped()) { consumer.stop() Factory.close() return } setTimeout(check, 1000) } mlt-6.20.0/src/swig/perl/000077500000000000000000000000001362234133600151005ustar00rootroot00000000000000mlt-6.20.0/src/swig/perl/Makefile.PL000066400000000000000000000006451362234133600170570ustar00rootroot00000000000000#!/bin/env perl use ExtUtils::MakeMaker; my $CXX = $ENV{'CXX'} || 'g++'; system( "ln -sf ../mlt.i" ); system( "swig -c++ -I../../mlt++ -I../.. -perl5 mlt.i" ); WriteMakefile( 'NAME' => 'mlt', 'CC' => '${CXX} -fPIC ${CXXFLAGS} -I../..', 'OPTIMIZE' => '-O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions', 'LIBS' => ['-L../../mlt++ -lmlt++'], 'OBJECT' => 'mlt_wrap.o', 'DESTDIR' => $ENV{'DESTDIR'}, ); mlt-6.20.0/src/swig/perl/build000077500000000000000000000002501362234133600161220ustar00rootroot00000000000000#!/bin/sh if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.perl mlt.pm ) exit 0 fi CXXFLAGS="$CXXFLAGS" perl Makefile.PL || exit 1 make mlt-6.20.0/src/swig/perl/play.pl000077500000000000000000000014571362234133600164140ustar00rootroot00000000000000#!/usr/bin/env perl # Import required modules use mlt; # Not sure why the mlt::Factory.init method fails... mlt::mlt_factory_init( undef ); # Establish the MLT profile $profile = new mlt::Profile( undef ); # Create the producer $p = new mlt::Producer( $profile, $ARGV[0] ); if ( $p->is_valid( ) ) { # Loop the video $p->set( "eof", "loop" ); # Create the consumer $c = new mlt::FilteredConsumer( $profile, "sdl" ); # Turn of the default rescaling $c->set( "rescale", "none" ); # Connect the producer to the consumer $c->connect( $p ); $e = $c->setup_wait_for( "consumer-stopped" ); # Start the consumer $c->start; # Wait until the user stops the consumer $c->wait_for( $e ); $e = undef; $c = undef; $p = undef; } else { print "Unable to open $ARGV[0]\n"; } mlt::mlt_factory_close( ); mlt-6.20.0/src/swig/php/000077500000000000000000000000001362234133600147255ustar00rootroot00000000000000mlt-6.20.0/src/swig/php/build000077500000000000000000000006171362234133600157560ustar00rootroot00000000000000#!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cpp *.so *.o mlt.i ../.php mlt.php *.h ) exit 0 fi ln -sf ../mlt.i swig -c++ -I../../mlt++ -I../.. -php5 -noproxy mlt.i ${CXX} -fPIC -DPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. `php-config --includes` mlt_wrap.cpp ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o mlt.so || exit $? mlt-6.20.0/src/swig/php/play.php000077500000000000000000000006661362234133600164160ustar00rootroot00000000000000 mlt-6.20.0/src/swig/python/000077500000000000000000000000001362234133600154575ustar00rootroot00000000000000mlt-6.20.0/src/swig/python/build000077500000000000000000000014211362234133600165020ustar00rootroot00000000000000#!/bin/sh CXX=${CXX:-g++} PYTHON=${PYTHON:-python3} path=`which "$PYTHON" 2> /dev/null` if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.python mlt.py* ) exit 0 fi if [ $? = 0 ] then # Change this as needed export PYTHON_INCLUDE="$("${PYTHON}-config" --includes)" [ -z "$PYTHON_INCLUDE" ] && echo "$PYTHON" development missing && exit 1 ln -sf ../mlt.i # Invoke swig swig -c++ -I../../mlt++ -I../.. -python mlt.i || exit $? # Compile the wrapper ${CXX} -fPIC -D_GNU_SOURCE ${CXXFLAGS} -c -I../.. $PYTHON_INCLUDE mlt_wrap.cxx || exit $? # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -L../../framework -lmlt $("${PYTHON}-config" --ldflags) -o _mlt.so || exit $? else echo Python not installed. exit 1 fi mlt-6.20.0/src/swig/python/codecs.py000077500000000000000000000010031362234133600172660ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Import required modules from __future__ import print_function import mlt # Start the mlt system mlt.Factory().init( ) # Create the consumer c = mlt.Consumer( mlt.Profile(), "avformat" ) # Ask for video codecs supports c.set( 'vcodec', 'list' ) # Start the consumer to generate the list c.start() # Get the vcodec property codecs = mlt.Properties( c.get_data( 'vcodec' ) ) # Print the list of codecs for i in range( 0, codecs.count()): print(codecs.get( i )) mlt-6.20.0/src/swig/python/getimage.py000077500000000000000000000015371362234133600176240ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- import mlt import sys from PIL import Image # setup mlt.Factory.init() profile = mlt.Profile('square_pal_wide') prod = mlt.Producer(profile, sys.argv[1]) # This builds a profile from the attributes of the producer: auto-profile. profile.from_producer(prod) # Ensure the image is square pixels - optional. profile.set_width(int(profile.width() * profile.sar())) profile.set_sample_aspect(1, 0) # Seek to 10% and get a Mlt frame. prod.seek(int(prod.get_length() * 0.1)) frame = prod.get_frame() # And make sure we deinterlace if input is interlaced - optional. frame.set("consumer_deinterlace", 1) # Now we are ready to get the image and save it. size = (profile.width(), profile.height()) rgb = frame.get_image(mlt.mlt_image_rgb24, *size) img = Image.fromstring('RGB', size, rgb) img.save(sys.argv[1] + '.png') mlt-6.20.0/src/swig/python/play.py000077500000000000000000000012511362234133600170000ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Import required modules from __future__ import print_function import mlt import time import sys # Start the mlt system mlt.Factory().init( ) # Establish a profile profile = mlt.Profile( ) # Create the producer p = mlt.Producer( profile, sys.argv[1] ) if p: # Create the consumer c = mlt.Consumer( profile, "sdl" ) # Turn off the default rescaling c.set( "rescale", "none" ) # Connect the producer to the consumer c.connect( p ) # Start the consumer c.start( ) # Wait until the user stops the consumer while c.is_stopped( ) == 0: time.sleep( 1 ) else: # Diagnostics print("Unable to open ", sys.argv[ 1 ]) mlt-6.20.0/src/swig/python/switcher.py000077500000000000000000000034741362234133600176740ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Import required modules import mlt import time import sys import tornado.ioloop import tornado.web # Start the mlt system mlt.mlt_log_set_level(40) # verbose mlt.Factory.init() # Establish a pipeline profile = mlt.Profile("atsc_1080i_5994") profile.set_explicit(1) tractor = mlt.Tractor() tractor.set("eof", "loop") fg_resource = "decklink:0" bg_resource = "decklink:1" if len(sys.argv) > 2: fg_resource = sys.argv[1] bg_resource = sys.argv[2] fg = mlt.Producer(profile, fg_resource) bg = mlt.Producer(profile, bg_resource) tractor.set_track(bg, 0) tractor.set_track(fg, 1) composite = mlt.Transition(profile, "composite") composite.set("fill", 1) tractor.plant_transition(composite) # Setup the consumer consumer = "decklink:2" if len(sys.argv) > 3: consumer = sys.argv[3] consumer = mlt.Consumer(profile, consumer) consumer.connect(tractor) consumer.set("real_time", -2) consumer.start() flip_flop = False def switch(): global composite, flip_flop frame = fg.frame() + 2 if flip_flop: s = "0=20%%/0:100%%x80%%; %d=20%%/0:100%%x80%%; %d=0/0:100%%x100%%" % (frame, frame + 30) composite.set("geometry", s) else: s = "0=0/0:100%%x100%%; %d=0/0:100%%x100%%; %d=20%%/0:100%%x80%%" % (frame, frame + 30) composite.set("geometry", s) flip_flop = not flip_flop def output_form(handler): handler.write('
') class SwitchHandler(tornado.web.RequestHandler): def get(self): switch() class MainHandler(tornado.web.RequestHandler): def get(self): output_form(self) def post(self): switch() output_form(self) application = tornado.web.Application([ (r"/", MainHandler), (r"/switch", SwitchHandler) ]) application.listen(8888) tornado.ioloop.IOLoop.instance().start() mlt-6.20.0/src/swig/python/test_animation.py000077500000000000000000000004611362234133600210530ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- from __future__ import print_function import mlt p = mlt.Properties() p.anim_set("foo", "bar", 10) a = p.get_anim("bar") print("bar is valid:", a.is_valid()) a = p.get_anim("foo") print("foo is valid:", a.is_valid()) print("serialize:", a.serialize_cut()) mlt-6.20.0/src/swig/python/waveforms.py000077500000000000000000000005541362234133600200510ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- import mlt from PIL import Image mlt.Factory.init() profile = mlt.Profile() prod = mlt.Producer(profile, 'test.wav') size = (320, 240) for i in range(0, prod.get_length()): frm = prod.get_frame() wav = frm.get_waveform(size[0], size[1]) img = Image.fromstring('L', size, wav) img.save('test-%04d.pgm' % (i)) mlt-6.20.0/src/swig/python/webvfx_generator.py000077500000000000000000000066601362234133600214130ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # webvfx_generator.py # Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Import required modules import mlt import time import sys import tornado.ioloop import tornado.web import shutil import tempfile import os import os.path # Start the mlt system mlt.mlt_log_set_level(40) # verbose mlt.Factory.init() # Establish a pipeline profile = mlt.Profile("atsc_1080i_5994") #profile = mlt.Profile('square_ntsc_wide') profile.set_explicit(1) tractor = mlt.Tractor() tractor.set('eof', 'loop') playlist = mlt.Playlist() playlist.append(mlt.Producer(profile, 'color:')) # Setup the consumer consumer = 'decklink:0' if len(sys.argv) > 1: consumer = sys.argv[1] consumer = mlt.Consumer(profile, consumer) consumer.connect(playlist) #consumer.set("real_time", -2) consumer.start() def switch(resource): global playlist resource = resource playlist.lock() playlist.append(mlt.Producer(profile, str(resource))) playlist.remove(0) playlist.unlock() state = {} state['tempdir'] = None class MainHandler(tornado.web.RequestHandler): def get(self): resource = self.get_argument('url', None) if resource: global state self.write('Playing %s\n' % (resource)) switch(resource) olddir = state['tempdir'] if olddir: shutil.rmtree(olddir, True) state['tempdir'] = None else: self.write('''

POST a bunch of files to / to change the output.

Or GET / with query string parameter "url" to display something from the network.

''') def post(self): if len(self.request.files) == 0: self.write('POST a bunch of files to / to change the output') else: global state olddir = state['tempdir'] resource = None state['tempdir'] = tempfile.mkdtemp() for key, items in self.request.files.iteritems(): for item in items: path = os.path.dirname(key) fn = item.filename if path: if not os.path.exists(os.path.join(state['tempdir'], path)): os.makedirs(os.path.join(state['tempdir'], path)) fn = os.path.join(path, fn) fn = os.path.join(state['tempdir'], fn) if not path and fn.endswith('.html') or fn.endswith('.qml'): resource = fn with open(fn, 'w') as fo: fo.write(item.body) self.write("Uploaded %s\n" % (fn)) if resource: self.write('Playing %s\n' % (resource)) switch(resource) if olddir: shutil.rmtree(olddir, True) application = tornado.web.Application([ (r"/", MainHandler), ]) application.listen(8888) try: tornado.ioloop.IOLoop.instance().start() except: pass consumer.stop() if state['tempdir']: shutil.rmtree(state['tempdir'], True) mlt-6.20.0/src/swig/ruby/000077500000000000000000000000001362234133600151175ustar00rootroot00000000000000mlt-6.20.0/src/swig/ruby/build000077500000000000000000000007541362234133600161520ustar00rootroot00000000000000#!/usr/bin/env ruby require 'mkmf' if ARGV.shift == 'clean' system( "rm -f *.cxx *.so *.o mlt.i ../.ruby Makefile" ) exit 0 end system( "ln -sf ../mlt.i" ) system( "swig -c++ -ruby -I../../mlt++ -I../.. mlt.i" ) $CFLAGS = $CFLAGS.to_s + " -I../.. " + (ENV.has_key?('CXXFLAGS')? ENV['CXXFLAGS'] : '') $CXXFLAGS = $CXXFLAGS.to_s + " -I../.. " + (ENV.has_key?('CXXFLAGS')? ENV['CXXFLAGS'] : '') $LDFLAGS = $LDFLAGS.to_s + " -L../../mlt++ -lmlt++" create_makefile('mlt') system( "make V=1" ) mlt-6.20.0/src/swig/ruby/metadata.rb000077500000000000000000000062051362234133600172320ustar00rootroot00000000000000#!/usr/bin/env ruby require 'erb' require 'yaml' require './mlt' $folder = 'markdown' $repo = Mlt::Factory::init $optional_params = [ 'minimum', 'maximum', 'default', 'unit', 'scale', 'format', 'widget' ] template = %q{--- layout: standard title: Documentation wrap_title: "<%= type_title %>: <%= yml['identifier'] %>" category: plugin --- * TOC {:toc} ## Plugin Information title: <%= yml['title'] %> % if yml['tags'] media types: % yml['tags'].each do |x| <%= x + " " %> % end <%= "\n" %> % end description: <%= ERB::Util.h(yml['description']) %> version: <%= yml['version'] %> creator: <%= yml['creator'] %> % yml['contributor'] and yml['contributor'].each do |x| contributor: <%= x %> % end <%= "copyright: #{yml['copyright']} \n" if yml['copyright'] %> <%= "license: #{yml['license']} \n" if yml['license'] %> <%= "URL: [#{yml['url']}](#{yml['url']}) \n" if yml['url'] %> % if yml['notes'] ## Notes <%= ERB::Util.h(yml['notes']) %> % end % if yml['bugs'] ## Bugs % yml['bugs'].each do |x| * <%= x %> % end % end % if yml['parameters'] ## Parameters % yml['parameters'].each do |param| ### <%= param['identifier'] %> <%= "title: #{param['title']} " if param['title'] %> % if param['description'] description: % if param['description'].include? "\n"
<%= param['description'] %>
% else <%= "#{ERB::Util.h(param['description'])} \n" %> % end % end type: <%= param['type'] %> readonly: <%= param['readonly'] or 'no' %> required: <%= param['required'] or 'no' %> % $optional_params.each do |key| <%= "#{key}: #{param[key]} \n" if param[key] %> % end % if param['values'] values: % param['values'].each do |value| * <%= value %> % end % end % end % end } $processor = ERB.new(template, 0, "%<>") def output(mlt_type, services, type_title) filename = File.join($folder, "Plugins#{type_title}s.md") index = File.open(filename, 'w') index.puts '---' index.puts 'title: Documentation' index.puts "wrap_title: #{type_title} Plugins" index.puts '---' unsorted = [] (0..(services.count - 1)).each do |i| unsorted << services.get_name(i) end unsorted.sort().each do |name| meta = $repo.metadata(mlt_type, name) if meta.is_valid filename = File.join($folder, type_title + name.capitalize.gsub('.', '-')) puts "Processing #{filename}" begin yml = YAML.load(meta.serialise_yaml) if yml File.open(filename + '.md', 'w') do |f| f.puts $processor.result(binding) end else puts "Failed to write file for #{filename}" end filename = type_title + name.capitalize.gsub('.', '-') index.puts "* [#{name}](../#{filename}/): #{meta.get('title')}\n" rescue SyntaxError puts "Failed to parse YAML for #{filename}" end end end index.close end Dir.mkdir($folder) if not Dir.exists?($folder) [ [Mlt::Consumer_type, $repo.consumers, 'Consumer'], [Mlt::Filter_type, $repo.filters, 'Filter'], [Mlt::Producer_type, $repo.producers, 'Producer'], [Mlt::Transition_type, $repo.transitions, 'Transition'] ].each {|x| output *x} mlt-6.20.0/src/swig/ruby/play.rb000077500000000000000000000015431362234133600164170ustar00rootroot00000000000000#!/usr/bin/env ruby # Import required modules require 'mlt' # Create the mlt system Mlt::Factory::init # Establish the mlt profile profile = Mlt::Profile.new # Get and check the argument file = ARGV.shift raise "Usage: test.rb file" if file.nil? # Create the producer producer = Mlt::Factory::producer( profile, file ) raise "Unable to load #{file}" if !producer.is_valid # Create the consumer consumer = Mlt::Consumer.new( profile, "sdl" ) raise "Unable to open sdl consumer" if !consumer.is_valid # Turn off the default rescaling consumer.set( "rescale", "none" ) # Set up a 'wait for' event event = consumer.setup_wait_for( "consumer-stopped" ) # Start the consumer consumer.start # Connect the producer to the consumer consumer.connect( producer ) # Wait until the user stops the consumer consumer.wait_for( event ) # Clean up consumer consumer.stop mlt-6.20.0/src/swig/ruby/playlist.rb000077500000000000000000000024721362234133600173150ustar00rootroot00000000000000#!/usr/bin/env ruby # Import required modules require 'mlt' # Create the mlt system Mlt::Factory::init # Establish the mlt profile profile = Mlt::Profile.new # Get and check the argument file = ARGV.shift raise "Usage: test.rb file1 file2" if file.nil? file2 = ARGV.shift raise "Usage: test.rb file1 file2" if file2.nil? pls = Mlt::Playlist.new(profile) # Create the producer producer = Mlt::Factory::producer( profile, file ) raise "Unable to load #{file}" if !producer.is_valid producer2 = Mlt::Factory::producer( profile, file2 ) raise "Unable to load #{file2}" if !producer2.is_valid pls.append(producer) #pls.repeat(0, 2) pls.append(producer2) # Create the consumer consumer = Mlt::Consumer.new( profile, "sdl" ) raise "Unable to open sdl consumer" if !consumer.is_valid # Turn off the default rescaling consumer.set( "rescale", "none" ) consumer.set("volume", 0.1) consumer.set("terminate_on_pause", 1) Mlt::PlaylistNextListener.new(pls, Proc.new { |i| info = pls.clip_info(i) puts "finished playing #{info.resource}\n" }) # Set up a 'wait for' event event = consumer.setup_wait_for( "consumer-stopped" ) # Start the consumer consumer.start # Connect the producer to the consumer consumer.connect( pls ) # Wait until the user stops the consumer consumer.wait_for( event ) # Clean up consumer consumer.stop mlt-6.20.0/src/swig/ruby/thumbs.rb000077500000000000000000000017371362234133600167610ustar00rootroot00000000000000#!/usr/bin/env ruby # Required modules require 'mlt' # Create the mlt system Mlt::Factory::init # Establish the mlt profile profile = Mlt::Profile.new( "quarter_pal" ) # Get and check the argument file = ARGV.shift name = ARGV.shift size = ARGV.shift size = "176x144" if size.nil? raise "Usage: thumbs.rb file name [ size ]" if file.nil? || name.nil? # Create the producer producer = Mlt::Producer.new( profile, file ) raise "Unable to load #{file}" if !producer.is_valid # Construct the playlist playlist = Mlt::Playlist.new( ) # Get the out point out = producer.get_int( "out" ); # Calculate position of frames [ 0, 0.25, 0.5, 0.75, 1 ].each { |x| playlist.append( producer, Integer(x*out), Integer(x*out) ) } # Create the thumb nail generator generator = Mlt::Consumer.new( profile, "avformat", "#{name}%d.jpg" ) generator.set( "real_time", "0" ) generator.set( "progressive", "1" ) generator.set( "s", size ) # Connect the consumer generator.connect( playlist ); generator.run mlt-6.20.0/src/swig/tcl/000077500000000000000000000000001362234133600147205ustar00rootroot00000000000000mlt-6.20.0/src/swig/tcl/build000077500000000000000000000010311362234133600157400ustar00rootroot00000000000000#!/bin/sh CXX=${CXX:-g++} if [ "$1" = "clean" ] then ( cd `dirname $0`; rm -f *.cxx *.so *.o mlt.i ../.tcl ) exit 0 fi path=`which tclsh 2>/dev/null` if [ "$path" != "" ] then ln -sf ../mlt.i # Invoke swig swig -c++ -I../../mlt++ -I../.. -tcl mlt.i || exit 1 # Compile the wrapper ${CXX} -fPIC -D_GNU_SOURCE ${CXXFLAGS} -c -rdynamic -pthread -I../.. mlt_wrap.cxx || exit 1 # Create the module ${CXX} ${CXXFLAGS} -shared mlt_wrap.o -L../../mlt++ -lmlt++ -o mlt.so || exit 1 else echo "Unable to locate tclsh." exit 1 fi mlt-6.20.0/src/swig/tcl/play.tcl000077500000000000000000000005531362234133600163770ustar00rootroot00000000000000#!/usr/bin/env tclsh load mlt.so Factory_init set profile [Profile] set arg1 [lindex $argv 0] set p [Factory_producer $profile loader $arg1] set c [Factory_consumer $profile sdl ""] Properties_set $c "rescale" "none" Consumer_connect $c $p Consumer_start $c while { ![Consumer_is_stopped $c] } { after 1000 } delete_Consumer $c delete_Producer $p Factory_close mlt-6.20.0/src/tests/000077500000000000000000000000001362234133600143275ustar00rootroot00000000000000mlt-6.20.0/src/tests/clock16ntsc.pgm000066400000000000000000025060211362234133600171740ustar00rootroot00000000000000P5 720 480 65535 ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''((((××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââââããããããããããããããããããääääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((ÖÖ××××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((ÖÖÖÖ××××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((((ÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääääååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááááââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääääååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))))))ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))))))))))))ÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))ÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååååææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))**ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))******ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââââââããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))********ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))************ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))******************ÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))**********************ÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************ÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))****************************ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááááââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))********************************ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááááââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************************************ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************************++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææççççççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************************++++++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************************++++++++++++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããããääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))****************************************++++++++++++++++ÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++ÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++++++++++++,,,,ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââââããããããããããããããääääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääääååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääääååååååååååååååææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááááââââââââââââââããããããããããããããããääääääääääääääååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääääååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------ÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããããããääääääääääääääååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------ÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------ÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââââããããããããããããããääääääääääääääååååååååååååææææææææææææççççççççççççççèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââââããããããããããããããääääääääääääååååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------....ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------........ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------............ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååååææææææææææææççççççççççççèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------................ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææææææççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------....................ÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããääääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------........................ÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------............................ÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååååææææææææææææççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïïïððððððððððññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..................................ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððññññññññññòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------......................................ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïïïððððððððððññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..........................................ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããääääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..............................................ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïïïððððððððññññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..............................................////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííîîîîîîîîîîïïïïïïïïïïððððððððññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææççççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïïïððððððððññññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââââããããããããããããääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////////////ÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââââããããããããããããääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïïïððððððððññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..........................................////////////////////////////ÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââââããããããããããããääääääääääääååååååååååååææææææææææççççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððññññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................////////////////////////////////ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââããããããããããããããääääääääääääååååååååååææææææææææææççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................////////////////////////////////////ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââããããããããããããääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................////////////////////////////////////////ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââããããããããããããääääääääääääååååååååååååææææææææææççççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïððððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................//////////////////////////////////////////////ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââããããããããããããääääääääääääååååååååååååææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííîîîîîîîîïïïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................////////////////////////////////////////////////00ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááââââââââââââââããããããããããããääääääääääääååååååååååææææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëììììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................//////////////////////////////////////////////00000000ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááââââââââââââââããããããããããããääääääääääååååååååååååææææææææææççççççççççççèèèèèèèèèèééééééééééêêêêêêêêëëëëëëëëëëììììììììììííííííííîîîîîîîîïïïïïïïïððððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................//////////////////////////////////////////////000000000000ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááââââââââââââããããããããããããääääääääääääååååååååååååææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////000000000000000000ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááââââââââââââããããããããããããääääääääääääååååååååååææææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêëëëëëëëëëëììììììììííííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////0000000000000000000000ÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááââââââââââââããããããããããããääääääääääääååååååååååææææææææææççççççççççççèèèèèèèèèèééééééééêêêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////00000000000000000000000000ÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááááââââââââââââããããããããããããääääääääääååååååååååååææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëììììììììííííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------..........................................////////////////////////////////////////////00000000000000000000000000000000ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááááââââââââââââããããããããããããääääääääääååååååååååååææææææææææççççççççççèèèèèèèèèèééééééééêêêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................//////////////////////////////////////////////000000000000000000000000000000000000ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááááââââââââââââããããããããããääääääääääääååååååååååææææææææææææççççççççççèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëììììììììííííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////000000000000000000000000000000000000000000ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááááââââââââââããããããããããããääääääääääääååååååååååææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////0000000000000000000000000000000000000000000000ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããããääääääääääääååååååååååææææææææææççççççççççèèèèèèèèèèééééééééêêêêêêêêêêëëëëëëëëììììììììííííííííííîîîîîîîîïïïïïïïïððððððððññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))****************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////00000000000000000000000000000000000000000000000011ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããããääääääääääååååååååååååææææææææææççççççççèèèèèèèèèèééééééééééêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................////////////////////////////////////////////00000000000000000000000000000000000000000000000000111111ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããããääääääääääååååååååååææææææææææççççççççççèèèèèèèèèèééééééééêêêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................////////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääääååååååååååææææææææææççççççççççèèèèèèèèééééééééééêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññòòòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................//////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111111111ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääääååååååååååææææææææææççççççççèèèèèèèèèèééééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòóóóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................//////////////////////////////////////////0000000000000000000000000000000000000000000000001111111111111111111111ÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääååååååååååææææææææææççççççççççèèèèèèèèèèééééééééêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððññññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................//////////////////////////////////////////0000000000000000000000000000000000000000000000001111111111111111111111111111ÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââããããããããããããääääääääääååååååååååææææææææææççççççççççèèèèèèèèééééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññòòòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................//////////////////////////////////////////00000000000000000000000000000000000000000000000011111111111111111111111111111111ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààààááááááááááááââââââââââããããããããããããääääääääääååååååååååææææææææææççççççççèèèèèèèèèèééééééééêêêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïððððððððññññññññòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""##############$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................//////////////////////////////////////////000000000000000000000000000000000000000000000011111111111111111111111111111111111111ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààààááááááááááââââââââââââããããããããããääääääääääääååååååååååææææææææççççççççççèèèèèèèèééééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððññññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääååååååååååææææææææææççççççççççèèèèèèèèééééééééêêêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîïïïïïïïïððððððððññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................//////////////////////////////////////////0000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääååååååååååææææææææææççççççççèèèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððññññññññòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................////////////////////////////////////////0000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111122ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääååååååååååææææææææççççççççççèèèèèèèèééééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîïïïïïïïïððððððððññññññòòòòòòòòóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................////////////////////////////////////////00000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââããããããããããããääääääääääååååååååææææææææææççççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïððððððððññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))**************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................////////////////////////////////////////000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââããããããããããääääääääääååååååååååææææææææææççççççççèèèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííîîîîîîîîïïïïïïïïððððððññññññññòòòòòòóóóóóóóóôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------..................................////////////////////////////////////////000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââããããããããããääääääääääååååååååååææææææææççççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïððððððððññññññòòòòòòòòóóóóóóôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------..................................////////////////////////////////////////0000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222222222ÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââããããããããããääääääääääååååååååååææææææææççççççççèèèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííîîîîîîîîïïïïïïïïððððððññññññòòòòòòòòóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------....................................//////////////////////////////////////0000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222222222222222ÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááââââââââââââããããããããããääääääääääååååååååææææææææææççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîïïïïïïïïððððððññññññññòòòòòòóóóóóóôôôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------....................................//////////////////////////////////////00000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111112222222222222222222222222222222222ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááââââââââââââããããããããããääääääääååååååååååææææææææççççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððððññññññòòòòòòóóóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................////////////////////////////////////////000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááââââââââââããããããããããääääääääääååååååååååææææææææççççççççèèèèèèèèèèééééééééêêêêêêêêëëëëëëììììììììííííííííîîîîîîïïïïïïïïððððððññññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááââââââââââããããããããããääääääääääååååååååææææææææææççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððððññññññòòòòòòóóóóóóôôôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////0000000000000000000000000000000000000000001111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááââââââââââããããããããããääääääääääååååååååææææææææççççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëììììììììííííííííîîîîîîïïïïïïïïððððððññññññòòòòòòóóóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................////////////////////////////////////0000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááââââââââââããããããããããääääääääååååååååååææææææææççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððññññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))************************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------................................//////////////////////////////////////00000000000000000000000000000000000000000011111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááááââââââââââããããããããããääääääääååååååååååææææææææççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëììììììììííííííííîîîîîîïïïïïïððððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------................................//////////////////////////////////////000000000000000000000000000000000000000011111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááááââââââââââããããããããääääääääääååååååååææææææææææççççççççèèèèèèèèééééééééêêêêêêëëëëëëëëììììììííííííííîîîîîîïïïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------................................////////////////////////////////////000000000000000000000000000000000000000000111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããããääääääääääååååååååææææææææççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððññññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..................................////////////////////////////////////0000000000000000000000000000000000000000111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333333333333333ËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããããääääääääååååååååååææææææææççççççççèèèèèèèèééééééééêêêêêêëëëëëëëëììììììííííííííîîîîîîïïïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................////////////////////////////////////0000000000000000000000000000000000000000001111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333ËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããããääääääääååååååååææææææææææççççççççèèèèèèééééééééêêêêêêêêëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................////////////////////////////////////00000000000000000000000000000000000000001111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333ËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããääääääääääååååååååææææææææççççççççèèèèèèèèééééééééêêêêêêëëëëëëëëììììììííííííííîîîîîîïïïïïïððððððññññññññòòòòòòóóóóóóôôôôôôõõõõõõöööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................////////////////////////////////////000000000000000000000000000000000000001111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333ËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããääääääääääååååååååææææææææççççççççèèèèèèèèééééééêêêêêêêêëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................//////////////////////////////////000000000000000000000000000000000000000011111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããääääääääååååååååååææææææææççççççççèèèèèèééééééééêêêêêêêêëëëëëëììììììííííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøùùùùùùúúúúúúûûûûûûüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////////0000000000000000000000000000000000000011111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââããããããããããääääääääååååååååææææææææççççççççèèèèèèèèééééééééêêêêêêëëëëëëëëììììììííííííîîîîîîïïïïïïððððððððññññññòòòòòòóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùúúúúúúûûûûûûüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................//////////////////////////////////0000000000000000000000000000000000000000111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333334444ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââããããããããããääääääääååååååååææææææææççççççççèèèèèèèèééééééêêêêêêêêëëëëëëììììììííííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùúúúúúúûûûûûûüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................//////////////////////////////////00000000000000000000000000000000000000111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333334444444444ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááââââââââââããããããããääääääääääååååååååææææææææççççççèèèèèèèèééééééééêêêêêêëëëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúûûûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................//////////////////////////////////000000000000000000000000000000000000001111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333334444444444444444ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááââââââââââããããããããääääääääååååååååææææææææççççççççèèèèèèèèééééééêêêêêêêêëëëëëëììììììííííííííîîîîîîïïïïïïððððððññññññòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷øøøøøøùùùùùùúúúúûûûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////000000000000000000000000000000000000001111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààààááááááááââââââââââããããããããääääääääååååååååææææææææççççççççèèèèèèééééééééêêêêêêëëëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøùùùùùùúúúúûûûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................//////////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444444444ÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààááááááááááââââââââââããããããããääääääääååååååååææææææææççççççèèèèèèèèééééééêêêêêêêêëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõöööööö÷÷÷÷÷÷øøøøùùùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................////////////////////////////////0000000000000000000000000000000000000011111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààááááááááááââââââââããããããããããääääääääååååååååææææææççççççççèèèèèèèèééééééêêêêêêëëëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòóóóóóóôôôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................////////////////////////////////00000000000000000000000000000000000011111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààááááááááááââââââââããããããããääääääääååååååååææææææææççççççççèèèèèèééééééééêêêêêêëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóôôôôôôõõõõõõöööö÷÷÷÷÷÷øøøøøøùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''(((((((((((((((((())))))))))))))))))********************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................////////////////////////////////000000000000000000000000000000000000111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààááááááááááââââââââããããããããääääääääååååååååææææææææççççççèèèèèèèèééééééêêêêêêëëëëëëëëììììììííííííîîîîîîïïïïïïððððññññññòòòòòòóóóóóóôôôôõõõõõõöööööö÷÷÷÷øøøøøøùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////000000000000000000000000000000000000111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààááááááááááââââââââããããããããääääääääååååååååææææææççççççççèèèèèèééééééééêêêêêêëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòóóóóóóôôôôôôõõõõöööööö÷÷÷÷øøøøøøùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((((())))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááááââââââââããããããããääääääääååååååææææææææççççççççèèèèèèééééééêêêêêêêêëëëëëëììììììííííííîîîîîîïïïïððððððññññññòòòòòòóóóóôôôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùùùúúúúûûûûûûüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""""##########$$$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................//////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444444555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââââããããããããääääääååååååååææææææææççççççèèèèèèèèééééééêêêêêêëëëëëëììììììííííííîîîîîîïïïïïïððððððññññòòòòòòóóóóóóôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùùùúúúúûûûûûûüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................//////////////////////////////00000000000000000000000000000000001111111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444455555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââããããããããääääääääååååååååææææææççççççççèèèèèèééééééêêêêêêêêëëëëëëììììììííííííîîîîïïïïïïððððððññññññòòòòóóóóóóôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùùùúúúúûûûûûûüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------..........................////////////////////////////////000000000000000000000000000000000011111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444445555555555555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââããããããããääääääääååååååååææææææççççççççèèèèèèééééééêêêêêêëëëëëëììììììííííííîîîîîîïïïïïïððððññññññòòòòòòóóóóôôôôôôõõõõöööööö÷÷÷÷øøøøøøùùùùúúúúûûûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------..........................//////////////////////////////000000000000000000000000000000000011111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444445555555555555555555555555555ÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââããããããããääääääääååååååææææææææççççççèèèèèèééééééééêêêêêêëëëëëëììììììííííîîîîîîïïïïïïððððððññññòòòòòòóóóóôôôôôôõõõõöööööö÷÷÷÷øøøøøøùùùùúúúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................//////////////////////////////0000000000000000000000000000000000111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââããããããããääääääååååååååææææææææççççççèèèèèèééééééêêêêêêëëëëëëììììììííííííîîîîîîïïïïððððððññññññòòòòóóóóóóôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùúúúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................////////////////////////////0000000000000000000000000000000000111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââããããããããääääääååååååååææææææççççççèèèèèèèèééééééêêêêêêëëëëëëììììììííííîîîîîîïïïïïïððððññññññòòòòòòóóóóôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùúúúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!""""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................//////////////////////////////00000000000000000000000000000000111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââããããããääääääääååååååååææææææççççççèèèèèèééééééêêêêêêëëëëëëììììììííííííîîîîîîïïïïððððððññññòòòòòòóóóóôôôôôôõõõõöööööö÷÷÷÷øøøøùùùùùùúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!""""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................//////////////////////////////000000000000000000000000000000001111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááááââââââããããããããääääääääååååååææææææææççççççèèèèèèééééééêêêêêêëëëëëëììììììííííîîîîîîïïïïïïððððññññññòòòòóóóóóóôôôôõõõõöööööö÷÷÷÷øøøøùùùùùùúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))))****************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////000000000000000000000000000000001111111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââââããããããããääääääääååååååææææææççççççèèèèèèèèééééééêêêêëëëëëëììììììííííííîîîîïïïïïïððððððññññòòòòóóóóóóôôôôõõõõõõöööö÷÷÷÷øøøøøøùùùùúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////0000000000000000000000000000000011111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555556666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââââããããããããääääääååååååååææææææççççççèèèèèèééééééêêêêêêëëëëëëììììììííííîîîîîîïïïïððððððññññòòòòòòóóóóôôôôôôõõõõöööö÷÷÷÷øøøøøøùùùùúúúúûûûûüüüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------........................////////////////////////////0000000000000000000000000000000011111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââââããããããããääääääååååååææææææææççççççèèèèèèééééééêêêêêêëëëëììììììííííííîîîîïïïïïïððððññññññòòòòóóóóôôôôôôõõõõöööö÷÷÷÷÷÷øøøøùùùùúúúúûûûûüüüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""""##########$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------........................////////////////////////////00000000000000000000000000000000111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555566666666666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââââããããããääääääääååååååææææææççççççèèèèèèééééééêêêêêêëëëëëëììììííííííîîîîîîïïïïððððððññññòòòòóóóóóóôôôôõõõõöööööö÷÷÷÷øøøøùùùùúúúúûûûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,----------------------........................////////////////////////////000000000000000000000000000000111111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââââããããããääääääääååååååææææææççççççèèèèèèééééééêêêêêêëëëëììììììííííííîîîîïïïïïïððððññññòòòòòòóóóóôôôôõõõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------..........................//////////////////////////000000000000000000000000000000111111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââããããããããääääääååååååååææææææççççççèèèèèèééééêêêêêêëëëëëëììììííííííîîîîîîïïïïððððññññññòòòòóóóóôôôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""##########$$$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................////////////////////////////0000000000000000000000000000001111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââããããããããääääääååååååææææææççççççèèèèèèééééééêêêêêêëëëëììììììííííîîîîîîïïïïððððððññññòòòòóóóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""##########$$$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................//////////////////////////0000000000000000000000000000001111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââããããããããääääääååååååææææææççççççèèèèèèééééééêêêêëëëëëëììììííííííîîîîïïïïïïððððññññòòòòòòóóóóôôôôõõõõöööö÷÷÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""##########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................//////////////////////////00000000000000000000000000000011111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááââââââââããããããääääääääååååååææææææççççççèèèèééééééêêêêêêëëëëììììììííííîîîîîîïïïïððððññññññòòòòóóóóôôôôõõõõöööööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((())))))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................//////////////////////////000000000000000000000000000011111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááââââââââããããããääääääååååååææææææççççççèèèèèèééééééêêêêëëëëëëììììííííííîîîîïïïïïïððððññññòòòòóóóóôôôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------......................//////////////////////////000000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááââââââââããããããääääääååååååææææææççççççèèèèèèééééêêêêêêëëëëììììììííííîîîîîîïïïïððððññññòòòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""""########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&''''''''''''''(((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------......................//////////////////////////0000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666667777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááââââââããããããããääääääååååååææææææççççççèèèèééééééêêêêëëëëëëììììííííííîîîîïïïïððððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""""########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))**************++++++++++++++++++,,,,,,,,,,,,,,,,,,--------------------......................////////////////////////0000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááááââââââããããããããääääääååååååææææççççççèèèèèèééééêêêêêêëëëëììììììííííîîîîïïïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,--------------------......................////////////////////////00000000000000000000000000001111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333334444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááááââââââããããããääääääååååååææææææççççççèèèèééééééêêêêêêëëëëììììííííííîîîîïïïïððððññññòòòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,--------------------....................//////////////////////////000000000000000000000000001111111111111111111111111111111122222222222222222222222222222222222233333333333333333333333333333333333333334444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777ÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááááââââââããããããääääääååååååææææææççççèèèèèèééééééêêêêëëëëììììììííííîîîîïïïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////000000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333334444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááááââââââããããããääääääååååååææææççççççèèèèèèééééêêêêêêëëëëììììííííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////0000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333334444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááââââââââããããããääääääååååææææææççççççèèèèééééééêêêêëëëëììììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüýýýýþþþþÿÿ  !!!!!!!!""""""""########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................//////////////////////0000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããããääääääååååååææææææççççèèèèèèééééêêêêêêëëëëììììííííîîîîîîïïïïððððññññòòòòóóóóôôôôõõõõöö÷÷÷÷øøøøùùùùúúúúûûûûüüýýýýþþþþÿÿ  !!!!!!!!""""""""########$$$$$$$$$$%%%%%%%%&&&&&&&&&&&&''''''''''(((((((((((())))))))))))))**************++++++++++++++,,,,,,,,,,,,,,,,,,------------------....................////////////////////////00000000000000000000000000111111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333333444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããããääääääååååååææææççççççèèèèèèééééêêêêëëëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷øøøøùùùùúúúúûûûûüüýýýýþþþþÿÿ  !!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................////////////////////////000000000000000000000000111111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333333444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããããääääääååååååææææççççççèèèèééééêêêêêêëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøùùùùúúúúûûûûüüüüýýþþþþÿÿ  !!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''(((((((((((())))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////000000000000000000000000001111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããããääääääååååææææææççççèèèèèèééééêêêêëëëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôõõõõöööö÷÷÷÷øøøøùùúúúúûûûûüüüüýýþþþþÿÿ  !!!!!!""""""""########$$$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''''(((((((((())))))))))))))************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////0000000000000000000000001111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããããääääååååååææææææççççèèèèééééééêêêêëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõöööö÷÷÷÷øøøøùùùùúúûûûûüüüüýýþþþþÿÿ  !!!!!!""""""""########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''(((((((((((())))))))))))**************++++++++++++++,,,,,,,,,,,,,,,,------------------..................//////////////////////0000000000000000000000000011111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããääääääååååååææææççççççèèèèééééêêêêëëëëëëììììííííîîîîïïïïððððññòòòòóóóóôôôôõõõõöö÷÷÷÷øøøøùùùùúúûûûûüüüüýýþþþþÿÿ  !!!!!!!!""""""""########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))**************++++++++++++++,,,,,,,,,,,,,,,,------------------..................//////////////////////00000000000000000000000011111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888ÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããääääääååååææææææççççèèèèèèééééêêêêëëëëììììííííîîîîïïïïððððññññòòòòóóôôôôõõõõöööö÷÷øøøøùùùùúúûûûûüüüüýýþþþþÿÿ  !!!!!!!!""""""""########$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''(((((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,,,----------------....................////////////////////00000000000000000000000011111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààààááááááââââããããããääääääååååææææææççççèèèèééééêêêêêêëëëëììììííííîîîîïïððððññññòòòòóóóóôôõõõõöööö÷÷÷÷øøùùùùúúúúûûüüüüýýþþþþÿÿ  !!!!!!!!""""""""######$$$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,,,----------------....................////////////////////000000000000000000000000111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââããããããääääååååååææææççççççèèèèééééêêêêëëëëììììííííîîîîïïïïððððññòòòòóóóóôôôôõõöööö÷÷÷÷øøùùùùúúúúûûüüüüýýþþþþÿÿ  !!!!!!!!""""""########$$$$$$$$%%%%%%%%%%&&&&&&&&''''''''''(((((((((((())))))))))**************++++++++++++++,,,,,,,,,,,,,,----------------..................//////////////////////0000000000000000000000111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââããããããääääååååååææææççççèèèèééééééêêêêëëëëììììííííîîïïïïððððññññòòòòóóôôôôõõõõöö÷÷÷÷øøøøùùúúúúûûüüüüýýýýþþÿÿ  !!!!!!!!""""""########$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,----------------..................////////////////////0000000000000000000000001111111111111111111111111122222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââããããääääääååååææææææççççèèèèééééêêêêëëëëììììííííîîîîïïïïððññññòòòòóóóóôôõõõõöööö÷÷øøøøùùúúúúûûüüüüýýýýþþÿÿ  !!!!!!!!""""""########$$$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,----------------..................////////////////////00000000000000000000001111111111111111111111111122222222222222222222222222223333333333333333333333333333333333444444444444444444444444444444444444444455555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââããããääääääååååææææççççèèèèèèééééêêêêëëëëììììííîîîîïïïïððððññòòòòóóóóôôõõõõöööö÷÷øøøøùùúúúúûûûûüüýýýýþþÿÿ  !!!!!!""""""""########$$$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))************++++++++++++++,,,,,,,,,,,,,,----------------..................//////////////////00000000000000000000001111111111111111111111111122222222222222222222222222223333333333333333333333333333333333444444444444444444444444444444444444444455555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââããããääääååååååææææççççèèèèééééêêêêëëëëììììííííîîîîïïððððññññòòóóóóôôôôõõöööö÷÷øøøøùùùùúúûûûûüüýýýýþþÿÿ  !!!!!!""""""""######$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''(((((((((())))))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////000000000000000000000011111111111111111111111122222222222222222222222222223333333333333333333333333333333333444444444444444444444444444444444444445555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââããããããääääååååææææææççççèèèèééééêêêêëëëëììììííîîîîïïïïððððññòòòòóóôôôôõõõõöö÷÷÷÷øøùùùùúúûûûûüüýýýýþþÿÿ  !!!!!!""""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////0000000000000000000011111111111111111111111122222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444444555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââããããããääääååååææææççççèèèèééééêêêêëëëëììììííííîîîîïïððððññññòòóóóóôôõõõõöö÷÷÷÷øøùùùùúúûûûûüüýýýýþþÿÿ  !!!!!!""""""########$$$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,,,----------------................//////////////////0000000000000000000000111111111111111111111111222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444444555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999ÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââããããääääääååååææææççççèèèèééééêêêêëëëëììííííîîîîïïïïððññññòòóóóóôôôôõõöööö÷÷øøùùùùúúûûûûüüýýýýþþÿÿ  !!!!!!""""""########$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))**********++++++++++++,,,,,,,,,,,,,,----------------................//////////////////00000000000000000000111111111111111111111111222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444455555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááââââââããããääääååååååææææççççèèèèééééêêëëëëììììííííîîïïïïððððññòòòòóóôôôôõõöööö÷÷øøøøùùúúúúûûüüýýýýþþÿÿ  !!!!!!""""""########$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,--------------................//////////////////00000000000000000000001111111111111111111111222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444455555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßßßààààààááááââââââããããääääååååææææççççèèèèééééêêêêëëëëììííííîîîîïïððððññññòòóóóóôôõõõõöö÷÷øøøøùùúúúúûûüüýýýýþþÿÿ  !!!!!!""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((())))))))))************++++++++++++,,,,,,,,,,,,--------------................//////////////////000000000000000000001111111111111111111111222222222222222222222222223333333333333333333333333333334444444444444444444444444444444444445555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßßßààààààááááââââââããããääääååååææææççççèèèèééééêêêêëëììììííííîîïïïïððññññòòóóóóôôõõõõöö÷÷÷÷øøùùúúúúûûüüýýýýþþÿÿ  !!!!!!""""""######$$$$$$$$%%%%%%&&&&&&&&''''''''(((((((((())))))))))**********++++++++++++,,,,,,,,,,,,--------------................//////////////////0000000000000000001111111111111111111111112222222222222222222222223333333333333333333333333333334444444444444444444444444444444444555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßßßààààààááááââââããããããääääååååææææççççèèèèééêêêêëëëëììííííîîîîïïððððññòòòòóóôôôôõõöö÷÷÷÷øøùùúúúúûûüüýýýýþþÿÿ  !!!!!!""""""######$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((())))))))))**********++++++++++++,,,,,,,,,,,,--------------................////////////////0000000000000000000011111111111111111111112222222222222222222222223333333333333333333333333333334444444444444444444444444444444444555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßßßààààààááááââââããããääääååååææææççççèèèèééééêêêêëëììììííííîîïïïïððññññòòóóôôôôõõöööö÷÷øøùùùùúúûûüüüüýýþþÿÿ  !!!!!!""""""######$$$$$$%%%%%%%%&&&&&&''''''''(((((((((())))))))************++++++++++,,,,,,,,,,,,--------------................////////////////000000000000000000111111111111111111111122222222222222222222222233333333333333333333333333334444444444444444444444444444444444555555555555555555555555555555555555555566666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßààààááááááââââããããääääååååææææççççèèèèééêêêêëëëëììííííîîïïïïððññññòòóóóóôôõõöööö÷÷øøùùùùúúûûüüüüýýþþÿÿ  !!!!!!""""######$$$$$$$$%%%%%%&&&&&&&&''''''''(((((((())))))))))**********++++++++++,,,,,,,,,,,,--------------..............////////////////0000000000000000000011111111111111111111222222222222222222222222333333333333333333333333333344444444444444444444444444444444445555555555555555555555555555555555555566666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××××ØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßààààááááááââââããããääääååååææææççççèèééééêêêêëëììììííîîîîïïððððññòòòòóóôôõõõõöö÷÷øøøøùùúúûûüüüüýýþþÿÿ  !!!!""""""######$$$$$$%%%%%%%%&&&&&&&&''''''(((((((((())))))))**********++++++++++,,,,,,,,,,,,--------------..............////////////////00000000000000000011111111111111111111222222222222222222222222333333333333333333333333333344444444444444444444444444444444555555555555555555555555555555555555556666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßààààááááááââââããããääääååååææççççèèèèééééêêëëëëììííííîîïïïïððññññòòóóôôôôõõöö÷÷øøøøùùúúûûüüüüýýþþÿÿ  !!!!""""""######$$$$$$%%%%%%%%&&&&&&''''''''(((((((())))))))**********++++++++++++,,,,,,,,,,--------------..............////////////////0000000000000000001111111111111111111122222222222222222222223333333333333333333333333333444444444444444444444444444444555555555555555555555555555555555555556666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââââããããääååååææææççççèèèèééêêêêëëììììííîîîîïïððððññòòóóóóôôõõöö÷÷÷÷øøùùúúûûûûüüýýþþÿÿ  !!!!""""""######$$$$$$%%%%%%&&&&&&&&''''''(((((((())))))))))**********++++++++++,,,,,,,,,,--------------..............//////////////00000000000000000011111111111111111111222222222222222222222233333333333333333333333333444444444444444444444444444444445555555555555555555555555555555555556666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââããããääääååååææææççççèèééééêêëëëëììííííîîïïïïððññòòòòóóôôõõöööö÷÷øøùùúúûûûûüüýýþþÿÿ  !!!!""""""######$$$$$$%%%%%%&&&&&&''''''''(((((((())))))))**********++++++++++,,,,,,,,,,--------------............////////////////000000000000000011111111111111111111222222222222222222222233333333333333333333333333444444444444444444444444444444555555555555555555555555555555555555666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââããããääääååååææææççèèèèééêêêêëëììììííîîîîïïððññññòòóóôôõõõõöö÷÷øøùùúúúúûûüüýýþþÿÿ  !!!!""""""####$$$$$$%%%%%%%%&&&&&&''''''(((((((())))))))))********++++++++++,,,,,,,,,,------------..............//////////////0000000000000000001111111111111111112222222222222222222222333333333333333333333333334444444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââããããääääååååææççççèèééééêêëëëëììííííîîïïððððññòòóóôôôôõõöö÷÷øøùùùùúúûûüüýýþþÿÿ  !!!!""""""####$$$$$$%%%%%%&&&&&&''''''''(((((((())))))))********++++++++++,,,,,,,,,,------------..............//////////////000000000000000011111111111111111122222222222222222222223333333333333333333333334444444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââããããääääååææææççèèèèééééêêëëììììííîîïïïïððññòòóóóóôôõõöö÷÷øøøøùùúúûûüüýýþþÿÿ  !!!!!!""""######$$$$$$%%%%%%&&&&&&''''''(((((((())))))))**********++++++++,,,,,,,,,,------------..............//////////////00000000000000001111111111111111112222222222222222222233333333333333333333333344444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââããããääååååææææççèèèèééêêêêëëììííííîîïïððññññòòóóôôõõöö÷÷÷÷øøùùúúûûüüýýþþÿÿ  !!!!!!""""######$$$$$$%%%%%%&&&&&&''''''(((((((())))))))********++++++++,,,,,,,,,,------------............//////////////00000000000000001111111111111111112222222222222222222233333333333333333333333344444444444444444444444444445555555555555555555555555555555566666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââããääääååååææççççèèééééêêëëììììííîîïïïïððññòòóóôôõõõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!!!""""######$$$$%%%%%%&&&&&&''''''''(((((())))))))********++++++++++,,,,,,,,,,----------............//////////////0000000000000011111111111111111122222222222222222222333333333333333333333333444444444444444444444444445555555555555555555555555555555566666666666666666666666666666666666666667777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÛÛÛÛÛÛÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßààààááááââããããääääååææææççèèèèééêêêêëëììííîîîîïïððññòòóóóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!!!""""####$$$$$$%%%%%%&&&&&&''''''(((((())))))))********++++++++++,,,,,,,,,,----------............////////////00000000000000001111111111111111222222222222222222223333333333333333333333444444444444444444444444444455555555555555555555555555555566666666666666666666666666666666666666667777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßààààááââââããããääääååææææççèèééééêêëëììììííîîïïððññññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!""""""####$$$$$$%%%%&&&&&&''''''''(((((())))))))********++++++++,,,,,,,,,,----------............////////////000000000000001111111111111111112222222222222222223333333333333333333333444444444444444444444444445555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááââââããããääååååææççççèèééêêêêëëììííîîîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!""""""####$$$$%%%%%%&&&&&&''''''(((((())))))))********++++++++,,,,,,,,,,----------..........//////////////00000000000000111111111111111122222222222222222233333333333333333333334444444444444444444444445555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞßßßßààááááââââããããääååååææççèèèèééêêëëììììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!""""######$$$$%%%%%%&&&&&&''''''(((((())))))********++++++++,,,,,,,,,,----------..........////////////000000000000001111111111111111222222222222222222333333333333333333334444444444444444444444444455555555555555555555555555556666666666666666666666666666666666667777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÛÛÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààááááââââããääääååææææççèèééééêêëëììííîîïïïïððññòòóóôôõõöö÷÷øøùùúúüüýýþþÿÿ  !!!!""""######$$$$%%%%&&&&&&''''''(((((())))))))******++++++++,,,,,,,,,,--------............////////////00000000000000111111111111112222222222222222223333333333333333333344444444444444444444444455555555555555555555555555555566666666666666666666666666666666667777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààááááââââããääääååææççççèèééêêëëììììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûýýþþÿÿ  !!!!""""####$$$$$$%%%%&&&&&&''''''(((((())))))******++++++++,,,,,,,,,,--------............//////////00000000000000111111111111112222222222222222223333333333333333333344444444444444444444444455555555555555555555555555556666666666666666666666666666666666777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààááááââããããääååååææççèèèèééêêëëììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûýýþþÿÿ  !!!!""""####$$$$%%%%%%&&&&''''''(((((())))))********++++++++,,,,,,,,--------..........////////////0000000000001111111111111111222222222222222233333333333333333333444444444444444444444455555555555555555555555555556666666666666666666666666666666677777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØÙÙÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààááááââããããääååææææççèèééêêëëëëììííîîïïððññòòóóõõöö÷÷øøùùúúûûüüþþÿÿ  !!!!""""####$$$$%%%%&&&&&&''''''(((())))))))******++++++++,,,,,,,,--------..........////////////000000000000111111111111112222222222222222333333333333333333444444444444444444444455555555555555555555555555556666666666666666666666666666666677777777777777777777777777777777777777778888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖ××××××ØØØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààááááââããääääååææççççèèééêêëëììííîîïïððññòòóóôôõõ÷÷øøùùúúûûüüþþÿÿ  !!!!""""####$$$$%%%%&&&&&&''''(((((())))))******++++++++,,,,,,,,--------..........//////////000000000000111111111111112222222222222222333333333333333333444444444444444444444455555555555555555555555555666666666666666666666666666666777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßààààááááââããääääååææççèèééêêêêëëììííîîððññòòóóôôõõöö÷÷ùùúúûûüüþþÿÿ  !!!!""""##$$$$$$%%%%&&&&''''''(((())))))******++++++++,,,,,,,,--------..........//////////00000000000011111111111122222222222222223333333333333333334444444444444444444455555555555555555555555555666666666666666666666666666666777777777777777777777777777777777777778888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××ØØØØÙÙÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÞÞÞÞßßààààááââââããääååååææççèèééêêëëììííîîïïððññòòôôõõöö÷÷øøúúûûüüýýÿÿ  !!!!""####$$$$%%%%&&&&&&''''(((((())))))******++++++,,,,,,,,--------........//////////000000000000111111111111112222222222222233333333333333333344444444444444444444555555555555555555555555666666666666666666666666666677777777777777777777777777777777777777888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÖÖÖÖÖÖ××××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÞÞÞÞßßààààááââââããääååææççèèèèééêêëëììîîïïððññòòóóõõöö÷÷øøúúûûüüýýÿÿ  !!""""####$$$$%%%%&&&&''''''(((())))))******++++++,,,,,,,,--------........//////////00000000001111111111111122222222222222333333333333333344444444444444444444555555555555555555555555666666666666666666666666666677777777777777777777777777777777777788888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÞÞÞÞßßààààááââããããääååææççèèééêêëëììííîîïïððòòóóôôõõ÷÷øøùùûûüüýýÿÿ  !!""""####$$$$%%%%&&&&''''(((((())))******++++++,,,,,,,,--------........//////////000000000011111111111122222222222222333333333333333344444444444444444455555555555555555555555566666666666666666666666666777777777777777777777777777777777777888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖ××××ØØØØØØÙÙÙÙÚÚÛÛÛÛÜÜÜÜÝÝÞÞÞÞßßààààááââããääääååææççèèééêêëëííîîïïððññóóôôõõööøøùùûûüüýýÿÿ  !!""""####$$%%%%&&&&''''''(((())))))****++++++,,,,,,,,--------........////////0000000000111111111111222222222222223333333333333333444444444444444444555555555555555555555566666666666666666666666666777777777777777777777777777777777788888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÚÛÛÜÜÜÜÝÝÝÝÞÞßßààààááââããääååææççèèééêêëëììííîîððññòòóóõõööøøùùúúüüýýÿÿ  !!""""##$$$$%%%%&&&&''''(((())))))******++++++,,,,,,------........//////////000000000011111111112222222222222233333333333333444444444444444444555555555555555555555566666666666666666666666666777777777777777777777777777777778888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÕÕÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÝÝÝÝÞÞßßààààááââããääååææççèèééêêëëííîîïïððòòóóôôöö÷÷ùùúúüüýýÿÿ  !!""""##$$$$%%%%&&&&''''(((())))******++++++,,,,,,------........////////000000000011111111111122222222222233333333333333444444444444444444555555555555555555556666666666666666666666667777777777777777777777777777777788888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖÖÖ××××ØØØØÙÙÚÚÚÚÛÛÛÛÜÜÝÝÝÝÞÞßßààááááââããääååææççèèêêëëììííïïððññóóôôöö÷÷ùùúúüüýýÿÿ  !!""####$$$$%%&&&&''''(((())))))****++++++,,,,,,------........////////00000000001111111111222222222222333333333333334444444444444444555555555555555555556666666666666666666666667777777777777777777777777777778888888888888888888888888888888888888899999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖ××××ØØØØÙÙÙÙÚÚÛÛÛÛÜÜÝÝÝÝÞÞßßààááááââããääååççèèééêêëëííîîïïññòòôôõõ÷÷øøúúüüýýÿÿ  !!""####$$%%%%&&&&''''(((())))****++++++,,,,,,------........////////0000000011111111112222222222223333333333333344444444444444555555555555555555556666666666666666666666777777777777777777777777777777888888888888888888888888888888888888999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<============================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÖÖÖÖ×××רØÙÙÙÙÚÚÚÚÛÛÜÜÜÜÝÝÞÞßßààááââããääååææççèèééëëììííïïððòòóóõõööøøúúûûýýÿÿ  !!""####$$%%%%&&''''(((())))******++++,,,,,,------......////////0000000011111111112222222222223333333333334444444444444444555555555555555555666666666666666666666677777777777777777777777777778888888888888888888888888888888888999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÖÖÖÖ××××ØØØØÙÙÚÚÚÚÛÛÜÜÜÜÝÝÞÞßßààááââããääååææççééêêëëííîîððññóóôôööøøùùûûýýÿÿ  !!""##$$$$%%&&&&''''(((())))****++++,,,,,,------......////////000000001111111122222222222233333333333344444444444444555555555555555555666666666666666666667777777777777777777777777777888888888888888888888888888888889999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================================ÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÖÖÖÖ×××רØÙÙÙÙÚÚÛÛÜÜÜÜÝÝÞÞßßààááââããääååææèèééêêììííïïññòòôôöö÷÷ùùûûýýÿÿ  !!!!""##$$$$%%&&&&''(((())))****++++,,,,,,------......//////0000000011111111112222222222333333333333444444444444445555555555555555666666666666666666667777777777777777777777777788888888888888888888888888888888999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÓÓÓÓÓÓÔÔÔÔÕÕÕÕÖÖ××××ØØØØÙÙÚÚÛÛÛÛÜÜÝÝÞÞßßààááââããääææççèèêêëëííîîððòòóóõõ÷÷ùùûûýýÿÿ  !!!!""##$$%%%%&&''''(())))****++++++,,,,------......//////000000001111111122222222223333333333444444444444445555555555555555666666666666666666667777777777777777777777778888888888888888888888888888889999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================================ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÕÖÖÖÖ××ØØØØÙÙÚÚÚÚÛÛÜÜÝÝÞÞßßààááââããååææççééêêììîîïïññóóõõ÷÷ùùûûýýÿÿ  !!""""##$$%%&&&&''(((())))****++++,,,,------....////////0000001111111122222222223333333333444444444444555555555555555566666666666666666677777777777777777777777788888888888888888888888888889999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================================================================================================================================ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÕÖÖ×××רØÙÙÙÙÚÚÛÛÜÜÝÝÞÞßßààááââääååææèèééëëííïïððòòôôööøøúúýýÿÿ  !!""####$$%%&&''''(())))****++++,,,,----......//////00000000111111222222222233333333334444444444445555555555555566666666666666666677777777777777777777778888888888888888888888888899999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================================================================================================ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÑÑÑÑÑÑÒÒÒÒÓÓÓÓÔÔÔÔÕÕÖÖÖÖ××ØØØØÙÙÚÚÛÛÜÜÝÝÞÞßßààááããääååççèèêêììîîððòòôôööøøúúýýÿÿ  !!""##$$%%%%&&''(((())****++++,,,,----......//////0000001111111122222222333333333344444444445555555555555566666666666666667777777777777777777777888888888888888888888888889999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================================================================================================================================ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÓÔÔÕÕÕÕÖÖ×××רØÙÙÚÚÛÛÜÜÝÝÞÞßßààááããääææççééëëííïïññóóõõøøúúüüÿÿ  !!""##$$%%&&&&''(())))**++++,,,,----......////0000001111111122222222333333334444444444445555555555556666666666666666777777777777777777778888888888888888888888889999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÐÐÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÓÔÔÕÕÕÕÖÖ×רØÙÙÙÙÚÚÛÛÜÜÞÞßßààââããååææèèêêììîîððòòõõ÷÷úúüüÿÿ  !!""##$$%%&&''(((())****++,,,,----....//////000000111111222222223333333344444444445555555555556666666666666677777777777777777777888888888888888888888899999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÒÓÓÔÔÔÔÕÕÖÖÖÖ×רØÙÙÚÚÛÛÜÜÞÞßßààââããååççééëëííïïòòôô÷÷ùùüüÿÿ  !!""##$$%%&&''(())****++,,,,----....//////00001111112222222233333333444444445555555555556666666666666677777777777777777788888888888888888888889999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÒÓÓÔÔÔÔÕÕÖÖ×רØÙÙÚÚÛÛÜÜÝÝßßààââääææèèêêììîîññóóööùùüüÿÿ  !!""$$%%&&''(())))**++++,,----....////000000111122222222333333444444444455555555556666666666666677777777777777778888888888888888888899999999999999999999999999::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÒÓÓÔÔÕÕÖÖÖÖ×רØÚÚÛÛÜÜÝÝßßááââääææèèëëííððóóööùùüüÿÿ  !!##$$%%&&''(())**++++,,----....////0000111111222222333333444444445555555555666666666666777777777777777788888888888888888899999999999999999999999999::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÍÍÍÍÎÎÎÎÎÎÏÏÏÏÐÐÑÑÑÑÒÒÓÓÓÓÔÔÕÕÖÖ×רØÙÙÚÚÜÜÝÝßßááããååççééììïïòòõõøøûûÿÿ  !!##$$%%''(())****++,,----..////00001111112222223333334444444455555555666666666666777777777777778888888888888888999999999999999999999999::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÍÍÍÍÎÎÎÎÏÏÏÏÐÐÐÐÑÑÒÒÓÓÓÓÔÔÕÕÖÖ××ÙÙÚÚÜÜÝÝßßááããååèèêêííññôô÷÷ûûÿÿ  !!##$$&&''(())**++,,----..////0000111122222233333344444455555555666666666677777777777777888888888888888899999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÍÍÍÍÎÎÎÎÏÏÐÐÐÐÑÑÒÒÓÓÔÔÕÕÖÖ×רØÚÚÛÛÝÝßßááããææééììïïóó÷÷ûûÿÿ  ""##%%&&(())**++,,----..////00111122222233334444445555555566666666667777777777778888888888888899999999999999999999::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËÌÌÌÌÍÍÍÍÎÎÎÎÏÏÏÏÐÐÑÑÒÒÓÓÔÔÕÕÖÖØØÙÙÛÛÝÝßßááääççêêîîòòööúúÿÿ  ""$$%%''((**++,,--....//000011112222333344444455555566666666777777777777888888888888999999999999999999::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊÊÊËËËËÌÌÌÌÍÍÍÍÎÎÎÎÏÏÐÐÑÑÒÒÓÓÔÔÕÕ××ÙÙÚÚÜÜßßââååèèììððõõúúÿÿ  ""$$&&(())**,,--..////00111122223333444455555566666666777777777788888888889999999999999999::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<============================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊËËËËÌÌÌÌÍÍÍÍÎÎÏÏÐÐÑÑÒÒÓÓÔÔÖÖØØÚÚÜÜßßââææêêîîóóùùÿÿ  ""%%''))**++--..//000011222233444444555566666666777777778888888888999999999999::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==============================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊËËËËÌÌÍÍÎÎÎÎÏÏÑÑÒÒÓÓÕÕ××ÙÙÜÜßßããççììòòøøÿÿ  ##%%((**++--..//0011222233444455556666667777777788888888999999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÇÇÇÇÇÇÈÈÈÈÈÈÉÉÉÉÊÊËËËËÌÌÍÍÎÎÏÏÐÐÒÒÔÔÖÖØØÛÛßßããééïï÷÷ÿÿ ##&&))++--..//112222334455556666667777778888889999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÇÇÇÇÇÇÈÈÈÈÉÉÊÊÊÊËËÌÌÍÍÎÎÐÐÒÒÔÔ××ÚÚßßååììõõÿÿ  $$((**--//0011223344556666777777888899999999::::::::::::;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<======================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÆÆÆÆÇÇÇÇÈÈÈÈÉÉÊÊËËÌÌÎÎÏÏÒÒÕÕÙÙßßççòòÿÿ  %%**--//11224455666677778888999999::::::::;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<========================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÅÅÅÅÆÆÆÆÇÇÈÈÉÉÊÊÌÌÎÎÒÒ××ßßììÿÿ ((--002244667777889999::::::;;;;;;;;<<<<<<<<<<<<============================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÄÄÅÅÆÆÇÇÉÉÌÌÒÒßßÿÿ --22667799::::;;;;<<<<<<==============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼»»»»»»ºº¹¹¸¸··µµ²²¬¬ŸŸ__RRMMIIHHFFEEEEDDDDCCCCCCBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»ºººººº¹¹¹¹¸¸¸¸··¶¶µµ´´²²°°¬¬§§ŸŸ’’ll__WWRROOMMKKIIHHHHGGFFFFEEEEEEDDDDDDDDCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»ºººººººººº¹¹¹¹¹¹¸¸¸¸····¶¶¶¶µµ´´³³²²°°¯¯¬¬©©¥¥ŸŸ——ŒŒrrgg__ZZUURRPPNNMMKKJJIIIIHHHHGGGGFFFFFFEEEEEEEEDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»ºººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸······¶¶¶¶µµ´´´´³³²²±±°°®®¬¬ªª§§¤¤ŸŸ™™’’‰‰uullee__[[WWUURRPPOONNMMLLKKJJIIIIHHHHHHGGGGFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸······¶¶¶¶¶¶µµµµ´´³³³³²²±±°°¯¯®®¬¬ªª¨¨¦¦££ŸŸ››••‡‡wwppiidd__\\YYVVTTRRQQPPNNMMMMLLKKKKJJIIIIIIHHHHHHGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸········¶¶¶¶¶¶µµµµµµ´´´´³³³³²²±±°°°°¯¯­­¬¬««©©§§¥¥¢¢ŸŸ››——’’ŒŒ††yyrrllggcc__\\ZZWWUUTTRRQQPPOONNMMMMLLKKKKJJJJIIIIIIHHHHHHHHGGGGGGGGFFFFFFFFFFFFEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸··········¶¶¶¶¶¶µµµµµµ´´´´³³³³³³²²±±±±°°¯¯®®­­¬¬««ªª¨¨¦¦¤¤¢¢ŸŸœœ˜˜””‹‹……yyttoojjffbb__]]ZZXXWWUUTTRRQQPPOOOONNMMMMLLKKKKKKJJJJIIIIIIIIHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸··········¶¶¶¶¶¶¶¶µµµµµµ´´´´´´³³³³²²²²±±±±°°°°¯¯®®­­¬¬««ªª©©§§¥¥¤¤¢¢ŸŸœœ™™––’’ŽŽ‰‰„„zzuuqqlliieebb__]][[YYWWVVUUSSRRQQPPPPOONNNNMMMMLLLLKKKKJJJJJJIIIIIIIIHHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´³³³³²²²²±±±±°°°°¯¯¯¯®®­­¬¬««ªª©©¨¨¦¦¥¥££¡¡ŸŸšš——””ŒŒˆˆ„„{{vvrrnnkkggddbb__]][[ZZXXWWUUTTSSRRQQQQPPOOOONNNNMMMMLLLLKKKKKKJJJJJJIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´³³³³³³²²²²²²±±±±°°°°¯¯®®®®­­¬¬««ªª©©¨¨§§¦¦¤¤££¡¡ŸŸ››˜˜••’’‹‹‡‡ƒƒ{{wwsspplliiffddbb__]]\\ZZYYWWVVUUTTSSRRRRQQPPPPOONNNNMMMMMMLLLLKKKKKKKKJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´³³³³³³²²²²²²±±±±°°°°¯¯¯¯®®®®­­¬¬««««ªª©©¨¨§§¥¥¤¤££¡¡ŸŸ››™™––””‘‘ŠŠ‡‡ƒƒ||xxttqqnnkkhhffccaa__^^\\[[YYXXWWVVUUTTSSRRRRQQPPPPOOOONNNNMMMMMMLLLLLLKKKKKKJJJJJJJJIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´³³³³³³³³²²²²²²±±±±°°°°°°¯¯¯¯®®­­­­¬¬««««ªª©©¨¨§§¦¦¥¥¤¤¢¢¡¡ŸŸ››™™——••’’ŒŒ‰‰††ƒƒ||yyuurroolljjggeeccaa__^^\\[[ZZXXWWVVUUUUTTSSRRRRQQPPPPOOOONNNNNNMMMMMMLLLLLLKKKKKKKKJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµ´´´´´´´´´´³³³³³³³³²²²²²²±±±±±±°°°°¯¯¯¯®®®®­­­­¬¬¬¬««ªª©©©©¨¨§§¦¦¥¥££¢¢¡¡ŸŸžžœœšš˜˜––““‘‘ŽŽ‹‹ˆˆ……‚‚||yyvvssppnnkkiiggeeccaa__^^\\[[ZZYYXXWWVVUUTTTTSSRRRRQQQQPPPPOOOONNNNNNMMMMMMLLLLLLKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´³³³³³³³³³³²²²²²²±±±±±±°°°°°°¯¯¯¯®®®®­­­­¬¬¬¬««ªªªª©©¨¨§§¦¦¥¥¤¤££¢¢¡¡ŸŸžžœœšš˜˜––””’’‹‹ˆˆ……‚‚||yywwttqqoolljjhhffddbbaa__^^]][[ZZYYXXWWWWVVUUTTTTSSRRRRQQQQPPPPOOOOOONNNNMMMMMMMMLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´³³³³³³³³²²²²²²²²±±±±±±°°°°°°¯¯¯¯¯¯®®®®­­­­¬¬¬¬««ªªªª©©¨¨¨¨§§¦¦¥¥¤¤££¢¢  ŸŸžžœœ››™™——••““‘‘ŒŒŠŠ‡‡……‚‚}}zzwwuurrppnnkkiiggffddbbaa__^^]]\\[[ZZYYXXWWVVUUUUTTSSSSRRRRQQQQPPPPPPOOOONNNNNNMMMMMMMMLLLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³²²²²²²²²±±±±±±±±°°°°°°¯¯¯¯®®®®®®­­­­¬¬¬¬««««ªª©©©©¨¨§§¦¦¥¥¥¥¤¤££¢¢  ŸŸžžœœ››™™˜˜––””’’ŽŽŒŒ‰‰‡‡„„‚‚}}zzxxuussqqoolljjiiggeeddbbaa__^^]]\\[[ZZYYXXWWWWVVUUUUTTSSSSRRRRQQQQPPPPPPOOOOOONNNNNNMMMMMMMMLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³²²²²²²²²±±±±±±±±°°°°°°¯¯¯¯¯¯®®®®®®­­­­¬¬¬¬««««ªª©©©©¨¨¨¨§§¦¦¥¥¤¤££¢¢¡¡  ŸŸžž››šš˜˜——••““‘‘‹‹‰‰††„„‚‚}}{{xxvvttqqoommkkjjhhffeeccbbaa__^^]]\\[[ZZYYYYXXWWVVVVUUTTTTSSSSRRRRQQQQQQPPPPOOOOOONNNNNNNNMMMMMMMMLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³²²²²²²²²±±±±±±±±°°°°°°°°¯¯¯¯¯¯®®®®­­­­­­¬¬¬¬««««ªªªª©©¨¨¨¨§§¦¦¦¦¥¥¤¤££¢¢¡¡  ŸŸžž››šš™™——––””’’ŽŽŒŒŠŠˆˆ††„„‚‚}}{{yyvvttrrppnnllkkiiggffddccbbaa__^^]]\\[[ZZZZYYXXWWWWVVUUUUTTTTSSSSRRRRQQQQQQPPPPPPOOOOOONNNNNNNNMMMMMMMMLLLLLLLLLLKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²±±±±±±±±°°°°°°¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬««««ªªªª©©©©¨¨§§§§¦¦¥¥¥¥¤¤££¢¢¡¡  ŸŸžžœœšš™™˜˜––••““‘‘ŽŽŒŒŠŠˆˆ††„„}}{{yywwuussqqoommlljjhhggeeddccbb``__^^]]\\\\[[ZZYYXXXXWWVVVVUUUUTTTTSSSSRRRRRRQQQQPPPPPPOOOOOOOONNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²±±±±±±±±°°°°°°°°¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬««««ªªªª©©©©¨¨¨¨§§§§¦¦¥¥¤¤¤¤££¢¢¡¡  ŸŸžžœœ››™™˜˜——••””’’‹‹‰‰‡‡……ƒƒ}}{{yywwuussrrppnnllkkiihhffeeddccbb``__^^]]]]\\[[ZZYYYYXXWWWWVVVVUUUUTTTTSSSSRRRRRRQQQQPPPPPPPPOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬««««««ªªªª©©©©¨¨§§§§¦¦¦¦¥¥¤¤££££¢¢¡¡  ŸŸžžœœ››šš˜˜——––””““‘‘ŽŽŒŒ‹‹‰‰‡‡……ƒƒ}}{{yyxxvvttrrppoommlljjiiggffeeddbbaa``__^^^^]]\\[[ZZZZYYXXXXWWWWVVUUUUTTTTTTSSSSRRRRRRQQQQQQPPPPPPOOOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯®®®®®®®®­­­­­­¬¬¬¬««««««ªªªª©©©©¨¨¨¨§§§§¦¦¥¥¥¥¤¤££££¢¢¡¡  ŸŸžžœœ››šš™™˜˜––••””’’‘‘ŒŒŠŠˆˆ‡‡……ƒƒ}}||zzxxvvttssqqoonnllkkjjhhggffeeccbbaa``__^^^^]]\\[[[[ZZYYYYXXWWWWVVVVUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­¬¬¬¬««««««ªªªª©©©©¨¨¨¨§§§§¦¦¦¦¥¥¤¤¤¤££¢¢¢¢¡¡  ŸŸžžœœ››šš™™˜˜——••””““‘‘ŽŽ‹‹ŠŠˆˆ††……ƒƒ}}||zzxxwwuussrrppoommlljjiihhggeeddccbbaa``____^^]]\\[[[[ZZYYYYXXXXWWWWVVVVUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®­­­­­­­­¬¬¬¬««««««ªªªªªª©©©©¨¨¨¨§§§§¦¦¥¥¥¥¤¤¤¤££¢¢¢¢¡¡  ŸŸžžœœ››šš™™˜˜——––••““’’‘‘ŽŽŒŒ‹‹‰‰ˆˆ††„„ƒƒ~~||zzyywwuuttrrqqoonnllkkjjiiggffeeddccbbaa``____^^]]\\\\[[ZZZZYYXXXXWWWWVVVVUUUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬««««ªªªªªª©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤££££¢¢¡¡¡¡  ŸŸžžœœ››šš™™——––••””““‘‘ŒŒŠŠ‰‰‡‡††„„‚‚~~||zzyywwvvttssqqppnnmmllkkiihhggffeeddccbbaa``____^^]]\\\\[[ZZZZYYYYXXXXWWWWVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­¬¬¬¬¬¬¬¬««««ªªªªªª©©©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤££££¢¢¡¡¡¡  ŸŸžžžžœœ››šš™™˜˜——––””““’’‘‘ŽŽ‹‹ŠŠˆˆ‡‡……„„‚‚~~||{{yyxxvvuussrrppoonnllkkjjiihhggffeeddccbbaa``____^^]]\\\\[[[[ZZYYYYXXXXWWWWVVVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­¬¬¬¬¬¬¬¬««««««ªªªª©©©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¡¡¡¡  ŸŸžžžžœœ››šš™™˜˜——––••””““‘‘ŽŽŒŒ‹‹ŠŠˆˆ‡‡……„„‚‚~~||{{yyxxvvuuttrrqqppnnmmllkkjjhhggffeeddddccbbaa``____^^]]]]\\[[[[ZZZZYYYYXXXXWWWWVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­¬¬¬¬¬¬¬¬««««««ªªªªªª©©©©¨¨¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤¤¤££¢¢¢¢¡¡¡¡  ŸŸžžžžœœ››šš™™˜˜——––••””““’’‘‘ŽŽŒŒ‹‹‰‰ˆˆ††……„„‚‚~~||{{yyxxwwuuttssqqppoonnllkkjjiihhggffeeddccbbbbaa``____^^]]]]\\[[[[ZZZZYYYYXXXXWWWWWWVVVVUUUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨§§§§¦¦¦¦¦¦¥¥¥¥¤¤££££¢¢¢¢¡¡    ŸŸžžžžœœ››šššš™™˜˜——––••””““’’ŽŽ‹‹ŠŠ‰‰ˆˆ††……ƒƒ‚‚~~||{{zzxxwwvvttssrrqqoonnmmllkkjjiihhggffeeddccbbbbaa``____^^]]]]\\\\[[ZZZZYYYYXXXXXXWWWWVVVVVVUUUUTTTTTTTTSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡    ŸŸžžžžœœ››››šš™™˜˜——––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰‡‡††……ƒƒ‚‚~~}}{{zzyywwvvuussrrqqppoonnllkkjjiihhggffffeeddccbbbbaa``____^^]]]]\\\\[[[[ZZZZYYYYXXXXWWWWWWVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸········································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««ªªªªªªªª©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡    ŸŸžžžžœœœœ››šš™™˜˜————––••””““’’ŽŽŒŒ‹‹ŠŠˆˆ‡‡††„„ƒƒ‚‚~~}}{{zzyywwvvuuttssqqppoonnmmllkkjjiihhggffeeeeddccbbaaaa``____^^^^]]\\\\[[[[ZZZZYYYYXXXXXXWWWWVVVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··········································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§¦¦¦¦¥¥¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡    ŸŸžžžžœœœœ››šš™™™™˜˜——––••””““’’‘‘ŽŽŒŒŠŠ‰‰ˆˆ‡‡††„„ƒƒ‚‚~~}}{{zzyyxxwwuuttssrrqqppoommllkkjjjjiihhggffeeddddccbbaaaa``____^^^^]]\\\\[[[[ZZZZYYYYYYXXXXWWWWWWVVVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··········································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¥¥¥¥¤¤¤¤¤¤££££¢¢¡¡¡¡    ŸŸŸŸžžœœ››šššš™™˜˜——––••””””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡……„„ƒƒ‚‚€€~~}}||zzyyxxwwvvttssrrqqppoonnmmllkkjjiihhggggffeeddccccbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYXXXXXXWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§¦¦¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžœœ››šššš™™˜˜————––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰‡‡††……„„ƒƒ‚‚€€~~}}||{{yyxxwwvvuuttssqqppoonnmmllkkkkjjiihhggffffeeddccccbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYYYXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¥¥¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžœœ››››šš™™˜˜˜˜——––••””““““’’‘‘ŽŽŒŒ‹‹‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{yyxxwwvvuuttssrrqqppoonnmmllkkjjiihhhhggffeeeeddccbbbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¤¤¤¤¤¤££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžœœ››››šš™™™™˜˜——––––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyywwvvuuttssrrqqppoonnmmllllkkjjiihhggggffeeddddccbbbbaaaa``____^^^^]]]]\\\\[[[[ZZZZZZYYYYXXXXXXWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¤¤¤¤¤¤££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžœœœœ››šššš™™˜˜————––••””““““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ€€~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmllkkjjiiiihhggffffeeddddccbbbbaaaa``____^^^^]]]]\\\\[[[[[[ZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUUUTTTTTTSSSSSSSSSSRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤££££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžœœœœ››šššš™™˜˜˜˜——––••••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„‚‚€€~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmllllkkjjiihhhhggffeeeeddccccbbbbaa````____^^^^]]]]\\\\\\[[[[ZZZZYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤££££¢¢¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœœ››››šš™™™™˜˜——––––••””““’’’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuttssrrqqppoooonnmmllkkjjjjiihhggggffeeeeddccccbbbbaa````____^^^^]]]]\\\\\\[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§§§¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££¢¢¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœœ››››šš™™™™˜˜————––••””””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuttssssrrqqppoonnmmllllkkjjiiiihhggffffeeddddccccbbbbaa````____^^^^]]]]]]\\\\[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªªªª©©©©©©¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸžžžžœœœœ››››šššš™™˜˜˜˜——––••••””““’’’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvvvuuttssrrqqppoonnnnmmllkkkkjjiihhhhggffffeeddddccccbbaaaa````____^^^^]]]]]]\\\\[[[[[[ZZZZZZYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤££££££¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸžžžžœœ››››šššš™™˜˜˜˜——––––••””””““’’‘‘ŽŽŒŒ‹‹‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyyyxxwwvvuuttssrrqqppppoonnmmllllkkjjiiiihhggggffeeeeddddccbbbbaaaa````____^^^^^^]]]]\\\\[[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWWVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¡¡¡¡      ŸŸŸŸžžžžœœœœ››šššš™™™™˜˜————––••••””““’’’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{{{zzyyxxwwvvuuttssrrrrqqppoonnnnmmllkkkkjjiihhhhggggffeeeeddddccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šš™™™™˜˜˜˜——––––••””””““’’‘‘‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡‡‡††……„„ƒƒ‚‚€€~~}}||||{{zzyyxxwwvvuuttttssrrqqppoooonnmmllllkkjjjjiihhhhggffffeeeeddccccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££¢¢¢¢¢¢¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šššš™™˜˜˜˜——––––••••””““’’’’‘‘ŽŽŒŒ‹‹‹‹ŠŠ‰‰ˆˆ‡‡††††……„„ƒƒ‚‚€€~~}}}}||{{zzyyxxwwvvvvuuttssrrqqqqppoonnnnmmllkkkkjjiiiihhggggffffeeddddccccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEºººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜————––••••””““““’’‘‘‘‘ŽŽŽŽŒŒ‹‹ŠŠŠŠ‰‰ˆˆ‡‡††…………„„ƒƒ‚‚€€~~}}}}||{{zzyyxxwwwwvvuuttssrrrrqqppoooonnmmllllkkjjjjiihhhhggggffeeeeddddccccbbbbaaaa````______^^^^]]]]]]\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEºººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜˜——––––••””””““’’’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰‰‰ˆˆ‡‡††……„„„„ƒƒ‚‚€€~~~~}}||{{zzyyxxxxwwvvuuttttssrrqqppppoonnmmmmllkkkkjjiiiihhhhggffffeeeeddddccccbbbbaaaa````______^^^^]]]]]]\\\\[[[[[[ZZZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVVVUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEºººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜˜————––••••””““““’’‘‘‘‘ŽŽŒŒŒŒ‹‹ŠŠ‰‰ˆˆˆˆ‡‡††……„„ƒƒƒƒ‚‚€€~~~~}}||{{zzyyyyxxwwvvuuuuttssrrqqqqppoooonnmmllllkkjjjjiiiihhggggffffeeeeddddccccbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEºº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžœœœœœœ››››šššš™™˜˜˜˜————––––••””””““’’’’‘‘ŽŽŽŽŒŒ‹‹‹‹ŠŠ‰‰ˆˆ‡‡‡‡††……„„ƒƒƒƒ‚‚€€~~~~}}||{{zzyyyyxxwwvvvvuuttssrrrrqqppppoonnmmmmllkkkkjjjjiihhhhggggffffeeddddccccbbbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXWWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜————––––••••””““““’’‘‘‘‘ŽŽŒŒ‹‹ŠŠŠŠ‰‰ˆˆ‡‡††††……„„ƒƒ‚‚‚‚€€~~~~}}||{{zzzzyyxxwwvvvvuuttssssrrqqqqppoonnnnmmllllkkkkjjiiiihhhhggffffeeeeddddccccbbbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜˜˜————––••••””””““’’’’‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠ‰‰‰‰ˆˆ‡‡††††……„„ƒƒ‚‚‚‚€€~~~~}}||{{{{zzyyxxwwwwvvuuttttssrrqqqqppoooonnmmmmllkkkkjjjjiiiihhggggffffeeeeddddccccbbbbaaaaaa````______^^^^]]]]]]\\\\\\[[[[[[[[ZZZZZZYYYYYYYYXXXXXXWWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžžžœœœœ››››šššš™™™™˜˜˜˜————––––••””””““““’’‘‘‘‘ŽŽŒŒ‹‹‹‹ŠŠ‰‰ˆˆˆˆ‡‡††…………„„ƒƒ‚‚‚‚€€~~~~}}||{{{{zzyyxxxxwwvvuuuuttssrrrrqqppppoonnnnmmllllkkkkjjiiiihhhhggggffffeeeeddddccccbbbbaaaaaa````______^^^^^^]]]]\\\\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžžžœœœœ››››šššš™™™™˜˜˜˜————––––••••””””““’’’’‘‘‘‘ŽŽŒŒ‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡††…………„„ƒƒ‚‚‚‚€€~~~~}}||{{{{zzyyxxxxwwvvuuuuttssssrrqqqqppoooonnmmmmllllkkjjjjiiiihhhhggggffffeeeeddddccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžžžœœœœ››››››šššš™™™™˜˜˜˜————––––••””””““““’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰ˆˆ‡‡‡‡††……„„„„ƒƒ‚‚‚‚€€~~}}||||{{zzyyyyxxwwvvvvuuttttssrrrrqqppppoonnnnmmllllkkkkjjjjiihhhhggggffffeeeeddddddccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸········································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸŸŸžžžžžžœœœœœœ››››šššš™™™™˜˜˜˜————––––••••””””““’’’’‘‘‘‘ŽŽŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆ‡‡‡‡††……„„„„ƒƒ‚‚€€~~}}||||{{zzyyyyxxwwwwvvuuttttssrrrrqqppppoooonnmmmmllllkkjjjjiiiihhhhggggffffeeeeddddccccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[[[ZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸········································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸŸŸžžžžžžœœœœœœ››››šššš™™™™˜˜˜˜————––––••••””””““““’’‘‘‘‘ŽŽŽŽŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆ‡‡††††……„„„„ƒƒ‚‚€€~~}}||||{{zzyyyyxxwwwwvvuuuuttssssrrqqqqppoooonnnnmmllllkkkkjjjjiiiihhhhggggffffeeeeddddccccbbbbbbaaaa``````______^^^^^^]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸········································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡¡¡    ŸŸŸŸŸŸžžžžžžœœœœœœ››››šššš™™™™™™˜˜˜˜————––––••••””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡††††……„„„„ƒƒ‚‚€€~~}}||||{{zzzzyyxxxxwwvvuuuuttssssrrrrqqppppoonnnnmmmmllllkkjjjjiiiihhhhggggffffeeeeeeddddccccbbbbbbaaaa``````______^^^^^^]]]]]]\\\\\\\\[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœ››››šššššš™™™™˜˜˜˜————––––••••””””““““’’’’‘‘ŽŽŒŒ‹‹‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡††††……„„ƒƒƒƒ‚‚€€~~}}||||{{zzzzyyxxxxwwvvvvuuttttssrrrrqqqqppoooonnnnmmllllkkkkjjjjiiiihhhhggggffffeeeeddddddccccbbbbbbaaaa``````______^^^^^^]]]]]]\\\\\\\\[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœ››››››šššš™™™™˜˜˜˜————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆ‡‡‡‡††…………„„ƒƒƒƒ‚‚€€~~}}}}||{{zzzzyyxxxxwwvvvvuuuuttssssrrqqqqppppoonnnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeeddddddccccbbbbbbaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœ››››››šššš™™™™˜˜˜˜˜˜————––––••••””””““““’’’’‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††…………„„ƒƒƒƒ‚‚€€~~}}}}||{{{{zzyyyyxxwwwwvvuuuuttssssrrrrqqppppoooonnnnmmllllkkkkjjjjiiiihhhhggggffffffeeeeddddccccccbbbbbbaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ››››šššš™™™™™™˜˜˜˜————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡††††…………„„ƒƒƒƒ‚‚€€~~}}}}||{{{{zzyyyyxxwwwwvvuuuuttttssrrrrqqqqppoooonnnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeeeeddddccccccbbbbaaaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ››››šššššš™™™™˜˜˜˜——————––––••••””””““““’’’’‘‘ŽŽŽŽŒŒ‹‹‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡††††……„„„„ƒƒƒƒ‚‚€€~~}}}}||{{{{zzyyyyxxwwwwvvvvuuttttssssrrqqqqppppoooonnmmmmllllkkkkjjjjiiiihhhhhhggggffffeeeeeeddddccccccbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ››››šššššš™™™™˜˜˜˜˜˜————––––••••””””““““’’’’‘‘‘‘ŽŽŒŒŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆˆˆ‡‡††††……„„„„ƒƒ‚‚‚‚€€~~}}}}||{{{{zzyyyyxxxxwwvvvvuuuuttssssrrrrqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhggggggffffeeeeddddddccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššš™™™™™™˜˜˜˜————––––••••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††††……„„„„ƒƒ‚‚‚‚€€~~}}}}||{{{{zzzzyyxxxxwwwwvvuuuuttttssrrrrqqqqppppoooonnmmmmllllkkkkjjjjjjiiiihhhhggggffffffeeeeddddddccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžžžœœœœœœ››››››šššš™™™™™™˜˜˜˜——————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒ‹‹‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡‡‡††…………„„„„ƒƒ‚‚‚‚€€~~}}}}||||{{zzzzyyxxxxwwwwvvuuuuttttssssrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhhhggggffffeeeeeeddddccccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœ››››››šššššš™™™™˜˜˜˜˜˜————––––••••””””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††…………„„„„ƒƒ‚‚‚‚€€€€~~}}}}||||{{zzzzyyyyxxwwwwvvvvuuttttssssrrrrqqqqppoooonnnnmmmmllllkkkkkkjjjjiiiihhhhggggggffffeeeeeeddddccccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸······················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››šššššš™™™™˜˜˜˜˜˜————––––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡††††…………„„ƒƒƒƒ‚‚‚‚€€€€~~}}}}||||{{zzzzyyyyxxwwwwvvvvuuuuttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiiihhhhggggffffffeeeeddddddccccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG··················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››šššššš™™™™™™˜˜˜˜——————––––••••””””““““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††††…………„„ƒƒƒƒ‚‚‚‚€€€€~~}}}}||||{{{{zzyyyyxxxxwwvvvvuuuuttttssssrrqqqqppppoooonnnnmmmmllllkkkkkkjjjjiiiihhhhhhggggffffffeeeeddddddccccccbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG········································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššš™™™™™™˜˜˜˜˜˜————––––••••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††††…………„„ƒƒƒƒ‚‚‚‚€€€€~~}}}}||||{{{{zzyyyyxxxxwwwwvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiiihhhhggggggffffeeeeeeddddddccccccbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGG······························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™˜˜˜˜˜˜————––––––••••””””““““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††……„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}||||{{{{zzyyyyxxxxwwwwvvvvuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiihhhhhhggggffffffeeeeeeddddddccccbbbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGG······················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜——————––––••••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}||||{{{{zzzzyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjjjiiiihhhhhhggggffffffeeeeeeddddccccccbbbbbbaaaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGG············································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜————––––––••••””””““““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}||||{{{{zzzzyyyyxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiiiihhhhggggggffffffeeeeddddddccccccbbbbbbaaaaaaaa``````________^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH··································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžœœœœœœœœ››››››šššš™™™™™™˜˜˜˜˜˜————––––––••••””””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚€€€€~~~~}}||||{{{{zzzzyyyyxxxxwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnnmmmmllllkkkkjjjjjjiiiihhhhhhggggggffffeeeeeeddddddccccccbbbbbbaaaaaaaa``````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH··························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™˜˜˜˜˜˜——————––––••••••””””““““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚€€€€~~~~}}}}||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiiiihhhhhhggggffffffeeeeeeddddddccccccbbbbbbaaaaaaaa``````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜——————––––––••••””””””““““’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒ‚‚‚‚€€€€~~~~}}}}||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnnmmmmllllkkkkkkjjjjiiiiiihhhhggggggffffffeeeeeeddddddccccccbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH······¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜————––––––••••••””””““““’’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒ‚‚‚‚€€€€~~~~}}}}||||{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppppoooonnnnmmmmllllllkkkkjjjjjjiiiihhhhhhggggggffffeeeeeeddddddccccccccbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜——————––––••••••””””””““““’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††……„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzyyyyxxxxwwwwvvvvuuuuttttssssssrrrrqqqqppppoooonnnnnnmmmmllllkkkkkkjjjjiiiiiihhhhhhggggffffffeeeeeeddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœ››››››››šššššš™™™™™™˜˜˜˜——————––––––••••””””””““““’’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooooonnnnmmmmllllllkkkkjjjjjjiiiihhhhhhggggggffffffeeeeeeddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜——————––––••••••””””““““““’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqqqppppoooonnnnmmmmmmllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜——————––––––••••””””””““““’’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssssrrrrqqqqppppoooooonnnnmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜——————––––––••••••””””““““““’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttttssssrrrrqqqqppppppoooonnnnmmmmmmllllkkkkkkjjjjjjiiiihhhhhhggggggffffffeeeeeeddddddddccccccbbbbbbaaaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››šššššššš™™™™™™˜˜˜˜˜˜——————––––••••••””””””““““’’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvvuuuuttttssssrrrrqqqqqqppppoooonnnnnnmmmmllllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddccccccccbbbbbbaaaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœ››››››››šššššš™™™™™™˜˜˜˜˜˜——————––––––••••••””””““““““’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuuttttssssrrrrrrqqqqppppoooooonnnnmmmmmmllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddccccccccbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœ››››››››šššššš™™™™™™˜˜˜˜˜˜——————––––––••••••””””””““““’’’’’’‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††………………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvuuuuttttttssssrrrrqqqqppppppoooonnnnnnmmmmllllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeeeddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœ››››››››šššššš™™™™™™™™˜˜˜˜˜˜——————––––––••••””””””““““““’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{{{zzzzyyyyxxxxwwwwvvvvuuuuuuttttssssrrrrqqqqqqppppoooooonnnnmmmmmmllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››šššššššš™™™™™™˜˜˜˜˜˜——————––––––••••••””””””““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{{{zzzzyyyyxxxxwwwwvvvvvvuuuuttttssssrrrrrrqqqqppppppoooonnnnnnmmmmllllllkkkkkkjjjjjjiiiihhhhhhhhggggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››šššššššš™™™™™™˜˜˜˜˜˜——————––––––••••••””””””““““““’’’’’’‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||||{{{{zzzzyyyyxxxxwwwwwwvvvvuuuuttttssssssrrrrqqqqqqppppoooooonnnnmmmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggggffffffffeeeeeeddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššš™™™™™™˜˜˜˜˜˜˜˜——————––––––••••••””””””““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€~~~~}}}}||||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuuttttttssssrrrrrrqqqqppppoooooonnnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeeeddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššš™™™™™™™™˜˜˜˜˜˜——————––––––••••••””””””““““““’’’’’’‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††††…………„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€~~~~}}}}}}||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuuuuttttssssrrrrrrqqqqppppppoooonnnnnnmmmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™˜˜˜˜˜˜——————––––––––••••••””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡††††††…………„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€~~~~}}}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvvvuuuuttttssssssrrrrqqqqqqppppoooooonnnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœ››››››››šššššššš™™™™™™˜˜˜˜˜˜˜˜——————––––––••••••””””””““““““’’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††………………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqppppppoooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeeeddddddddccccccbbbbbbbbaaaaaaaaaa````````__________^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››šššššššš™™™™™™™™˜˜˜˜˜˜——————––––––••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††………………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}}}||||{{{{zzzzzzyyyyxxxxwwwwwwvvvvuuuuttttttssssrrrrrrqqqqqqppppoooooonnnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeeeddddddccccccccbbbbbbbbaaaaaaaaaa````````__________^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››šššššš™™™™™™™™˜˜˜˜˜˜————————––––––••••••””””””““““““’’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††…………„„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||{{{{zzzzzzyyyyxxxxwwwwwwvvvvuuuuuuttttssssssrrrrqqqqqqppppppoooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffffeeeeeeeeddddddccccccccbbbbbbbbaaaaaaaaaa````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™˜˜˜˜˜˜˜˜——————––––––••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††…………„„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqppppppoooooonnnnmmmmmmllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggffffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™˜˜˜˜˜˜˜˜——————––––––––••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††…………„„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqqqppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜————————––––––••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††…………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||{{{{{{zzzzyyyyyyxxxxwwwwwwvvvvuuuuuuttttssssssrrrrqqqqqqppppppoooooonnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜——————––––––••••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||||{{{{zzzzyyyyyyxxxxwwwwwwvvvvuuuuuuttttssssssrrrrrrqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhhhggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššš™™™™™™˜˜˜˜˜˜˜˜——————––––––––••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{zzzzyyyyyyxxxxwwwwwwvvvvvvuuuuttttttssssrrrrrrqqqqqqppppppoooonnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhggggggggffffffffeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜————————––––––••••••””””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssssrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜————————––––––••••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††………………„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppoooooonnnnnnmmmmmmllllllkkkkkkkkjjjjjjiiiiiihhhhhhhhggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜——————––––––––••••••””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††…………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––••••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††††…………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{{{zzzzyyyyyyxxxxwwwwwwvvvvvvuuuuttttttssssssrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››››šššššššš™™™™™™™™˜˜˜˜˜˜————————––––––––••••••””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††††…………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxwwwwwwvvvvvvuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜——————––––––––••••••””””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††………………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––••••••••””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqppppppoooooonnnnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••””””””””““““““’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzzzyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜——————––––––––••••••••””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzzzyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppppoooooonnnnnnnnmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••””””””””““““““’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††…………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††…………„„„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜————————––––––••••••••””””””””““““““’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••””””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbaaaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””““““““““’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddddccccccccbbbbbbbbbbaaaaaaaaaa````````````__________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––••••••””””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzzzyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””““““““““’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––••••••••””””””““““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††…………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqqqppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLL²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––––••••••••””””””””““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttttssssssrrrrrrqqqqqqqqppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrrrqqqqqqppppppoooooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––••••••••••””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvvvuuuuuuttttttssssssrrrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜——————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMM±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜——————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeddddddddddccccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMM±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMM±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxwwwwwwvvvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMM±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜——————————––––––––••••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMM±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrrrqqqqqqqqppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––••••••••””””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜——————————––––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––––••••••••””””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––••••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––––••••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––••••••••••””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••””””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––••••••••••””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••””””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONN°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––••••••••••””””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””””““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••””””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””””““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––––••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››››šššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––––••••••••••””””””””””““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••••””””””””””““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPP®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••””””””””””””““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPP®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPP®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPP®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPP®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––––••••••••••””””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••””””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––––••••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••””””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••••””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••””””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQ­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––••••••••••••””””””””””””““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””””““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””””““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••””””””””””””““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSS««««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSS««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••””””””””””””””““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSS««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSS««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSS««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSS««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTT««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTT««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––••••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````______________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––––••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUU©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUU©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUU©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••••””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUU©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUU©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUU©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUU©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVV©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVV©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————————––––––––––––––••••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————————––––––––––––––––••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWW¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWW§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWW§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWW§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWW§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWmlt-6.20.0/src/tests/clock16pal.pgm000066400000000000000000031240211362234133600167760ustar00rootroot00000000000000P5 720 576 65535 ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''((((×××××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßàààààààààààààààààààààáááááááááááááááááááááâââââââââââââââââââãããããããããããããããããããäääääääääääääääääåååååååååååååååååæææææææææææææææææçççççççççççççççççèèèèèèèèèèèèèèèèéééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëìììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððððññññññññññññò$òòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ Ø ÜÝÞßàãäæçéêëí î!!!!!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""""""ð###########################ñ$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''''''''õ'õ((((×××××××××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßßßßßßßàFààààààààààààààààààààáGááááááááááááááááááááââââââââââââââââââââãIããããããããããããããããããääääääääääääääääääåKååååååååååååååååæLææææææææææææææææççççççççççççççççèNèèèèèèèèèèèèèèèèééééééééééééééêPêêêêêêêêêêêêêêëQëëëëëëëëëëëëëëììììììììììììììíSííííííííííííîTîîîîîîîîîîîîïUïïïïïïïïïïïïðVððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôZôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøù_ùùùùùùùùùùú`úúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿžŸ ¡ ¥ §­®±´µ¶·¸¹ º!!!!!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""""""¼###########################½$$$$$$$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''''''''''''Á((((((((×××××××××××××××××××××××××××××רqØqØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßßßßßßßàyààààààààààààààààààààázááááááááááááááááááâ{ââââââââââââââââââââããããããããããããããããããä}ääääääääääääääääääååååååååååååååååååææææææææææææææææç€ççççççççççççççççèèèèèèèèèèèèèèèé‚ééééééééééééééêƒêêêêêêêêêêêêêêëëëëëëëëëëëëëëì…ììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððñŠññññññññññññòòòòòòòòòòòòóŒóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷ø‘øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúû”ûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿjo s vwxy{|}~€ƒ„…† ‡!!!!!!!!!!!!!!!!!!!!!!!ˆ!ˆ"""""""""""""""""""""""""‰###########################Š$$$$$$$$$$$$$$$$$$$$$$$$$$$‹$‹%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''Ž'Ž((((((((((×××××××××××××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛܩܩÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßßßßßßßà­ààààààààààààààààààààá®ááááááááááááááááááâ¯ââââââââââââââââââã°ããããããããããããããããããä±ääääääääääääääääå²ååååååååååååååååæ³ææææææææææææææææç´ççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêë¸ëëëëëëëëëëëëëëììììììììììììììíºííííííííííííî»îîîîîîîîîîîîï¼ïïïïïïïïïïïïððððððððððððððññññññññññññò¿òòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõÂõõõõõõõõõõöÃöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüÉüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ5 < = AFKLMNPQR S!!!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""""U"U#########################V#V$$$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''''''Z((((((((((((((××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((ÖÖ××××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""############################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((ÖÖ× ×××××××××××××××××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßßàààààààààààààààààààààááááááááááááááááááááâââââââââââââââââââãããããããããããããããããããäääääääääääääääääåååååååååååååååååæææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèéééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëëììììììììììììììíííííííííííííî îîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððñ#ññññññññññññòòòòòòòòòòòòó%óóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ Ú ÜÝáãäæèéêëìí î!!!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""""""ð###########################ñ$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''''''''õ((((((((((((((((((((ÖÖÖÖ×=×=×××××××××××××××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßßßßßßßàFààààààààààààààààààáGááááááááááááááááááááâHââââââââââââââââââãIããããããããããããããããããääääääääääääääääääåKååååååååååååååååææææææææææææææææçMççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêPêêêêêêêêêêêêêêëëëëëëëëëëëëëëìRììììììììììììììííííííííííííííîîîîîîîîîîîîïUïïïïïïïïïïïïðVððððððððððððññññññññññññòXòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýcýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ¢ ª«¬®±²³´µ¶·¸¹ º!!!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""""""¼###########################½$$$$$$$$$$$$$$$$$$$$$$$$$$$¾$¾%%%%%%%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''''''''''Á'Á((((((((((((((((((((((ÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààázááááááááááááááááááááâ{ââââââââââââââââââã|ããããããããããããããããä}ääääääääääääääääääååååååååååååååååæææææææææææææææææç€ççççççççççççççèèèèèèèèèèèèèèèé‚ééééééééééééééêƒêêêêêêêêêêêêë„ëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõŽõõõõõõõõõõööööööööööö÷÷÷÷÷÷÷÷÷÷÷ø‘øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿhmn p q tvz|}~€ƒ„…† ‡!!!!!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""""""""‰###########################Š$$$$$$$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''Ž((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßßßßßà­ààààààààààààààààààààá®ááááááááááááááááááááâ¯ââââââââââââââââââããããããããããããããããããä±ääääääääääääääääå²ååååååååååååååååæ³ææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêë¸ëëëëëëëëëëëëì¹ììììììììììììíºííííííííííííî»îîîîîîîîîîîîï¼ïïïïïïïïïïïïððððððððððððñ¾ññññññññññññòòòòòòòòòòòòóÀóóóóóóóóóóôÁôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùÆùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ8 > ADEGIKLMNOPQR S!!!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""""U#########################V#V$$$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááááââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''((((((((((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßàààààààààààààààààààààáááááááááááááááááááâââââââââââââââââââãããããããããããããããããããääääääääääääääääääåååååååååååååååååææææææææææææææææçççççççççççççççèèèèèèèèèèèèèèèéééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëìììììììììììììíííííííííííííî îîîîîîîîîîîîï!ïïïïïïïïïïïïððððððððððððñ#ññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿÒ Ûàâãäåæçèéêëìí î!!!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""""ð"ð#########################ñ$$$$$$$$$$$$$$$$$$$$$$$$$$$ò$ò%%%%%%%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''''''õ'õ((((((((((((((((((((((((((((((((((ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßßßßßàFààààààààààààààààààààáGááááááááááááááááááâHââââââââââââââââââãIããããããããããããããããäJääääääääääääääääääååååååååååååååååæLææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêPêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïðVððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôZôôôôôôôôôôõ[õõõõõõõõõõö\öööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûaûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿ¢ £ ¤ ¥ ¨ª«­®³´µ¶·¸¹ º º!!!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""""¼###########################½$$$$$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''''''''''Á(((((((((((((((((((((((((((((((((((((ÂÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßßßßßàyààààààààààààààààààààázááááááááááááááááááâ{ââââââââââââââââââã|ããããããããããããããããä}ääääääääääääääääå~ååååååååååååååååæææææææææææææææç€ççççççççççççççèèèèèèèèèèèèèèèé‚ééééééééééééééêêêêêêêêêêêêêêë„ëëëëëëëëëëëëì…ììììììììììììí†ííííííííííííî‡îîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññò‹òòòòòòòòòòóŒóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþþþÿÿÿÿn s tvy|}~‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!!!!!ˆ!ˆ"""""""""""""""""""""""‰###########################Š$$$$$$$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''Ž'Ž(((((((((((((((((((((((((((((((((((())ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßßßßßà­ààààààààààààààààààààá®ááááááááááááááááááâ¯ââââââââââââââââââã°ããããããããããããããããä±ääääääääääääääääå²ååååååååååååååååææææææææææææææææç´ççççççççççççççèµèèèèèèèèèèèèèèééééééééééééééê·êêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîï¼ïïïïïïïïïïïïððððððððððððñ¾ññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øÅøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþËþþþþþþþþþþÿÿÿÿ69 ACDFGIJKLOPQR S!!!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""""U#########################V#V$$$$$$$$$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((((((((([))))))ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((())))))))))))ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßàààààààààààààààààààààáááááááááááááááááááâââââââââââââââââãããããããããããããããããããääääääääääääääääääååååååååååååååååæææææææææææææææçççççççççççççççèèèèèèèèèèèèèèèéééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîï!ïïïïïïïïïïïïððððððððððððññññññññññññò$òòòòòòòòòòó%óóóóóóóóóóô&ôôôôôôôôôôõ'õõõõõõõõõõö(öööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿÓ × Ø Ú Ûßàáâãäåæçèéêëìí î!!!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""""""ð#########################ñ$$$$$$$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''''''õ(((((((((((((((((((((((((((((((((((ö(ö))))))))))))ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßßßßßàFààààààààààààààààààààáGááááááááááááááááááâHââââââââââââââââãIããããããããããããããããäJääääääääääääääääåKååååååååååååååååæLææææææææææææææçMççççççççççççççèNèèèèèèèèèèèèèèééééééééééééééêPêêêêêêêêêêêêëQëëëëëëëëëëëëìRììììììììììììíSííííííííííííîTîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððñWññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷]÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûübüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ¢ ¨ª­°±²´µ·¸¹ º!!!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""""""¼#########################½$$$$$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''''''''''''''Á'Á(((((((((((((((((((((((((((((((((((Â))))))))))))))))ÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßßßßßàyààààààààààààààààààààázááááááááááááááááááââââââââââââââââââã|ããããããããããããããããä}ääääääääääääääääå~ååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèé‚ééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííííîîîîîîîîîîîîïˆïïïïïïïïïïð‰ððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùú“úúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿn vxy{|€‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""""""‰"‰#######################Š#Š$$$$$$$$$$$$$$$$$$$$$$$$$‹$‹%%%%%%%%%%%%%%%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''Ž(((((((((((((((((((((((((((((((((((())))))))))))))))))ÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßßßßßà­ààààààààààààààààààààááááááááááááááááááâ¯ââââââââââââââââââã°ããããããããããããããããä±ääääääääääääääääå²ååååååååååååååæ³ææææææææææææææç´ççççççççççççççèµèèèèèèèèèèèèèèé¶ééééééééééééê·êêêêêêêêêêêêë¸ëëëëëëëëëëëëì¹ììììììììììììíºííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððñ¾ññññññññññò¿òòòòòòòòòòóÀóóóóóóóóóóôÁôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øÅøøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ57 = > ? @CFGIJKLOPR S!!!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""U#########################V$$$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((((((([))))))))))))))))))))))ÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßàààààààààààààààààààáááááááááááááááááááâââââââââââââââââââããããããããããããããããããääääääääääääääääåååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèéééééééééééééêêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììíííííííííííííîîîîîîîîîîîîï!ïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõö(öööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿÖ Þáâäæèéêëìí î!!!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""""ð#########################ñ$$$$$$$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''''''õ(((((((((((((((((((((((((((((((((((ö))))))))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßßßßßàFààààààààààààààààààáGááááááááááááááááááâHââââââââââââââââãIããããããããããããããããäJääääääääääääääääåKååååååååååååååæLææææææææææææææçMççççççççççççççèNèèèèèèèèèèèèèèééééééééééééééêPêêêêêêêêêêêêëQëëëëëëëëëëëëìRììììììììììììííííííííííííîTîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððñWññññññññññòXòòòòòòòòòòóYóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷]÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøù_ùùùùùùùùùùúúúúúúúúúúûaûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ ¥ ¦ §¨«¬¯°±³´µ¶·¸¹ º!!!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""""¼#########################½$$$$$$$$$$$$$$$$$$$$$$$$$¾$¾%%%%%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''''''''''''''Á'Á(((((((((((((((((((((((((((((((((Â(Â))))))))))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××××××××××רqØqØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßßßßßàyààààààààààààààààààázááááááááááááááááááâ{ââââââââââââââââã|ããããããããããããããããä}ääääääääääääääääå~ååååååååååååååæææææææææææææææç€ççççççççççççççèèèèèèèèèèèèèèé‚ééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììí†ííííííííííííîîîîîîîîîîîîïˆïïïïïïïïïïð‰ððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôõõõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿgln q vwz{|~€‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!!!ˆ!ˆ"""""""""""""""""""""""‰#######################Š#Š$$$$$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''''Ž(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßßßßßà­ààààààààààààààààààá®ááááááááááááááááááâ¯ââââââââââââââââã°ããããããããããããããããä±ääääääääääääääääååååååååååååååååæ³ææææææææææææææççççççççççççççèµèèèèèèèèèèèèèèé¶ééééééééééééê·êêêêêêêêêêêêë¸ëëëëëëëëëëëëì¹ììììììììììììííííííííííííî»îîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõÂõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ6 < DEIKLMNPQR S!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""U"U#######################V$$$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((((((([)))))))))))))))))))))))))))))))))))))\ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååååææææææææææææææççççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))**ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""##########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))******ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßßàààààààààààààààààààáááááááááááááááááááââââââââââââââââââãããããããããããããããããääääääääääääääääåååååååååååååååæææææææææææææææçççççççççççççççèèèèèèèèèèèèèèéééééééééééééêêêêêêêêêêêêêëëëëëëëëëëëëëìììììììììììììííííííííííííî îîîîîîîîîîï!ïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóô&ôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿÔÖ Ù Þàåæçèéêëìí î!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""""ð#########################ñ$$$$$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''''õ'õ(((((((((((((((((((((((((((((((((ö)))))))))))))))))))))))))))))))))))))÷******ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßßßßßàFààààààààààààààààààáGááááááááááááááááâHââââââââââââââââââããããããããããããããããäJääääääääääääääääåKååååååååååååååæLææææææææææææææçMççççççççççççèNèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììíSííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõ[õõõõõõõõõõöööööööööö÷]÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ ¤ ¨«­®¯°²³´µ¶·¸¹ º!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""""¼#########################½$$$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''''''''''''''Á(((((((((((((((((((((((((((((((((Â(Â)))))))))))))))))))))))))))))))))))Ã)Ã********ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßßßßßàyààààààààààààààààààázááááááááááááááááâ{ââââââââââââââââã|ããããããããããããããããä}ääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèé‚ééééééééééééêƒêêêêêêêêêêêêë„ëëëëëëëëëëëëì…ììììììììììììííííííííííííî‡îîîîîîîîîîïˆïïïïïïïïïïð‰ððððððððððñŠññññññññññò‹òòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøù’ùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿkn p s tvwy{|~€‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""""""‰#######################Š#Š$$$$$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''Ž'Ž((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬߬ßßßßßßßßßßßßßßßßßßà­ààààààààààààààààààá®ááááááááááááááááâ¯ââââââââââââââââã°ããããããããããããããããä±ääääääääääääääå²ååååååååååååååæ³ææææææææææææææç´ççççççççççççççèèèèèèèèèèèèèèé¶ééééééééééééê·êêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììíºííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòóÀóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöÃöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüÉüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ > DFIJKMNOPQR S!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""""U#######################V$$$$$$$$$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))))))))\****************ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))******************ÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))**********************ÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßàààààààààààààààààààáááááááááááááááááâââââââââââââââââãããããããããããããããããääääääääääääääääåååååååååååååååæææææææææææææææççççççççççççççèèèèèèèèèèèèèéééééééééééééêêêêêêêêêêêêêëëëëëëëëëëëëëììììììììììììíííííííííííî îîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññò$òòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõ'õõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúû-ûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿÏ Ú ßáåæçèéëìí î!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""ð"ð#######################ñ$$$$$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''''õ(((((((((((((((((((((((((((((((((ö)))))))))))))))))))))))))))))))))))÷)÷**********************ÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßßßàFààààààààààààààààààááááááááááááááááááâHââââââââââââââââãIããããããããããããããäJääääääääääääääääååååååååååååååååææææææææææææææçMççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëìRììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòóYóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷]÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  £ ¥ ¬®¯°±µ¶·¸¹ º!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""¼#######################½#½$$$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''''''''''''Á'Á(((((((((((((((((((((((((((((((Â(Â)))))))))))))))))))))))))))))))))))Ã**************************ÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßßßàyààààààààààààààààázááááááááááááááááááâ{ââââââââââââââââããããããããããããããããä}ääääääääääääääå~ååååååååååååååæææææææææææææææç€ççççççççççççèèèèèèèèèèèèèé‚ééééééééééééêƒêêêêêêêêêêêêë„ëëëëëëëëëëëëììììììììììììí†ííííííííííî‡îîîîîîîîîîïˆïïïïïïïïïïð‰ððððððððððñŠññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôõõõõõõõõõõöööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùú“úúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿn tuvwxz|}€‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""""‰#######################Š$$$$$$$$$$$$$$$$$$$$$$$$$‹$‹%%%%%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''''Ž((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßßßà­ààààààààààààààààá®ááááááááááááááááááâ¯ââââââââââââââã°ããããããããããããããããä±ääääääääääääääå²ååååååååååååååæ³ææææææææææææææççççççççççççççèµèèèèèèèèèèèèé¶ééééééééééééê·êêêêêêêêêêêêëëëëëëëëëëëëì¹ììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññò¿òòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöÃöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ5 = ? EFGJKLMNOPQR S!!!!!!!!!!!!!!!!!!!T!T"""""""""""""""""""""U#######################V$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))))))\)\********************************ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááááââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååååææææææææææææææççççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))************************************ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããããääääääääääääääääååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßàààààààààààààààààáááááááááááááááááâââââââââââââââââãããããããããããããããããääääääääääääääääååååååååååååååæææææææææææææææçççççççççççççèèèèèèèèèèèèèéééééééééééééêêêêêêêêêêêêêëëëëëëëëëëëëìììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððñ#ññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷ø*øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿÒ × Ù Ûàáæèéêëìí î!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""""ð#######################ñ$$$$$$$$$$$$$$$$$$$$$$$ò$ò%%%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''õ'õ(((((((((((((((((((((((((((((((ö(ö)))))))))))))))))))))))))))))))))÷)÷*************************************øÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßßßàFààààààààààààààààáGááááááááááááááááâHââââââââââââââââãIããããããããããããããäJääääääääääääääåKååååååååååååååæLææææææææææææææççççççççççççççèNèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëQëëëëëëëëëëëëììììììììììììííííííííííííîTîîîîîîîîîîïUïïïïïïïïïïððððððððððððññññññññññòXòòòòòòòòòòóóóóóóóóóóôZôôôôôôôôôôõõõõõõõõõõöööööööööö÷]÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿŸ ¦ ©ª«®¯°±²³µ¶·¸¹ º!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""¼"¼#####################½#½$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''''''''''''Á(((((((((((((((((((((((((((((((Â(Â)))))))))))))))))))))))))))))))))Ã)Ã*************************************Ä*Ä++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßßßàyààààààààààààààààázááááááááááááááááâ{ââââââââââââââââã|ããããããããããããããä}ääääääääääääääå~ååååååååååååååæææææææææææææç€ççççççççççççççèèèèèèèèèèèèé‚ééééééééééééêƒêêêêêêêêêêêêëëëëëëëëëëëëì…ììììììììììí†ííííííííííííîîîîîîîîîîîîïïïïïïïïïïð‰ððððððððððññññññññññññòòòòòòòòòòóŒóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõööööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿm q uyz|}~€‚„† ‡!!!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""""‰#######################Š$$$$$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''Ž'Ž((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))*************************************‘*‘++++++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßßßà­ààààààààààààààààá®ááááááááááááááááâ¯ââââââââââââââââã°ããããããããããããããä±ääääääääääääääå²ååååååååååååååææææææææææææææç´ççççççççççççèµèèèèèèèèèèèèé¶ééééééééééééêêêêêêêêêêêêë¸ëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððñ¾ññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõÂõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ:; > @CDGKLMNOPQR S!!!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""U#######################V$$$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))))))\)\*************************************]++++++++++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääääååååååååååååååææææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))****************************************++++++++++++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããããääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))****************************************++++++++++++++++ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßßààààààààààààààààààáááááááááááááááááâââââââââââââââãããããããããããããããããääääääääääääääåååååååååååååååæææææææææææææçççççççççççççèèèèèèèèèèèèèéééééééééééééêêêêêêêêêêêëëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððñ#ññññññññññòòòòòòòòòòó%óóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷ø*øøøøøøøøù+ùùùùùùùùú,úúúúúúúúû-ûûûûûûûûü.üüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ Ø Üâäåçêëìí î!!!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""ð#######################ñ$$$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''''''''õ(((((((((((((((((((((((((((((((ö(ö)))))))))))))))))))))))))))))))))÷*************************************ø*ø++++++++++++++++ÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßàFààààààààààààààààààáGááááááááááááááááâHââââââââââââââãIããããããããããããããäJääääääääääääääåKååååååååååååååææææææææææææææçMççççççççççççèNèèèèèèèèèèèèéOééééééééééééêêêêêêêêêêêêëQëëëëëëëëëëìRììììììììììíSííííííííííîTîîîîîîîîîîïUïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõö\öööööööö÷]÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþþþÿÿÿÿ ¥ §©ª«¬­¯²³´µ¶·¸¹ º!!!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""¼#####################½#½$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''''''''''Á'Á(((((((((((((((((((((((((((((((Â)))))))))))))))))))))))))))))))))Ã)Ã***********************************Ä*Ä++++++++++++++++++++ÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××××××××××רqØqØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßàyààààààààààààààààààázááááááááááááááááââââââââââââââââã|ããããããããããããããä}ääääääääääääääå~ååååååååååååæææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêƒêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïð‰ððððððððððññññññññññò‹òòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõŽõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüý–ýýýýýýýýþþþþþþþþþþÿÿÿÿ s u{|}~€‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""""‰#####################Š$$$$$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''''Ž(((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))*************************************‘++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßà­ààààààààààààààààààá®ááááááááááááááâ¯ââââââââââââââââã°ããããããããããããããä±ääääääääääääääååååååååååååååæ³ææææææææææææç´ççççççççççççèµèèèèèèèèèèèèé¶ééééééééééééêêêêêêêêêêêêë¸ëëëëëëëëëëì¹ììììììììììíºííííííííííî»îîîîîîîîîîï¼ïïïïïïïïïïððððððððððñ¾ññññññññññòòòòòòòòòòóóóóóóóóóóôÁôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþþþÿÿÿÿ89:; < CDEFGJKMNOPQR S!!!!!!!!!!!!!!!!!!!T!T"""""""""""""""""""U"U#####################V$$$$$$$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))))))\)\***********************************]*]++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààààááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**************************************++++++++++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßàààààààààààààààààáááááááááááááááááâââââââââââââââââããããããããããããããããääääääääääääääåååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèéééééééééééééêêêêêêêêêêêêëëëëëëëëëëëìììììììììììíííííííííííî îîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññò$òòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþþþÿÿÿÿÑ Ù ÜÞßàáâäåæçèêëìí î!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""ð#######################ñ$$$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&&&ô&ô'''''''''''''''''''''''''''õ'õ(((((((((((((((((((((((((((((ö(ö)))))))))))))))))))))))))))))))))÷***********************************ø*ø++++++++++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßàFààààààààààààààààáGááááááááááááááááâHââââââââââââââãIããããããããããããããäJääääääääääääääåKååååååååååååæLææææææææææææçMççççççççççççèNèèèèèèèèèèèèéOééééééééééêPêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîïUïïïïïïïïïïððððððððððñWññññññññññòòòòòòòòòòóóóóóóóóóóôZôôôôôôôôõ[õõõõõõõõö\öööööööö÷]÷÷÷÷÷÷÷÷ø^øøøøøøøøù_ùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýþdþþþþþþþþÿÿÿÿ ¦ §©¯±²´µ¶·¸¹ º!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""""¼#####################½#½$$$$$$$$$$$$$$$$$$$$$¾$¾%%%%%%%%%%%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''''''Á(((((((((((((((((((((((((((((((Â)))))))))))))))))))))))))))))))))Ã)Ã*********************************Ä*Ä++++++++++++++++++++++++++++++++++++++ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßàyààààààààààààààààázááááááááááááááááâ{ââââââââââââââã|ããããããããããããããä}ääääääääääääääååååååååååååååæææææææææææææç€ççççççççççççèèèèèèèèèèèèèééééééééééééêƒêêêêêêêêêêë„ëëëëëëëëëëì…ììììììììììí†ííííííííííî‡îîîîîîîîîîïïïïïïïïïïð‰ððððððððððññññññññññòòòòòòòòòòóŒóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ uxyz{|}€‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""""‰#####################Š$$$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''Ž'Ž(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))***********************************‘+++++++++++++++++++++++++++++++++++++++’+’ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßà­ààààààààààààààààá®ááááááááááááááááâ¯ââââââââââââââã°ããããããããããããããä±ääääääääääääå²ååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèé¶ééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîï¼ïïïïïïïïïïððððððððððñ¾ññññññññò¿òòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúÇúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ5:; < = > CIJKLMNOPQR S!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""""U#####################V$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''''Z((((((((((((((((((((((((((((((([)))))))))))))))))))))))))))))))))\***********************************]*]+++++++++++++++++++++++++++++++++++++^+^,,,,ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääääååååååååååååååææææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""########################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßàààààààààààààààààáááááááááááááááááââââââââââââââââãããããããããããããäääääääääääääääåååååååååååååæææææææææææææçççççççççççççèèèèèèèèèèèéééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííî îîîîîîîîîîïïïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòó%óóóóóóóóô&ôôôôôôôôõ'õõõõõõõõö(öööööööö÷)÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿÓ ÜÞáäèéêëìí î!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""ð#####################ñ#ñ$$$$$$$$$$$$$$$$$$$$$ò$ò%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&&&ô&ô'''''''''''''''''''''''''''õ(((((((((((((((((((((((((((((ö(ö)))))))))))))))))))))))))))))))÷)÷*********************************ø*ø+++++++++++++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßàFààààààààààààààààáGááááááááááááááâHââââââââââââââââããããããããããããããäJääääääääääääääååååååååååååååæLææææææææææææçMççççççççççççèèèèèèèèèèèèéOééééééééééêPêêêêêêêêêêëQëëëëëëëëëëìRììììììììììíSííííííííííîîîîîîîîîîïUïïïïïïïïïïððððððððððñWññññññññòXòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷ø^øøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ ¤ ¥ ©«¬®¯°±²³´·¸¹ º!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""¼#####################½$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''''Á'Á(((((((((((((((((((((((((((((Â)))))))))))))))))))))))))))))))))Ã***********************************Ä+++++++++++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,ÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßàyààààààààààààààààázááááááááááááááâ{ââââââââââââââã|ããããããããããããããä}ääääääääääääå~ååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèèééééééééééééêƒêêêêêêêêêêë„ëëëëëëëëëëììììììììììììííííííííííî‡îîîîîîîîîîïïïïïïïïïïð‰ððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿko p s twz{~€‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""‰#####################Š$$$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''''Ž(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))*********************************‘*‘+++++++++++++++++++++++++++++++++++++’,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛܩܩÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßà­ààààààààààààààààá®ááááááááááááááâ¯ââââââââââââââã°ããããããããããããããä±ääääääääääääå²ååååååååååååæ³ææææææææææææç´ççççççççççççèèèèèèèèèèèèé¶ééééééééééééêêêêêêêêêêêêëëëëëëëëëëì¹ììììììììììíºííííííííííîîîîîîîîîîï¼ïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóÀóóóóóóóóôÁôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùÆùùùùùùùùúúúúúúúúúúûûûûûûûûüÉüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ: ADEHIKLNOPQR S!!!!!!!!!!!!!!!!!T!T"""""""""""""""""""U#####################V$$$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''Z'Z((((((((((((((((((((((((((((([)))))))))))))))))))))))))))))))\)\*********************************]*]+++++++++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääääååååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßàààààààààààààààààáááááááááááááááâââââââââââââââãããããããããããããäääääääääääääääåååååååååååååæææææææææææææççççççççççççèèèèèèèèèèèéééééééééééééêêêêêêêêêêêêëëëëëëëëëëìììììììììììíííííííííííîîîîîîîîîîï!ïïïïïïïïð"ððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööö÷)÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùú,úúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿÔ Ø Ù Ú ÛÜàäåæçèéêëí î!!!!!!!!!!!!!!!!!ï"""""""""""""""""""""ð###################ñ#ñ$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&&&&&&&ô&ô'''''''''''''''''''''''''õ'õ(((((((((((((((((((((((((((((ö)))))))))))))))))))))))))))))))÷)÷*********************************ø+++++++++++++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßàFààààààààààààààààáGááááááááááááááâHââââââââââââââãIããããããããããããäJääääääääääääääååååååååååååååææææææææææææçMççççççççççççèèèèèèèèèèèèéOééééééééééêPêêêêêêêêêêëQëëëëëëëëëëìRììììììììììííííííííííîTîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòXòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ £ ©«­®¯°²³´¶·¸¹ º!!!!!!!!!!!!!!!!!»"""""""""""""""""""¼"¼###################½$$$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''''Á(((((((((((((((((((((((((((((Â(Â)))))))))))))))))))))))))))))Ã)Ã*********************************Ä*Ä+++++++++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßàyààààààààààààààààááááááááááááááááâ{ââââââââââââââããããããããããããããä}ääääääääääääå~ååååååååååååæææææææææææææç€ççççççççççèèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììí†ííííííííííîîîîîîîîîîïïïïïïïïïïð‰ððððððððñŠññññññññññòòòòòòòòóŒóóóóóóóóôôôôôôôôôõŽõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷ø‘øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿlo wy~€‚ƒ„…† ‡ ‡!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""‰#####################Š$$$$$$$$$$$$$$$$$$$$$‹$‹%%%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''Ž'Ž(((((((((((((((((((((((((((()))))))))))))))))))))))))))))))*********************************‘*‘+++++++++++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛܩܩÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßà­ààààààààààààààá®ááááááááááááááááââââââââââââââã°ããããããããããããããä±ääääääääääääå²ååååååååååååæ³ææææææææææææççççççççççççèµèèèèèèèèèèé¶ééééééééééê·êêêêêêêêêêë¸ëëëëëëëëëëì¹ììììììììììííííííííííî»îîîîîîîîï¼ïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ: > ? @ABDFGHIJKNPQR S!!!!!!!!!!!!!!!!!!!T"""""""""""""""""""U#####################V$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&&&Y&Y'''''''''''''''''''''''''Z((((((((((((((((((((((((((((([)))))))))))))))))))))))))))))))\)\*******************************]*]+++++++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââââããããããããããããããääääääääääääääääååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))************************************++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßàààààààààààààààáááááááááááááááâââââââââââââââãããããããããããããäääääääääääääääååååååååååååæææææææææææææçççççççççççèèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëìììììììììììííííííííííîîîîîîîîîîï!ïïïïïïïïð"ððððððððððññññññññññòòòòòòòòó%óóóóóóóóô&ôôôôôôôôõõõõõõõõõõöööööööö÷)÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüý/ýýýýýýýýþþþþþþþþÿÿÿÿ × ßáåçèéêëìí î!!!!!!!!!!!!!!!!!!!ï"""""""""""""""""""ð###################ñ#ñ$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&ô&ô'''''''''''''''''''''''''õ(((((((((((((((((((((((((((((ö)))))))))))))))))))))))))))))))÷*********************************ø*ø+++++++++++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú--------ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßàFààààààààààààààáGááááááááááááááâHââââââââââââââãIããããããããããããäJääääääääääääåKååååååååååååæLææææææææææææççççççççççççèNèèèèèèèèèèéOééééééééééêPêêêêêêêêêêëëëëëëëëëëëëììììììììììíSííííííííîTîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôõ[õõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿž ¢ ¦ §¨©ª¬®¯°±²³´¶·¸¹ º!!!!!!!!!!!!!!!!!!!»"""""""""""""""""""¼###################½$$$$$$$$$$$$$$$$$$$$$¾$¾%%%%%%%%%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''Á'Á(((((((((((((((((((((((((((Â(Â)))))))))))))))))))))))))))))Ã)Ã*******************************Ä*Ä+++++++++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ------------ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××××רqØqØØØØØØØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßàyààààààààààààààázááááááááááááááâ{ââââââââââââââã|ããããããããããããä}ääääääääääääå~ååååååååååååæææææææææææç€ççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêë„ëëëëëëëëëëì…ììììììììììííííííííííîîîîîîîîîîïˆïïïïïïïïð‰ððððððððñŠññññññññò‹òòòòòòòòóóóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷ø‘øøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿi q r xz‚ƒ„…† ‡!!!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""‰"‰###################Š$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''Ž((((((((((((((((((((((((((((())))))))))))))))))))))))))))))*******************************‘*‘+++++++++++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“----------------ÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßà­ààààààààààààààá®ááááááááááááááâ¯ââââââââââââââããããããããããããããä±ääääääääääääå²ååååååååååååææææææææææææç´ççççççççççèµèèèèèèèèèèé¶ééééééééééê·êêêêêêêêêêë¸ëëëëëëëëëëììììììììììíºííííííííî»îîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòóÀóóóóóóóóôôôôôôôôôôõõõõõõõõöÃöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúÇúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ @ABEGHIJKLMNOPQR S!!!!!!!!!!!!!!!!!!!T"""""""""""""""""U#####################V$$$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''''Z((((((((((((((((((((((((((([([)))))))))))))))))))))))))))))\*********************************]+++++++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_--------------------ÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããããããääääääääääääääååååååååååååææææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!!!""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------ÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""######################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------ÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßàààààààààààààààáááááááááááááááâââââââââââââãããããããããããããäääääääääääääåååååååååååååæææææææææææçççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêëëëëëëëëëëëììììììììììííííííííííî îîîîîîîîï!ïïïïïïïïð"ððððððððñ#ññññññññò$òòòòòòòòóóóóóóóóóóôôôôôôôôõ'õõõõõõõõöööööööö÷)÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûü.üüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ Ù àâæèéëìí î!!!!!!!!!!!!!!!!!ï"""""""""""""""""""ð###################ñ#ñ$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&ô&ô'''''''''''''''''''''''''õ(((((((((((((((((((((((((((ö)))))))))))))))))))))))))))))÷)÷*******************************ø*ø+++++++++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú----------------------------ÑÑÑÑÑÑÑÑÑÑÒ8Ò8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßàFààààààààààààààáGááááááááááááááâHââââââââââââãIããããããããããããäJääääääääääääåKååååååååååååææææææææææææçMççççççççççèNèèèèèèèèèèéOééééééééééêPêêêêêêêêêêëëëëëëëëëëìRììììììììíSííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòóYóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööööö÷÷÷÷÷÷÷÷øøøøøøøøù_ùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ¢ ¤ §¨©ª«­¯°±²³´µ¶·¸¹ º!!!!!!!!!!!!!!!!!»"""""""""""""""""""¼###################½$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''Á'Á(((((((((((((((((((((((((Â(Â)))))))))))))))))))))))))))))Ã*******************************Ä*Ä+++++++++++++++++++++++++++++++++++Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ------------------------------ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßàyààààààààààààààázááááááááááááááââââââââââââââã|ããããããããããããä}ääääääääääääå~ååååååååååæææææææææææææççççççççççççèèèèèèèèèèèééééééééééééêêêêêêêêêêë„ëëëëëëëëëëììììììììììííííííííííî‡îîîîîîîîïˆïïïïïïïïð‰ððððððððññññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôõõõõõõõõööööööööö÷÷÷÷÷÷÷÷øøøøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿjm s yz{}~€ƒ„…† ‡!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""""‰###################Š$$$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''''Ž((((((((((((((((((((((((((())))))))))))))))))))))))))))))*******************************‘+++++++++++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“----------------------------------ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßà­ààààààààààààààá®ááááááááááááâ¯ââââââââââââââã°ããããããããããããä±ääääääääääääååååååååååååæ³ææææææææææç´ççççççççççççèèèèèèèèèèé¶ééééééééééê·êêêêêêêêêêëëëëëëëëëëì¹ììììììììíºííííííííííîîîîîîîîîîïïïïïïïïïïððððððððñ¾ññññññññò¿òòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øÅøøøøøøøøùùùùùùùùúúúúúúúúûÈûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ < > BCDGHKLMNOPQR S!!!!!!!!!!!!!!!!!T"""""""""""""""""""U###################V$$$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''Z'Z((((((((((((((((((((((((([([)))))))))))))))))))))))))))\)\*******************************]*]+++++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_--------------------------------------ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââââããããããããããããããääääääääääääååååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))**********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääääååååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------....ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßßàààààààààààààààááááááááááááááâââââââââââââãããããããããããããäääääääääääääåååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêëëëëëëëëëëìììììììììíííííííííííîîîîîîîîîîïïïïïïïïð"ððððððððñ#ññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööö÷)÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿÖ Ø Ú Þßâåçèéêëìí î!!!!!!!!!!!!!!!!!ï"""""""""""""""""ð###################ñ$$$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&&&ô&ô'''''''''''''''''''''''õ'õ(((((((((((((((((((((((((ö(ö)))))))))))))))))))))))))))÷)÷*****************************ø*ø+++++++++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú---------------------------------------û-û....ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßßßàFààààààààààààáGááááááááááááááâHââââââââââââãIããããããããããããäJääääääääääääåKååååååååååæLææææææææææçMççççççççççèNèèèèèèèèèèéOééééééééééêêêêêêêêêêëQëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññòXòòòòòòòòóóóóóóóóôZôôôôôôôôõõõõõõõõö\öööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùú`úúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿŸ ¨©¬­¯°²³µ¶·¸¹ º!!!!!!!!!!!!!!!!!»"""""""""""""""""¼###################½$$$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''''Á(((((((((((((((((((((((((Â(Â)))))))))))))))))))))))))))Ã)Ã*******************************Ä+++++++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ---------------------------------------Ç-Ç........ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßßßàyààààààààààààázááááááááááááááâ{ââââââââââââã|ããããããããããããä}ääääääääääääååååååååååååæææææææææææç€ççççççççççèèèèèèèèèèèééééééééééêƒêêêêêêêêêêëëëëëëëëëëì…ììììììììí†ííííííííî‡îîîîîîîîïˆïïïïïïïïð‰ððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøù’ùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ p r t{}~€„…† ‡!!!!!!!!!!!!!!!!!ˆ"""""""""""""""""‰###################Š$$$$$$$$$$$$$$$$$$$‹$‹%%%%%%%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''Ž'Ž((((((((((((((((((((((((()))))))))))))))))))))))))))))*******************************‘*‘+++++++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“---------------------------------------”-”............ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßßßààààààààààààààá®ááááááááááááááâ¯ââââââââââââã°ããããããããããããä±ääääääääääå²ååååååååååååææææææææææææç´ççççççççççèèèèèèèèèèé¶ééééééééééê·êêêêêêêêë¸ëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððñ¾ññññññññòòòòòòòòóÀóóóóóóóóôôôôôôôôõÂõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øÅøøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ9 ? BCDEFHJKMNOPR S!!!!!!!!!!!!!!!!!T"""""""""""""""""U###################V$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''''Z((((((((((((((((((((((((([([)))))))))))))))))))))))))))\)\*****************************]*]+++++++++++++++++++++++++++++++++^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------------------`-`................ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææææææççççççççççèèèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïïïððððððððððññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------....................ÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááááââââââââââââââããããããããããããääääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------------........................ÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßàààààààààààààààáááááááááááááááââââââââââââââããããããããããããäääääääääääääåååååååååååæææææææææææçççççççççççèèèèèèèèèèèééééééééééêêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïð"ððððððððññññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷)÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿÖ Ù ÛÞßàáâäåæçéêëìí î!!!!!!!!!!!!!!!ï"""""""""""""""""""ð#################ñ$$$$$$$$$$$$$$$$$$$ò$ò%%%%%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&&&&&ô&ô'''''''''''''''''''''''õ(((((((((((((((((((((((((ö(ö)))))))))))))))))))))))))))÷*******************************ø+++++++++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú---------------------------------------û-û........................ÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßßßàFààààààààààààààáGááááááááááááâHââââââââââââãIããããããããããããäJääääääääääääååååååååååååæLææææææææææçMççççççççççèèèèèèèèèèéOééééééééééêêêêêêêêêêëQëëëëëëëëìRììììììììíSííííííííîTîîîîîîîîïïïïïïïïïïððððððððñWññññññññòòòòòòòòóYóóóóóóóóôôôôôôôôõõõõõõõõö\öööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿš £ ¦ ¨©¯±²³´µ¶·¸¹ º!!!!!!!!!!!!!!!»"""""""""""""""""¼"¼#################½$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''Á'Á(((((((((((((((((((((((((Â)))))))))))))))))))))))))))Ã)Ã*****************************Ä*Ä+++++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ-------------------------------------Ç-Ç-Ç............................ÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××רqØqØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßßßàyààààààààààààààázááááááááááááâ{ââââââââââââã|ããããããããããããä}ääääääääääå~ååååååååååååææææææææææææççççççççççèèèèèèèèèèèé‚ééééééééêƒêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîïˆïïïïïïïïð‰ððððððððññññññññò‹òòòòòòòòóóóóóóóóôôôôôôôôõŽõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÿÿhij q wxyz{}‚ƒ„…† ‡!!!!!!!!!!!!!!!ˆ"""""""""""""""""‰###################Š$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''''Ž(((((((((((((((((((((((((())))))))))))))))))))))))))*****************************‘*‘+++++++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“-------------------------------------”-”..................................ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßßßà­ààààààààààààààá®ááááááááááááâ¯ââââââââââââã°ããããããããããããä±ääääääääääå²ååååååååååæ³ææææææææææç´ççççççççççèµèèèèèèèèèèééééééééééê·êêêêêêêêë¸ëëëëëëëëì¹ììììììììíºííííííííî»îîîîîîîîïïïïïïïïïïððððððððñ¾ññññññññòòòòòòòòóóóóóóóóôÁôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúÇúúúúúúûÈûûûûûûüÉüüüüüüýÊýýýýýýþËþþþþþþÿÿÿÿ78 > @BHJKLMOPQR S!!!!!!!!!!!!!!!T"""""""""""""""""U###################V$$$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''Z'Z((((((((((((((((((((((([([)))))))))))))))))))))))))))\*****************************]*]+++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-------------------------------------`-`......................................ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææææççççççççççççèèèèèèèèèèééééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïïïððððððððððññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""####################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..........................................ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßßààààààààààààààààááááááááááááááââââââââââââââããããããããããããääääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññòòòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþÿÿÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------..............................................ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßßàààààààààààààààáááááááááááááâââââââââââââãããããããããããäääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèèééééééééééêêêêêêêêêëëëëëëëëëìììììììììíííííííííî îîîîîîîîïïïïïïïïð"ððððððððññññññññò$òòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõö(öööööö÷)÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþÿ1ÿÿÖ × ÝÞäçéêëìí î î!!!!!!!!!!!!!!!ï"""""""""""""""""ð#################ñ$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''õ'õ(((((((((((((((((((((((ö(ö)))))))))))))))))))))))))÷)÷*****************************ø*ø+++++++++++++++++++++++++++++++ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú-------------------------------------û-û...........................................ü.üÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßEßßßßßßßßßßßßàFààààààààààààààáGááááááááááááâHââââââââââââããããããããããããäJääääääääääåKååååååååååæLææææææææææçMççççççççççèNèèèèèèèèéOééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîïUïïïïïïïïððððððððñWññññññññòòòòòòòòóóóóóóóóôZôôôôôôõ[õõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ ¤ §«¬­®¯±²´µ¶·¸¹ º!!!!!!!!!!!!!!!!!»"""""""""""""""""¼#################½$$$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''''Á(((((((((((((((((((((((((Â)))))))))))))))))))))))))))Ã*****************************Ä*Ä+++++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ-------------------------------------Ç-Ç.........................................È.È.È////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××××רqØqØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßàyààààààààààààààázááááááááááááâ{ââââââââââã|ããããããããããããä}ääääääääääå~ååååååååååæææææææææææç€ççççççççççèèèèèèèèèèé‚ééééééééêƒêêêêêêêêë„ëëëëëëëëì…ììììììììí†ííííííííîîîîîîîîîîïïïïïïïïð‰ððððððððññññññññòòòòòòòòóŒóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ r s uw}€‚ƒ„…† ‡!!!!!!!!!!!!!!!!!ˆ"""""""""""""""‰"‰#################Š$$$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''Ž'Ž(((((((((((((((((((((((())))))))))))))))))))))))))***************************‘*‘+++++++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“-------------------------------------”-”.........................................•.•//////////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«Þ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßà­ààààààààààààààááááááááááááááââââââââââââã°ããããããããããããä±ääääääääääå²ååååååååååæ³ææææææææææççççççççççèµèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííî»îîîîîîîîïïïïïïïïïïððððððððññññññññò¿òòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ @BDEFGHIJKLMOPQ S!!!!!!!!!!!!!!!!!T"""""""""""""""U#################V#V$$$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''''Z((((((((((((((((((((((((([)))))))))))))))))))))))))\)\*****************************]+++++++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-------------------------------------`-`.........................................a.a//////////////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââââããããããããããããããääääääääääääååååååååååååææææææææææççççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïïïððððððððññññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))********************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââââããããããããããããääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------............................................//////////////////////ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßàààààààààààààáááááááááááááâââââââââââââãããããããããããäääääääääääåååååååååååæææææææææææçççççççççèèèèèèèèèèèééééééééééêêêêêêêêêëëëëëëëëëììììììììììííííííííî îîîîîîîîïïïïïïïïð"ððððððððññññññññòòòòòòòòó%óóóóóóô&ôôôôôôõ'õõõõõõõõöööööööö÷÷÷÷÷÷ø*øøøøøøù+ùùùùùùúúúúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿÓÔ Ø Ù Ýßàãäæçèéêëìí î!!!!!!!!!!!!!!!ï"""""""""""""""""ð#################ñ$$$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''''õ(((((((((((((((((((((((ö(ö)))))))))))))))))))))))))÷)÷***************************ø*ø+++++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú-------------------------------------û-û.......................................ü.ü.ü//////////////////////ÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßàFààààààààààààáGááááááááááááâHââââââââââââãIããããããããããäJääääääääääåKååååååååååæLææææææææææççççççççççèNèèèèèèèèéOééééééééééêêêêêêêêêêëëëëëëëëìRììììììììíSííííííííîîîîîîîîïUïïïïïïïïððððððððññññññññòXòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùú`úúúúúúûûûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿž ¦ §ª­®¯±²³´µ·¸¹ º!!!!!!!!!!!!!!!»"""""""""""""""""¼#################½$$$$$$$$$$$$$$$$$¾$¾%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''Á'Á(((((((((((((((((((((((Â)))))))))))))))))))))))))Ã)Ã***************************Ä*Ä+++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ-----------------------------------Ç-Ç-Ç.......................................È.È////////////////////////////ÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßàyààààààààààààázááááááááááááâ{ââââââââââââããããããããããããä}ääääääääääå~ååååååååååææææææææææç€ççççççççççèèèèèèèèèèé‚ééééééééêƒêêêêêêêêë„ëëëëëëëëììììììììììííííííííî‡îîîîîîîîïïïïïïïïð‰ððððððñŠññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúû”ûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ uy}~€‚ƒ„…† ‡!!!!!!!!!!!!!!!ˆ"""""""""""""""""‰#################Š$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''Ž(((((((((((((((((((((((()))))))))))))))))))))))))***************************‘*‘+++++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“-----------------------------------”-”.........................................•.•////////////////////////////////ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßà­ààààààààààààá®ááááááááááááâ¯ââââââââââã°ããããããããããããääääääääääääååååååååååæ³ææææææææææç´ççççççççèµèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëì¹ììììììììíºííííííííîîîîîîîîï¼ïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôÁôôôôôôõÂõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ6 < = > BDFGHIKMOPQR S!!!!!!!!!!!!!!!T"""""""""""""""""U###############V#V$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''Z'Z((((((((((((((((((((((([)))))))))))))))))))))))))\)\***************************]+++++++++++++++++++++++++++++++^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-----------------------------------`-`.........................................a.a////////////////////////////////////ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââããããããããããããääääääääääääååååååååååååææææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""""################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------............................................////////////////////////////////////////ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááááââââââââââââããããããããããããääääääääääääååååååååååååææææææææææççççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëììììììììììííííííííííîîîîîîîîïïïïïïïïððððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""##################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))******************************++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------..........................................//////////////////////////////////////////////ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßàààààààààààààáááááááááááááâââââââââââãããããããããããäääääääääääåååååååååååæææææææææçççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëìììììììììíííííííííîîîîîîîîïïïïïïïïð"ððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷ø*øøøøøøùùùùùùùùúúúúúúúúûûûûûûü.üüüüüüýýýýýýýýþþþþþþþþÿÿÔ ÜÝßáâãäåçèéêëìí î!!!!!!!!!!!!!!!ï"""""""""""""""ð#################ñ$$$$$$$$$$$$$$$$$ò$ò%%%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''õ'õ(((((((((((((((((((((((ö)))))))))))))))))))))))))÷***************************ø*ø+++++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú-----------------------------------û-û.......................................ü.ü/////////////////////////////////////////////ýÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßàFààààààààààààáGááááááááááááââââââââââââãIããããããããããäJääääääääääåKååååååååååææææææææææçMççççççççèNèèèèèèèèéOééééééééêPêêêêêêêêëQëëëëëëëëììììììììììííííííííîîîîîîîîïUïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøù_ùùùùùùúúúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ ª¬¯°²³´µ¶·¸¹ º!!!!!!!!!!!!!!!»"""""""""""""""¼#################½$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&&&&&À'''''''''''''''''''''Á(((((((((((((((((((((((Â(Â)))))))))))))))))))))))Ã)Ã*************************Ä*Ä+++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ-----------------------------------Ç-Ç.......................................È.È///////////////////////////////////////////É/É/É00ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××××רqØØØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßàyààààààààààààázááááááááááâ{ââââââââââââã|ããããããããããä}ääääääääääååååååååååæææææææææææççççççççççèèèèèèèèèé‚ééééééééêƒêêêêêêêêëëëëëëëëì…ììììììììí†ííííííî‡îîîîîîîîïïïïïïïïððððððððñŠññññññò‹òòòòòòóŒóóóóóóôôôôôôôõŽõõõõõõöööööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿil p q r s tuxz{~‚ƒ„† ‡!!!!!!!!!!!!!!!ˆ"""""""""""""""‰#################Š$$$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&&'''''''''''''''''''Ž'Ž(((((((((((((((((((((()))))))))))))))))))))))))***************************‘+++++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“-----------------------------------”-”.......................................•.•///////////////////////////////////////////–/–00000000ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßà­ààààààààààààá®ááááááááááâ¯ââââââââââââããããããããããããääääääääääå²ååååååååååæ³ææææææææç´ççççççççççèèèèèèèèèèééééééééééêêêêêêêêë¸ëëëëëëëëì¹ììììììììííííííííîîîîîîîîï¼ïïïïïïð½ððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõöÃöööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ; BCEGHIJKLNPQR S!!!!!!!!!!!!!!!T"""""""""""""""U###############V#V$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''''Z((((((((((((((((((((((([)))))))))))))))))))))))))\)\*************************]*]+++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-----------------------------------`-`.....................................a.a.a/////////////////////////////////////////b/b/b000000000000ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááââââââââââââããããããããããããääääääääääääååååååååååååææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////000000000000000000ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààààááááááááááááââââââââââââããããããããããããääääääääääääååååååååååææææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêëëëëëëëëëëììììììììííííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))****************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------------........................................//////////////////////////////////////////////0000000000000000000000ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßàààààààààààààáááááááááááâââââââââââãããããããããããäääääääääääåååååååååæææææææææææççççççççççèèèèèèèèèéééééééééêêêêêêêêëëëëëëëëëìììììììíííííííííîîîîîîîîï!ïïïïïïð"ððððððñ#ññññññò$òòòòòòó%óóóóóóô&ôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýýýþþþþþþþþÿÿÒÕ Ù Ú ÛÜÝàâãåæçèéêëìí î!!!!!!!!!!!!!ï!ï"""""""""""""ð"ð###############ñ$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''''õ(((((((((((((((((((((((ö)))))))))))))))))))))))))÷*************************ø*ø+++++++++++++++++++++++++++++ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú-----------------------------------û-û.....................................ü.ü///////////////////////////////////////////ý/ý0000000000000000000000ÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßàFààààààààààààááááááááááááâHââââââââââãIããããããããããäJääääääääääååååååååååæLææææææææçMççççççççççèèèèèèèèèèééééééééêPêêêêêêêêëQëëëëëëëëììììììììííííííííîTîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôõ[õõõõõõöööööööö÷÷÷÷÷÷ø^øøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüýcýýýýýýþþþþþþþþÿÿ  ¤ ª«­¯°±²´µ¶·¸¹ º!!!!!!!!!!!!!»"""""""""""""""¼#################½$$$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''Á'Á(((((((((((((((((((((Â(Â)))))))))))))))))))))))Ã)Ã*************************Ä+++++++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ---------------------------------Ç-Ç-Ç.....................................È.È/////////////////////////////////////////É/É/É00000000000000000000000000ÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××××רqØqØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßàyààààààààààázááááááááááááâ{ââââââââââã|ããããããããããä}ääääääääå~ååååååååååæææææææææç€ççççççççèèèèèèèèèé‚ééééééééêêêêêêêêêêëëëëëëëëì…ììììììí†ííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôôôõõõõõõööööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúû”ûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ p uy{€‚ƒ„…† ‡!!!!!!!!!!!!!ˆ"""""""""""""""‰#################Š$$$$$$$$$$$$$$$‹$‹%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&&&'''''''''''''''''''''Ž((((((((((((((((((((((())))))))))))))))))))))))*************************‘*‘+++++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“---------------------------------”-”.....................................•.•.•/////////////////////////////////////////–/–00000000000000000000000000000000ÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÞ«Þ«ÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßà­ààààààààààá®ááááááááááááâ¯ââââââââââã°ããããããããããääääääääääå²ååååååååååææææææææææç´ççççççççèµèèèèèèèèééééééééê·êêêêêêêêë¸ëëëëëëëëììììììììííííííííî»îîîîîîï¼ïïïïïïð½ððððððñ¾ññññññò¿òòòòòòóÀóóóóóóôôôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùÆùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ; > ? @BCDFHIJKLNOPQR S!!!!!!!!!!!!!T"""""""""""""""U###############V#V$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''Z'Z((((((((((((((((((((([([)))))))))))))))))))))))\*************************]*]+++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------------`-`.....................................a.a/////////////////////////////////////////b/b/b000000000000000000000000000000000000ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááááââââââââââââããããããããããääääääääääääååååååååååææææææææææææççççççççççèèèèèèèèééééééééééêêêêêêêêêêëëëëëëëëììììììììííííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////000000000000000000000000000000000000000000ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááááââââââââââããããããããããããääääääääääääååååååååååææææææææææççççççççççèèèèèèèèèèééééééééééêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------........................................////////////////////////////////////////////0000000000000000000000000000000000000000000000ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××××ר Ø ØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßàààààààààààáááááááááááááââââââââââãããããããããããäääääääääääåååååååååæææææææææçççççççççèèèèèèèèèéééééééééêêêêêêêêëëëëëëëëëììììììììííííííííî îîîîîîï!ïïïïïïð"ððððððñ#ññññññòòòòòòòòóóóóóóóóôôôôôôõ'õõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿÏÕ ÛÜÝÞßáâãçèéëìí î!!!!!!!!!!!!!ï"""""""""""""""ð###############ñ$$$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&&&ô'''''''''''''''''''õ'õ(((((((((((((((((((((ö)))))))))))))))))))))))÷)÷*************************ø+++++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú---------------------------------û-û.....................................ü.ü/////////////////////////////////////////ý/ý000000000000000000000000000000000000000000000þÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××××ר>ØØØØØØØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßàFààààààààààáGááááááááááâHââââââââââãIããããããããããäJääääääääääååååååååååæLææææææææçMççççççççèèèèèèèèèèééééééééêPêêêêêêêêëëëëëëëëìRììììììíSííííííííîîîîîîîîïïïïïïïïððððððððññññññòXòòòòòòóYóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷ø^øøøøøøùùùùùùú`úúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿ £ ¦ ¬­¯°±²³´µ¶·¸¹ º!!!!!!!!!!!!!»"""""""""""""""¼###############½$$$$$$$$$$$$$$$¾$¾%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''Á(((((((((((((((((((((Â(Â)))))))))))))))))))))))Ã*************************Ä*Ä+++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ---------------------------------Ç-Ç...................................È.È.È///////////////////////////////////////É/É/É000000000000000000000000000000000000000000000Ê0Ê11ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××רqØqØØØØØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜuÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßàyààààààààààázááááááááááâ{ââââââââââã|ããããããããããä}ääääääääå~ååååååååååææææææææææççççççççèèèèèèèèèé‚ééééééééêêêêêêêêë„ëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòòòóóóóóóôôôôôôôõõõõõõööööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþþþÿÿmo r vw{}~€‚ƒ„…† ‡!!!!!!!!!!!!!ˆ"""""""""""""‰"‰#############Š#Š$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&&&'''''''''''''''''''Ž'Ž((((((((((((((((((((())))))))))))))))))))))))***********************‘*‘+++++++++++++++++++++++++++’,,,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“---------------------------------”-”...................................•.•/////////////////////////////////////////–/–000000000000000000000000000000000000000000000—0—0—111111ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜݪݪÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßßßà­ààààààààààá®ááááááááááâ¯ââââââââââã°ããããããããããääääääääääå²ååååååååæ³ææææææææç´ççççççççèµèèèèèèèèééééééééê·êêêêêêêêëëëëëëëëì¹ììììììíºííííííî»îîîîîîï¼ïïïïïïð½ððððððñ¾ññññññòòòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûüÉüüüüüüýýýýýýþþþþþþþþÿÿ7 = @ADEFHKMNOPQR S S!!!!!!!!!!!!!T"""""""""""""U###############V$$$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&&&&&Y'''''''''''''''''''Z((((((((((((((((((((([([)))))))))))))))))))))\)\***********************]*]+++++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------------`-`...................................a.a///////////////////////////////////////b/b/b0000000000000000000000000000000000000000000c0c0c111111111111ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääääååååååååååææææææææææççççççççççèèèèèèèèééééééééééêêêêêêêêëëëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññòòòòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................//////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111111111ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääääååååååååååææææææææææççççççççèèèèèèèèèèééééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððððññññññññòòòòòòóóóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------------......................................//////////////////////////////////////////0000000000000000000000000000000000000000000000001111111111111111111111ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××××ר ØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßßàààààààààààáááááááááááâââââââââââãããããããããäääääääääääååååååååååææææææææææççççççççèèèèèèèèèéééééééééêêêêêêêêëëëëëëëëìììììììíííííííî îîîîîîï!ïïïïïïð"ððððððññññññññòòòòòòó%óóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúûûûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿÕ × Ù ÜÝÞßàáãåæçèêëìí î!!!!!!!!!!!!!ï!ï"""""""""""""ð###############ñ$$$$$$$$$$$$$$$ò$ò%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&ô'''''''''''''''''''õ(((((((((((((((((((((ö(ö)))))))))))))))))))))÷)÷***********************ø*ø+++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú-------------------------------û-û-û.................................ü.ü.ü/////////////////////////////////////ý/ý/ý0000000000000000000000000000000000000000000þ0þ0þ1111111111111111111111ÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßßßàFààààààààààáGááááááááááâHââââââââââããããããããããäJääääääääåKååååååååæLææææææææçMççççççççèNèèèèèèèèééééééééêPêêêêêêëQëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððñWññññññòòòòòòòòóóóóóóôZôôôôôôõõõõõõö\öööööö÷÷÷÷÷÷øøøøøøøøùùùùùùúúúúúúûaûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿŸ §®¯°²³µ¶·¸¹ º!!!!!!!!!!!!!»"""""""""""""""¼###############½$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&À&À'''''''''''''''''''Á(((((((((((((((((((Â(Â)))))))))))))))))))))Ã)Ã***********************Ä*Ä+++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ-------------------------------Ç-Ç...................................È.È///////////////////////////////////////É/É000000000000000000000000000000000000000000000Ê0Ê1111111111111111111111111111ÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××××רqØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßßßààààààààààààázááááááááááâ{ââââââââã|ããããããããããä}ääääääääå~ååååååååæææææææææç€ççççççççèèèèèèèèé‚ééééééééêêêêêêêêë„ëëëëëëì…ììììììí†ííííííî‡îîîîîîïˆïïïïïïððððððððññññññò‹òòòòòòóóóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷ø‘øøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿio q s vwxyz}~‚„…† ‡!!!!!!!!!!!!!ˆ"""""""""""""""‰#############Š#Š$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&'''''''''''''''''''Ž'Ž((((((((((((((((((()))))))))))))))))))))))***********************‘*‘+++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“-------------------------------”-”...................................•.•///////////////////////////////////////–/–0000000000000000000000000000000000000000000—0—0—11111111111111111111111111111111ÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××ר¥ØØØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÛÛÛÛܩܩÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßà­ààààààààààààá®ááááááááááââââââââââã°ããããããããããääääääääääå²ååååååååæ³ææææææææççççççççèµèèèèèèèèééééééééê·êêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïð½ððððððññññññññòòòòòòóÀóóóóóóôôôôôôõÂõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ9 AGHJKLMNOPQR S!!!!!!!!!!!!!T"""""""""""""U"U#############V$$$$$$$$$$$$$$$W$W%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&&&Y'''''''''''''''''''Z((((((((((((((((((([([)))))))))))))))))))))\)\***********************]+++++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-------------------------------`-`...................................a.a/////////////////////////////////////b/b/b0000000000000000000000000000000000000000000c0c11111111111111111111111111111111111111ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààààááááááááááââââââââââââããããããããããääääääääääääååååååååååææææææææççççççççççèèèèèèèèééééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïïïððððððññññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõöööööö÷÷÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))**************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------......................................////////////////////////////////////////000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââââããããããããããääääääääääååååååååååææææææææææççççççççççèèèèèèèèééééééééêêêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîïïïïïïïïððððððððññññññòòòòòòòòóóóóóóôôôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""################$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((((())))))))))))))))))))))**************************++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------....................................//////////////////////////////////////////0000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××××ר ØØØØØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßàààààààààààáááááááááááâââââââââââãããããããããäääääääääåååååååååæææææææææçççççççççèèèèèèèèéééééééêêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîï!ïïïïïïð"ððððððññññññò$òòòòòòóóóóóóô&ôôôôôôõõõõõõö(öööööö÷÷÷÷÷÷øøøøøøùùùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþ0þþþþþþÿÿÑÕ Ø Ú Ýãäåçèêëí î!!!!!!!!!!!!!ï"""""""""""""ð###############ñ$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&&&ô'''''''''''''''''''õ(((((((((((((((((((ö(ö)))))))))))))))))))))÷***********************ø*ø+++++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú-----------------------------û-û.................................ü.ü/////////////////////////////////////ý/ý/ý00000000000000000000000000000000000000000þ0þ0þ11111111111111111111111111111111111111111111111ÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××ר>Ø>ØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÝCÝCÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßàFààààààààààáGááááááááááâHââââââââââãIããããããããäJääääääääåKååååååååæLææææææææççççççççèNèèèèèèèèééééééééêPêêêêêêëQëëëëëëìRììììììíSííííííîTîîîîîîïïïïïïïïððððððñWññññññòòòòòòóYóóóóóóôôôôôôõõõõõõõõöööööö÷÷÷÷÷÷øøøøøøù_ùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýýýþþþþþþÿÿ ¨«¬­®¯±²µ¶·¸¹ º!!!!!!!!!!!!!»"""""""""""""¼###############½$$$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&&&À&À'''''''''''''''''Á'Á(((((((((((((((((((Â)))))))))))))))))))))Ã)Ã*********************Ä*Ä+++++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ-----------------------------Ç-Ç.................................È.È/////////////////////////////////////É/É0000000000000000000000000000000000000000000Ê0Ê1111111111111111111111111111111111111111111111111Ë1Ë22ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××××רqØØØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛtÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÞwÞwÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßàyààààààààààázááááááááááâ{ââââââââââããããããããããä}ääääääääå~ååååååååææææææææç€ççççççççèèèèèèèé‚ééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîïˆïïïïïïððððððððññññññòòòòòòòòóóóóóóôôôôôôõŽõõõõõõöööööö÷÷÷÷÷÷ø‘øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýýýþþþþþþÿÿko r tvw}€‚ƒ„…† ‡!!!!!!!!!!!!!ˆ"""""""""""""‰#############Š#Š$$$$$$$$$$$$$‹$‹%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&'''''''''''''''''''Ž(((((((((((((((((((())))))))))))))))))))***********************‘+++++++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“-----------------------------”-”.................................•.•/////////////////////////////////////–/–00000000000000000000000000000000000000000—0—0—11111111111111111111111111111111111111111111111˜1˜1˜222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××ר¥Ø¥ØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛܩܩÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßà­ààààààààààá®ááááááááááâ¯ââââââââã°ããããããããããääääääääääååååååååæ³ææææææææç´ççççççççèèèèèèèèé¶ééééééê·êêêêêêë¸ëëëëëëì¹ììììììíºííííííîîîîîîîîïïïïïïð½ððððððññññññò¿òòòòòòóóóóóóôÁôôôôôôõõõõõõöööööö÷Ä÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýýýþþþþþþÿÿ8 < ? ADEFGHJLMNOPQR S!!!!!!!!!!!!!T"""""""""""""U#############V$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%X%X&&&&&&&&&&&&&&&Y&Y'''''''''''''''''Z'Z((((((((((((((((([([)))))))))))))))))))))\***********************]*]+++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,,,_,_-----------------------------`-`...............................a.a.a///////////////////////////////////b/b/b00000000000000000000000000000000000000000c0c11111111111111111111111111111111111111111111111d1d1d222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââããããããããããääääääääääååååååååååææææææææææççççççççèèèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííîîîîîîîîïïïïïïïïððððððññññññññòòòòòòóóóóóóóóôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------..................................////////////////////////////////////////000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááááââââââââââããããããããããääääääääääååååååååååææææææææççççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííííîîîîîîîîïïïïïïððððððððññññññòòòòòòòòóóóóóóôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------------------------------..................................////////////////////////////////////////0000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111222222222222222222222222ÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××××ר Ø ØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßàààààààààààáááááááááááâââââââââãããããããããäääääääääåååååååååæææææææçççççççççèèèèèèèèéééééééêêêêêêêëëëëëëëìììììììíííííííîîîîîîîîïïïïïïð"ððððððññññññòòòòòòòòóóóóóóôôôôôôõõõõõõö(öööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûü.üüüüüüýýýýýýþþþþþþÿÿÕ Ù ÛÝßàáâãäæêëìí î!!!!!!!!!!!ï!ï"""""""""""ð"ð#############ñ$$$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&ô'''''''''''''''''õ'õ(((((((((((((((((ö(ö)))))))))))))))))))÷)÷*********************ø*ø+++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú---------------------------û-û-û...............................ü.ü///////////////////////////////////ý/ý/ý000000000000000000000000000000000000000þ0þ0þ111111111111111111111111111111111111111111111ÿ1ÿ1ÿ222222222222222222222222ÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××××××ר>ØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßàFààààààààààáGááááááááááââââââââââãIããããããããäJääääääääåKååååååååææææææææçMççççççèNèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííîTîîîîîîïïïïïïïïððððððññññññòXòòòòòòóóóóóóôôôôôôõ[õõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûaûûûûûûüüüüüüýýýýýýþþþþþþÿÿ¢ ¦ ¨ª®±²³´µ¶·¸¹ º!!!!!!!!!!!»"""""""""""""¼###############½$$$$$$$$$$$$$¾$¾%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&&&À&À'''''''''''''''''Á(((((((((((((((((((Â)))))))))))))))))))))Ã***********************Ä+++++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ---------------------------Ç-Ç.................................È.È///////////////////////////////////É/É00000000000000000000000000000000000000000Ê0Ê11111111111111111111111111111111111111111111111Ë1Ë222222222222222222222222222222ÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××××רqØqØØØØØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßàyààààààààààázááááááááâ{ââââââââââã|ããããããããä}ääääääääååååååååæææææææææççççççççèèèèèèèé‚ééééééêƒêêêêêêë„ëëëëëëì…ììììììí†ííííííîîîîîîïˆïïïïïïððððððñŠññññññòòòòòòóóóóóóôôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøù’ùùùùú“úúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ p tvyz|}~€‚ƒ„…† ‡!!!!!!!!!!!ˆ"""""""""""""‰#############Š#Š$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&&&'''''''''''''''''Ž'Ž(((((((((((((((((())))))))))))))))))))*********************‘*‘+++++++++++++++++++++++’,,,,,,,,,,,,,,,,,,,,,,,,,,,“,“---------------------------”-”...............................•.•.•///////////////////////////////////–/–000000000000000000000000000000000000000—0—0—111111111111111111111111111111111111111111111˜1˜1˜2222222222222222222222222222222222ÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××××××ר¥ØØØØØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßà­ààààààààààá®ááááááááâ¯ââââââââââããããããããããääääääääå²ååååååååæ³ææææææç´ççççççççèèèèèèèèé¶ééééééê·êêêêêêë¸ëëëëëëììììììììííííííî»îîîîîîïïïïïïð½ððððððññññññò¿òòòòóÀóóóóóóôôôôôôõõõõõõöööööö÷Ä÷÷÷÷øÅøøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ = CDFGHIKMNOPQR S!!!!!!!!!!!T"""""""""""""U#############V$$$$$$$$$$$$$$$W%%%%%%%%%%%%%%%X&&&&&&&&&&&&&&&Y&Y'''''''''''''''''Z((((((((((((((((((([)))))))))))))))))))\)\*********************]*]+++++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------`-`...............................a.a///////////////////////////////////b/b/b000000000000000000000000000000000000000c0c111111111111111111111111111111111111111111111d1d1d2222222222222222222222222222222222222222ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááââââââââââããããããããããääääääääääååååååååååææææææææççççççççèèèèèèèèèèééééééééêêêêêêêêëëëëëëììììììììííííííííîîîîîîïïïïïïïïððððððññññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''''(((((((((((((((((((())))))))))))))))))))************************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààààááááááááááââââââââââããããããããããääääääääääååååååååææææææææææççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððððññññññòòòòòòóóóóóóôôôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------..................................//////////////////////////////////////0000000000000000000000000000000000000000001111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××ר Ø ØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßàààààààààààáááááááááâââââââââãããããããããäääääääääåååååååæææææææææççççççççèèèèèèèéééééééêêêêêêêëëëëëëëììììììììííííííî îîîîîîïïïïïïð"ððððððññññññòòòòòòóóóóóóô&ôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿÒÓÔÕ ÛÝßáâãäåçéêëìí î!!!!!!!!!!!ï"""""""""""""ð#############ñ$$$$$$$$$$$$$ò%%%%%%%%%%%%%%%ó&&&&&&&&&&&&&&&&&ô'''''''''''''''''õ(((((((((((((((((ö(ö)))))))))))))))))))÷)÷*********************ø+++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,,,ú,ú---------------------------û-û...............................ü.ü/////////////////////////////////ý/ý/ý000000000000000000000000000000000000000þ0þ111111111111111111111111111111111111111111111ÿ1ÿ222222222222222222222222222222222222222222222222223ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××××ר>ØØØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßßßàFààààààààààáGááááááááâHââââââââãIããããããããäJääääääääååååååååæLææææææçMççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëìRììììììíSííííííîîîîîîïUïïïïïïððððððññññññòXòòòòóYóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿœ¢ £ ¤ ¨ª¬²³´·¸¹ º!!!!!!!!!!!»"""""""""""¼"¼###########½#½$$$$$$$$$$$$$¾%%%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&À&À'''''''''''''''Á'Á(((((((((((((((((Â)))))))))))))))))))Ã)Ã*********************Ä*Ä+++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ---------------------------Ç-Ç.............................È.È.È/////////////////////////////////É/É000000000000000000000000000000000000000Ê0Ê0Ê1111111111111111111111111111111111111111111Ë1Ë1Ë2222222222222222222222222222222222222222222222222Ì2Ì2Ì33ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××רqØqØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßßßàyààààààààààááááááááááâ{ââââââââã|ããããããããä}ääääääå~ååååååååæææææææç€ççççççèèèèèèèé‚ééééééêƒêêêêêêë„ëëëëëëììììììììííííííî‡îîîîîîïïïïïïððððððñŠññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ r s vxz{|}~‚ƒ„…† ‡!!!!!!!!!!!ˆ"""""""""""‰#############Š$$$$$$$$$$$$$$$‹%%%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&&&'''''''''''''''''Ž(((((((((((((((((()))))))))))))))))))*********************‘*‘+++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,,,“,“---------------------------”-”.............................•.•///////////////////////////////////–/–0000000000000000000000000000000000000—0—0—1111111111111111111111111111111111111111111˜1˜1˜2222222222222222222222222222222222222222222222222™2™2™33333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××××ר¥ØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßà­ààààààààá®ááááááááááâ¯ââââââââã°ããããããããääääääääå²ååååååååææææææææç´ççççççèµèèèèèèé¶ééééééê·êêêêêêëëëëëëì¹ììììììííííííííîîîîîîïïïïïïð½ððððððññññññòòòòòòóóóóóóôôôôôôõÂõõõõöÃöööö÷Ä÷÷÷÷øÅøøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ4 @CEHIJLMNOPQR S!!!!!!!!!!!T"""""""""""U#############V$$$$$$$$$$$$$W$W%%%%%%%%%%%%%X&&&&&&&&&&&&&&&Y&Y'''''''''''''''Z'Z((((((((((((((((([)))))))))))))))))))\)\*******************]*]+++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,,,_,_---------------------------`-`.............................a.a/////////////////////////////////b/b/b0000000000000000000000000000000000000c0c1111111111111111111111111111111111111111111d1d1d2222222222222222222222222222222222222222222222222e2e2e33333333333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááááââââââââââããããããããääääääääääååååååååææææææææææççççççççèèèèèèèèééééééééêêêêêêëëëëëëëëììììììííííííííîîîîîîïïïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøùùùùùùúúúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,------------------------------................................////////////////////////////////////000000000000000000000000000000000000000000111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333333333ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããããääääääääääååååååååææææææææççççççççèèèèèèèèééééééééêêêêêêêêëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððññññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúûûûûûûüüüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..................................////////////////////////////////////0000000000000000000000000000000000000000111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222233333333333333333333333333ËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××××ר ØØØØØØØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßàààààààààáááááááááâââââââââãããããããããäääääääääååååååååæææææææçççççççèèèèèèèéééééééêêêêêêêëëëëëëìììììììííííííî îîîîîîïïïïïïð"ððððñ#ññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúû-ûûûûüüüüüüýýýýýýþþþþþþÿÿÖ × Ø Ù Þàâãäåæçèéêëìí î î!!!!!!!!!!!ï"""""""""""ð#############ñ$$$$$$$$$$$$$ò%%%%%%%%%%%%%ó%ó&&&&&&&&&&&&&&&ô'''''''''''''''õ'õ(((((((((((((((((ö)))))))))))))))))))÷*********************ø+++++++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,ú,ú-------------------------û-û.............................ü.ü.ü///////////////////////////////ý/ý/ý0000000000000000000000000000000000000þ0þ1111111111111111111111111111111111111111111ÿ1ÿ22222222222222222222222222222222222222222222222233333333333333333333333333333ËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××ר>Ø>ØØØØØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÞDÞDÞÞÞÞÞÞÞÞßEßßßßßßßßßßàFààààààààáGááááááááâHââââââââãIããããããããäJääääääåKååååååååææææææææçMççççççèNèèèèèèééééééééêêêêêêëQëëëëëëììììììíSííííííîîîîîîïUïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûüüüüüüýýýýýýþþþþþþÿÿ ¡ ¦ §¨«­°±²´¶·¸¹ º!!!!!!!!!!!»!»"""""""""""¼###########½#½$$$$$$$$$$$¾$¾%%%%%%%%%%%%%¿&&&&&&&&&&&&&&&À&À'''''''''''''''Á(((((((((((((((((Â(Â)))))))))))))))))Ã)Ã*******************Ä*Ä+++++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ-------------------------Ç-Ç.............................È.È/////////////////////////////////É/É0000000000000000000000000000000000000Ê0Ê0Ê11111111111111111111111111111111111111111Ë1Ë1Ë22222222222222222222222222222222222222222222222Ì2Ì2Ì33333333333333333333333333333333ËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××××רqØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÝvÝvÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞßxßßßßßßßßßßàyààààààààázááááááááâ{ââââââââã|ããããããããääääääääå~ååååååæææææææææççççççççèèèèèèé‚ééééééêƒêêêêêêëëëëëëì…ììììììííííííî‡îîîîîîïïïïïïððððððññññññò‹òòòòóŒóóóóôôôôôõŽõõõõööööö÷÷÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûüüüüüüýýýýýýþþþþþþÿÿl vy{|€‚ƒ„…† ‡!!!!!!!!!!!ˆ"""""""""""""‰###########Š$$$$$$$$$$$$$‹%%%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&'''''''''''''''Ž'Ž(((((((((((((((())))))))))))))))))*******************‘*‘+++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,,,“,“-------------------------”-”.............................•.•/////////////////////////////////–/–00000000000000000000000000000000000—0—0—11111111111111111111111111111111111111111˜1˜1˜22222222222222222222222222222222222222222222222™2™2™33333333333333333333333333333333333333ËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××ר¥ØØØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßà­ààààààààá®ááááááááâ¯ââââââââã°ããããããä±ääääääääå²ååååååæ³ææææææç´ççççççèµèèèèèèé¶ééééééêêêêêêë¸ëëëëëëììììììíºííííííîîîîîîï¼ïïïïð½ððððñ¾ññññññòòòòòòóóóóóóôôôôôôõõõõõõöööö÷Ä÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûüÉüüüüýýýýýýþþþþþþÿÿ CDFHIJKLMNPQR S!!!!!!!!!!!T"""""""""""U"U###########V$$$$$$$$$$$$$W%%%%%%%%%%%%%X%X&&&&&&&&&&&&&Y&Y'''''''''''''''Z((((((((((((((((([)))))))))))))))))))\*******************]*]+++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,,,_,_-------------------------`-`.............................a.a///////////////////////////////b/b/b00000000000000000000000000000000000c0c11111111111111111111111111111111111111111d1d1d22222222222222222222222222222222222222222222222e2e2e33333333333333333333333333333333333333333333ËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããääääääääääååååååååææææææææççççççççèèèèèèèèééééééêêêêêêêêëëëëëëììììììììííííííîîîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷øøøøøøùùùùùùúúúúúúûûûûûûüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''''(((((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------................................//////////////////////////////////000000000000000000000000000000000000000011111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááááââââââââââããããããããääääääääååååååååååææææææææççççççççèèèèèèééééééééêêêêêêêêëëëëëëììììììííííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøùùùùùùúúúúúúûûûûûûüüüüýýýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""##############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////////0000000000000000000000000000000000000011111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××××ר ØØØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞßßßßßßßßßßßàààààààààáááááááááâââââââââããããããããäääääääåååååååååææææææææççççççççèèèèèèéééééééêêêêêêêëëëëëëìììììíííííííîîîîîîïïïïïïððððððñ#ññññò$òòòòó%óóóóôôôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøù+ùùùùúúúúúúûûûûûûüüüüýýýýýýþþþþþþÿÿÖ Þßáãäèéêëìí î!!!!!!!!!!!ï"""""""""""ð#############ñ$$$$$$$$$$$ò$ò%%%%%%%%%%%%%ó&&&&&&&&&&&&&ô&ô'''''''''''''''õ(((((((((((((((((ö)))))))))))))))))÷)÷*******************ø*ø+++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,,,ú,ú-------------------------û-û...........................ü.ü///////////////////////////////ý/ý/ý00000000000000000000000000000000000þ0þ11111111111111111111111111111111111111111ÿ1ÿ1ÿ22222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333344ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××××ר>Ø>ØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞßEßßßßßßßßßßàFààààààààáGááááááááâHââââââãIããããããããäJääääääåKååååååæLææææææçMççççççèNèèèèèèéOééééééêêêêêêëQëëëëëëììììììííííííîTîîîîïUïïïïðVððððððññññññòòòòòòóóóóôZôôôôõ[õõõõöööööö÷÷÷÷÷÷øøøøøøùùùùúúúúúúûûûûûûüüüüýýýýýýþþþþþþÿÿš¡ ¬®°±²³´µ¶·¸¹ º!!!!!!!!!!!»"""""""""""¼###########½#½$$$$$$$$$$$¾%%%%%%%%%%%%%¿%¿&&&&&&&&&&&&&À'''''''''''''''Á'Á(((((((((((((((Â(Â)))))))))))))))))Ã*******************Ä*Ä+++++++++++++++++++++Å,,,,,,,,,,,,,,,,,,,,,,,Æ,Æ-------------------------Ç-Ç...........................È.È///////////////////////////////É/É00000000000000000000000000000000000Ê0Ê0Ê111111111111111111111111111111111111111Ë1Ë1Ë222222222222222222222222222222222222222222222Ì2Ì2Ì33333333333333333333333333333333333333333333333333333Í3Í3Í4444ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××××רqØØØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞßxßßßßßßßßßßàyààààààààázááááááááââââââââã|ããããããããääääääääå~ååååååæææææææç€ççççççèèèèèèèèééééééêƒêêêêêêëëëëëëì…ììììí†ííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôôôõõõõööööö÷÷÷÷÷÷øøøøøøùùùùú“úúúúûûûûûûüüüüý–ýýýýþþþþþþÿÿm r s tuvwz|~€ƒ„…† ‡!!!!!!!!!!!ˆ"""""""""""‰###########Š$$$$$$$$$$$$$‹%%%%%%%%%%%%%Œ&&&&&&&&&&&&&&&'''''''''''''''Ž((((((((((((((((())))))))))))))))))*****************‘*‘+++++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,“,“-------------------------”-”...........................•.•///////////////////////////////–/–00000000000000000000000000000000000—0—111111111111111111111111111111111111111˜1˜1˜222222222222222222222222222222222222222222222™2™2™33333333333333333333333333333333333333333333333333333š3š3š4444444444ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××××ר¥Ø¥ØØØØØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞ߬ßßßßßßßßßßà­ààààààààá®ááááááâ¯ââââââââã°ããããããä±ääääääääååååååååææææææææççççççèµèèèèèèé¶ééééééêêêêêêë¸ëëëëëëììììììííííííî»îîîîï¼ïïïïð½ððððñ¾ññññò¿òòòòóóóóóóôôôôôôõõõõõõöööö÷Ä÷÷÷÷øøøøøøùùùùùùúúúúûûûûûûüüüüüüýýýýþþþþþþÿÿ < = DEGHILNOPQR S!!!!!!!!!!!T"""""""""""U###########V$$$$$$$$$$$$$W%%%%%%%%%%%%%X&&&&&&&&&&&&&Y&Y'''''''''''''Z'Z((((((((((((((([([)))))))))))))))\)\*******************]+++++++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,,,_,_-------------------------`-`...........................a.a/////////////////////////////b/b/b000000000000000000000000000000000c0c0c1111111111111111111111111111111111111d1d1d222222222222222222222222222222222222222222222e2e2e333333333333333333333333333333333333333333333333333f3f3f3f4444444444444444ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßßßààààààààààááááááááââââââââââããããããããääääääääååååååååææææææææççççççççèèèèèèèèééééééêêêêêêêêëëëëëëììììììííííííííîîîîîîïïïïïïððððððññññññòòòòóóóóóóôôôôôôõõõõõõöööööö÷÷÷÷øøøøøøùùùùùùúúúúûûûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((((())))))))))))))))))**********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,----------------------------..............................////////////////////////////////000000000000000000000000000000000000001111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààààááááááááââââââââââããããããããääääääääååååååååææææææææççççççççèèèèèèééééééééêêêêêêëëëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôõõõõõõöööööö÷÷÷÷÷÷øøøøùùùùùùúúúúûûûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,--------------------------..............................//////////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333444444444444444444444444444444ÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××××ר Ø ØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßàààààààààááááááááâââââââââãããããããäääääääåååååååæææææææçççççççèèèèèèéééééééêêêêêêëëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòòòóóóóóóôôôôõ'õõõõöööööö÷÷÷÷÷÷øøøøùùùùùùúúúúû-ûûûûüüüüüüýýýýþþþþþþÿÿ Ù Ú ßàãåæçèéëìí î!!!!!!!!!!!ï"""""""""ð"ð#########ñ#ñ$$$$$$$$$$$ò%%%%%%%%%%%%%ó&&&&&&&&&&&&&ô&ô'''''''''''''õ'õ(((((((((((((((ö)))))))))))))))))÷)÷*****************ø*ø+++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,,,ú,ú-----------------------û-û...........................ü.ü/////////////////////////////ý/ý/ý000000000000000000000000000000000þ0þ111111111111111111111111111111111111111ÿ1ÿ1ÿ22222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333444444444444444444444444444444444ÊÊÊÊÊÊË1Ë1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××××ר>ØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞÞÞßEßßßßßßßßàFààààààáGááááááááâHââââââââããããããããäJääääääåKååååååæLææææææççççççèNèèèèèèéOééééêPêêêêêêëëëëëëìRììììíSííííîTîîîîïUïïïïðVððððñWññññòXòòòòóóóóóóôôôôôôõõõõö\öööö÷÷÷÷÷÷øøøøùùùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿœ¡ ¤ ­®°²³´µ¶·¸¹ º!!!!!!!!!»!»"""""""""¼###########½$$$$$$$$$$$$$¾%%%%%%%%%%%%%¿&&&&&&&&&&&&&À'''''''''''''''Á(((((((((((((((Â(Â)))))))))))))))Ã)Ã*****************Ä*Ä+++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,,,Æ,Æ-----------------------Ç-Ç...........................È.È/////////////////////////////É/É000000000000000000000000000000000Ê0Ê0Ê1111111111111111111111111111111111111Ë1Ë1Ë2222222222222222222222222222222222222222222Ì2Ì2Ì3333333333333333333333333333333333333333333333333Í3Í3Í3Í444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××××רqØqØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÛtÛtÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞÞÞßxßßßßßßßßàyààààààázááááááááâ{ââââââã|ããããããããääääääääååååååååææææææç€ççççççèèèèèèèééééééêƒêêêêë„ëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòóŒóóóóôôôôôôõõõõõõöööö÷÷÷÷÷÷øøøøù’ùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿk p tuvwxyz|~ƒ„…† ‡!!!!!!!!!ˆ"""""""""""‰###########Š$$$$$$$$$$$‹$‹%%%%%%%%%%%Œ%Œ&&&&&&&&&&&&&'''''''''''''Ž'Ž((((((((((((((()))))))))))))))))*******************‘+++++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,,,“,“-----------------------”-”...........................•.•/////////////////////////////–/–000000000000000000000000000000000—0—1111111111111111111111111111111111111˜1˜1˜2222222222222222222222222222222222222222222™2™2™3333333333333333333333333333333333333333333333333š3š3š44444444444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××××ר¥ØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞÞÞ߬ßßßßßßßßà­ààààààá®ááááááááâ¯ââââââã°ããããããä±ääääääå²ååååååæ³ææææææç´ççççççèèèèèèé¶ééééééêêêêêêëëëëëëì¹ììììíºííííî»îîîîï¼ïïïïð½ððððññññññòòòòòòóóóóôÁôôôôõõõõõõöööö÷Ä÷÷÷÷øøøøøøùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿ9 ? GHIJKLNOPQR S!!!!!!!!!T"""""""""""U###########V$$$$$$$$$$$W%%%%%%%%%%%%%X&&&&&&&&&&&&&Y&Y'''''''''''''Z((((((((((((((([([)))))))))))))))\)\*****************]*]+++++++++++++++++++^,,,,,,,,,,,,,,,,,,,,,_,_-----------------------`-`.........................a.a.a///////////////////////////b/b/b0000000000000000000000000000000c0c0c11111111111111111111111111111111111d1d1d2222222222222222222222222222222222222222222e2e2e3333333333333333333333333333333333333333333333333f3f3f44444444444444444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààááááááááááââââââââããããããããääääääääååååååååææææææææççççççèèèèèèèèééééééêêêêêêëëëëëëëëììììììííííííîîîîîîïïïïïïððððññññññòòòòòòóóóóóóôôôôõõõõõõöööööö÷÷÷÷øøøøøøùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''''(((((((((((((((())))))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////000000000000000000000000000000000000111111111111111111111111111111111111111122222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞÞÞßßßßßßßßßßààààààààááááááááááââââââââããããããããääääääääååååååååææææææççççççççèèèèèèééééééééêêêêêêëëëëëëììììììííííííîîîîîîïïïïïïððððððññññññòòòòóóóóóóôôôôôôõõõõöööööö÷÷÷÷øøøøøøùùùùúúúúúúûûûûüüüüüüýýýýþþþþþþÿÿ  !!!!!!!!!!""""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((((())))))))))))))))********************++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------............................////////////////////////////////0000000000000000000000000000000000001111111111111111111111111111111111111111222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444444444ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××ר Ø ØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßßààààààààáááááááááâââââââãããããããäääääääåååååååææææææçççççççèèèèèéééééééêêêêêêëëëëëìììììíííííî îîîîï!ïïïïððððððññññññòòòòó%óóóóôôôôôôõõõõö(öööö÷÷÷÷ø*øøøøùùùùú,úúúúûûûûü.üüüüýýýýþþþþþþÿÿ Ø ÛÜâãåçèéêëìí î!!!!!!!!!ï"""""""""""ð#########ñ#ñ$$$$$$$$$$$ò%%%%%%%%%%%ó%ó&&&&&&&&&&&ô&ô'''''''''''''õ(((((((((((((((ö(ö)))))))))))))))÷*****************ø*ø+++++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,ú,ú-----------------------û-û.........................ü.ü///////////////////////////ý/ý/ý0000000000000000000000000000000þ0þ0þ11111111111111111111111111111111111ÿ1ÿ1ÿ22222222222222222222222222222222222222223333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××××ר>ØØØØØØØØØØØØÙ?Ù?ÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÛÛÜBÜBÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞßEßßßßßßàFààààààààáGááááááááââââââââãIããããããäJääääääååååååæLææææææçMççççççèèèèèèéOééééêPêêêêêêëëëëëëììììììííííííîîîîîîïïïïðVððððñWññññòòòòòòóóóóôZôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùùùúúúúûûûûûûüüüüýýýýþþþþþþÿÿŸ¡ £ ¦ ª«¬­®°±²´µ¶·¸¹ º!!!!!!!!!»"""""""""¼"¼#########½$$$$$$$$$$$¾$¾%%%%%%%%%%%¿&&&&&&&&&&&&&À'''''''''''''Á'Á(((((((((((((Â(Â)))))))))))))))Ã)Ã*****************Ä+++++++++++++++++++Å+Å,,,,,,,,,,,,,,,,,,,Æ,Æ-----------------------Ç-Ç.........................È.È///////////////////////////É/É0000000000000000000000000000000Ê0Ê0Ê11111111111111111111111111111111111Ë1Ë1Ë22222222222222222222222222222222222222222Ì2Ì2Ì33333333333333333333333333333333333333333333333Í3Í3Í444444444444444444444444444444444444444444444444444444444Î4Î4Î4Î555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××רqØqØØØØØØØØØØØØÙrÙÙÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞßxßßßßßßàyààààààààázááááááâ{ââââââââããããããããääääääå~ååååååæææææææççççççèèèèèèèééééééêêêêêêë„ëëëëì…ììììí†ííííî‡îîîîïïïïïïððððððññññò‹òòòòóóóóóóôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùùùúúúúûûûûûûüüüüýýýýþþþþþþÿÿj r uv|}€„…† ‡!!!!!!!!!ˆ"""""""""‰###########Š$$$$$$$$$$$‹%%%%%%%%%%%%%Œ&&&&&&&&&&&&'''''''''''''Ž((((((((((((((()))))))))))))))))*****************‘*‘+++++++++++++++++’+’,,,,,,,,,,,,,,,,,,,“,“-----------------------”-”.......................•.•.•///////////////////////////–/–0000000000000000000000000000000—0—11111111111111111111111111111111111˜1˜1˜22222222222222222222222222222222222222222™2™2™33333333333333333333333333333333333333333333333š3š3š4444444444444444444444444444444444444444444444444444444›4›4›4›55555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖפפ×××××××××××ר¥ØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜݪݪÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞ߬ßßßßßßà­ààààààààá®ááááááâ¯ââââââã°ããããããä±ääääääå²ååååååæ³ææææç´ççççççèµèèèèé¶ééééê·êêêêêêëëëëëëììììììííííííîîîîï¼ïïïïð½ððððññññññòòòòóÀóóóóôôôôõÂõõõõöööö÷Ä÷÷÷÷øøøøùÆùùùùúúúúûûûûûûüüüüýýýýþËþþþþÿÿ @DEFGHJLMNOPQR S!!!!!!!!!T"""""""""U###########V$$$$$$$$$$$W%%%%%%%%%%%X%X&&&&&&&&&&&Y'''''''''''''Z'Z((((((((((((([([)))))))))))))))\)\***************]*]+++++++++++++++++^+^,,,,,,,,,,,,,,,,,,,_,_-----------------------`-`.......................a.a///////////////////////////b/b/b00000000000000000000000000000c0c0c11111111111111111111111111111111111d1d22222222222222222222222222222222222222222e2e2e33333333333333333333333333333333333333333333333f3f3f4444444444444444444444444444444444444444444444444444444g4g4g5555555555555555555555ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââããããããããääääääääååååååååææææææççççççççèèèèèèééééééêêêêêêëëëëëëììììììííííííîîîîîîïïïïïïððððññññññòòòòòòóóóóôôôôôôõõõõöööööö÷÷÷÷øøøøøøùùùùúúúúûûûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,--------------------------..........................//////////////////////////////000000000000000000000000000000000011111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444445555555555555555555555555555ÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààààááááááááââââââââããããããããääääääääååååååææææææææççççççèèèèèèééééééééêêêêêêëëëëëëììììììííííîîîîîîïïïïïïððððððññññòòòòòòóóóóôôôôôôõõõõöööööö÷÷÷÷øøøøøøùùùùúúúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,------------------------............................//////////////////////////////0000000000000000000000000000000000111111111111111111111111111111111111112222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555ÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××××ר ØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞßßßßßßßàààààààààáááááááâââââââãããããããäääääääååååååæææææææçççççèèèèèéééééééêêêêêêëëëëëëììììììííííî îîîîï!ïïïïððððððññññò$òòòòóóóóô&ôôôôõõõõö(öööö÷÷÷÷ø*øøøøùùùùúúúúúúûûûûüüüüýýýýýýþþþþÿÿ Ù Üßàáâãäæèéêëìí î!!!!!!!!!ï"""""""""ð#########ñ#ñ$$$$$$$$$ò$ò%%%%%%%%%%%ó&&&&&&&&&&&ô&ô'''''''''''õ'õ(((((((((((((ö(ö)))))))))))))))÷*****************ø+++++++++++++++++ù+ù,,,,,,,,,,,,,,,,,,,ú,ú---------------------û-û.........................ü.ü/////////////////////////ý/ý/ý00000000000000000000000000000þ0þ0þ111111111111111111111111111111111ÿ1ÿ1ÿ2222222222222222222222222222222222222233333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××××ר>Ø>ØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÜBÜBÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞßEßßßßßßàFààààààààáGááááááâHââââââãIããããããäJääääåKååååååæLææææææççççççèèèèèèéOééééêPêêêêëQëëëëìRììììíSííííîîîîîîïïïïðVððððññññññòòòòóYóóóóôôôôõõõõõõöööö÷÷÷÷÷÷øøøøùùùùúúúúúúûûûûüüüüýýýýýýþþþþÿÿž¡ £ §ª±²³µ¶·¸¹ º!!!!!!!»!»"""""""""¼#########½$$$$$$$$$$$¾%%%%%%%%%%%¿%¿&&&&&&&&&&&À'''''''''''''Á(((((((((((((((Â)))))))))))))))Ã)Ã***************Ä*Ä+++++++++++++++++Å,,,,,,,,,,,,,,,,,,,Æ,Æ---------------------Ç-Ç.......................È.È.È/////////////////////////É/É00000000000000000000000000000Ê0Ê0Ê111111111111111111111111111111111Ë1Ë1Ë222222222222222222222222222222222222222Ì2Ì2Ì333333333333333333333333333333333333333333333Í3Í3Í44444444444444444444444444444444444444444444444444444Î4Î4Î4Î555555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖ×p×p×××××××××××רqØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞßxßßßßßßàyààààààààázááááááâ{ââââââã|ããããããääääääå~ååååååæææææç€ççççèèèèèèèééééééêêêêêêëëëëëëììììììííííî‡îîîîïˆïïïïððððñŠññññòòòòòòóóóóôôôôõŽõõõõöööö÷÷÷÷÷øøøøùùùùú“úúúúûûûûüüüüýýýýýýþþþþÿÿh q s uvyz{|}~€‚ƒ„† ‡!!!!!!!ˆ"""""""""""‰#########Š$$$$$$$$$$$‹%%%%%%%%%%%Œ&&&&&&&&&&&&'''''''''''Ž'Ž(((((((((((((())))))))))))))***************‘*‘+++++++++++++++++’+’,,,,,,,,,,,,,,,,,“,“---------------------”-”.......................•.•///////////////////////////–/–00000000000000000000000000000—0—111111111111111111111111111111111˜1˜1˜222222222222222222222222222222222222222™2™2™3333333333333333333333333333333333333333333š3š3š3š444444444444444444444444444444444444444444444444444›4›4›4›55555555555555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ£ÖÖÖÖÖÖÖÖÖÖÖÖÖÖפ×××××××××××ר¥ØØØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞ߬ßßßßßßà­ààààààààááááááááâ¯ââââââããããããä±ääääääå²ååååååææææææç´ççççèµèèèèé¶ééééê·êêêêë¸ëëëëì¹ììììíºííííîîîîîîïïïïð½ððððññññò¿òòòòóóóóôÁôôôôõõõõöööööö÷÷÷÷øøøøùùùùùùúúúúûûûûüüüüýýýýýýþþþþÿÿ8; CDKLMOPQR S S!!!!!!!T"""""""""U"U#########V$$$$$$$$$W$W%%%%%%%%%%%X&&&&&&&&&&&Y'''''''''''''Z((((((((((((([([)))))))))))))))\***************]*]+++++++++++++++++^+^,,,,,,,,,,,,,,,,,_,_---------------------`-`.......................a.a/////////////////////////b/b/b000000000000000000000000000c0c0c111111111111111111111111111111111d1d222222222222222222222222222222222222222e2e2e3333333333333333333333333333333333333333333f3f3f44444444444444444444444444444444444444444444444444444g4g4g5555555555555555555555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááááââââââããããããããääääääääååååååææææææææççççççèèèèèèééééééêêêêêêëëëëëëììììììííííîîîîîîïïïïïïððððññññññòòòòóóóóóóôôôôõõõõöööööö÷÷÷÷øøøøùùùùùùúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$%%%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))))))****************++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////000000000000000000000000000000001111111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââââããããããããääääääääååååååææææææççççççèèèèèèèèééééééêêêêëëëëëëììììììííííííîîîîïïïïïïððððððññññòòòòóóóóóóôôôôõõõõõõöööö÷÷÷÷øøøøøøùùùùúúúúûûûûüüüüýýýýýýþþþþÿÿ  !!!!!!!!!!""""""""""############$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''(((((((((((((((())))))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,------------------------..........................////////////////////////////0000000000000000000000000000000011111111111111111111111111111111111122222222222222222222222222222222222222222233333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555556666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××××ר ØØØØØØØØØØØØÙ ÙÙÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞßßßßßßßàààààààáááááááâââââââãããããããäääääääååååååæææææçççççèèèèèèèééééééêêêêëëëëëìììììíííííîîîîï!ïïïïððððððññññòòòòó%óóóóôôôôõ'õõõõöööö÷÷÷÷øøøøøøùùùùúúúúûûûûüüüüý/ýýýýþþþþÿÿÔ × Ú ÜÞßâãäåçèéêëìí î!!!!!!!!!ï"""""""""ð#########ñ#ñ$$$$$$$$$ò%%%%%%%%%%%ó&&&&&&&&&&&ô&ô'''''''''''õ(((((((((((((ö(ö)))))))))))))÷)÷***************ø*ø+++++++++++++++ù+ù,,,,,,,,,,,,,,,,,ú,ú---------------------û-û.....................ü.ü.ü///////////////////////ý/ý/ý000000000000000000000000000þ0þ0þ1111111111111111111111111111111ÿ1ÿ1ÿ2222222222222222222222222222222222223333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555566666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××ר>Ø>ØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÝCÝCÝÝÝÝÝÝÞDÞÞÞÞÞÞÞÞßEßßßßßßàFààààààáGááááááâHââââââãIããããããäJääääåKååååååææææææçMççççèNèèèèéOééééêPêêêêëQëëëëììììììííííîTîîîîïïïïðVððððññññòXòòòòóóóóôôôôôôõõõõöööö÷÷÷÷ø^øøøøùùùùúúúúûûûûüüüüüüýýýýþþþþÿÿœ ¤ §©¬­²³µ¶·¸¹ º!!!!!!!!!»"""""""""¼#########½$$$$$$$$$$$¾%%%%%%%%%%%¿&&&&&&&&&&&À'''''''''''''Á(((((((((((((Â)))))))))))))))Ã***************Ä*Ä+++++++++++++++Å+Å,,,,,,,,,,,,,,,,,Æ,Æ---------------------Ç-Ç.....................È.È/////////////////////////É/É00000000000000000000000000000Ê0Ê1111111111111111111111111111111Ë1Ë1Ë2222222222222222222222222222222222222Ì2Ì2Ì33333333333333333333333333333333333333333Í3Í3Í3Í4444444444444444444444444444444444444444444444444Î4Î4Î4Î55555555555555555555555555555555555555555555555555555555555Ï5Ï5Ï5Ï666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××××רqØØØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÛÛÜuÜuÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÞwÞÞÞÞÞÞÞÞßxßßßßßßàyààààààázááááááâ{ââââââã|ããããããääääääå~ååååæææææææççççççèèèèèèééééééêêêêêêëëëëì…ììììííííííîîîîïˆïïïïððððñŠññññòòòòóóóóôôôôôõõõõöööö÷÷÷÷÷øøøøùùùùúúúúûûûûü•üüüüýýýýþþþþÿÿn r x{|}~‚ƒ„…† ‡!!!!!!!!!ˆ"""""""""‰#########Š$$$$$$$$$‹$‹%%%%%%%%%Œ%Œ&&&&&&&&&&'''''''''''Ž'Ž(((((((((((())))))))))))))*************‘*‘+++++++++++++++’+’,,,,,,,,,,,,,,,,,,,“---------------------”-”.....................•.•/////////////////////////–/–000000000000000000000000000—0—0—1111111111111111111111111111111˜1˜2222222222222222222222222222222222222™2™2™33333333333333333333333333333333333333333š3š3š4444444444444444444444444444444444444444444444444›4›4›4›55555555555555555555555555555555555555555555555555555555555œ5œ5œ5œ66666666666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖפפ×××××××××ר¥Ø¥ØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÛ¨Û¨ÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜÜÜݪÝÝÝÝÝÝÞ«ÞÞÞÞÞÞÞÞ߬ßßßßßßà­ààààààá®ááááááâ¯ââââââã°ããããä±ääääääå²ååååæ³ææææç´ççççèµèèèèé¶ééééê·êêêêë¸ëëëëììììíºííííî»îîîîïïïïð½ððððññññòòòòóÀóóóóôôôôõõõõöÃöööö÷÷÷÷øøøøùùùùúúúúûûûûûûüüüüýýýýþþþþÿÿ6; ? ACEFIJLMOPQR S!!!!!!!!!T"""""""""U#########V$$$$$$$$$W%%%%%%%%%%%X&&&&&&&&&&&Y'''''''''''''Z((((((((((((([)))))))))))))\)\***************]+++++++++++++++++^,,,,,,,,,,,,,,,,,,,_,_-----------------`-`-`.....................a.a///////////////////////b/b/b0000000000000000000000000c0c0c1111111111111111111111111111111d1d1d22222222222222222222222222222222222e2e2e33333333333333333333333333333333333333333f3f3f4444444444444444444444444444444444444444444444444g4g4g55555555555555555555555555555555555555555555555555555555555h5h5h5h6666666666666666666666666666ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââââããããããääääääääååååååææææææççççççèèèèèèééééééêêêêêêëëëëììììììííííííîîîîïïïïïïððððññññòòòòòòóóóóôôôôõõõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&&&''''''''''''''(((((((((((((())))))))))))))******************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------..........................//////////////////////////000000000000000000000000000000111111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááááââââââããããããããääääääååååååååææææææççççççèèèèèèééééêêêêêêëëëëëëììììííííííîîîîîîïïïïððððññññññòòòòóóóóôôôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!!!""""""""##########$$$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))))****************++++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------........................////////////////////////////0000000000000000000000000000001111111111111111111111111111111111222222222222222222222222222222222222222233333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××××ר Ø ØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßàààààààáááááááâââââãããããããäääääåååååååææææææççççççèèèèèèééééêêêêêëëëëëììììíííííîîîîîîïïïïððððñ#ññññòòòòóóóóô&ôôôôõõõõöööö÷÷÷÷øøøøùùùùú,úúúúûûûûüüüüýýýýþþþþÿÿÓ Ø ÜÞàãäåæçéêëìí î!!!!!!!!!ï"""""""ð#########ñ$$$$$$$$$$$ò%%%%%%%%%ó&&&&&&&&&&&ô&ô'''''''''''õ(((((((((((ö(ö)))))))))))))÷)÷*************ø*ø+++++++++++++++ù+ù,,,,,,,,,,,,,,,,,ú,ú-----------------û-û.....................ü.ü///////////////////////ý/ý/ý0000000000000000000000000þ0þ0þ11111111111111111111111111111ÿ1ÿ1ÿ22222222222222222222222222222222223333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖ×=×=×××××××××ר>ØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÝÝÞDÞÞÞÞÞÞßEßßßßßßàFààààààáGááááááâHââââãIããããããäJääääåKååååæLææææçMççççèNèèèèéOééééêPêêêêëëëëìRììììííííîTîîîîïïïïðVððððññññòòòòóYóóóóôôôôõõõõöööö÷÷÷÷ø^øøù_ùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  ¥ ¦ ©«­®±²´µ·¸¹ º!!!!!!!!!»"""""""¼#########½$$$$$$$$$¾$¾%%%%%%%%%¿&&&&&&&&&&&À'''''''''''Á'Á(((((((((((Â)))))))))))))Ã)Ã*************Ä*Ä+++++++++++++++Å+Å,,,,,,,,,,,,,,,,,Æ,Æ-----------------Ç-Ç.....................È.È///////////////////////É/É000000000000000000000000000Ê0Ê11111111111111111111111111111Ë1Ë1Ë22222222222222222222222222222222222Ì2Ì2Ì333333333333333333333333333333333333333Í3Í3Í44444444444444444444444444444444444444444444444Î4Î4Î4Î5555555555555555555555555555555555555555555555555555555Ï5Ï5Ï5Ï6666666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖÖÖ×p×××××××××רqØqØØØØØØØØÙrÙrÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÛtÛtÛÛÛÛÛÛÜuÜuÜÜÜÜÜÜÝvÝÝÝÝÝÝÝÝÞwÞÞÞÞÞÞßxßßßßßßàyààààààázááááááââââââã|ããããããääääääå~ååååæææææç€ççççèèèèèééééééêêêêë„ëëëëììììí†ííííîîîîïˆïïïïððððññññò‹òòòòóóóóôôôôõõõõöööö÷÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿno twy{|}€‚ƒ„…† ‡!!!!!!!ˆ!ˆ"""""""‰#########Š$$$$$$$$$‹%%%%%%%%%Œ%Œ&&&&&&&&&&'''''''''''Ž(((((((((((()))))))))))))***************‘+++++++++++++++’+’,,,,,,,,,,,,,,,,,“,“-----------------”-”.....................•.•///////////////////////–/–0000000000000000000000000—0—0—11111111111111111111111111111˜1˜22222222222222222222222222222222222™2™2™333333333333333333333333333333333333333š3š3š444444444444444444444444444444444444444444444›4›4›4›5555555555555555555555555555555555555555555555555555555œ5œ5œ5œ666666666666666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖפפ×××××××××ר¥ØØØØØØØØØØÙ¦ÙÙÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÛÛÜ©ÜÜÜÜÜÜݪÝÝÝÝÝÝÝÝÞ«ÞÞÞÞÞÞ߬ßßßßßßà­ààààààá®ááááâ¯ââââââã°ããããä±ääääääååååååææææææççççççèèèèé¶ééééê·êêêêëëëëì¹ììììííííî»îîîîïïïïð½ððñ¾ññññòòòòóóóóôôôôõÂõõöÃöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ < = ADFIJKLMNOPQR S!!!!!!!T"""""""""U#########V$$$$$$$$$W%%%%%%%%%X&&&&&&&&&&&Y'''''''''''Z'Z((((((((((([)))))))))))))\)\*************]*]+++++++++++++^+^,,,,,,,,,,,,,,,,,_,_-----------------`-`...................a.a.a/////////////////////b/b/b0000000000000000000000000c0c11111111111111111111111111111d1d1d222222222222222222222222222222222e2e2e3333333333333333333333333333333333333f3f3f3f444444444444444444444444444444444444444444444g4g4g5555555555555555555555555555555555555555555555555555555h5h5h5h66666666666666666666666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááââââââââããããããääääääååååååææææææççççççèèèèèèééééééêêêêëëëëëëììììííííííîîîîïïïïïïððððññññòòòòóóóóôôôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""""##########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------......................//////////////////////////000000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààààááááááââââââââããããããääääääååååååææææææççççççèèèèèèééééêêêêêêëëëëììììììííííîîîîîîïïïïððððññññòòòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""""########$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&&&''''''''''''''(((((((((((())))))))))))))****************++++++++++++++++,,,,,,,,,,,,,,,,,,,,--------------------......................//////////////////////////0000000000000000000000000000111111111111111111111111111111112222222222222222222222222222222222222233333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666667777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ× × ×××××××ר Ø ØØØØØØØØÙ Ù ÙÙÙÙÙÙÙÙÚ ÚÚÚÚÚÚÚÚÛ ÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßàààààààáááááâââââââããããããäääääåååååæææææçççççèèèèèééééêêêêêëëëëìììììííííî îîîîïïïïððððñ#ññò$òòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ ÛÞßáãäéêëìí î!!!!!!!ï"""""""""ð#######ñ$$$$$$$$$ò%%%%%%%%%ó%ó&&&&&&&&&ô'''''''''''õ'õ(((((((((((ö)))))))))))))÷*************ø*ø+++++++++++++++ù,,,,,,,,,,,,,,,,,ú,ú-----------------û-û...................ü.ü/////////////////////ý/ý/ý00000000000000000000000þ0þ0þ111111111111111111111111111ÿ1ÿ1ÿ22222222222222222222222222222222333333333333333333333333333333333333333444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666677777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÕÕÖ<ÖÖÖÖÖÖÖÖÖÖÖÖ×=×××××××××ר>ØØØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÛAÛAÛÛÛÛÛÛÜBÜÜÜÜÜÜÜÜÝCÝÝÝÝÝÝÞDÞÞÞÞÞÞßEßßßßßßàFààààààááááááâHââââãIããããããäJääääåKååååæLææææççççççèèèèéOééééêêêêëQëëëëììììíSííííîîîîïïïïðVððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûûûüüüüýýýýþþþþÿÿ ¨©¬®°±²³´µ¶·¸¹ º!!!!!!!»"""""""¼"¼#######½$$$$$$$$$¾%%%%%%%%%¿&&&&&&&&&&&À'''''''''''Á(((((((((((Â(Â)))))))))))Ã)Ã*************Ä+++++++++++++++Å+Å,,,,,,,,,,,,,,,Æ,Æ-----------------Ç-Ç...................È.È/////////////////////É/É0000000000000000000000000Ê0Ê111111111111111111111111111Ë1Ë1Ë222222222222222222222222222222222Ì2Ì2Ì33333333333333333333333333333333333Í3Í3Í3Í4444444444444444444444444444444444444444444Î4Î4Î4Î555555555555555555555555555555555555555555555555555Ï5Ï5Ï5Ï66666666666666666666666666666666666666666666666666666666666666666Ð6Ð6Ð6Ð6Ð777777777777777777ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÖoÖoÖÖÖÖÖÖÖÖÖÖ×p×p×××××××רqØqØØØØØØØØÙrÙÙÙÙÙÙÙÙÚsÚsÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÜuÜÜÜÜÜÜÜÜÝvÝÝÝÝÝÝÞwÞÞÞÞÞÞßxßßßßßßàyààààázááááááâ{ââââã|ããããããääääääååååååææææç€ççççèèèèèééééêƒêêêêë„ëëì…ììììííííî‡îîïˆïïïïððððññññòòòòóŒóóôôôõŽõõööö÷÷÷ø‘øøù’ùùúúúúûûûûüüüüýýýýþþþþÿÿlmno p q r wxz|€‚ƒ„…† ‡!!!!!!!ˆ"""""""‰#########Š$$$$$$$$$‹%%%%%%%%%Œ&&&&&&&&&&'''''''''Ž'Ž(((((((((())))))))))))*************‘*‘+++++++++++++’+’,,,,,,,,,,,,,,,“,“-----------------”-”.................•.•.•/////////////////////–/–00000000000000000000000—0—0—111111111111111111111111111˜1˜222222222222222222222222222222222™2™2™33333333333333333333333333333333333š3š3š4444444444444444444444444444444444444444444›4›4›4›555555555555555555555555555555555555555555555555555œ5œ5œ5œ666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777ȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖÖÖפ×××××××××ר¥ØØØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛܩܩÜÜÜÜÜÜݪÝÝÝÝÝÝÞ«ÞÞÞÞÞÞ߬ßßßßßßà­ààààá®ááááááâ¯ââââã°ããããä±ääääå²ååååæ³ææææç´ççççèèèèé¶ééééê·êêêêëëëëììììíºííííîîîîïïïïð½ððñ¾ññò¿òòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùúÇúúûÈûûüüüüýýýýþþþþÿÿ7 ? @AEGIJLMNPQR S!!!!!!!T"""""""U#########V$$$$$$$W$W%%%%%%%X%X&&&&&&&&&Y'''''''''''Z((((((((((([)))))))))))))\*************]*]+++++++++++++^+^,,,,,,,,,,,,,,,_,_---------------`-`-`.................a.a/////////////////////b/b/b00000000000000000000000c0c111111111111111111111111111d1d1d22222222222222222222222222222e2e2e2e33333333333333333333333333333333333f3f3f4444444444444444444444444444444444444444444g4g4g55555555555555555555555555555555555555555555555555555h5h5h666666666666666666666666666666666666666666666666666666666666666i6i6i6i6i777777777777777777777777777777777777ÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááááââââââããããããääääääååååååææææææççççèèèèèèééééééêêêêëëëëììììììííííîîîîïïïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////000000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333334444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááááââââââããããããääääääååååååææææççççççèèèèèèééééêêêêêêëëëëììììííííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûüüüüýýýýþþþþÿÿ  !!!!!!!!""""""""########$$$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''''(((((((((((())))))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,,,------------------......................////////////////////////0000000000000000000000000011111111111111111111111111111122222222222222222222222222222222223333333333333333333333333333333333333333334444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ× ×××××××××ר ØØØØØØØØÙ Ù ÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞÞÞßßßßßßßàààààáááááááââââââãããããäääääåååååææææçççççèèèèèééééêêêêêëëëëììììíííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøøøùùùùúúúúûûü.üüýýýýþþþþÿÿ ßàâãäæçèéêëìí î!!!!!!!ï"""""""ð#######ñ$$$$$$$$$ò%%%%%%%%%ó&&&&&&&&&ô'''''''''''õ(((((((((((ö)))))))))))÷)÷***********ø*ø+++++++++++++ù+ù,,,,,,,,,,,,,,,ú,ú---------------û-û...................ü.ü///////////////////ý/ý/ý000000000000000000000þ0þ0þ1111111111111111111111111ÿ1ÿ1ÿ22222222222222222222222222223333333333333333333333333333333333333444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖ×=×=×××××××ר>Ø>ØØØØØØØØÙ?ÙÙÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÚÚÛAÛÛÛÛÛÛÜBÜÜÜÜÜÜÝCÝÝÝÝÝÝÞDÞÞÞÞÞÞßEßßßßßßàFààààáGááááâHââââââããããããääääääååååæLææææçMççççèèèèéOééééêêêêëQëëìRììììííííîîîîïUïïðVððñWññòXòòóYóóôZôôõ[õõö\öö÷÷÷÷øøøøùùùùúúúúûûûûüüýýýýþþþþÿÿœ¢ £ ¤ ¥ ¦ §¨©ª­®°±²³´µ¶·¹ º!!!!!!!»"""""""¼#######½$$$$$$$$$¾%%%%%%%¿%¿&&&&&&&&&À'''''''''Á'Á(((((((((Â(Â)))))))))))Ã*************Ä+++++++++++++Å+Å,,,,,,,,,,,,,,,Æ,Æ---------------Ç-Ç.................È.È.È///////////////////É/É00000000000000000000000Ê0Ê111111111111111111111111111Ë1Ë22222222222222222222222222222Ì2Ì2Ì33333333333333333333333333333333333Í3Í3Í44444444444444444444444444444444444444444Î4Î4Î4Î5555555555555555555555555555555555555555555555555Ï5Ï5Ï5Ï66666666666666666666666666666666666666666666666666666666666Ð6Ð6Ð6Ð6Ð77777777777777777777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖÖÖ×p×××××××××רqØØØØØØØØÙrÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÚÚÛtÛÛÛÛÛÛÜuÜÜÜÜÜÜÝvÝvÝÝÝÝÞwÞÞÞÞÞÞßxßßßßßßàyààààázááááâ{ââââã|ããããä}ääääå~ååååæææææççççèèèèèé‚ééêƒêêêêëëëëì…ììí†ííî‡îîîîïïïïððððññññòòòòóóóóôôôôõõõõöö÷÷÷øøøøùùùùúúúúûûûûüüýýýýþþþþÿÿn xy|~€‚ƒ„…† ‡!!!!!ˆ!ˆ"""""‰"‰#######Š$$$$$$$‹$‹%%%%%%%Œ&&&&&&&&&&'''''''''Ž((((((((((())))))))))))***********‘*‘+++++++++++++’,,,,,,,,,,,,,,,“,“---------------”-”.................•.•/////////////////////–/–000000000000000000000—0—0—1111111111111111111111111˜1˜1˜222222222222222222222222222™2™2™33333333333333333333333333333333333š3š3š444444444444444444444444444444444444444›4›4›4›5555555555555555555555555555555555555555555555555œ5œ5œ5œ66666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖפפ×××××××ר¥Ø¥ØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÚ§Ú§ÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛܩܩÜÜÜÜÜÜݪÝÝÝÝÞ«ÞÞÞÞÞÞ߬ßßßßßßà­ààààá®ááááâ¯ââââã°ããããä±ääääå²ååååæ³ææç´ççççèµèèèèééééêêêêë¸ëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷øÅøøùùùùúúúúûûûûüüýÊýýþþþþÿÿ9 AFGIKMNOPQR S!!!!!T"""""""U#########V$$$$$$$W%%%%%%%%%X&&&&&&&&&Y'''''''''Z'Z((((((((([([)))))))))\)\***********]*]+++++++++++++^+^,,,,,,,,,,,,,_,_---------------`-`.................a.a///////////////////b/b/b000000000000000000000c0c1111111111111111111111111d1d1d222222222222222222222222222e2e2e333333333333333333333333333333333f3f3f3f444444444444444444444444444444444444444g4g4g5555555555555555555555555555555555555555555555555h5h5h5h666666666666666666666666666666666666666666666666666666666i6i6i6i6i7777777777777777777777777777777777777777777777777777777777777777777777777j7j7j7jÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããããääääääååååååææææççççççèèèèééééêêêêêêëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôôôõõõõöööö÷÷÷÷øøùùùùúúúúûûûûüüüüýýþþþþÿÿ  !!!!!!""""""""##########$$$$$$$$%%%%%%%%%%&&&&&&&&&&''''''''''(((((((((((())))))))))))**************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////000000000000000000000000001111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßßßààààààááááááââââââããããããääääääååååææææææççççèèèèèèééééêêêêëëëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôõõõõöööö÷÷÷÷øøøøùùúúúúûûûûüüüüýýþþþþÿÿ  !!!!!!""""""""########$$$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''''(((((((((())))))))))))))************++++++++++++++++,,,,,,,,,,,,,,,,------------------....................//////////////////////0000000000000000000000001111111111111111111111111111222222222222222222222222222222223333333333333333333333333333333333333344444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ× × ×××××××ר ØØØØØØØØÙ Ù ÙÙÙÙÙÙÚ ÚÚÚÚÚÚÛ Û ÛÛÛÛÛÛÜÜÜÜÜÜÜÝÝÝÝÝÝÝÞÞÞÞÞßßßßßßßàààààáááááâââââãããããäääääååååæææææçççèèèèèééééêêêëëëëëììììííííîîîîïïïïððððññññòòòòóóóóôôõ'õõöööö÷÷÷÷øøøøùùú,úúûûûûüüüüýýþþþþÿÿÏ × áâãåçèéëìí î!!!!!ï"""""""ð#######ñ$$$$$$$ò$ò%%%%%%%ó&&&&&&&&&ô'''''''''õ'õ(((((((((ö)))))))))))÷)÷***********ø+++++++++++++ù+ù,,,,,,,,,,,,,ú,ú---------------û-û...............ü.ü.ü/////////////////ý/ý/ý000000000000000000000þ0þ11111111111111111111111ÿ1ÿ1ÿ2222222222222222222222222233333333333333333333333333333333333444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÓÓÔ:ÔÔÔÔÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖ×=×××××××ר>Ø>ØØØØØØØØÙ?ÙÙÙÙÙÙÚ@Ú@ÚÚÚÚÚÚÛAÛÛÛÛÛÛÜBÜÜÜÜÜÜÝCÝÝÝÝÝÝÞDÞÞÞÞßEßßßßßßàFààààáGááááâHââââãIããããääääåKååååæLææææççççèNèèéOééééêêêêëëëëìRììíSííîTîîïUïïðVððñWññòòòòóóóóôôôôõõö\öö÷÷÷÷øøøøùùùùúúûûûûüüüüýýþþþþÿÿž¢ §¨©ª«¬­°²´µ¶·¸¹ º º!!!!!»"""""""¼#######½$$$$$$$¾%%%%%%%%%¿&&&&&&&&&À'''''''''Á(((((((((Â(Â)))))))))Ã)Ã***********Ä*Ä+++++++++++Å+Å,,,,,,,,,,,,,Æ,Æ---------------Ç-Ç...............È.È///////////////////É/É000000000000000000000Ê0Ê0Ê11111111111111111111111Ë1Ë222222222222222222222222222Ì2Ì2Ì333333333333333333333333333333333Í3Í3Í4444444444444444444444444444444444444Î4Î4Î4Î555555555555555555555555555555555555555555555Ï5Ï5Ï5Ï6666666666666666666666666666666666666666666666666666666Ð6Ð6Ð6Ð77777777777777777777777777777777777777777777777777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ8888888888888888888888888888Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÔmÔmÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖ×p×p×××××××רqØØØØØØØØÙrÙÙÙÙÙÙÙÙÚsÚÚÚÚÚÚÛtÛÛÛÛÛÛÜuÜÜÜÜÜÜÝvÝÝÝÝÝÝÞwÞÞÞÞßxßßßßßßàyààààázááááâ{ââââã|ããä}ääääå~ååååææææç€ççççèèèèé‚ééêƒêêë„ëëëëììììííííîîîîïïïïððððññò‹òòóŒóóôôôôõõõõöö÷÷÷øøøøùùùùúúûûûûüüüüýýþþþþÿÿn r s {|~€‚ƒ„…† ‡!!!!!!!ˆ"""""""‰#######Š$$$$$$$‹%%%%%%%Œ%Œ&&&&&&&&'''''''Ž'Ž((((((((()))))))))))***********‘*‘+++++++++++’+’,,,,,,,,,,,,,“,“-------------”-”-”...............•.•/////////////////–/–/–0000000000000000000—0—0—11111111111111111111111˜1˜1˜2222222222222222222222222™2™2™3333333333333333333333333333333š3š3š3š44444444444444444444444444444444444›4›4›4›555555555555555555555555555555555555555555555œ5œ5œ5œ666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777ž7ž7ž7ž7ž88888888888888888888888888888888888888ÆÆÆÆÇ”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖפפ×××××ר¥Ø¥ØØØØØØÙ¦Ù¦ÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÛ¨ÛÛÛÛÛÛÜ©ÜÜÜÜÜÜݪÝÝÝÝÝÝÞ«ÞÞÞÞ߬ßßßßßßààààààá®ááááâ¯ââââããããä±ääääå²ååæ³ææææç´ççèµèèèèééééêêêêë¸ëëì¹ììíºííî»îîï¼ïïððððññññòòòòóóôÁôôõõõõöööö÷÷øÅøøùùùùúúûÈûûüüüüýýþþþþÿÿ = BCDEFIJLNOPQR S!!!!!!!T"""""""U#####V#V$$$$$$$W%%%%%%%X&&&&&&&&&Y'''''''''Z((((((((([([)))))))))\)\*********]*]+++++++++++^+^,,,,,,,,,,,,,_,_-------------`-`.................a.a/////////////////b/b000000000000000000000c0c11111111111111111111111d1d1d2222222222222222222222222e2e2e3333333333333333333333333333333f3f3f4444444444444444444444444444444444444g4g4g555555555555555555555555555555555555555555555h5h5h5h66666666666666666666666666666666666666666666666666666i6i6i6i777777777777777777777777777777777777777777777777777777777777777777777j7j7j7j7j888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààààááááááââââããããããääääääååååææææææççççèèèèééééêêêêêêëëëëììììííííîîîîïïððððññññòòòòóóóóôôõõõõöööö÷÷÷÷øøùùùùúúúúûûüüüüýýþþþþÿÿ  !!!!!!!!""""""""######$$$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''''(((((((((())))))))))))************++++++++++++++,,,,,,,,,,,,,,,,----------------....................////////////////////000000000000000000000000111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââããããããääääååååååææææççççççèèèèééééêêêêëëëëììììííííîîîîïïïïððððññòòòòóóóóôôôôõõöööö÷÷÷÷øøùùùùúúúúûûüüüüýýþþþþÿÿ  !!!!!!!!""""""########$$$$$$$$%%%%%%%%%%&&&&&&&&''''''''''(((((((((((())))))))))**************++++++++++++++,,,,,,,,,,,,,,----------------..................//////////////////////0000000000000000000000111111111111111111111111112222222222222222222222222222223333333333333333333333333333333333334444444444444444444444444444444444444444555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ× × ×××××ר Ø ØØØØØØÙ ÙÙÙÙÙÙÚ Ú ÚÚÚÚÚÚÛ ÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááâââââãããããäääåååååæææçççççèèèèééééêêêëëëìììíííî îîïïïïððððññò$òòó%óóôôôôõõö(öö÷÷÷÷øøù+ùùúúúúûûüüüüýýþ0þþÿÿÑ Ú ßàáâãåæèêëìí î!!!!!!!ï"""""ð#######ñ$$$$$$$ò%%%%%%%ó%ó&&&&&&&ô'''''''''õ(((((((((ö(ö)))))))))÷***********ø*ø+++++++++++ù+ù,,,,,,,,,,,ú,ú-------------û-û...............ü.ü/////////////////ý/ý/ý0000000000000000000þ0þ11111111111111111111111ÿ1ÿ22222222222222222222222233333333333333333333333333333333344444444444444444444444444444444444445555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖÖÖ×=×××××××ר>ØØØØØØÙ?Ù?ÙÙÙÙÙÙÚ@ÚÚÚÚÚÚÛAÛÛÛÛÛÛÜBÜÜÜÜÜÜÝCÝÝÝÝÞDÞÞÞÞßEßßßßàFààààáGááááâHââââãIããããääääåKååååææææçMççèNèèéOééééêêêêëëëëììììííííîîïUïïðVððññññòòòòóóôZôôõõõõöö÷]÷÷øøøøùùúúúúûûüüüüýýýýþþÿÿŸ¢ ¥ ¨©ª°±³´µ¶·¸¹ º!!!!!!!»"""""¼#######½$$$$$$$¾%%%%%%%¿&&&&&&&À&À'''''''Á'Á(((((((Â(Â)))))))))Ã)Ã*********Ä*Ä+++++++++++Å+Å,,,,,,,,,,,Æ,Æ-------------Ç-Ç...............È.È/////////////////É/É0000000000000000000Ê0Ê0Ê111111111111111111111Ë1Ë1Ë22222222222222222222222Ì2Ì2Ì33333333333333333333333333333Í3Í3Í3Í444444444444444444444444444444444Î4Î4Î4Î55555555555555555555555555555555555555555Ï5Ï5Ï5Ï666666666666666666666666666666666666666666666666666Ð6Ð6Ð6Ð77777777777777777777777777777777777777777777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ888888888888888888888888888888888888888888888888888888888888888888888888888888ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖ×p×p×××××רqØqØØØØØØÙrÙÙÙÙÙÙÚsÚÚÚÚÚÚÛtÛÛÛÛÛÛÜuÜÜÜÜÜÜÝvÝÝÝÝÞwÞÞÞÞßxßßßßàyààààázááááâ{ââââã|ããä}ääääå~ååæææææççççèèèèé‚ééêƒêêë„ëëì…ììí†ííîîîîïïïïððñŠññòòòòóóóóôôõõõõöööö÷÷øøøøùùúúúúûûü•üüýýýýþþÿÿn q txyz{|}€‚„…† ‡!!!!!ˆ!ˆ"""""‰#######Š$$$$$$$‹%%%%%%%Œ&&&&&&&'''''''''Ž((((((((())))))))))*********‘*‘+++++++++++’+’,,,,,,,,,,,“,“-------------”-”...............•.•///////////////–/–/–00000000000000000—0—0—111111111111111111111˜1˜1˜22222222222222222222222™2™2™33333333333333333333333333333š3š3š44444444444444444444444444444444444›4›4›55555555555555555555555555555555555555555œ5œ5œ5œ66666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777ž7ž7ž7ž7ž888888888888888888888888888888888888888888888888888888888888888888888888888888888Ÿ8Ÿ8Ÿ8ŸÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”ǔǔǔǔÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÒÒÓ ÓÓÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖÖÖפ×××××××ר¥ØØØØØØÙ¦Ù¦ÙÙÙÙÚ§Ú§ÚÚÚÚÛ¨Û¨ÛÛÛÛÜ©ÜÜÜÜÜÜݪÝÝÝÝÞ«ÞÞÞÞ߬ßßßßà­ààààá®ááááâ¯ââââã°ããä±ääääååååæ³ææç´ççèµèèèèééééêêêêëëëëììììííî»îîï¼ïïððððññò¿òòóóóóôôõÂõõöööö÷÷øøøøùùúÇúúûûûûüüýýýýþþÿÿ < ? BCJKLMNOPQR S!!!!!T"""""""U#####V#V$$$$$W$W%%%%%X%X&&&&&&&Y'''''''Z'Z((((((([([)))))))))\***********]+++++++++++^+^,,,,,,,,,,,_,_-------------`-`.............a.a.a///////////////b/b0000000000000000000c0c111111111111111111111d1d1d22222222222222222222222e2e2e33333333333333333333333333333f3f3f444444444444444444444444444444444g4g4g4g555555555555555555555555555555555555555h5h5h5h6666666666666666666666666666666666666666666666666i6i6i6i777777777777777777777777777777777777777777777777777777777777777j7j7j7j7j8888888888888888888888888888888888888888888888888888888888888888888888888888888k8k8k8k8k8k99999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââââããããääääååååååææææççççèèèèééééêêêêëëëëììììííííîîîîïïððððññññòòóóóóôôôôõõöööö÷÷øøøøùùùùúúûûûûüüýýýýþþÿÿ  !!!!!!""""""""######$$$$$$$$%%%%%%%%&&&&&&&&&&''''''''(((((((((())))))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////000000000000000000000011111111111111111111111122222222222222222222222222223333333333333333333333333333333333444444444444444444444444444444444444445555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßßßààààààááááááââââããããããääääååååææææææççççèèèèééééêêêêëëëëììììííîîîîïïïïððððññòòòòóóôôôôõõõõöö÷÷÷÷øøùùùùúúûûûûüüýýýýþþÿÿ  !!!!!!""""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''''(((((((((())))))))))************++++++++++++,,,,,,,,,,,,,,----------------................////////////////////0000000000000000000011111111111111111111111122222222222222222222222222223333333333333333333333333333333344444444444444444444444444444444444444555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖ× ×××××ר Ø ØØØØØØÙ ÙÙÙÙÙÙÚ ÚÚÚÚÚÚÛ ÛÛÛÛÛÛÜÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßßßàààààáááááâââãããããäääåååæææææççççèèèèééééêêêêëëëëììììííî îîï!ïïððððññò$òòóóô&ôôõõõõöö÷÷÷÷øøùùùùúúûûûûüüýýýýþþÿÿÐÒÔÖ Ø ÛÞßçèêëìí î!!!!!ï"""""ð"ð#####ñ$$$$$$$ò%%%%%%%ó&&&&&&&ô'''''''õ'õ(((((((ö(ö)))))))÷)÷*********ø*ø+++++++++ù+ù,,,,,,,,,,,ú,ú-------------û-û.............ü.ü///////////////ý/ý/ý00000000000000000þ0þ111111111111111111111ÿ1ÿ2222222222222222222222333333333333333333333333333334444444444444444444444444444444444455555555555555555555555555555555555555555666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999Æ,ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÔÔÕ;ÕÕÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖ×=×=×××××ר>ØØØØØØÙ?Ù?ÙÙÙÙÚ@Ú@ÚÚÚÚÛAÛÛÛÛÛÛÜBÜÜÜÜÝCÝÝÝÝÞDÞÞÞÞßEßßßßàFààààáGááááâHââãIããããääääåKååæLææçMççèNèèéOééêPêêëQëëìRììííííîîîîïïðVððññññòòóóóóôôõ[õõöö÷]÷÷øøùùùùúúûûûûüüýýýýþþÿÿ ¦ ©¬­®¯°±²³´µ¶·¸¹ º!!!!!»"""""¼#######½$$$$$¾$¾%%%%%¿%¿&&&&&À&À'''''''Á(((((((((Â)))))))))Ã*********Ä*Ä+++++++++Å+Å,,,,,,,,,,,Æ,Æ-------------Ç-Ç.............È.È///////////////É/É00000000000000000Ê0Ê0Ê1111111111111111111Ë1Ë1Ë222222222222222222222Ì2Ì2Ì333333333333333333333333333Í3Í3Í4444444444444444444444444444444Î4Î4Î4Î5555555555555555555555555555555555555Ï5Ï5Ï5Ï66666666666666666666666666666666666666666666666Ð6Ð6Ð6Ð77777777777777777777777777777777777777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ888888888888888888888888888888888888888888888888888888888888888888888888888Ò8Ò8Ò8Ò8Ò8Ò999999999999999999999999999999999999999999ÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÐÐÑjÑjÑjÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÕÕÖoÖÖÖÖÖÖÖÖ×p×××××רqØqØØØØØØÙrÙÙÙÙÙÙÚsÚÚÚÚÛtÛtÛÛÛÛÜuÜÜÜÜÝvÝÝÝÝÞwÞÞÞÞßxßßßßàyààààázááááââââã|ããä}ääääååååæææç€ççèèèé‚ééêƒêêëëëëììí†ííî‡îîïïïïððñŠññòòóŒóóôôôôõõöööö÷÷øøù’ùùúúû”ûûüüýýýýþþÿÿ p r uwx{|}~€‚ƒ„…† ‡!!!!!ˆ"""""‰#######Š$$$$$‹%%%%%%%Œ&&&&&&&'''''''Ž'Ž(((((((())))))))*********‘+++++++++++’,,,,,,,,,,,“,“-----------”-”-”...........•.•.•/////////////–/–/–00000000000000000—0—1111111111111111111˜1˜1˜222222222222222222222™2™2™333333333333333333333333333š3š3š4444444444444444444444444444444›4›4›5555555555555555555555555555555555555œ5œ5œ5œ6666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777ž7ž7ž7ž7ž8888888888888888888888888888888888888888888888888888888888888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖפפ×××××ר¥ØØØØØØÙ¦ÙÙÙÙÙÙÚ§ÚÚÚÚÚÚÛ¨ÛÛÛÛÜ©ÜÜÜÜݪݪÝÝÞ«Þ«ÞÞ߬ßßßßà­ààààá®ááâ¯ââââã°ããä±ääå²ååååææææççççèèèèééééêêë¸ëëì¹ììííííîîï¼ïïððððññò¿òòóóôÁôôõõöÃöö÷÷øøøøùùúúúúûûüüýýýýþþÿÿ8: @BEFMNOPQR S!!!!!T"""""U#####V#V$$$$$W%%%%%%%X&&&&&&&Y'''''''Z((((((([([)))))))\)\*********]*]+++++++++^+^,,,,,,,,,_,_-----------`-`.............a.a///////////////b/b00000000000000000c0c0c11111111111111111d1d1d222222222222222222222e2e2e3333333333333333333333333f3f3f3f44444444444444444444444444444g4g4g4g55555555555555555555555555555555555h5h5h5h666666666666666666666666666666666666666666666i6i6i6i777777777777777777777777777777777777777777777777777777777j7j7j7j7j8888888888888888888888888888888888888888888888888888888888888888888888888k8k8k8k8k999999999999999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßßßààààààááááââââââããããääääååååææææççççèèèèééééêêêêëëëëììííííîîîîïïððððññññòòóóóóôôõõõõöö÷÷øøøøùùúúúúûûüüýýýýþþÿÿ  !!!!!!""""""######$$$$$$$$%%%%%%%%&&&&&&&&''''''''(((((((())))))))))************++++++++++++,,,,,,,,,,,,--------------................//////////////////000000000000000000001111111111111111111111222222222222222222222222223333333333333333333333333333334444444444444444444444444444444444445555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××××ØØØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßßßààààààááááââââââããããääääååååææææççççèèèèééééêêêêëëììììííííîîïïïïððññññòòóóóóôôõõõõöö÷÷÷÷øøùùúúúúûûüüýýýýþþÿÿ  !!!!!!""""""######$$$$$$$$%%%%%%&&&&&&&&''''''''(((((((((())))))))))**********++++++++++++,,,,,,,,,,,,--------------................//////////////////0000000000000000001111111111111111111111112222222222222222222222223333333333333333333333333333334444444444444444444444444444444444555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅøÅøÅøÅøÅøÅøÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ× × ×××××ר ØØØØØØÙ ÙÙÙÙÙÙÚ ÚÚÚÚÛ ÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÞÞÞßßßßßàààààáááâââââããããäääåååæææçççèèèéééêêêêëëìììííííîîï!ïïððñ#ññòòó%óóôôõ'õõöö÷÷÷÷øøùùúúúúûûüüýýýýþþÿÿÎÑÖ Þàáäåæçèéêëìí î!!!!!ï"""""ð#####ñ$$$$$ò$ò%%%%%ó&&&&&&&ô'''''''õ(((((((ö(ö)))))))÷)÷*******ø*ø+++++++++ù+ù,,,,,,,,,ú,ú-----------û-û.............ü.ü/////////////ý/ý/ý000000000000000þ0þ1111111111111111111ÿ1ÿ1ÿ222222222222222222333333333333333333333333333444444444444444444444444444444455555555555555555555555555555555555556666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ,Æ,Æ,Æ,Æ,Æ,ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÕÕÖ<Ö<ÖÖÖÖÖÖ×=×××××ר>Ø>ØØØØÙ?Ù?ÙÙÙÙÚ@ÚÚÚÚÛAÛAÛÛÛÛÜBÜÜÜÜÝCÝÝÝÝÞDÞÞßEßßßßàFààààáGááâHââãIããããääääååååææææççççèèèèééêPêêëQëëììíSííîTîîïïðVððññòXòòóóôôôôõõöö÷]÷÷øøùùú`úúûûüüýcýýþþÿÿ  £ ¥ §©«®¯³¶·¸¹ º!!!!!»"""""¼#####½$$$$$¾%%%%%%%¿&&&&&À&À'''''Á'Á(((((((Â)))))))Ã)Ã*********Ä+++++++++Å+Å,,,,,,,,,Æ,Æ-----------Ç-Ç.............È.È/////////////É/É000000000000000Ê0Ê0Ê11111111111111111Ë1Ë1Ë2222222222222222222Ì2Ì2Ì33333333333333333333333Í3Í3Í3Í444444444444444444444444444Î4Î4Î4Î555555555555555555555555555555555Ï5Ï5Ï5Ï66666666666666666666666666666666666666666Ð6Ð6Ð6Ð6Ð77777777777777777777777777777777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ888888888888888888888888888888888888888888888888888888888888888888888Ò8Ò8Ò8Ò8Ò999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999Ó9Ó9Ó9ÓÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËdËËËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐiÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÕnÕnÕÕÕÕÕÕÖoÖÖÖÖÖÖ×p×p×××××רqØØØØØØÙrÙÙÙÙÚsÚÚÚÚÚÚÛtÛÛÛÛÜuÜÜÜÜÝvÝÝÝÝÞwÞÞßxßxßßàyààààááááâ{ââã|ããä}ääå~ååæææç€ççèèèé‚ééêêêêëëì…ììííííîîïïïïððññññòòóóôôôõõöööö÷÷øøùùùùúúûûüüüüýýþþÿÿ s uwyz|}~‚ƒ„…† ‡!!!!!ˆ"""‰"‰###Š#Š$$$$$‹%%%%%Œ%Œ&&&&&'''''''Ž(((((((()))))))*********‘*‘+++++++’+’,,,,,,,,,“,“-----------”-”...........•.•.•///////////–/–/–000000000000000—0—11111111111111111˜1˜1˜2222222222222222222™2™2™33333333333333333333333š3š3š44444444444444444444444444444›4›4›555555555555555555555555555555555œ5œ5œ5œ6666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777ž7ž7ž7ž7ž8888888888888888888888888888888888888888888888888888888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9 9 9 9 9 9 ::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐўўўÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÖ£Ö£ÖÖÖÖÖÖפ×××××ר¥Ø¥ØØØØÙ¦ÙÙÙÙÚ§Ú§ÚÚÚÚÛ¨ÛÛÛÛÜ©ÜÜÜÜݪÝÝÝÝÞ«ÞÞÞÞ߬ßßà­ààá®ááááâ¯ââã°ããä±ääå²ååæ³ææç´ççèµèèééê·êêë¸ëëììíºííîîï¼ïïððñ¾ññòòóÀóóôôõõöÃöö÷÷øøùÆùùúúûûüüüüýýþþÿÿ7: = @BDFGJKLMNOPQR S!!!T!T"""U#####V$$$$$W$W%%%%%X&&&&&&&Y'''''Z'Z((((((([)))))))\)\*******]*]+++++++^+^,,,,,,,,,_,_-----------`-`...........a.a/////////////b/b000000000000000c0c0c111111111111111d1d1d2222222222222222222e2e2e33333333333333333333333f3f3f444444444444444444444444444g4g4g4g5555555555555555555555555555555h5h5h5h66666666666666666666666666666666666666666i6i6i6i777777777777777777777777777777777777777777777777777j7j7j7j7j88888888888888888888888888888888888888888888888888888888888888888k8k8k8k8k8k99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999l9l9l9l9l9l::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××××ØØØØØØÙÙÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßààààááááááââââããããääääååååææææççççèèééééêêêêëëììììííîîîîïïððððññòòòòóóôôõõõõöö÷÷øøøøùùúúûûüüüüýýþþÿÿ  !!!!""""""######$$$$$$%%%%%%%%&&&&&&&&''''''(((((((((())))))))**********++++++++++,,,,,,,,,,,,--------------..............////////////////00000000000000000011111111111111111111222222222222222222222222333333333333333333333333333344444444444444444444444444444444555555555555555555555555555555555555556666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßßààààááááááââââããããääääååååææççççèèèèééééêêëëëëììííííîîïïïïððññññòòóóôôôôõõöö÷÷øøøøùùúúûûüüüüýýþþÿÿ  !!!!""""""######$$$$$$%%%%%%%%&&&&&&''''''''(((((((())))))))**********++++++++++++,,,,,,,,,,--------------..............////////////////0000000000000000001111111111111111111122222222222222222222223333333333333333333333333333444444444444444444444444444444555555555555555555555555555555555555556666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::Ä÷ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅøÅøÅøÅøÅøÅøÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖ× × ×××ר ØØØØØØÙ ÙÙÙÙÚ ÚÚÚÚÛ Û ÛÛÜÜÜÜÝÝÝÝÝÞÞÞÞÞßßßàààáááááââââãããäääååååææçççèèèéééêêëëëììíííîîï!ïïððñ#ññòòóóô&ôôõõöö÷÷ø*øøùùúúûûü.üüýýþþÿÿÓ × Ú Üßáâäåèéêëìí î!!!ï"""""ð#####ñ$$$$$ò%%%%%ó%ó&&&&&ô'''''õ'õ(((((ö(ö)))))))÷*********ø+++++++++ù+ù,,,,,,,ú,ú-----------û-û...........ü.ü///////////ý/ý/ý0000000000000þ0þ0þ111111111111111ÿ1ÿ1ÿ22222222222222223333333333333333333333344444444444444444444444444445555555555555555555555555555555556666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999:::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÅ+Å+Å+Å+Å+Å+Å+ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ,Æ,Æ,Æ,Æ,Æ,ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1Ë1ËËËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÔÔÕ;Õ;ÕÕÕÕÖ<Ö<ÖÖÖÖÖÖ×=×××ר>Ø>ØØØØÙ?ÙÙÙÙÚ@Ú@ÚÚÚÚÛAÛÛÛÛÜBÜÜÝCÝCÝÝÞDÞÞÞÞßEßßàFààáGááâHââââããããääåKååæLææçMççèNèèééêPêêëëìRììííîTîîïïðVððññòòóYóóôôõõöö÷]÷÷øøùùúúûûûûüüýýþþÿÿ §©¬®¯±²³·¸¹ º!!!»"""""¼#####½$$$$$¾%%%%%¿&&&&&À&À'''''Á(((((((Â)))))))Ã)Ã*******Ä*Ä+++++++Å+Å,,,,,,,Æ,Æ-----------Ç-Ç.........È.È.È///////////É/É0000000000000Ê0Ê0Ê111111111111111Ë1Ë1Ë22222222222222222Ì2Ì2Ì333333333333333333333Í3Í3Í4444444444444444444444444Î4Î4Î4Î55555555555555555555555555555Ï5Ï5Ï5Ï6666666666666666666666666666666666666Ð6Ð6Ð6Ð6Ð77777777777777777777777777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ8888888888888888888888888888888888888888888888888888888888888Ò8Ò8Ò8Ò8Ò8Ò99999999999999999999999999999999999999999999999999999999999999999999999999999999999Ó9Ó9Ó9Ó9Ó9Ó::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÓÓÔmÔÔÔÔÔÔÔÔÕnÕÕÕÕÕÕÖoÖoÖÖÖÖ×p×p×××רqØØØØÙrÙrÙÙÙÙÚsÚÚÚÚÛtÛÛÛÛÜuÜÜÜÜÝvÝÝÞwÞÞÞÞßxßßàyààázááâ{ââã|ããä}ääå~ååæææç€ççèèé‚ééêêë„ëëììí†ííîîïˆïïððññò‹òòóóôôõõööö÷÷øøùùúúû”ûûüüýýþþÿÿm q r uwxz|}€‚ƒ„…† ‡!!!ˆ"""""‰###Š#Š$$$‹$‹%%%%%Œ&&&&&'''''Ž'Ž(((((()))))))*******‘*‘+++++++’+’,,,,,,,“,“---------”-”-”.........•.•///////////–/–/–0000000000000—0—111111111111111˜1˜1˜22222222222222222™2™2™333333333333333333333š3š3š4444444444444444444444444›4›4›55555555555555555555555555555œ5œ5œ5œ666666666666666666666666666666666666666677777777777777777777777777777777777777777777777ž7ž7ž7ž7ž88888888888888888888888888888888888888888888888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ999999999999999999999999999999999999999999999999999999999999999999999999999999999 9 9 9 9 9 9 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’Å’ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÔ¡Ô¡ÔÔÔÔÔÔÕ¢Õ¢ÕÕÕÕÕÕÖ£ÖÖÖÖÖÖפ×××ר¥Ø¥ØØØØÙ¦ÙÙÙÙÚ§ÚÚÚÚÛ¨ÛÛÛÛÜ©ÜÜÜÜݪÝÝÞ«ÞÞÞÞ߬ßßà­ààá®ááâ¯ââã°ããä±ääå²ååæ³ææççèµèèééê·êêëëì¹ììííî»îîïïððñ¾ññòòóóôôõÂõõöö÷÷øøùùúÇúúûûüüýýþþÿÿ4: ? BEGIJLMNOPQR S!!!T"""""U###V$$$$$W%%%%%X%X&&&Y&Y'''''Z((((((([)))))))\)\*****]*]+++++++^+^,,,,,,,_,_---------`-`...........a.a///////////b/b0000000000000c0c0c1111111111111d1d1d22222222222222222e2e2e3333333333333333333f3f3f3f44444444444444444444444g4g4g4g555555555555555555555555555h5h5h5h66666666666666666666666666666666666i6i6i6i6i777777777777777777777777777777777777777777777j7j7j7j7j888888888888888888888888888888888888888888888888888888888k8k8k8k8k8k9999999999999999999999999999999999999999999999999999999999999999999999999999999l9l9l9l9l9l9l::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââããããääääååååææççççèèééééêêëëëëììííííîîïïððððññòòóóôôôôõõöö÷÷øøùùùùúúûûüüýýþþÿÿ  !!!!""""""####$$$$$$%%%%%%&&&&&&''''''''(((((((())))))))********++++++++++,,,,,,,,,,------------..............//////////////000000000000000011111111111111111122222222222222222222223333333333333333333333334444444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞÞÞßßßßààààááááââââããããääääååææææççèèèèééééêêëëììììííîîïïïïððññòòóóóóôôõõöö÷÷øøøøùùúúûûüüýýþþÿÿ  !!!!!!""""######$$$$$$%%%%%%&&&&&&''''''(((((((())))))))**********++++++++,,,,,,,,,,------------..............//////////////00000000000000001111111111111111112222222222222222222233333333333333333333333344444444444444444444444444445555555555555555555555555555555555666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅøÅøÅøÅøÅøÅøÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýËËËËËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÖÖÖÖÖÖÖ× ×××ר Ø ØØØØÙ ÙÙÙÙÚ ÚÚÚÚÛ ÛÛÜÜÜÜÜÝÝÝÞÞÞÞÞßßßàààáááâââãããäääååæææçèèèéééêêëìììííî ï!ïïððññò$ó%óóôôõõöö÷÷ø*øøùùúúûûüüýýþþÿÿÐ × Ø ÜÝßàâäæçéêëìí î!!!!!ï"""ð#####ñ$$$$$ò%%%%%ó&&&&&ô'''''õ(((((((ö)))))))÷*******ø*ø+++++ù+ù,,,,,,,ú,ú---------û-û.........ü.ü.ü/////////ý/ý/ý00000000000þ0þ0þ1111111111111ÿ1ÿ1ÿ222222222222223333333333333333333334444444444444444444444444555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ+Å+Å+Å+Å+Å+Å+ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ,Æ,Æ,Æ,Æ,Æ,ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1Ë1ËËËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÏÏÐ6Ð6Ð6ÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÓÓÔ:Ô:ÔÔÔÔÕ;Õ;ÕÕÕÕÖ<Ö<ÖÖÖÖ×=×=×××ר>ØØØØÙ?ÙÙÙÙÚ@ÚÚÚÚÛAÛÛÜBÜBÜÜÝCÝÝÞDÞÞÞÞßEßßàFààáGááâHââãIããääåKååæLææççèNèèééêPêêëëììíSííîîïïðVñWññòòóóôôõõö\÷]÷÷øøùùúúûûüüýýþþÿÿ ¥ ¦ ª­¯°±³´¶·¸¹ º!!!!!»"""¼#####½$$$¾$¾%%%¿%¿&&&À&À'''''Á(((((Â(Â)))))Ã)Ã*****Ä*Ä+++++++Å,,,,,,,,,Æ---------Ç-Ç.........È.È///////////É/É00000000000Ê0Ê0Ê1111111111111Ë1Ë1Ë222222222222222Ì2Ì2Ì3333333333333333333Í3Í3Í444444444444444444444Î4Î4Î4Î5555555555555555555555555Ï5Ï5Ï5Ï666666666666666666666666666666666Ð6Ð6Ð6Ð6Ð77777777777777777777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ88888888888888888888888888888888888888888888888888888Ò8Ò8Ò8Ò8Ò8Ò9999999999999999999999999999999999999999999999999999999999999999999999999Ó9Ó9Ó9Ó9Ó9Ó9Ó:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::Ô:Ô:Ô:Ô:Ô:Ô:Ô:Ô;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÓlÓlÓÓÓÓÓÓÔmÔmÔÔÔÔÕnÕnÕÕÕÕÖoÖoÖÖÖÖ×p×××רqØqØØÙrÙrÙÙÚsÚsÚÚÛtÛÛÛÛÜuÜÜÝvÝÝÞwÞwÞÞßxßßàyààázááâ{ââããä}ääå~ååææç€ççèèé‚ééêêë„ì…ììííîîïˆïïððññòòóóôõŽõõöö÷÷øøùùúúûûüüýýþþÿÿkl tuxy{|~€‚ƒ„…† ‡!!!!!ˆ"""‰###Š#Š$$$‹%%%%%Œ&&&&&'''''Ž'Ž(((())))))*****‘*‘+++++++’+’,,,,,,,“,“-------”-”.........•.•/////////–/–/–00000000000—0—1111111111111˜1˜1˜222222222222222™2™2™33333333333333333š3š3š3š444444444444444444444›4›4›5555555555555555555555555œ5œ5œ5œ66666666666666666666666666666666666677777777777777777777777777777777777777777ž7ž7ž7ž7ž888888888888888888888888888888888888888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ99999999999999999999999999999999999999999999999999999999999999999999999 9 9 9 9 9 9 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::¡:¡:¡:¡:¡:¡:¡:¡;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’Å’ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘˘ËËËËËËËËËËËËËËËË̙̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÑÑÒŸÒŸÒÒÒÒÒÒÓ Ó ÓÓÓÓÓÓÔ¡ÔÔÔÔÔÔÕ¢ÕÕÕÕÕÕÖ£ÖÖÖÖפפ×××ר¥ØØØØÙ¦ÙÙÙÙÚ§ÚÚÛ¨Û¨ÛÛÜ©ÜÜݪÝÝÝÝÞ«ÞÞ߬ßßà­ààá®ááââã°ããä±ää岿³ææç´èµèèééê·êêëëììíºî»îîïïððññò¿óÀóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ9:; BCFGIKMNPQR S!!!T!T"""U###V$$$$$W%%%X%X&&&Y&Y'''''Z((((([)))))))\*******]+++++++^+^,,,,,,,_,_-------`-`.........a.a/////////b/b00000000000c0c0c1111111111111d1d222222222222222e2e2e33333333333333333f3f3f444444444444444444444g4g4g4g55555555555555555555555h5h5h5h6666666666666666666666666666666i6i6i6i6i777777777777777777777777777777777777777j7j7j7j7j888888888888888888888888888888888888888888888888888k8k8k8k8k99999999999999999999999999999999999999999999999999999999999999999999999l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÝÝÞÞÞÞßßßßààààááââââããããääääååææææççèèééééêêëëììììííîîïïððññññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!""""""####$$$$$$%%%%&&&&&&''''''''(((((())))))))********++++++++,,,,,,,,,,----------............////////////000000000000001111111111111111112222222222222222223333333333333333333333444444444444444444444444445555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÜÜÝÝÝÝÞÞÞÞßßßßààààááââââããããääååååææççççèèééêêêêëëììííîîîîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ  !!!!""""""####$$$$%%%%%%&&&&&&''''''(((((())))))))********++++++++,,,,,,,,,,----------..........//////////////00000000000000111111111111111122222222222222222233333333333333333333334444444444444444444444445555555555555555555555555555556666666666666666666666666666666666666677777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃöÃöÃöÃöÃöÃöÃöÃöÃöÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅøÅøÅøÅøÅøÅøÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýÊýËËËËËËËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖ× × ×ר Ø ØØÙ Ù ÙÙÚ ÚÚÚÚÛ ÛÛÜÜÜÜÜÝÝÝÞÞÞßßßàààááâââãããäåååæçççèèéêêêëëììíî îîïïððññòòóóôôõõöö÷÷øøùùúúûûüüýýþþÿÿ ÛÜÝáâäåçéêëìí î!!!ï"""ð"ð###ñ$$$ò%%%%%ó&&&&&ô'''''õ(((((ö)))))÷)÷*****ø*ø+++++ù+ù,,,,,,,ú,ú-------û-û.......ü.ü/////////ý/ý/ý000000000þ0þ0þ11111111111ÿ1ÿ1ÿ222222222222333333333333333334444444444444444444444555555555555555555555555566666666666666666666666666666666777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ*Ä*Ä*Ä*Ä*Ä*Ä*Ä*Ä*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ+Å+Å+Å+Å+Å+Å+Å+ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ,Æ,Æ,Æ,Æ,Æ,ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍ3Í3ÍÍÍÍÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÑÑÒ8Ò8ÒÒÒÒÒÒÓ9Ó9ÓÓÓÓÔ:Ô:ÔÔÔÔÕ;Õ;ÕÕÖ<Ö<ÖÖÖÖ×=×××ר>ØØØØÙ?ÙÙÚ@Ú@ÚÚÛAÛÛÜBÜBÜÜÝCÝÝÞDÞÞßEßßàFáGááâHââãIããääåKååææçMèNèèééêPëQìRììííîîïïðVñWòXóYôZõ[ö\÷]ø^ù_ú`ûaüüýýþþÿÿ ª«¬¯°²³´µ¶·¸¹ º!!!»"""¼#####½$$$¾%%%¿%¿&&&À&À'''Á'Á(((Â(Â)))))Ã*****Ä*Ä+++++Å+Å,,,,,,,Æ,Æ-----Ç-Ç-Ç.......È.È/////////É/É00000000000Ê0Ê11111111111Ë1Ë1Ë2222222222222Ì2Ì2Ì333333333333333Í3Í3Í4444444444444444444Î4Î4Î4Î55555555555555555555555Ï5Ï5Ï66666666666666666666666666666Ð6Ð6Ð6Ð7777777777777777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ888888888888888888888888888888888888888888888Ò8Ò8Ò8Ò8Ò8Ò999999999999999999999999999999999999999999999999999999999999999Ó9Ó9Ó9Ó9Ó9Ó9Ó:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::Ô:Ô:Ô:Ô:Ô:Ô:Ô:Ô;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ]Ä]Ä]Ä]Ä]Ä]Ä]Ä]Ä]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËËËËËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÍÍÍÍÎgÎgÎÎÎÎÎÎÎÎÎÎÏhÏhÏÏÏÏÏÏÏÏÐiÐiÐiÐÐÐÐÐÐÑjÑjÑÑÑÑÑÑÒkÒkÒÒÒÒÒÒÓlÓÓÓÓÓÓÔmÔmÔÔÔÔÕnÕÕÕÕÖoÖoÖÖ×p×p×רqØqØØÙrÙrÙÙÚsÚÚÛtÛtÛÛÜuÜÜÝvÝÝÞwÞÞßxßßàyázááâ{ââã|ä}ääå~æææççèé‚ééêêëëììí†î‡ïˆïïððññòòóóôôõõöö÷÷øøùùúúü•ýýþþÿÿ z{|~ƒ„…† ‡!!!ˆ"""‰###Š#Š$$$‹%%%Œ&&&&&'''''Ž((((())))))***‘*‘+++++’+’,,,,,,,“,“-----”-”.........•.•///////–/–/–000000000—0—0—111111111˜1˜1˜2222222222222™2™2™333333333333333š3š3š4444444444444444444›4›4›55555555555555555555555œ5œ5œ5œ66666666666666666666666666666677777777777777777777777777777777777ž7ž7ž7ž7ž888888888888888888888888888888888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ999999999999999999999999999999999999999999999999999999999999999 9 9 9 9 9 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::¡:¡:¡:¡:¡:¡:¡:¡;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃđđđđđđđđđÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘˘ËËËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍÍÍΛΛΛÎÎÎÎÎÎÎÎϜϜϜÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐўўÑÑÑÑÑÑÒŸÒŸÒÒÒÒÓ Ó ÓÓÓÓÓÓÔ¡ÔÔÔÔÕ¢Õ¢ÕÕÕÕÖ£ÖÖÖÖפ×××ר¥ØØØØÙ¦ÙÙÚ§ÚÚÚÚÛ¨ÛÛÜ©ÜÜݪÝÝÞ«ÞÞ߬ßßà­á®ááâ¯ââããä±ääååæ³ç´ççèèé¶ê·ë¸ì¹ììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûýýþþÿÿ:; < = > ? @ABCDEIJLMNOPR S!!!T"""U###V$$$W$W%%%X&&&Y&Y'''Z'Z((([([)))\)\*****]+++++++^,,,,,,,_,_-----`-`.......a.a.a///////b/b000000000c0c0c11111111111d1d2222222222222e2e2e333333333333333f3f3f44444444444444444g4g4g4g555555555555555555555h5h5h5h6666666666666666666666666i6i6i6i6i777777777777777777777777777777777j7j7j7j7j8888888888888888888888888888888888888888888k8k8k8k8k8k99999999999999999999999999999999999999999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØØØÙÙÙÙÚÚÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààááááââããããääååååææççèèèèééêêëëììííîîïïððññòòóóôôõõöö÷÷øøùùúúûûýýþþÿÿ  !!!!""""####$$$$%%%%%%&&&&''''''(((((())))))********++++++++,,,,,,,,--------..........////////////0000000000001111111111111111222222222222222233333333333333333333444444444444444444444455555555555555555555555555556666666666666666666666666666666677777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××××ØØØØÙÙÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÝÝÞÞÞÞßßßßààááááââããããääååææææççèèééêêëëëëììííîîïïððññòòóóõõöö÷÷øøùùúúûûüüþþÿÿ  !!!!""""####$$$$%%%%&&&&&&''''''(((())))))))******++++++++,,,,,,,,--------..........////////////000000000000111111111111112222222222222222333333333333333333444444444444444444444455555555555555555555555555556666666666666666666666666666666677777777777777777777777777777777777777778888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃöÃöÃöÃöÃöÃöÃöÃöÃöÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅøÅøÅøÅøÅøÅøÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýÊýËËËËËËËËËËËËËþËþËþËþÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÑÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÖÖÖÖ× ×××ר ØØÙ Ù ÙÙÚ ÚÚÛ ÛÛÜÜÜÝÝÝÞÞÞßßßàáááâãããääåæææççèéêëëëììííîîïïððññòòóóõ'ö(÷÷øøùùúúûûüüþþÿÿÒ ÛÜÝÞßàáåæèéëìí î!!!ï"""ð###ñ$$$ò%%%ó&&&&&ô'''õ'õ(((ö)))))÷)÷***ø*ø+++++ù+ù,,,,,ú,ú-----û-û.......ü.ü///////ý/ý/ý0000000þ0þ0þ111111111ÿ1ÿ1ÿ222222222233333333333333344444444444444444445555555555555555555555566666666666666666666666666777777777777777777777777777777777778888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;< < < < < < < < < < <<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ*Ä*Ä*Ä*Ä*Ä*Ä*Ä*Ä*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ+Å+Å+Å+Å+Å+Å+ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ,Æ,Æ,Æ,Æ,Æ,ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÍ3Í3Í3ÍÍÍÍÍÍÍÍÎ4Î4Î4ÎÎÎÎÎÎÎÎÏ5Ï5ÏÏÏÏÏÏÏÏÐ6Ð6ÐÐÐÐÐÐÑ7Ñ7ÑÑÑÑÒ8Ò8ÒÒÒÒÓ9Ó9ÓÓÓÓÔ:Ô:ÔÔÕ;Õ;ÕÕÕÕÖ<ÖÖ×=×=×ר>Ø>ØØÙ?ÙÙÚ@ÚÚÛAÛÛÜBÜÜÝCÝÝÞDÞÞßEßßààáGááâHãIäJääåKæLçMççèèééêêëQìRíSîTïUððññòòóóôôõõ÷]ø^ùùúúûûüüþþÿÿ ¤ ¥ ¦®¯°±³´µ¶·¸¹ º!!!»"""¼#½#½$$$¾%%%¿&&&À&À'''Á(((Â(Â)))Ã)Ã***Ä*Ä+++++Å+Å,,,,,Æ,Æ-----Ç-Ç.......È.È///////É/É000000000Ê0Ê111111111Ë1Ë1Ë22222222222Ì2Ì2Ì3333333333333Í3Í3Í444444444444444Î4Î4Î4Î5555555555555555555Ï5Ï5Ï5Ï66666666666666666666666Ð6Ð6Ð6Ð7777777777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ888888888888888888888888888888888888888Ò8Ò8Ò8Ò8Ò8Ò99999999999999999999999999999999999999999999999999999Ó9Ó9Ó9Ó9Ó9Ó9Ó:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::Ô:Ô:Ô:Ô:Ô:Ô:Ô:Ô;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ]Ä]Ä]Ä]Ä]Ä]Ä]Ä]Ä]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^Å^ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËdËËËËËËËËËËËËÌeÌeÌeÌÌÌÌÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÏhÏhÏhÏÏÏÏÏÏÐiÐiÐÐÐÐÐÐÑjÑjÑÑÑÑÒkÒkÒÒÒÒÓlÓlÓÓÓÓÔmÔmÔÔÕnÕnÕÕÖoÖoÖÖ×p×p×רqØØÙrÙÙÚsÚsÛtÛtÜuÜuÝvÝvÞwÞÞßxàyààázááââã|ä}ääååæç€èé‚êƒêêëëììííîîð‰ñŠò‹óŒôôõõöö÷÷ù’úúûûüüþ—ÿÿj p xyz{|€ƒ„…† ‡!!!ˆ"‰"‰#Š$$$‹$‹%Œ%Œ&&&'''Ž'Ž((()))))*****‘+++++’+’,,,,,“,“-----”-”.....•.•.•/////–/–/–0000000—0—0—111111111˜1˜22222222222™2™2™3333333333333š3š3š444444444444444›4›4›5555555555555555555œ5œ5œ5œ666666666666666666666666677777777777777777777777777777ž7ž7ž7ž7ž8888888888888888888888888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ999999999999999999999999999999999999999999999999999 9 9 9 9 9 9 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::¡:¡:¡:¡:¡:¡:¡:¡;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;¢;¢;¢;¢;¢;¢;¢;¢;¢;¢<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃđđđđđđđđđÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËËËË̙̙̙ÌÌÌÌÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍÍÍΛΛÎÎÎÎÎÎÎÎϜϜÏÏÏÏÏÏÐÐÐÐÐÐÐÐўўÑÑÑÑÒŸÒŸÒÒÒÒÓ Ó ÓÓÓÓÔ¡ÔÔÔÔÕ¢Õ¢ÕÕÖ£ÖÖÖÖפ×ר¥Ø¥Ù¦Ù¦ÙÙÚ§ÚÚÛ¨ÛÛÜ©ÜÜݪޫÞÞ߬à­ààá®â¯ââã°ä±å²ååææççèèé¶ê·ë¸ì¹íºîîïïððññòòôÁõõöö÷÷øøúúûûüüýýÿÿ:; @ABCIJKLNOPQR S!T!T"U###V$$$W%%%X&&&Y&Y'''Z((([([)))\)\***]*]+++^+^,,,,,_,_-----`-`.....a.a///////b/b0000000c0c0c111111111d1d1d222222222e2e2e33333333333f3f3f3f4444444444444g4g4g4g55555555555555555h5h5h5h666666666666666666666i6i6i6i77777777777777777777777777777j7j7j7j7j88888888888888888888888888888888888k8k8k8k8k8k9999999999999999999999999999999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÔÔÕÕÕÕÖÖÖÖÖÖ××××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÞÞÞÞßßààààááââââããääååææççèèèèééêêëëììîîïïððññòòóóõõöö÷÷øøúúûûüüýýÿÿ  !!""""####$$$$%%%%&&&&''''''(((())))))******++++++,,,,,,,,--------........//////////00000000001111111111111122222222222222333333333333333344444444444444444444555555555555555555555555666666666666666666666666666677777777777777777777777777777777777788888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÏÏÐÐÐÐÐÐÐÐÑÑÑÑÑÑÑÑÒÒÒÒÒÒÒÒÓÓÓÓÓÓÔÔÔÔÔÔÕÕÕÕÕÕÖÖÖÖÖÖ××××ØØØØÙÙÙÙÚÚÚÚÛÛÛÛÜÜÜÜÝÝÞÞÞÞßßààààááââããããääååææççèèééêêëëììííîîïïððòòóóôôõõ÷÷øøùùûûüüýýÿÿ  !!""""####$$$$%%%%&&&&''''(((((())))******++++++,,,,,,,,--------........//////////000000000011111111111122222222222222333333333333333344444444444444444455555555555555555555555566666666666666666666666666777777777777777777777777777777777777888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃöÃöÃöÃöÃöÃöÃöÃöÃöÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅøÅøÅøÅøÅøÅøÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇúÇúÇúÇúÇúÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉüÉüÉüÉüÊÊÊÊÊÊÊÊÊÊÊÊÊýÊýÊýÊýËËËËËËËËËËËþËþËþÌÌÌÌÌÌÌÌÌÌÌÿÌÿÌÿÍÍÍÍÍÍÎÎÎÎÎÎÎÎÎÏÏÏÏÏÏÏÏÐÐÐÐÐÐÑÑÑÑÑÑÒÒÒÒÒÒÓÓÓÓÔÔÔÔÕÕÕÕÖÖÖÖ× ×ר ØØÙ Ù Ú Ú Û ÛÛÜÜÜÝÞÞÞßàààáâãããääåæçèéêëìííîîïïððò$óóôôõõ÷)øøùùûûüüýýÿÿÐÔ Ø ÝÞßçèéêëìí î!ï"""ð###ñ$ò$ò%ó%ó&ô&ô'''õ(((ö(ö)))÷***ø*ø+++ù+ù,,,,,ú,ú-----û-û.....ü.ü/////ý/ý/ý00000þ0þ0þ1111111ÿ1ÿ1ÿ22222222333333333333344444444444444455555555555555555556666666666666666666666777777777777777777777777777777888888888888888888888888888888888888899999999999999999999999999999999999999999999999999:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;< < < < < < < < < < < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃ)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ*Ä*Ä*Ä*Ä*Ä*Ä*Ä*Ä*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ+Å+Å+Å+Å+Å+Å+ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ,Æ,Æ,Æ,Æ,Æ,ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈ.È.È.È.È.ÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ0Ê0Ê0Ê0ÊÊÊÊÊÊÊÊÊÊÊÊË1Ë1Ë1ËËËËËËËËËËÌ2Ì2Ì2ÌÌÌÌÌÌÌÌÌÌÍ3Í3ÍÍÍÍÍÍÍÍÎ4Î4ÎÎÎÎÎÎÏ5Ï5Ï5ÏÏÏÏÐ6Ð6ÐÐÐÐÑ7Ñ7Ñ7ÑÑÒ8Ò8ÒÒÒÒÓ9Ó9ÓÓÔ:Ô:ÔÔÕ;Õ;ÕÕÖ<ÖÖ×=×=Ø>Ø>ØØÙ?ÙÙÚ@ÛAÛAÜBÜÜÝCÞDÞDßEàFààáGâHãIäJääååææççèèééêêëëíSîTïUððññóYôZõõööøøùùûaüüýýÿÿŸ £ §¨¬­®¯°±²³´µ¶·¸¹ º!»"""¼#½#½$¾%%%¿&&&À'''Á'Á(Â(Â)))Ã)Ã***Ä+++++Å,,,,,Æ,Æ---Ç-Ç-Ç...È.È.È/////É/É0000000Ê0Ê1111111Ë1Ë1Ë222222222Ì2Ì2Ì333333333Í3Í3Í3Í44444444444Î4Î4Î4Î555555555555555Ï5Ï5Ï5Ï6666666666666666666Ð6Ð6Ð6Ð7777777777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ8888888888888888888888888888888Ò8Ò8Ò8Ò8Ò8Ò9999999999999999999999999999999999999999999Ó9Ó9Ó9Ó9Ó9Ó9Ó:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::Ô:Ô:Ô:Ô:Ô:Ô:Ô:Ô;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃ\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ]Ä]Ä]Ä]Ä]Ä]Ä]Ä]Ä]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊÊÊÊÊÊÊÊÊÊÊÊËdËdËdËdËËËËËËËËÌeÌeÌeÌeÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÍÍÎgÎgÎgÎÎÎÎÎÎÏhÏhÏÏÏÏÐiÐiÐiÐÐÐÐÑjÑjÑÑÒkÒkÒÒÒÒÓlÓlÓÓÔmÔmÔÔÕnÕÕÖoÖoÖÖ×p×רqØØÙrÙÙÚsÚÚÛtÜuÜuÝvÝÝÞwßxàyààázâ{ã|ä}å~æç€èé‚êƒë„ì…ííîîð‰ñŠòòóóõŽööø‘ùùúúüüýýÿÿo swx}~€‚„…† ‡!ˆ"""‰#Š$$$‹%%%Œ&&&'''Ž((())))***‘*‘+++’+’,,,“,“---”-”.....•.•/////–/–/–00000—0—0—1111111˜1˜222222222™2™2™333333333š3š3š4444444444444›4›4›555555555555555œ5œ5œ5œ66666666666666666666677777777777777777777777ž7ž7ž7ž7ž88888888888888888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ9999999999999999999999999999999999999999999 9 9 9 9 9 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::¡:¡:¡:¡:¡:¡:¡:¡;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;¢;¢;¢;¢;¢;¢;¢;¢;¢;¢<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃđđđđđđđđđÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’Å’ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊÊÊÊÊ˘˘˘ËËËËËËËËËË̙̙̙ÌÌÌÌÌÌ͚͚͚ÍÍÍÍÍÍΛΛΛÎÎÎÎϜϜϜÏÏÏÏÐÐÐÐÐÐўўÑÑÒŸÒŸÒÒÒÒÓ Ó ÓÓÔ¡ÔÔÕ¢Õ¢ÕÕÖ£ÖÖפפإإ٦٦ڧÚÚÛ¨ÛÛܩݪÝÝޫ߬à­ààááâ¯ã°ä±å²æ³ç´èµé¶êêëëíºî»ïïððò¿óóôôöö÷÷ùùúúüüýýÿÿ > ABEFGHOPQR S!T"U"U#V$$$W%X%X&Y&Y'Z'Z([([)))\***]*]+++^+^,,,_,_---`-`.....a.a/////b/b0000000c0c1111111d1d1d2222222e2e2e333333333f3f3f44444444444g4g4g4g5555555555555h5h5h5h66666666666666666i6i6i6i77777777777777777777777j7j7j7j7j88888888888888888888888888888k8k8k8k8k99999999999999999999999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Ù?Ù?Ú@ÚÚÛAÜBÜÜÝCÞDßEàFáGâHãIäJåKæLçMèNééëQìRííïUððòXóóõ[ööøøú`ûûýýÿÿ›  ¥ ¨«®¯°³´µ¶·¸¹ º!»"¼#½#½$¾%¿%¿&À'''Á(((Â)))Ã***Ä*Ä+Å+Å,,,Æ,Æ---Ç-Ç...È.È/////É/É00000Ê0Ê11111Ë1Ë1Ë2222222Ì2Ì2Ì3333333Í3Í3Í444444444Î4Î4Î4Î55555555555Ï5Ï5Ï5Ï6666666666666Ð6Ð6Ð6Ð6Ð7777777777777777777Ñ7Ñ7Ñ7Ñ7Ñ88888888888888888888888Ò8Ò8Ò8Ò8Ò8Ò99999999999999999999999999999999999Ó9Ó9Ó9Ó9Ó9Ó9Ó:::::::::::::::::::::::::::::::::::::::::::::::::Ô:Ô:Ô:Ô:Ô:Ô:Ô:Ô;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö========================================================================Â[Â[Â[ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃ\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ]Ä]Ä]Ä]Ä]Ä]Ä]Ä]Ä]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÉÉÉÉÉÉÊcÊcÊcÊÊÊÊÊÊÊÊËdËdËdËdËËËËËËÌeÌeÌeÌÌÌÌÌÌÍfÍfÍfÍÍÍÍÎgÎgÎgÎÎÏhÏhÏhÏÏÐiÐiÐÐÑjÑjÑjÑÑÒkÒkÓlÓlÓÓÔmÔmÕnÕnÖoÖo×p×pØqØqÙrÚsÚsÛtÜuÜÜÝvÞwßxàyázâ{ã|ä}å~æç€é‚êƒë„í†î‡ð‰ññóŒôôööø‘ùùûûýýÿÿo q tvwyz}~‚ƒ„…† ‡ ‡!ˆ"‰#Š$$$‹%Œ&&&'Ž'Ž(())*‘*‘+’+’,,,“,“---”-”...•.•///–/–/–000—0—0—11111˜1˜2222222™2™2™3333333š3š3š444444444›4›4›55555555555œ5œ5œ5œ66666666666666667777777777777777777ž7ž7ž7ž7ž88888888888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ999999999999999999999999999999999 9 9 9 9 9 9 :::::::::::::::::::::::::::::::::::::::::::::::¡:¡:¡:¡:¡:¡:¡:¡;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;¢;¢;¢;¢;¢;¢;¢;¢;¢;¢<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<£<£<£<£<£<£<£<£<£<£<£<£<£<£==================================================================================================ÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃđđđđđđđđđÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇȕȕȕȕȕÈÈÈÈÈÈÈÈÈÈÈÈÉ–É–É–É–ÉÉÉÉÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊÊÊÊÊ˘˘˘ËËËËËË̙̙̙ÌÌÌÌÌÌ͚͚͚ÍÍÍÍΛΛÎÎÎÎϜϜÏÏÐÐÐÐÐўўÑÑÒŸÒŸÓ Ó ÓÓԡԡբբ֣֣פ×ר¥Ù¦Ù¦Ú§Û¨Ü©Ü©ÝªÞ«ß¬à­á®â¯ã°ä±å²ææèµé¶êêì¹ííï¼ñ¾òòôÁöÃ÷÷ùùûûýýÿÿ68 ?ADGHJKLMOPQR S!!!T"U#V$W$W%X&Y&Y'Z([([)\)\*]*]+++^,,,_,_---`-`...a.a///b/b00000c0c11111d1d1d22222e2e2e33333f3f3f3f4444444g4g4g4g555555555h5h5h5h6666666666666i6i6i6i77777777777777777j7j7j7j7j888888888888888888888k8k8k8k8k8k99999999999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Ù?Ù?Ú@ÛAÜBÝCÞDßEàFáGâHäJåKæLèNéOëQíSïUððòòôôööøøúúýýÿÿ¢ ¤ ¦¨ª¬­¯±²´µ¶·¹ º!»"¼#½$W$¾%¿&À'Á'Á(Â)Ã)Ã*Ä*Ä+Å+Å,Æ,Æ-Ç-Ç...È.È///É/É000Ê0Ê0Ê111Ë1Ë22222Ì2Ì2Ì33333Í3Í3Í44444Î4Î4Î4Î5555555Ï5Ï5Ï5Ï666666666Ð6Ð6Ð6Ð6Ð7777777777777Ñ7Ñ7Ñ7Ñ7Ñ88888888888888888Ò8Ò8Ò8Ò8Ò9999999999999999999999999Ó9Ó9Ó9Ó9Ó9Ó9Ó:::::::::::::::::::::::::::::::::::::Ô:Ô:Ô:Ô:Ô:Ô:Ô:Ô;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö==============================================================================================================================================================================================================ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂ[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃ\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ]Ä]Ä]Ä]Ä]Ä]Ä]Ä]Ä]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^Å^ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈaÈaÈaÈaÈÈÈÈÈÈÈÈÈÈÉbÉbÉbÉbÉÉÉÉÉÉÊcÊcÊcÊcÊÊÊÊÊÊËdËdËdËËËËÌeÌeÌeÌÌÍfÍfÍfÍÍÎgÎgÎgÎÎÏhÏhÏÏÐiÐiÑjÑjÑjÒkÒkÓlÓlÔmÔmÕnÖoÖo×pØqØqÙrÚsÛtÜuÝvÞwßxàyázãä}å~ç€èêƒì…î‡ð‰ò‹ôöøøúúý–ÿÿkm tvxz{}€‚ƒ„ì ‡!ˆ"‰#Š$‹%Œ%Œ&'Ž(()*‘*‘+’+’,“,“-”-”...•.•/–/–/–0—0—0—111˜1˜1˜222™2™2™333š3š3š3š44444›4›4›5555555œ5œ5œ5œ6666666666667777777777777ž7ž7ž7ž7ž888888888888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ99999999999999999999999 9 9 9 9 9 :::::::::::::::::::::::::::::::::::¡:¡:¡:¡:¡:¡:¡:¡;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;¢;¢;¢;¢;¢;¢;¢;¢;¢;¢<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<£<£<£<£<£<£<£<£<£<£<£<£<£===========================================================================================================================================================================================================¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃđđđđđđđđđÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”Ç”ÇÇÇÇÇÇÇÇÇÇȕȕȕȕȕÈÈÈÈÈÈÈÈÉ–É–É–É–ÉÉÉÉÉÉÊ—Ê—Ê—Ê—ÊÊÊÊ˘˘˘˘ËË̙̙̙ÌÌ͚͚͚ÍÍΛΛΛϜϜϜÐÐÐÐўўҟҟӠӠԡÕnÕ¢Ö£×pפإ٦ڧÛtÜuÝvÞw߬à­á®ã°ä±æ³ç´é¶ë¸íºï¼ñ¾óÀõõøÅúúüüÿÿ < >EGHJLMOP… S!T"U#V$W%X&Y&'Z([)\)\*]+^+^,_,_-`-`.a.a.a/b/b000c0c111d1d1d222e2e2e333f3f3f44444g4g4g4g55555h5h5h5h6666666i6i6i6i6i77777777777j7j7j7j7j8888888888888k8k8k8k8k8k999999999999999999999l9l9l9l9l9l9l:::::::::::::::::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÐÐÐÐÐÐÑÑÑÑÒÒÒÒÓÓÓÓÔÔÕÕÕÕÖÖ×רØÙÙÙÙÚÚÛÛÜÜÞÞßßààââããååææèèêêììîîððòòõõ÷÷úúüüÿÿ  !!""##$$%%&&''(((())****++,,,,----....//////000000111111222222223333333344444444445555555555556666666666666677777777777777777777888888888888888888888899999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊÊÊËËËËËËËËÌÌÌÌÌÌÌÌÍÍÍÍÍÍÎÎÎÎÎÎÏÏÏÏÏÏÐÐÐÐÑÑÑÑÒÒÒÒÓÓÔÔÔÔÕÕÖÖÖÖ×רØÙÙÚÚÛÛÜÜÞÞßßààââããååççééëëííïïòòôô÷÷ùùüüÿÿ  !!""##$$%%&&''(())****++,,,,----....//////00001111112222222233333333444444445555555555556666666666666677777777777777777788888888888888888888889999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==========================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃöÃöÃöÃöÃöÃöÃöÃöÃöÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅøÅøÅøÅøÅøÅøÆÆÆÆÆÆÆÆÆÆÆÆÆùÆùÆùÆùÆùÆùÇÇÇÇÇÇÇÇÇúÇúÇúÇúÇúÈÈÈÈÈÈÈûÈûÈûÈûÉÉÉÉÉüÉüÉüÉüÊÊÊýÊýÊýËËËþËþËþÌÌÌÿÌÿÌÿÎÎÎÏÏÏÐÐÑÑÒÒÓÓ9ÔÕÕ;Ö× Ø Ù Ú Û ÜÝCÞDàáGãåçéëíï!ò$ô&÷)ùùüüÿÿÐÓ Ø Ûݬâäæ´¶ë¹ î!ï#½$¾%¿&À'Á(Â(ö)÷*ø*ø+ù,ú,ú-û-û.ü.ü/ý/ý/ý0þ0þ1ÿ1ÿ1ÿ223334444445555555666666666677777777777788888888888888899999999999999999999:::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;< < < < < < < < < < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<= = = = = = = = = = = = = = ==================================================================================================================================================================> > > > > > > > > > > > > > > > > > > > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Á'Á'Á'Á'Á'Á'Á'Á'ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂ(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃ)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ*Ä*Ä*Ä*Ä*Ä*Ä*Ä*Ä*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ+Å+Å+Å+Å+Å+Å+ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ,Æ,Æ,Æ,Æ,Æ,ÆÆÆÆÆÆÆÆÆÆÆÆÇ-Ç-Ç-Ç-Ç-ÇÇÇÇÇÇÇÇÈ.È.È.È.ÈÈÈÈÈÈÉ/É/É/É/ÉÉÉÉÊ0Ê0Ê0ÊÊË1Ë1Ë1Ë1Ì2Ì2Ì2Ì2Í3Í3Í3Î4Î4Î4Ï5Ï5Ð6Ð6Ñ7Ñ7Ò8Ò8Ó9Ó Ô:Õ;Ö<×=פإ٦ÛAÜBÝCÞ«àFâHã°å²ç´é¶ìRîTñWóóööùùüüÿÿ¢ ¥¨ª­¯±³µ·R º"U#V$¾%¿&À'Á(Â)Ã*]*Ä+Å,_,Æ-Ç-Ç.È.È/É/É0Ê0Ê0Ê1Ë1Ë222Ì2Ì2Ì3Í3Í3Í444Î4Î4Î4Î555Ï5Ï5Ï5Ï66666Ð6Ð6Ð6Ð6Ð7777777Ñ7Ñ7Ñ7Ñ7Ñ888888888Ò8Ò8Ò8Ò8Ò8Ò999999999999999Ó9Ó9Ó9Ó9Ó9Ó:::::::::::::::::::::::Ô:Ô:Ô:Ô:Ô:Ô:Ô:Ô;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö=====================================================================================================================================================×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂ[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃ\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ]Ä]Ä]Ä]Ä]Ä]Ä]Ä]Ä]ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÆÆÆÆÆÆÆÆÇ`Ç`Ç`Ç`Ç`ÇÇÇÇÇÇÈaÈaÈaÈaÈaÈÈÈÈÉbÉbÉbÉbÉÉÊcÊcÊcÊcÊÊËdËdËdËËÌeÌeÌeÍfÍfÍfÎgÎgÏhÏhÐÐiÑÑjÒkÒkÓlÔmÕÖÖo×pØqÚ Û ÜÝvßáâ{ä}æèëí†ð‰óŒöù’ü•ÿÿ p svß{äæèêì ‡!ï#Š$‹%ó&ô'õ(ö)*‘+’+ù,“-”-û.•.ü/–/ý0—0—1˜1˜1˜2™2™2™3š3š3š444›4›4›555œ5œ5œ5œ666666667777777ž7ž7ž7ž7ž8888888Ÿ8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ9999999999999 9 9 9 9 9 9 :::::::::::::::::::¡:¡:¡:¡:¡:¡:¡:¡;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;¢;¢;¢;¢;¢;¢;¢;¢;¢;¢<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<£<£<£<£<£<£<£<£<£<£<£<£<£<£=======================================================================================================================================¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃđđđđđđđđđÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’Å’ÅÅÅÅÅÅÅÅÅÅÅÅÆ“Æ“Æ“Æ“Æ“Æ“ÆÆÆÆÆÆÆÆÇ”Ç”Ç”Ç”Ç”Ç”ÇÇÇÇȕȕȕȕȕÈÈÉ–É–É–É–ÉÉʗʗʗʗ˘˘˘˘̙̙̙͚͚ÎgΛΛϜϜÐÑjÑžÒkÓlÓ Ô¡ÕnÖo×pØqÙrÚ§Üuݪßxázã|å~ç€é¶ì…ïˆò¿õÂøÅûûÿÿ9 = @CF|Kƒ… S!ˆ#V$‹%Œ'Z([)\*]*‘+’,_-`-”.a/b/b0c0c1d1d1d2e2e2e3f3f3f4g4g4g4g5h5h5h5h666i6i6i6i6i77777j7j7j7j7j8888888k8k8k8k8k99999999999l9l9l9l9l9l9l:::::::::::::::::m:m:m:m:m:m:m:m;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÉÉÊÊÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÍÍÍÍÎÎÎÎÏÏÏÏÐÐÐÐÑÑÒÒÓÓÓÓÔÔÕÕÖÖ××ÙÙÚÚÜÜÝÝßßááããååèèêêííññôô÷÷ûûÿÿ  !!##$$&&''(())**++,,----..////0000111122222233333344444455555555666666666677777777777777888888888888888899999999999999999999::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÇÇÇÇÈÈÈÈÈÈÈÈÈÈÈÈÉÉÉÉÉÉÉÉÊÊÊÊÊÊËËËËËËÌÌÌÌÌÌÍÍÍÍÎÎÎÎÏÏÐÐÐÐÑÑÒÒÓÓÔÔÕÕÖÖ×רØÚÚÛÛÝÝßßááããææééììïïóó÷÷ûûÿÿ  ""##%%&&(())**++,,----..////00111122222233334444445555555566666666667777777777778888888888888899999999999999999999::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃöÃöÃöÃöÃöÃöÃöÃöÃöÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ÷Ä÷Ä÷Ä÷Ä÷Ä÷Ä÷ÅÅÅÅÅÅÅÅÅÅÅøÅøÅøÅøÅøÅøÆÆÆÆÆùÆùÆùÆùÆùÇÇÇúÇúÇúÇúÇúÈÈÈûÈûÈûÈûÉüÉüÉüÊýÊýÊýË1ËþËþÌÿÌÿÎÎÏÏ5ÐÐ6Ñ7Ò8Ó9Ô:Õ;Ö<×=Ù?Ú@ÜBÞDàFâHåKèNëQï!ó%÷)û-ÿÿÖ Úªá±´¶†!»#½$¾&À'Á)Ã*Ä+Å,Æ-Ç-û.È/É/ý0Ê0þ1ÿ1ÿ333Í44555666677777778888888999999999999:::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;< < < < < < < < < < < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<= = = = = = = = = = = = = ================================================================================================> > > > > > > > > > > > > > > > > > > > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁ'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂ(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃ)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ*Ä*Ä*Ä*Ä*Ä*Ä*Ä*Ä*ÄÄÄÄÄÄÄÄÄÄÅ+Å+Å+Å+Å+Å+Å+Å+ÅÅÅÅÅÅÆ,Æ,Æ,Æ,Æ,Æ,ÆÆÇ-Ç-Ç-Ç-Ç-Ç-È.È.È.È.È.É/É/É/É–Ê0Ê0Ê0Ë1Ë1˘Ì2Ì™Í3ÍšÎ4ΛÏ5ÏœÐÑžÒŸÓ Ô¡Õ¢× Ø¥Ú§ÜÞà­ã°æ³é¶íºñ¾ö\úúÿÿž £ §EHLOì!T#V%X&ô([)\+^,_-`.a.È/b0c0Ê1d1Ë2e2Ì3f3Í4g4Î4Î5h5Ï5Ï5Ï6Ð6Ð6Ð6Ð777Ñ7Ñ7Ñ7Ñ7Ñ8Ò8Ò8Ò8Ò8Ò8Ò99999Ó9Ó9Ó9Ó9Ó9Ó9Ó:::::::::Ô:Ô:Ô:Ô:Ô:Ô:Ô:Ô;;;;;;;;;;;;;;;;;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö=================================================================================×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>ØÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂ[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃ\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\Ã\ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄ]Ä]Ä]Ä]Ä]Ä]Ä]Ä]Ä]ÄÄÄÄÄÄÄÄÅ^Å^Å^Å^Å^Å^Å^ÅÅÅÅÆ_Æ_Æ_Æ_Æ_Æ_ÆÆÇ`Ç`Ç`Ç`Ç`ÈaÈaÈaÈaÈûÉbÉbÉüÊcÊcÊýËdËþÌeÌÿÍfÎÎgÏÐÑÒÓÔÕÖ£Ø¥Ú ÜÞ«á®ä±èìð"õ'ú“ÿÿl qÜáJNQ î#V%X'Z(ö)÷+^,ú-û.ü/–/ý0þ1˜1ÿ333š44›55œ5œ5œ66667ž7ž7ž7ž7ž8Ÿ8Ÿ8Ÿ8Ÿ8Ÿ999 9 9 9 9 9 9 :::::::¡:¡:¡:¡:¡:¡:¡:¡;;;;;;;;;;;;;¢;¢;¢;¢;¢;¢;¢;¢;¢;¢<<<<<<<<<<<<<<<<<<<<<<<<<<<<<£<£<£<£<£<£<£<£<£<£<£<£<£=====================================================================¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥??????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃđđđđđđđđđÄÄÄÄÅ’Å’Å’Å’Å’Å’Å’ÅÅÆ“ƓƓƓƓƓǔǔǔǔǔȕȕȕÉbÉ–É–ÊcÊ—ËdËdÌeÌeÍfÍfÎgÏhÐiÑ7Ò8ÓlÔmÖ<Ø>Ú@ÜBßEâHæLêPî‡óÀùÆÿÿ rª¯³· º"¼%¿'Á)*Ä+Å-”.•/–0—0—1˜2™2™3š4g4›4›5h5œ6i6i6i67j7j7j7ž8k8k8k8k8k9l9l9l9l9l9l:::::m:m:m:m:m:m:m:m;;;;;;;;;n;n;n;n;n;n;n;n;n;n<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÆÆÇÇÇÇÇÇÇÇÈÈÈÈÈÈÉÉÉÉÉÉÊÊÊÊËËËËÌÌÍÍÎÎÎÎÏÏÑÑÒÒÓÓÕÕ××ÙÙÜÜßßããççììòòøøÿÿ  ##%%((**++--..//0011222233444455556666667777777788888888999999999999::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<==================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÅÅÅÅÅÅÅÅÅÅÅÆÆÆÆÆÆÆÆÇÇÇÇÇÇÈÈÈÈÈÈÉÉÉÉÊÊËËËËÌÌÍÍÎÎÏÏÐÐÒÒÔÔÖÖØØÛÛßßããééïï÷÷ÿÿ ##&&))++--..//112222334455556666667777778888889999999999::::::::::::::;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<====================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿ò¿òÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÀóÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÁôÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÂõÃÃÃÃÃöÃöÃöÃöÃöÃöÃöÃöÄ*Ä÷Ä÷Ä÷Ä÷Ä÷Å+ÅøÅøÅøÆ,Æ,ÆùÇ-Ç-ÇúÈ.È.É/ÉbÊ0Ë1ËdÌeÍfÎgϜўӠբإÛÛà­æ³í†ö\ÿÿ £EL#V'')\,_.a/–0—1˜2Ì3Í4›5œ5Ï6Ð6Ð77Ñ7Ñ8Ò8Ò8Ò99Ó9Ó:::::Ô;;;;;;;< < < < < < < < < < <<<<<<= = = = = = = = = = = = = = ==========================> > > > > > > > > > > > > > > > > > > > > >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&À&ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁ'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'Á'ÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂ(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(Â(ÂÂÂÂÃ)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)Ã)ÃÄ*Ä*Ä*Ä*Ä*đđđÅ+Å+Å’Å’Æ,ƓƓƓǔǔȕÈûÉ–ÉüÊýËþÌÿÎgÐiÑÑÔÔ××ÜBâHééô&ÿÿ Ù·##'Á++--/–0þ2™3š4›566i7j7j8k8k8k9l9l9Ó9Ó:m:m:Ô:Ô:Ô:Ô;n;n;Õ;Õ;Õ;Õ;Õ;Õ;Õ;Õ<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö<Ö===============×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×=×>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø>Ø????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀYÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁZÁÁÁÁÁÁÁÁÁÁÁÁÂ[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[Â[ÂõÂõÃ\Ã\Ã\Ã\Ã\Ã\ÃöÃöÃöÄ]Ä÷Ä÷Ä÷Ä÷Å’ÅøÆ“Æ“Ç”Ç”È•É/Ê0Ë1ÌÌÍÍÐiÓ× Ü©ä±ð‰ÿÿvN#V([,“//1133445h6i7j7j8k8k99l:::;;;;;¢;¢< < < < <£<£<£<£<£<£<£<£<£<£=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤=¤>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥>¥????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÁŽÂ[Â[Â[Â[Â[ÂÂÂÂÃ\Ã\Ã\Ã\Ã\Ã\Ä*Ä]Ä]Å+Å+ÅøÆ,ÆùÇÇÈÈÉɢÍfÑÕ¢ÝCêPÿÿ¯"¼**.ü1˜3š5h78899Ó9Ó:Ô:Ô:Ô;¢;¢;¢;¢<£<£<£<£<£<£=p=p=p=p=p=p=p=p=p=p=p=p=p=p>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q>q??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÁÂÂÂÂÂÂÂÂÂÂÃÃÃÃÃÃÄÄÅÅÆÆÇÇÉÉÌÌÒÒßßÿÿ --22667799::::;;;;<<<<<<==============>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾ñ¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$½V½V½V½V½V¼‰¼‰¼‰»»ºíº ¹R··µN°}¦ Y&NµJ±GzF­EDDDDCvCvB©B©B©AÛAÛAÛAÛAÛAÛAÛAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¿%¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$½Š½Š½Š½Š½Š½Š½#½#½#¼‰¼‰¼‰¼‰¼‰¼‰»î»ˆ»ˆºíºíºS¹ì¹R¸¸··¶¶µ³€°I¬¤×—Êg4Z'SSNµLJ}HâG®F­F­E¬EEDDDCvCvCvCvBuBuBuBuBuBuBuAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛ@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¿X¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W½½½½½½½½½½½V½V½V½V½V½V½V½V½V½V½V½V½V½V¼ï¼ï¼U¼U¼U¼U¼U¼U»î»î»î»T»Tºíºíºíº‡¹ì¹†¹†¸…¸…·„·¶µ³³²²°I­à©Ü¤qœi)nÕb•ZUˆQ¸OOMMKKJJI|H{GzGzFyFyFExEEEDDDDCªCªCªCCCB©B©B©B©B©B©B©B©B©B©A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¿Œ¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š¼¼¼¼¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼U»ˆ»ˆ»ˆ»ˆ»ˆ»T»T»Tº‡º‡ºSºSºS¹R¹R¹R¸Q¸Q·P·¶O¶µ´³±ä¯â®®««¨¨¤qžk––‹òs hh`“[[V‰SSQQOMçLKJIãHâH¯G®G®F­F­F­E¬E¬ExExD«D«DwDwDwDwCªCªCvCvCvCvCvCvCvCvBuBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»ºººººººººººº¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸······¶¶¶¶µµ´´´´³³²²±±°°®®¬¬ªª§§¤¤ŸŸ™™’’‰‰uullee__[[WWUURRPPOONNMMLLKKJJIIIIHHHHHHGGGGFFFFFFFFEEEEEEEEEEEEDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸······¶¶¶¶¶¶µµµµ´´³³³³²²±±°°¯¯®®¬¬ªª¨¨¦¦££ŸŸ››••‡‡wwppiidd__\\YYVVTTRRQQPPNNMMMMLLKKKKJJIIIIIIHHHHHHGGGGGGFFFFFFFFFFEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»î»î»î»î»î»î»î»î»î»î»î»î»»»»»»»»ºíºíºíºíºíºíºíºíºíºº¹ì¹ì¹ì¹ì¹ì¹ì¹¸ë¸ë¸ë¸ë¸¸·ê·ê··¶é¶¶µèµµ´´³²±J±°®G­F¬EªC¨A¦?£p mœi˜e“,&†¹yr kÒfÍb•^Å[ÂYŒV½T»SºQëP·O¶NèMçLæLæKåJäJäJIãIIHâHHGáGáGGGFàFFFFFFEEEEEEEEDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½#½#½#½#½#½#½#½#½#½#½#½#½#½#½#½#¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼"¼"¼"¼"¼"¼"¼"¼"¼"¼"¼"»»»»»»»»»»»»»»»!»!»!»!»!»!»!»!»!ººººº º º º º º º º ¹¹¹¹¹¹¸¸¸¸¸···¶ƒ¶¶µ‚µ´´´³€²²±~°}¯|®á­à¬y«x©Ü§Ú¥Ø£Ö ÓЙ̕ȑ^‹ò…ìyys¦n;i6e2a.^+\)Y&W$V#T!S Q„PƒO‚NNM€LLK~JäJ}J}IãI|HâHâHâH{GáGáGáGzFàFàFàFàFàEßEßEßEßEßEßEßEEDÞDÞDÞDÞDÞDÞDÞDÞDDDDDDDDCÝCÝCÝCÝCÝCÝCÝCÝCÝCÝCCCCCCCCCCCCCCCCCCCCCCBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@Ú@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½V½V½V½V½V½V½V½V½V½V½V½V½V½V½V½V¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼U¼U¼U¼U¼U¼U¼U¼U¼U¼U¼U¼U»»»»»»»»»»»»»»»»»»»T»T»T»T»T»T»T»T»TºººººººººSºSºSºSºSºSºS¹¹¹¹¹R¹R¹R¹R¹R¹R¸¸¸Q¸Q¸Q¸Q¸Q·P·P·P·P¶é¶O¶OµèµNµN´ç´M³æ³L²å²å±ä±J°ã¯â®á­à¬ß«ÞªÝ©v§t¥Ø£Ö¡nžk›h—Ê“ÆÂн…yàtÛp=k8gšc–`“^+[ŽYŒX%V#U"S†RQPO¶ONM´MLLK²KJ±JI°I°I°H¯H¯H¯H¯G®G®G®G®G®F­F­F­F­F­FFFFE¬E¬E¬E¬E¬E¬EEEEEED«D«D«D«D«D«D«D«DDDDDDDDDDDDCªCªCªCªCªCªCªCªCªCªCªCCCCCCCCCCCCCCCCCCCCCCCCCCB©B©B©B©B©B©B©B©B©B©B©B©B©B©BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBA¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@§@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰»»»»»»»»»»»»»»»»»»»»»»»»»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆººººººººººººº‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¸¸¸…¸…¸…¸…¸…¸…·„·„·„·„·„¶ƒ¶ƒ¶ƒ¶ƒµ‚µ‚µ‚´´´M³€³L²²K±~±J°}°I¯H®G­F¬E«DªC©§@¥>¤ ¢Ÿ8œ5™2–/’+Ž'‰V„„z­u¨q¤lÓiÐeÌbÉ_ù]Ä[ÂYÀWñV½U¼SºR¹Q¸P·PƒO¶NµNM´M€L³LK²K~J±J}J}I°I|I|I|H{H{H{H{HHGzGzGzGzGzFyFyFyFyFyFyFFFFExExExExExExExEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s@s¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´³³³³²²²²±±±±°°°°¯¯¯¯®®­­¬¬««ªª©©¨¨¦¦¥¥££¡¡ŸŸšš——””ŒŒˆˆ„„{{vvrrnnkkggddbb__]][[ZZXXWWUUTTSSRRQQQQPPOOOONNNNMMMMLLLLKKKKKKJJJJJJIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµ´´´´´´³³³³³³²²²²²²±±±±°°°°¯¯®®®®­­¬¬««ªª©©¨¨§§¦¦¤¤££¡¡ŸŸ››˜˜••’’‹‹‡‡ƒƒ{{wwsspplliiffddbb__]]\\ZZYYWWVVUUTTSSRRRRQQPPPPOONNNNMMMMMMLLLLKKKKKKKKJJJJJJIIIIIIIIIIHHHHHHHHHHHHGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½ð½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»î»î»î»î»î»î»î»î»î»î»î»î»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºíºíºíºíºíºíºíºíºíºººººººººººººººººº¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸·ê·ê·ê·ê·ê····¶é¶é¶é¶é¶¶µèµèµèµèµè´ç´ç´ç´ç³æ³æ³æ²å²å²å±ä±ä±°ã°¯â¯®á­à­¬«ÞªÝ©Ü¨Û§Ú¦ ¥ £Ö¢ žœš–ý”Ç‘Äô퇇ƒƒ|IxEtAq>nkheÿc0`ú^ø]÷[õZôXòWñVðUïTîSíS RQëQPOéONèNNMMLæLLLKKKJJJJIIIIIIHHHHHHHHHGGGGGGGGGGFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$¾$½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½#½#½#½#½#½#½#½#½#½#½#½#½#½#½#½#½#¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼"¼"¼"¼"¼"¼"¼"¼"¼"¼"¼"»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»!»!»!»!»!»!»!»!»!ººººººººººººººººººººººº º º º º º º ¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸·········¶¶¶¶¶¶µµµµµµ´´´´³³³²²±~±±°°¯®{®­z¬y¬«ªw©v¨u§t¦s¥ £p¢o mžkœišg˜e•ü“`]Œó‰ð†íƒƒ||yur oliœgd—b•`“^ø][õZYŒWñVðUïTîT‡S†RìQëQ„PêOéOéNèNèMçMçMçLæLæLæKåKåKåJäJäJäJäIãIãIãIãIIHâHâHâHâHâHHHHGáGáGáGáGáGGGGGGFàFàFàFàFàFFFFFFFFFFEßEßEßEßEßEßEßEEEEEEEEEEEEEEEEDÞDÞDÞDÞDÞDÞDÞDÞDÞDDDDDDDDDDDDDDDDDDDDDDDDDDDDCÝCÝCÝCÝCÝCÝCÝCÝCÝCÝCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBÜBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAÛAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W¾W½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½V½V½V½V½V½V½V½V½V½V½V½V½V½V½V½V¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼U¼U¼U¼U¼U¼U¼U¼U¼U¼U¼U¼U»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»T»T»T»T»T»T»T»T»TºººººººººººººººººººººººººSºSºSºSºSºSºSºS¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹R¹R¹R¹R¹R¹R¸¸¸¸¸¸¸¸¸¸¸Q¸Q¸Q¸Q¸Q·······P·P·P·P·P¶¶¶¶¶O¶O¶O¶OµµµNµNµNµN´´´M´M´M³³³L³L³L²K²K²K±J±J°I°I¯â¯H®á®G­F­F¬E«DªÝªC©B¨A§@¦?¤×£Ö¢; ÓŸ86›4™2—0”Ç’+(Œ%‰"†ƒ||yyuÜrÙoÖm:jÑh5f3d1b/`-^Å]*[ÂZÁY&X%W$V#U¼T»SºS R¹Q¸QP·PO¶ONµNµM´M´M´L³L³L³K²K²K²KKJ±J±J±JJI°I°I°I°IIIIH¯H¯H¯H¯HHHHHHG®G®G®G®G®GGGGGGF­F­F­F­F­F­FFFFFFFFFFFFE¬E¬E¬E¬E¬E¬E¬EEEEEEEEEEEEEEEEEEEED«D«D«D«D«D«D«D«DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCªCªCªCªCªCªCªCªCªCªCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCB©B©B©B©B©B©B©B©B©B©B©B©B©BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBA¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹¾‹½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆººººººººººººººººººººººººººººº‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…·········„·„·„·„¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµ‚µ‚µ‚´´´´´´³³³€³€³€²²²±~±~±~°}°}¯|¯|®{®{­z­z¬y¬E«xªw©v©B¨A§@¦?¥>£p¢o¡:Ÿlž7œ5š3˜1–/“`‘^Ž[‹‹ˆˆ……‚‚||yyv©s¦p£n¡kžiœgše˜c–a”_Æ^‘\Ã[ÂZYŒX‹WŠV‰U¼T»T‡S†R¹R…Q„Q„PƒPƒO‚O‚NNNM€M€MMLLLK~K~K~KKJ}J}J}J}JJI|I|I|I|IIIIH{H{H{H{H{HHHHHHGzGzGzGzGzGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBuBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´³³³³³³³³³³²²²²²²±±±±±±°°°°°°¯¯¯¯®®®®­­­­¬¬¬¬««ªªªª©©¨¨§§¦¦¥¥¤¤££¢¢¡¡ŸŸžžœœšš˜˜––””’’‹‹ˆˆ……‚‚||yywwttqqoolljjhhffddbbaa__^^]][[ZZYYXXWWWWVVUUTTTTSSRRRRQQQQPPPPOOOOOONNNNMMMMMMMMLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµ´´´´´´´´´´³³³³³³³³²²²²²²²²±±±±±±°°°°°°¯¯¯¯¯¯®®®®­­­­¬¬¬¬««ªªªª©©¨¨¨¨§§¦¦¥¥¤¤££¢¢  ŸŸžžœœ››™™——••““‘‘ŒŒŠŠ‡‡……‚‚}}zzwwuurrppnnkkiiggffddbbaa__^^]]\\[[ZZYYXXWWVVUUUUTTSSSSRRRRQQQQPPPPPPOOOONNNNNNMMMMMMMMLLLLLLLLKKKKKKKKKKJJJJJJJJJJIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»î»î»î»î»î»î»î»î»î»î»î»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºíºíºíºíºíºíºíºíºíºººººººººººººººººººººººººººººººººº¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê··········¶é¶é¶é¶é¶é¶¶¶¶¶¶µèµèµèµèµèµµµµ´ç´ç´ç´ç´´´´³æ³æ³æ³³²å²å²å²²±ä±ä±ä±±°ã°ã°°¯â¯â®á®á®á­à­à¬ß¬ß«Þ«ÞªÝ©Ü©Ü¨Û§Ú§ ¦ ¥Ø¤×£Ö¢Õ¡ŸÒžÑ›Îš˜Ë–ɔǒÅÃŽÁŒŒ‰¼‡‡„·‚‚}}zzxEuus@q>o¤=£<¢;¡: 9Ÿ8ž7œÏ›4š3˜1—0•.“,‘*(&‹$‰"††„„‚‚}}{{xßvvttqØoÖmÔkÒjÑhÏfÍeÌcÊbÉaÈ`-^Å]Ä\Ã[ÂZÁYÀYÀX¿W¾V½V½U¼T»T»SºSºR¹R¹Q¸Q¸QQP·P·O¶O¶O¶NµNµNµNNM´M´M´MML³L³L³LLK²K²K²K²KKKKJ±J±J±JJJJJJI°I°I°I°IIIIIIIIH¯H¯H¯H¯HHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGF­F­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFE¬E¬E¬E¬E¬E¬EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEED«D«D«D«D«D«D«D«DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCªCªCªCªCªCªCªCªCªCªCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCB©B©B©B©B©B©B©B©B©B©B©B©B©B©BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBA¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨A¨½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š½Š¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆººººººººººººººººººººººººººººººººººººººººººººº‡º‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…···············„·„·„·„·„¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´³³³³³€³€³€²²²²²²±±±±±~±~°°°}°}°}¯|¯|¯|®{®{­­­z­z¬y¬y«x«xªwªw©v¨u¨u§t¦s¦s¥r¤q£p¢o¡n mŸlžkj›hšg™f—d–c”a’_]ŽŽŒŒŠŠˆˆ††„„‚O}}{{yyv©t§r¥p£n¡lŸkžiœgšf™d—c–b•a”_’^‘]\[ŽZÁZYŒX‹WŠWŠV‰UˆUˆT‡T‡S†S†R…R…Q„Q„Q„PƒPƒPPO‚O‚OONNNNNM€M€MMMMLLLLLLLK~K~K~KKKKJ}J}J}J}JJJJJJI|I|I|I|IIIIIIIIH{H{H{H{H{HHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²±±±±±±±±°°°°°°¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬««««ªªªª©©©©¨¨§§§§¦¦¥¥¥¥¤¤££¢¢¡¡  ŸŸžžœœšš™™˜˜––••““‘‘ŽŽŒŒŠŠˆˆ††„„}}{{yywwuussqqoommlljjhhggeeddccbb``__^^]]\\\\[[ZZYYXXXXWWVVVVUUUUTTTTSSSSRRRRRRQQQQPPPPPPOOOOOOOONNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´³³³³³³³³³³³³²²²²²²²²²²±±±±±±±±°°°°°°°°¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬««««ªªªª©©©©¨¨¨¨§§§§¦¦¥¥¤¤¤¤££¢¢¡¡  ŸŸžžœœ››™™˜˜——••””’’‹‹‰‰‡‡……ƒƒ}}{{yywwuussrrppnnllkkiihhffeeddccbb``__^^]]]]\\[[ZZYYYYXXWWWWVVVVUUUUTTTTSSSSRRRRRRQQQQPPPPPPPPOOOOOONNNNNNNNMMMMMMMMMMLLLLLLLLLLKKKKKKKKKKKKKKJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½½¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»î»î»î»î»î»î»î»î»î»î»î»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºíºíºíºíºíºíºíºíºíºººººººººººººººººººººººººººººººººººººººººººººººººººº¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê·ê················¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµèµµµµµµµµ´ç´ç´ç´ç´´´´´´³æ³æ³æ³æ³³³³³³²å²å²å²²²²±ä±ä±ä±±°ã°ã°ã°°¯â¯â¯â¯¯®á®á®®­à­à­­¬ß¬ß«Þ«Þ««ªÝªª©Ü©©¨Û§Ú§Ú¦Ù¦Ù¥Ø¤×£Ö£Ö¢Õ¡Ô ÓŸÒžÑМϛΚ͘˗ʖɔǓƑĎÁŒ¿‹‹‰‰‡‡……ƒƒ}}{{yyxEvCtArrppo¥>¤=£<£<¢;¡: 9Ÿ8ž76œ5›4š3™2˜1–/•.”-’+‘*(Œ%ŠŠˆˆ‡ ……ƒƒ}}||zzxxvÝtÛssqØoÖnÕlÓkÒjjhÏgÎfÍeecÊbÉaÈ`Ç_Æ^Å^Å]Ä\Ã[Â[[ZÁYÀYYX¿W¾W¾V½V½U¼U¼T»T»SºSºSSR¹R¹RRQ¸Q¸QQP·P·PPO¶O¶O¶OONµNµNµNNNNM´M´M´MMMML³L³L³LLLLLLK²K²K²KKKKKKKKJ±J±J±JJJJJJJJJJI°I°I°I°IIIIIIIIIIIIH¯H¯H¯H¯H¯HHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGF­F­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE¬E¬E¬E¬E¬E¬E¬EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEED«D«D«D«D«D«D«D«DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCªCªCªCªCªCªCªCªCªCªCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCB©B©B©B©B©B©B©B©B©B©B©B©B©B©BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…¸…·····················„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´³³³³³³³€³€³€²²²²²²²²²±±±±±~±~±~°°°°°}°}¯¯¯¯¯|¯|®®®{®{®{­­­z­z¬y¬y«««x«xªªªw©©©v¨¨¨u§§§t¦s¦s¥r¤¤¤q£p¢¢¢o¡n mŸlžkjœi›hšg™f˜e—d••”a“`‘‘]ŽŽZ‹‹ŠWˆˆ††…Rƒƒ}°||zzx«wwuus¦rrp£oom lljiœh›gge˜d—c–b•a”`“_’__^‘]\[Ž[ŽZYŒYŒX‹XXWŠWWV‰VVUˆUUT‡T‡S†S†SSR…R…RRQ„Q„QQPƒPƒPPPPO‚O‚OOOONNNNNNNNM€M€MMMMMMLLLLLLLLLK~K~K~KKKKKKKKJ}J}J}J}JJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBuBuBuBuBuBuBuBuBuBuBuBuBuBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®­­­­­­­­¬¬¬¬««««««ªªªªªª©©©©¨¨¨¨§§§§¦¦¥¥¥¥¤¤¤¤££¢¢¢¢¡¡  ŸŸžžœœ››šš™™˜˜——––••““’’‘‘ŽŽŒŒ‹‹‰‰ˆˆ††„„ƒƒ~~||zzyywwuuttrrqqoonnllkkjjiiggffeeddccbbaa``____^^]]\\\\[[ZZZZYYXXXXWWWWVVVVUUUUUUTTTTSSSSSSRRRRRRQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬««««ªªªªªª©©©©¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤££££¢¢¡¡¡¡  ŸŸžžœœ››šš™™——––••””““‘‘ŒŒŠŠ‰‰‡‡††„„‚‚~~||zzyywwvvttssqqppnnmmllkkiihhggffeeddccbbaa``____^^]]\\\\[[ZZZZYYYYXXXXWWWWVVVVUUUUTTTTTTSSSSSSRRRRRRQQQQQQQQPPPPPPPPOOOOOOOONNNNNNNNNNMMMMMMMMMMMMLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»î»î»î»î»î»î»î»î»î»î»î»î»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºíºíºíºíºíºíºíºíºíºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê······················¶é¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´³æ³æ³æ³æ³³³³³³³³²å²å²å²²²²²²±ä±ä±ä±±±±±±°ã°ã°ã°°°°¯â¯â¯¯¯¯®á®á®®®®­à­à­­¬ß¬ß¬ß¬¬«Þ«ÞªÝªÝªª©Ü©Ü©©¨Û¨¨§Ú§§¦Ù¦¦¥Ø¥¥¤×£Ö££¢Õ¡Ô¡¡ ÓŸÒžÑžžœÏ›ÎšÍ™Ì˜Ë——––”Ǔƒő‘ÂŽÁ‹¾ŠŠˆ»‡‡…¸„„‚‚~~||{HyyxEvvuBssr?ppo¤¤¤=£<£<¢;¡¡¡: 9Ÿ8žžž76œ5›4š3™2˜1—0–/•.”-“,‘‘(Ž'ŒŒ‹‹Š#ˆˆ‡ ……„„‚‚~~||{{yyxxvÝuuttrÙqqppnÕmÔllkkjjhÏgÎfÍeÌdËddccbbaÈ`Ç_Æ__^Å]Ä]]\Ã[Â[[ZÁZZYÀYYX¿XXW¾WWV½V½U¼U¼UUT»T»TTSºSºSSR¹R¹RRQ¸Q¸QQQQP·P·PPPPO¶O¶O¶OOOONµNµNµNNNNNNM´M´MMMMMMMML³L³L³LLLLLLLLK²K²K²KKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIH¯H¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE¬E¬E¬E¬E¬E¬E¬EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEED«D«D«D«D«D«D«D«DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCªCªCªCªCªCªCªCªCªCªCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰¼‰»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…¸…···························„·„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´³³³³³³³³³³³€³€³€³€²²²²²²²²²²±±±±±±±±±~±~±~°°°°°}°}°}¯¯¯¯¯|¯|®®®®®{®{®{­­­z­z¬¬¬¬¬y¬y«««x«xªªªwªw©©©v¨¨¨u¨u§t§t¦¦¦s¥¥¥r¤q¤q£p¢¢¢o¡n¡n mŸlžžžkjœi›hšš™™˜˜——––••””““’’‘^]ŽŽŒY‹X‰‰ˆˆ††……„Q‚‚~~||{{y¬xxwwu¨ttssq¤p£oonnlŸkžjiœh›gšf™e˜d—c–b•bba”`“_’__^‘]]]\[Ž[ŽZZZYŒYYX‹XXWŠWŠWWV‰VVUˆUˆUUT‡TTTTS†S†SSR…R…RRQ„Q„Q„QQPƒPƒPƒPPPPO‚O‚OOOOOONNNNNNNNM€M€M€MMMMMMMMLLLLLLLLLLLK~K~K~K~KKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨§§§§¦¦¦¦¦¦¥¥¥¥¤¤££££¢¢¢¢¡¡    ŸŸžžžžœœ››šššš™™˜˜——––••””““’’ŽŽ‹‹ŠŠ‰‰ˆˆ††……ƒƒ‚‚~~||{{zzxxwwvvttssrrqqoonnmmllkkjjiihhggffeeddccbbbbaa``____^^]]]]\\\\[[ZZZZYYYYXXXXXXWWWWVVVVVVUUUUTTTTTTTTSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼¼»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««ªªªªªª©©©©©©¨¨¨¨¨¨§§§§¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡    ŸŸžžžžœœ››››šš™™˜˜——––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰‡‡††……ƒƒ‚‚~~}}{{zzyywwvvuussrrqqppoonnllkkjjiihhggffffeeddccbbbbaa``____^^]]]]\\\\[[[[ZZZZYYYYXXXXWWWWWWVVVVUUUUUUTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼¼¼¼»î»î»î»î»î»î»î»î»î»î»î»î»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºíºíºíºíºíºíºíºíºíºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê······························¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´³æ³æ³æ³æ³³³³³³³³³³²å²å²å²²²²²²²²²²±ä±ä±ä±±±±±±°ã°ã°ã°°°°°°¯â¯â¯â¯¯¯¯¯¯®á®á®®®®­à­à­­­­¬ß¬ß¬¬¬¬«Þ«Þ««ªÝªÝªªªª©Ü©©¨Û¨Û¨¨§Ú§Ú§§¦Ù¦¦¥Ø¥¥¤×¤¤£Ö££¢Õ¢¢¡Ô Ó  ŸÒžÑžžÐœÏœœ››šÍ™Ì˜Ë—Ê——––••””““’’ÃÂŽÁÀŒŒ‹‹ŠŠˆ»‡‡††„·ƒƒ‚‚~~}}{{zzyywwvvuutAs@qqppoonnm:l9k8j7i6h5g4ffeee2d1c0bbaaa.`-___,^+^+]*\\\)[[[(ZZZ'YYY&XXX%X%WWW$VVV#V#UUU"U"TTT!T!SSSSS S RRRRRRQQQQQQPPPPPPPPOOOOOOOOONNNNNNNNNMMMMMMMMMMMLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC¼"¼"»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»!»!»!»!»!»!»!»!»!ººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº º º º º º º ¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³²²²²²²²²²²²²²±±±±±±±±±±°°°°°°°°°°¯¯¯¯¯¯¯¯¯®®®®®®­­­­­­¬¬¬¬¬¬«««««ªªªªªª©©©¨¨¨¨¨§§§ § ¦ ¦ ¥¥¥ ¤¤¤ £££ ¢¢¢¡   Ÿžžžœœœ›š™™˜˜——–ý•ü”û“ú’ù‘øŽŽŒŒŠñ‰ðˆˆ‡‡††„„ƒƒ‚‚~~}}{{zzyyxwuuttssr q p ommllkkjjjihgeÿeeddcýbüaûaa`ú_ù__^ø^^]÷\ö\\[õ[[ZôZZYóYYXòXòXXWñWWVðVðVVUïUïUUTîTîTTSíSíSSSSRìRìRRRRQëQëQQQQPêPêPPPPPPOéOéOOOOOOOONèNèNNNNNNNNMçMçMçMMMMMMMMLæLæLæLLLLLLLLLLKåKåKåKåKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJIãIãIãIãIIIIIIIIIIIIIIIIIIIIHâHâHâHâHâHHHHHHHHHHHHHHHHHHHHHHHHHHGáGáGáGáGáGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFàFàFàFàFàFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEßEßEßEßEßEßEßEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDÞDÞDÞDÞDÞDÞDÞDÞDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCÝCÝCÝCÝCÝCÝCÝCÝCÝCÝCÝCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»T»T»T»T»T»T»T»T»TºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººSºSºSºSºSºSºS¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹R¹R¹R¹R¹R¹R¹R¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸Q¸Q¸Q¸Q¸Q·································P·P·P·P·P¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶O¶O¶O¶OµµµµµµµµµµµµµµµµµµµµµNµNµNµN´´´´´´´´´´´´´´´M´M´M´M³³³³³³³³³³³³³L³L³L²²²²²²²²²²²K²K²K±±±±±±±±±J±J±J°°°°°°°I°I°I¯¯¯¯¯¯¯H¯H®®®®®®®G®G­­­­­F­F¬¬¬¬¬E¬E«««««D«DªªªCªC©©©B©B¨¨¨A¨A§§§@¦¦¦?¥¥¥>¥>¤¤¤=£££<¢;¢;¡:   9ŸŸžžž7œœœ5›4šš™™™2˜1—0–/•.””““’’‘‘(Ž'&Œ%ŠŠ‰‰ˆˆ‡‡†„„ƒƒ‚‚~~}}{âzzyyxxwwuÜttssrrqqppoomÔlÓkÒjÑjjiihhggfÍeÌdËddccbÉaÈaa`Ç_Æ__^Å^^]Ä\Ã\Ã[Â[ÂZÁZÁYÀYÀYYX¿XXW¾W¾WWV½VVVVU¼UUUUT»TTTTSºSºSSSSR¹R¹RRRRQ¸Q¸QQQQP·P·P·PPPPO¶O¶O¶OOOOOONµNµNµNNNNNNNNM´M´MMMMMMMMMML³L³L³LLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF­F­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE¬E¬E¬E¬E¬E¬EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEED«D«D«D«D«D«D«D«DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCªCªCªCªCªCªCªCªCªCªCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº‡º‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…···································„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²±±±±±±±±±~±~±~°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯|¯|®®®®®®®{®{­­­­­z­z¬¬¬¬¬y¬y«««««x«xªªªwªw©©©©©v¨¨¨¨¨u§§§t§t¦¦¦s¥¥¥r¤¤¤q¤q£p£p¢o¡¡¡n   mŸŸŸlžkjœi›hšššg™f˜e——––••”””a“`’_‘^]ŽŽŒŒ‹‹ŠŠ‰‰ˆU‡T……„„ƒƒ‚‚€€~~}}||z­yyxxwwvvt§s¦r¥qqppoonnmmllkkjiœh›gšggffe˜d—c–ccb•a”aa`“_’__^‘^^]]]\\\[Ž[[ZZZYŒYYX‹X‹XXWŠWWV‰V‰VVUˆUˆUUT‡T‡TTTTS†S†SSSSR…R…RRRRQ„Q„QQQQQQPƒPƒPPPPPPO‚O‚OOOOOOOONNNNNNNNNNM€M€M€MMMMMMMMMMLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCvCvCvCvCvCvCvCvCvCvCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§¦¦¦¦¦¦¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžœœ››šššš™™˜˜————––••””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰‡‡††……„„ƒƒ‚‚€€~~}}||{{yyxxwwvvuuttssqqppoonnmmllkkkkjjiihhggffffeeddccccbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYYYXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··············································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¥¥¥¥¥¥¤¤¤¤££££¢¢¢¢¡¡¡¡    ŸŸŸŸžžœœ››››šš™™˜˜˜˜——––••””““““’’‘‘ŽŽŒŒ‹‹‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{yyxxwwvvuuttssrrqqppoonnmmllkkjjiihhhhggffeeeeddccbbbbaaaa``____^^^^]]]]\\\\[[[[ZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUTTTTTTTTSSSSSSSSRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCC»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»ºíºíºíºíºíºíºíºíºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê····································¶é¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´´´´´´´´´´´´´´´´´´³æ³æ³æ³³³³³³³³³³³³³³²å²å²å²å²²²²²²²²²²±ä±ä±ä±±±±±±±±±±°ã°ã°ã°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯®á®á®®®®®®­à­à­­­­­­¬ß¬ß¬¬¬¬«Þ«Þ««««ªÝªÝªªªª©Ü©Ü©©¨Û¨Û¨¨§Ú§Ú§§¦Ù¦Ù¦¦¥Ø¥¥¤×¤×¤¤£Ö££¢Õ¢¢¡Ô¡¡ Ó  ŸÒŸŸžÑМϛΛ›šÍ™Ì™™˜˜—Ê–É––••””““’Å‘ÄÃÂŽÁÀŒ¿‹¾Š½‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zGyFwwvvuuttssrrqqppoonnmmlll9k8j7i6hhggg4f3eeddd1c0bbb/aaa.`-___,^^^+]]]*\\\)[[[(ZZZ'Z'YYY&XXXXX%WWWWW$VVVVV#UUUUU"U"TTTTT!T!SSSSS S RRRRRRQQQQQQQQPPPPPPPPPPOOOOOOOOOONNNNNNNNNNNNNMMMMMMMMMMMMMLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»!»!»!»!»!»!»!»!»!ººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº º º º º º º º ¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··········································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯®®®®®®®®­­­­­­­­¬¬¬¬¬¬¬«««««««ªªªªªª©©©©©¨¨¨¨¨§§§§§ ¦¦¦¦¦ ¥¥¥ ¤¤¤¤¤ £££ ¢¢¢¡¡¡   ŸŸŸžœœ›››šš™™˜ÿ—þ——––•ü”û“ú““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒè€€~~}}||{{zzyyxwvut s r q p onmllkkjjiiihggffeÿdþddcýbübbaûaa`ú_ù__^ø^^]÷]]\ö\\[õ[[ZôZôZZYóYYXòXòXXWñWñWWVðVðVVUïUïUUUUTîTîTTTTSíSSSSSSRìRìRRRRQëQëQQQQQQPêPêPêPPPPPPOéOéOOOOOOOONèNèNèNNNNNNNNNNMçMçMçMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLKåKåKåKKKKKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJJJJJIãIãIãIãIIIIIIIIIIIIIIIIIIIIIIIIHâHâHâHâHâHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGáGáGáGáGáGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFàFàFàFàFàFàFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEßEßEßEßEßEßEßEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDÞDÞDÞDÞDÞDÞDÞDÞDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»T»T»T»T»T»T»T»T»TºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººSºSºSºSºSºSºS¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹R¹R¹R¹R¹R¹R¹R¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸Q¸Q¸Q¸Q¸Q¸Q·······································P·P·P·P·P¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶O¶O¶O¶OµµµµµµµµµµµµµµµµµµµµµµµµµNµNµNµN´´´´´´´´´´´´´´´´´´´M´M´M³³³³³³³³³³³³³³³³³L³L³L²²²²²²²²²²²²²²²K²K±±±±±±±±±±±±±J±J°°°°°°°°°°°I°I¯¯¯¯¯¯¯¯¯H¯H¯H®®®®®®®G®G­­­­­­­F­F¬¬¬¬¬¬¬E¬E«««««D«DªªªªªC©©©©©B©B¨¨¨A¨A§§§@§@¦¦¦?¦?¥¥¥>¤¤¤=¤=£££<¢¢¢;¡¡¡:   9ŸŸŸ8ž76œœœ5›4ššš3™2˜˜———0–/••””“““,’+‘*)ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒ€€~~}}||{{zzyyxxwwvvuuttssrrqqppoonnmmllkÒjÑiÐiihhgÎfÍffeedËddccbÉbbaÈaa``_Æ__^Å^^]Ä]]\Ã\\[Â[Â[[ZÁZZYÀYYYYX¿XXXXW¾WWWWV½VVVVU¼U¼UUUUT»TTTTSºSºSSSSSSR¹R¹RRRRQ¸Q¸Q¸QQQQQQP·P·PPPPPPO¶O¶O¶OOOOOOOONµNµNµNNNNNNNNNNM´M´MMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF­F­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE¬E¬E¬E¬E¬E¬E¬EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEED«D«D«D«D«D«D«D«DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆ»ˆººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…·········································„·„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²±±±±±±±±±±±~±~±~°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®{®{­­­­­­­z­z¬¬¬¬¬¬¬y¬y«««««x«xªªªªªwªw©©©©©v¨¨¨¨¨u§§§§§t¦¦¦¦¦s¥¥¥¥¥r¤¤¤q£££p£p¢¢¢o¡¡¡n   mŸŸŸlžžjœœœi››šššg™™˜˜˜e—d––•••b”a“`’’‘‘\Ž[ZŒY‹XŠW‰VˆU‡T†S…R„Q‚‚€€~~}}||{{zzyyxxwwvvuuttssrrqqppoon¡m lŸllkkjjiœh›hhggf™e˜eed—c–ccb•bba”`“``_’__^‘^^]]]\\\\\[Ž[[ZZZYŒYŒYYX‹X‹XXWŠWŠWWV‰V‰VVVVUˆUUUUT‡T‡TTTTS†S†SSSSSSR…R…RRRRRRQ„Q„QQQQQQPƒPƒPPPPPPPPO‚O‚OOOOOOOOOONNNNNNNNNNNNM€M€M€MMMMMMMMMMMMLLLLLLLLLLLLLLLLLK~K~K~K~KKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»»»»»»»»»»»»»»»»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤££££¢¢¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœœ››››šš™™™™˜˜——––––••””““’’’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuttssrrqqppoooonnmmllkkjjjjiihhggggffeeeeddccccbbbbaa````____^^^^]]]]\\\\\\[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD»»»»ºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨§§§§§§§§¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££¢¢¢¢¢¢¡¡¡¡    ŸŸŸŸžžžžœœœœ››››šš™™™™˜˜————––••””””““’’‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvuuttssssrrqqppoonnmmllllkkjjiiiihhggffffeeddddccccbbbbaa````____^^^^]]]]]]\\\\[[[[ZZZZZZYYYYYYXXXXXXWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOONNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDºíºíºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê·ê··········································¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±°ã°ã°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯®á®á®á®®®®®®­à­à­à­­­­­­¬ß¬ß¬¬¬¬¬¬«Þ«Þ««««ªÝªÝªªªªªª©Ü©©©©¨Û¨Û¨¨¨¨§Ú§§§§¦Ù¦Ù¦¦¥Ø¥¥¥¥¤×¤¤¤¤£Ö££¢Õ¢¢¡Ô¡Ô¡¡ Ó  ŸÒŸŸžÑžžÐœÏœœ›Î››šÍšš™™˜Ë˜˜——–ɕȕ•””“Æ’Å’’‘‘ÂŽÁÀŒŒ‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyxxwwvvvCuBtAssrrqqppoonnn;m:llkkk8j7iihhh5g4fff3e2ddd1ccc0bbaaa.```-___,^^^+]]]]]*\\\)[[[([(ZZZ'Z'YYY&XXXXX%X%WWW$W$VVVVV#V#UUUUU"U"TTTTT!T!SSSSSSS S RRRRRRRRQQQQQQQQQPPPPPPPPPPOOOOOOOOOOOONNNNNNNNNNNNNNNMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº º º º º º º ¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·················································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®­­­­­­­­­­¬¬¬¬¬¬¬¬«««««««ªªªªªªªª©©©©©¨¨¨¨¨¨§§§§§ ¦¦¦¦¦ ¥¥¥¥¥ ¤¤¤ ¤ £££ ¢¢¢¡¡¡¡¡   ŸŸŸžžžœœœ›››ššš˜ÿ˜˜—þ–ý––••”û””““’’‘ø÷ŽŽŒó‹ò‹‹ŠŠ‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{zzyyyxwvvuuttssrrqqppp onnmmlllkkjjiiihhgggffeedþddcýccbbaûaa`ú``_ù__^ø^^]÷]÷]]\ö\\[õ[[[[ZôZZZZYóYYXòXòXXXXWñWWWWVðVðVVVVUïUUUUUUTîTTTTTTSíSíSSSSSSRìRìRRRRRRQëQëQQQQQQQQPêPêPPPPPPPPOéOéOOOOOOOOOONèNèNèNNNNNNNNNNNNMçMçMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLLLKåKåKåKKKKKKKKKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJJJJJJJJJIãIãIãIãIIIIIIIIIIIIIIIIIIIIIIIIIIIIHâHâHâHâHâHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGáGáGáGáGáGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFàFàFàFàFàFàFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEßEßEßEßEßEßEßEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDÞDÞDÞDÞDÞDÞDÞDÞDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººSºSºSºSºSºSºSºS¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹R¹R¹R¹R¹R¹R¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸Q¸Q¸Q¸Q¸Q·············································P·P·P·P·P¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶O¶O¶O¶OµµµµµµµµµµµµµµµµµµµµµµµµµµµµµNµNµNµN´´´´´´´´´´´´´´´´´´´´´´´M´M´M³³³³³³³³³³³³³³³³³³³L³L³L³L²²²²²²²²²²²²²²²K²K²K±±±±±±±±±±±±±J±J±J°°°°°°°°°°°I°I°I¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®G®G­­­­­­­­­F­F¬¬¬¬¬¬¬E¬E«««««««D«DªªªªªCªC©©©©©B©B¨¨¨¨¨A§§§§§@§@¦¦¦?¦?¥¥¥>¥>¤¤¤=£££££<¢¢¢;¡¡¡:¡:   9ŸŸŸ8žžž76œœ›››4ššš3™™˜˜˜1——–––/••”””-“,’’‘‘)ŽŽŒŒ‹‹‹$Š#‰‰ˆˆ‡‡††……„„ƒƒ‚‚€€~~}}||{{záyàyyxxwwvvuuttssrrqØp×ppoonnmÔlÓllkkjÑiÐiihhgÎggfÍeÌeedËddcÊbÉbbaÈaa`Ç``_Æ__^Å^^^^]Ä]]\Ã\\[Â[Â[[ZÁZZZZYÀYYYYX¿XXXXW¾W¾WWWWV½VVVVU¼U¼UUUUT»T»TTTTTTSºSºSSSSSSR¹R¹RRRRRRQ¸Q¸QQQQQQQQP·P·PPPPPPPPO¶O¶O¶OOOOOOOOOONµNµNµNNNNNNNNNNM´M´M´MMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLK²K²K²K²KKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF­F­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE¬E¬E¬E¬E¬E¬EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEED«D«D«D«D«D«D«D«DDDDDDDDDDDDDDDDDDDDDDDDººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…¸…···············································„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±~±~°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯|¯|¯|®®®®®®®®®{®{­­­­­­­­­z­z¬¬¬¬¬¬¬y¬y«««««««x«xªªªªªwªw©©©©©v©v¨¨¨¨¨u¨u§§§§§t¦¦¦¦¦s¥¥¥¥¥r¤¤¤¤¤q£££££p¢¢¢o¡¡¡n     mŸŸŸlžžžkjœœœi››šššg™™™f˜˜———d––•••b””““’’’_‘‘\Ž[ŒŒ‹‹ŠŠ‰‰‰VˆU‡‡††……„„ƒƒ‚‚€€~~}}||{®{{zzyyxxwwvvuutts¦r¥rrqqppoon¡nnmmllkžkkjjiœh›hhgšggffe˜eed—ddccb•bba”aa`“``_’__^‘^^^^]]]\\\\\[Ž[[ZZZZYŒYŒYYX‹X‹XXXXWŠWWWWV‰V‰VVVVUˆUˆUUUUT‡T‡TTTTTTS†SSSSSSSSR…R…RRRRRRQ„Q„QQQQQQQQPƒPƒPPPPPPPPPPO‚O‚OOOOOOOOOOOONNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDwDwDwDwDwDwDwDwDDDDDDDDºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šš™™™™˜˜˜˜——––––••””””““’’‘‘‘‘ŽŽŒŒ‹‹ŠŠ‰‰ˆˆ‡‡‡‡††……„„ƒƒ‚‚€€~~}}||||{{zzyyxxwwvvuuttttssrrqqppoooonnmmllllkkjjjjiihhhhggffffeeeeddccccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXWWWWWWWWVVVVVVVVUUUUUUUUTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDºººººººººººººººººººººººººººººººººººººººººººººººººººººººººººººº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸··························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££¢¢¢¢¢¢¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šššš™™˜˜˜˜——––––••••””““’’’’‘‘ŽŽŒŒ‹‹‹‹ŠŠ‰‰ˆˆ‡‡††††……„„ƒƒ‚‚€€~~}}}}||{{zzyyxxwwvvvvuuttssrrqqqqppoonnnnmmllkkkkjjiiiihhggggffffeeddddccccbbbbaaaa````____^^^^^^]]]]\\\\\\[[[[[[ZZZZZZYYYYYYXXXXXXXXWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEºººººººººººººººººººººººººººººººººººººººººººººº¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹ì¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê················································¶é¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®­à­à­­­­­­­­¬ß¬ß¬¬¬¬¬¬«Þ«Þ««««««ªÝªÝªªªªªª©Ü©Ü©©©©¨Û¨Û¨¨¨¨§Ú§Ú§§§§¦Ù¦¦¦¦¥Ø¥Ø¥¥¤×¤×¤¤£Ö£Ö££¢Õ¢¢¢¢¡Ô¡¡ Ó    ŸÒŸŸžÑžžÐœÏœœ›Î››šÍšš™Ì™™˜˜—Ê——––•È••”ǓƓ“’’‘Ä‘‘ŽÁŽŽŒŒ‹‹Š½ŠŠ‰‰ˆˆ‡‡††…¸……„„ƒƒ‚‚€€~~}}}}||{{zzyyxxwwwDvvuuttssrrr?qqppooo¤¤¤¤¤=£££££<¢¢¢¢¢;¡¡¡:     9ŸŸŸ8žžž76œœœ5›››4ššš3™™™2˜˜˜1——–––/••”””-““’’’+‘‘)ŽŽ&ŒŒ‹‹ŠŠ‰‰‰"ˆˆ‡‡††……„„„ƒƒ‚‚€€~~~~}}||{{zzyyxßxxwwvvuutÛttssrrqqp×ppoonÕmÔmmllkÒkkjjiÐiihÏhhggfÍffeÌeedËddcÊccbÉbbaÈaa`Ç``_Æ____^Å^^]Ä]]]]\Ã\\[Â[Â[[ZÁZÁZZZZYÀYYYYX¿XXXXXXW¾WWWWV½V½VVVVVVU¼UUUUUUT»T»TTTTTTSºSºSSSSSSR¹R¹RRRRRRRRQ¸Q¸Q¸QQQQQQQQP·P·PPPPPPPPPPPPO¶O¶OOOOOOOOOOOONµNµNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF­F­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE¬E¬E¬E¬E¬E¬E¬EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEººº‡º‡º‡º‡º‡º‡º‡º‡¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…·····················································„·„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®{®{­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬y«««««««««x«xªªªªªªªwªw©©©©©v©v¨¨¨¨¨¨¨u§§§§§t§t¦¦¦¦¦s¥¥¥¥¥r¥r¤¤¤¤¤q£££££p¢¢¢o¢o¡¡¡n     mŸŸŸlžžžkjœœœœ››››šššš™™™f˜˜˜e———d––•••b””“““`’’‘‘‘^\ŽŽŒŒŒY‹‹ŠŠ‰‰ˆˆˆU‡‡††……„„ƒƒƒƒ‚‚€€~~~~}}||{{zzyyyyxxwwvvu¨uuttssrrq¤qqppo¢oonnmmlŸllkkjjjiœiihhgšggf™ffe˜eeddddccccbbbba”aa`“``_’____^‘^^]]]]]\\\\\[Ž[[[[ZZZZZYŒYYYYX‹X‹XXXXWŠWWWWWWV‰V‰VVVVUˆUˆUUUUUUT‡T‡TTTTTTS†S†SSSSSSR…R…RRRRRRRRRRQ„Q„QQQQQQQQPƒPƒPƒPPPPPPPPPPO‚O‚OOOOOOOOOOOONNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExExExExExExEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEºº¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžœœœœœœ››››šššš™™˜˜˜˜————––––••””””““’’’’‘‘ŽŽŽŽŒŒ‹‹‹‹ŠŠ‰‰ˆˆ‡‡‡‡††……„„ƒƒƒƒ‚‚€€~~~~}}||{{zzyyyyxxwwvvvvuuttssrrrrqqppppoonnmmmmllkkkkjjjjiihhhhggggffffeeddddccccbbbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZYYYYYYYYXXXXXXWWWWWWWWWWVVVVVVVVUUUUUUUUUUTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¤¤¤¤¤¤££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžœœœœ››››šššš™™™™˜˜————––––••••””““““’’‘‘‘‘ŽŽŒŒ‹‹ŠŠŠŠ‰‰ˆˆ‡‡††††……„„ƒƒ‚‚‚‚€€~~~~}}||{{zzzzyyxxwwvvvvuuttssssrrqqqqppoonnnnmmllllkkkkjjiiiihhhhggffffeeeeddddccccbbbbbbaaaa````______^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê························································¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®­à­à­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬«Þ«Þ««««««ªÝªÝªªªªªª©Ü©Ü©©©©©©¨Û¨Û¨¨¨¨¨¨§Ú§§§§§§¦Ù¦¦¦¦¥Ø¥Ø¥¥¥¥¤×¤¤¤¤£Ö££££¢Õ¢¢¢¢¡Ô¡¡¡¡ Ó    ŸÒŸŸžÑžžÐœÏœœ›Î››šÍšš™Ì™™˜Ë˜˜————––•È••”Ç””““’Å’’‘‘ÃŽÁŽŽŒ¿ŒŒ‹‹ŠŠ‰¼‰‰ˆˆ‡‡††††……„„ƒƒ‚‚‚‚€€~~~~}}||{{{HzzyyxxwwwDvvuutttAssrrqqqqppooo¥>¤¤¤¤¤=£££££<¢¢¢¢¢;¡¡¡¡¡:     9ŸŸŸ8žžžžž76œœœ5›››4ššš3™™™2˜˜˜1———0–––/••””””“““,’’‘‘‘‘ŽŽŒŒ‹‹‹$ŠŠ‰‰ˆˆˆˆ‡‡††…………„„ƒƒ‚‚‚‚€€~~~~}}||{{{{zzyyxxxxwwvvuuuuttssrÙrrqqp×ppoonÕnnmmlÓllkÒkkjjiÐiihÏhhgÎggfÍffeÌeedËddcÊccbÉbbaÈaaaa`Ç``_Æ____^Å^^^^]Ä]]\Ã\Ã\\\\[Â[[[[ZÁZZZZYÀYÀYYYYX¿XXXXXXW¾WWWWWWV½V½VVVVVVU¼U¼UUUUUUT»T»TTTTTTSºSºSSSSSSSSR¹R¹RRRRRRRRRRQ¸Q¸QQQQQQQQQQP·P·P·PPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLK²K²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF­F­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE¬E¬E¬E¬E¬E¬E¬EEEEEE¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹†¹†¹†¹†¹†¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…·····························································„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®{®{­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««x«xªªªªªªªwªw©©©©©©©v©v¨¨¨¨¨¨¨u§§§§§§§t§t¦¦¦¦¦s¦s¥¥¥¥¥r¤¤¤¤¤q¤q£££££p¢¢¢¢¢o¡¡¡¡¡n     mŸŸŸlžžžžžkjœœœi››››šššš™™™™˜˜˜˜————––––•••b”””a““’’’_‘‘‘^\ŽŽZŒŒ‹‹ŠŠŠŠ‰‰ˆˆˆU‡‡††………R„„ƒƒ‚‚‚‚€€~±~~}}||{®{{zzyyx«xxwwvvu¨uutts¦ssrrq¤qqppo¢oonnm mmllllkkjjjiœiihhhhggggffffeeeeddddc–ccb•bba”aaaa`“``_’____^‘^^^^]]]]]\\\\\[Ž[[[[ZZZZZZZYŒYYYYX‹X‹XXXXWŠWŠWWWWWWV‰VVVVVVVVUˆUUUUUUUUT‡TTTTTTTTS†S†SSSSSSSSR…R…RRRRRRRRRRQ„Q„QQQQQQQQQQQQPƒPƒPPPPPPPPPPPPO‚O‚OOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFExExEx¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸······································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸžžžžžžœœœœ››››››šššš™™™™˜˜˜˜————––––••””””““““’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰ˆˆ‡‡‡‡††……„„„„ƒƒ‚‚‚‚€€~~}}||||{{zzyyyyxxwwvvvvuuttttssrrrrqqppppoonnnnmmllllkkkkjjjjiihhhhggggffffeeeeddddddccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸········································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡    ŸŸŸŸŸŸžžžžžžœœœœœœ››››šššš™™™™˜˜˜˜————––––••••””””““’’’’‘‘‘‘ŽŽŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆ‡‡‡‡††……„„„„ƒƒ‚‚€€~~}}||||{{zzyyyyxxwwwwvvuuttttssrrrrqqppppoooonnmmmmllllkkjjjjiiiihhhhggggffffeeeeddddccccccbbbbaaaaaa````______^^^^^^]]]]]]\\\\\\[[[[[[[[ZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸ë¸ë¸ë¸ë¸ë¸ë¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê······························································¶é¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±°ã°ã°°°°°°°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯®á®á®á®®®®®®®®®®®®­à­à­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««ªÝªÝªªªªªªªª©Ü©Ü©©©©©©¨Û¨Û¨¨¨¨¨¨§Ú§§§§§§¦Ù¦Ù¦¦¦¦¥Ø¥Ø¥¥¥¥¤×¤×¤¤¤¤£Ö££££¢Õ¢Õ¢¢¢¢¡Ô¡¡¡¡ Ó  ŸÒŸŸŸŸžÑžžžžÐœÏœœœœ›Î››šÍšš™Ì™™˜Ë˜˜—Ê——–É––•È••”Ç””“Æ““’’‘Ä‘‘ÃŽÁŽŽŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆ‡‡†¹††……„„„„ƒƒ‚‚€€~~}}||||{{zzyyyyxxwwwwvvuuuBttsss@rrqqq>ppoooonnn;mmllllkkk8jjj7iii6hhh5ggg4fff3eee2ddd1ccccbbbbb/aaaa`````-_____,^^^^^+]]]]]*\\\\\)[[[[[[[(ZZZZZ'Z'YYYYY&Y&XXXXX%X%WWWWWWW$W$VVVVVVV#V#UUUUUUU"U"TTTTTTTTT!T!SSSSSSSSS S RRRRRRRRRRRRQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¹¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªª©©©©©©©©¨¨¨¨¨¨¨¨§§§§§§§ ¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤ £££££ ¢¢¢¢¢¢¡¡¡¡¡   ŸŸŸŸŸžžžžžœœœœœ›››ššš™™™™˜˜˜˜————––––••••””“ú““’ù’’‘‘‘‘öŽŽŽŽŒóŒŒ‹‹ŠñŠŠ‰‰ˆïˆˆ‡‡††††……„„„„ƒƒ‚‚€€~~}}||||{{zzzyyxxxwwvvuuuuttssssrrr qqppp oonnnnmmmlllkkjjjjiiiihhhhggggffffeeeedþddcýccbübbbbaûaa`ú````_ù____^ø^^^^]÷]]]]\ö\\\\[õ[õ[[[[ZôZZZZZZYóYYYYYYXòXXXXXXWñWWWWWWWWVðVVVVVVVVUïUUUUUUUUTîTîTTTTTTTTSíSíSSSSSSSSRìRìRRRRRRRRRRQëQëQëQQQQQQQQQQPêPêPêPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOONèNèNèNNNNNNNNNNNNNNNNMçMçMçMMMMMMMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLLLLLLLLLLLKåKåKåKåKKKKKKKKKKKKKKKKKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIãIãIãIãIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHâHâHâHâHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGáGáGáGáGáGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFàFàFàFàFàFàFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹¹¹¹¹R¹R¹R¹R¹R¹R¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸Q¸Q¸Q¸Q¸Q¸Q·································································P·P·P·P¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶O¶O¶O¶O¶OµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµNµNµNµN´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´M´M´M´M³³³³³³³³³³³³³³³³³³³³³³³³³³³L³L³L²²²²²²²²²²²²²²²²²²²²²²²²²K²K²K±±±±±±±±±±±±±±±±±±±J±J±J°°°°°°°°°°°°°°°°°I°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®G®G®G­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««DªªªªªªªªªCªC©©©©©©©©©B¨¨¨¨¨¨¨¨¨A§§§§§§§@§@¦¦¦¦¦¦¦?¥¥¥¥¥¥¥>¤¤¤¤¤=¤=£££££<¢¢¢¢¢;¡¡¡¡¡:¡:   9ŸŸŸŸŸ8žžžžž7œœœœœ5›››4šššš™™™™™2˜˜˜1———0–––/•••.””““““’’’’‘‘‘*ŽŽŽ'ŒŒŒ%‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡††††……„„„ƒƒ‚‚€€~~}}||||{{zzzzyyxxxxwwvvuÜuuttsÚssrrrrqqp×ppoonÕnnmÔmmllllkkjÑjjiÐiihÏhhgÎggfÍffeÌeeeeddddcÊccbÉbbbbaÈaa`Ç````_Æ____^Å^^^^]Ä]]]]\Ã\\\\\\[Â[[[[ZÁZZZZZZYÀYYYYYYX¿XXXXXXW¾W¾WWWWWWV½V½VVVVVVU¼U¼UUUUUUUUT»TTTTTTTTTTSºSºSSSSSSSSR¹R¹RRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQP·P·PPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF­F­F­F­F­F­FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¹†¹†¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…···································································„·„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±~±~°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®{®{­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««x«xªªªªªªªªªw©©©©©©©©©v©v¨¨¨¨¨¨¨u¨u§§§§§§§t¦¦¦¦¦¦¦s¦s¥¥¥¥¥r¥r¤¤¤¤¤q£££££££p¢¢¢¢¢o¡¡¡¡¡n     mŸŸŸŸŸlžžžžžkjœœœi››››šššššg™™™f˜˜˜e———d–––c•••b”””a“““`’’’_‘‘\ŽŽŒŒ‹‹‹‹ŠŠŠW‰‰ˆˆˆU‡‡†††S……„„ƒƒƒƒ‚‚€€~~}}|¯||{{zzzzyyxxxxwwvvvvuut§ttssr¥rrqqqqppo¢oonnnnmmlŸllkžkkjjjiœiih›hhgšggf™ffe˜eed—ddddc–ccb•bbbba”aa`“````_’____^‘^^^^]]]]]\\\\\\[Ž[[[[ZZZZZZYŒYŒYYYYX‹X‹XXXXXXWŠWWWWWWWWV‰VVVVVVVVUˆUUUUUUUUT‡T‡TTTTTTTTTTS†SSSSSSSSSSR…R…RRRRRRRRRRRRQ„Q„QQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPO‚O‚OOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFyFyFyFyFyFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœ››››››šššš™™™™˜˜˜˜————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆ‡‡‡‡††…………„„ƒƒƒƒ‚‚€€~~}}}}||{{zzzzyyxxxxwwvvvvuuuuttssssrrqqqqppppoonnnnmmmmllllkkkkjjjjiiiihhhhggggffffeeeeddddddccccbbbbbbaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸············································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœ››››››šššš™™™™˜˜˜˜˜˜————––––••••””””““““’’’’‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††…………„„ƒƒƒƒ‚‚€€~~}}}}||{{{{zzyyyyxxwwwwvvuuuuttssssrrrrqqppppoooonnnnmmllllkkkkjjjjiiiihhhhggggffffffeeeeddddccccccbbbbbbaaaa``````______^^^^^^]]]]]]]]\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê·ê····································································¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬«Þ««««««««««ªÝªÝªªªªªªªª©Ü©Ü©©©©©©©©¨Û¨Û¨¨¨¨¨¨§Ú§Ú§§§§§§¦Ù¦Ù¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¤×¤¤¤¤¤¤£Ö££££¢Õ¢Õ¢¢¢¢¡Ô¡¡¡¡ Ó    ŸÒŸŸŸŸžÑžžžžÐœÏœœœœ››››šÍšš™Ì™™™™˜˜˜˜—Ê——–É––•È••”Ç””“Æ““’Å’’‘Ä‘‘ŽÁŽŽÀŒŒŒŒ‹‹ŠŠŠŠ‰‰ˆ»ˆˆ‡‡†¹††…………„„ƒƒƒƒ‚‚€€~~}}}}||{{{{zzyyyyxxwwwwvvuuuutttAssrrrrqqq>ppoooonnnnmmm:lll9kkk8jjj7iii6hhh5ggg4ffffeeeee2ddd1ccccc0bbbbaaaaa.`````-_____,^^^^^+]]]]]]]*\\\\\)[[[[[[[(ZZZZZZZ'YYYYYYY&Y&XXXXXXX%WWWWWWWWW$VVVVVVVVV#V#UUUUUUUUU"U"TTTTTTTTT!T!SSSSSSSSSSS S RRRRRRRRRRRRRRQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFFFFFFFFFFFFFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸···········································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««ªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨§§§§§§§§§ ¦¦¦¦¦¦¦ ¦ ¥¥¥¥¥ ¥ ¤¤¤¤¤ ¤ £££££ ¢¢¢¢¢¢¢¡¡¡¡¡     ŸŸŸŸŸžžžžžœœœœœ›››šššš™™™™˜ÿ˜˜—þ————––––••••””””““““’’’’‘‘÷öŽŽŽŽŒŒ‹ò‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡††††……„ë„„ƒƒƒƒ‚‚€€~~}}}}||{{{{zzyyyyxxwwwwvvvuuttttsss rrqqqqppp ooonnmmmmllllkkkkjjjjiiiihhhhhgggffeÿeeeedþddcýccccbübbaûaaaa`ú````_ù____^ø^^^^]÷]÷]]]]\ö\\\\[õ[õ[[[[ZôZôZZZZYóYóYYYYYYXòXXXXXXWñWñWWWWWWVðVðVVVVVVVVUïUUUUUUUUUUTîTTTTTTTTTTSíSíSSSSSSSSSSRìRìRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQPêPêPPPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOOOONèNèNèNNNNNNNNNNNNNNNNNNMçMçMçMMMMMMMMMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLLLLLLLLLLLLLLLKåKåKåKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIãIãIãIãIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHâHâHâHâHâHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGáGáGáGáGáGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFàFàFàFàFàFàFFFFFF¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸Q¸Q¸Q¸Q¸Q·······································································P·P·P·P·P¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶O¶O¶O¶O¶OµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµNµNµN´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´M´M´M´M³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³L³L³L²²²²²²²²²²²²²²²²²²²²²²²²²K²K²K±±±±±±±±±±±±±±±±±±±±±±±J±J°°°°°°°°°°°°°°°°°°°°°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H¯H®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««DªªªªªªªªªªªCªC©©©©©©©©©B¨¨¨¨¨¨¨¨¨A¨A§§§§§§§@§@¦¦¦¦¦¦¦?¥¥¥¥¥¥¥>¤¤¤¤¤¤¤=£££££££<¢¢¢¢¢¢¢;¡¡¡¡¡:     9ŸŸŸŸŸ8žžžžž76œœœœœ5›››4ššššš3™™™2˜˜˜˜—————0–––/•••.”””-“““,’’’+‘‘ŽŽŽ'&ŒŒ‹‹‹‹ŠŠŠ#‰‰ˆˆˆˆ‡‡††††……„„„„ƒƒƒ‚‚€€~~}}}}||{{{{zzyyyyxxwÞwwvvvvuutÛttssssrrqØqqppppoooonnmÔmmlÓllkÒkkjÑjjiÐiihÏhhhhggggfÍffeÌeeeeddddcÊccccbbbbaÈaaaa`Ç````_Æ____^Å^^^^^^]Ä]]]]\Ã\\\\\\[Â[[[[[[ZÁZZZZZZYÀYYYYYYX¿X¿XXXXXXW¾WWWWWWWWV½V½VVVVVVU¼U¼UUUUUUUUT»T»TTTTTTTTTTSºSºSSSSSSSSSSR¹R¹RRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGF­F­F­¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸…¸…¸…¸…¸…¸…·········································································„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®{®{®{­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««x«xªªªªªªªªªwªw©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨u§§§§§§§§§t¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥r¤¤¤¤¤¤¤q£££££££p¢¢¢¢¢¢¢o¡¡¡¡¡n     mŸŸŸŸŸlžžžžžkjœœœœœi››››šššššg™™™™˜˜˜˜˜e———d–––c••••””””““““’’’’‘‘‘‘]\ŽŽŒŒŒŒ‹‹‹XŠŠ‰‰‰‰ˆˆˆU‡‡††††……„„„„ƒƒ‚‚‚‚€€~~}}}}||{{{{zzy¬yyxxxxwwv©vvuuuutts¦ssrrrrqqp£ppo¢oon¡nnmmmmllllkkkkjjjjiœiih›hhgšggggffffe˜eed—ddddc–ccb•bbbba”aaaa`“````_’____^‘^^^^^^]]]]]\\\\\\\[Ž[[[[[[ZZZZZZZYŒYYYYYYYYX‹XXXXXXWŠWŠWWWWWWWWV‰VVVVVVVVUˆUˆUUUUUUUUT‡T‡TTTTTTTTTTS†S†SSSSSSSSSSR…R…RRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPO‚O‚OOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššš™™™™™™˜˜˜˜————––––••••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††††……„„„„ƒƒ‚‚‚‚€€~~}}}}||{{{{zzzzyyxxxxwwwwvvuuuuttttssrrrrqqqqppppoooonnmmmmllllkkkkjjjjjjiiiihhhhggggffffffeeeeddddddccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸····················································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸžžžžžžžžœœœœœœ››››››šššš™™™™™™˜˜˜˜——————––––••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒ‹‹‹‹ŠŠŠŠ‰‰ˆˆˆˆ‡‡‡‡††…………„„„„ƒƒ‚‚‚‚€€~~}}}}||||{{zzzzyyxxxxwwwwvvuuuuttttssssrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiihhhhhhggggffffeeeeeeddddccccccbbbbbbaaaaaa``````______^^^^^^^^]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸¸·ê·ê·ê·ê·ê··········································································¶é¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°°°°°°°°°°°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®á®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««ªÝªÝªªªªªªªªªª©Ü©Ü©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨§Ú§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤£Ö££££££¢Õ¢¢¢¢¡Ô¡¡¡¡¡¡ Ó    ŸÒŸŸŸŸŸŸžžžžžžÐœœœœ›Î››››šÍšššš™™™™˜Ë˜˜˜˜————–É––•È••”Ç””””““““’’’’‘‘‘‘ŽÁŽŽÀŒ¿ŒŒ‹‹‹‹ŠŠ‰¼‰‰ˆˆˆˆ‡‡‡‡††…………„„„„ƒƒ‚‚‚‚€³€€~~}}}}||||{{zzzzyyyFxxwwwwvvvCuuttttssssrrr?qqq>ppoooonnnnmmmmllllkkkkk8jjj7iii6hhhhggggg4fff3eeeee2ddd1ccccc0bbbbb/aaaaa.`````-_____,^^^^^^^+]]]]]*]*\\\\\)\)[[[[[([(ZZZZZZZ'YYYYYYY&Y&XXXXXXX%X%WWWWWWWWW$VVVVVVVVV#V#UUUUUUUUUUU"TTTTTTTTTTTTT!SSSSSSSSSSSSS S RRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸¸¸¸¸¸¸¸¸¸···············································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««ªªªªªªªªªªªª©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨§§§§§§§ § ¦¦¦¦¦¦¦ ¦ ¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤ ¤ £££££ £ ¢¢¢¢¢¡¡¡¡¡¡¡     ŸŸŸŸŸŸŸžžžžžœœœœ›››››ššššš™™˜ÿ˜˜˜˜—þ——–ý––––••••””””“ú““’ù’’‘ø‘‘÷öŽŽŽŽŒŒŒŒ‹‹ŠñŠŠ‰‰‰‰ˆˆˆˆ‡‡†í††…………„„ƒêƒƒ‚‚‚‚€€€€~~}}}}||||{{zzzzyyyyxxwwwwvvvvuuuttssssrrrrqqqqppp ooonnnmmmlllkkkkjjjjiiiiihhhggggffffeÿeeeeddddcýccccbübbbbaûaaaa`ú````_ù____^ø^^^^^^]÷]]]]]]\ö\\\\\\[õ[[[[[[ZôZZZZZZYóYYYYYYYYXòXXXXXXXXWñWWWWWWWWVðVðVVVVVVVVUïUïUUUUUUUUTîTîTTTTTTTTTTSíSíSSSSSSSSSSSSRìRìRRRRRRRRRRRRQëQëQëQQQQQQQQQQQQQQPêPêPPPPPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOOOOOONèNèNèNNNNNNNNNNNNNNNNNNNNMçMçMçMMMMMMMMMMMMMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLLLLLLLLLLLLLLLKåKåKåKåKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIãIãIãIãIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHâHâHâHâHâHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGáGáGáGáGáGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG¸Q¸Q···············································································P·P·P·P¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶O¶O¶O¶O¶OµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµNµNµN´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´M´M´M´M³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³L³L³L²²²²²²²²²²²²²²²²²²²²²²²²²²²²²K²K²K±±±±±±±±±±±±±±±±±±±±±±±J±J±J°°°°°°°°°°°°°°°°°°°°°I°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««DªªªªªªªªªªªCªC©©©©©©©©©©©B¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§@¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤=£££££££<¢¢¢¢¢¢¢;¡¡¡¡¡¡¡:     9ŸŸŸŸŸŸŸ8žžžžž76œœœœœ5›››4ššššš3™™™™˜˜˜˜˜1————–––––/•••.”””-““““’’’’‘‘‘‘ŽŽŽŽ&ŒŒŒ%‹‹ŠŠŠŠ‰‰‰‰ˆˆˆ!‡‡††††…………„„ƒƒƒƒ‚‚‚‚€€€€~~}}}}||||{{zázzyyyyxxwÞwwvvvvuuuuttsÚssrÙrrqqqqppppoooonnnnmmmmllllkkkkjÑjjiÐiiiihhhhgÎggfÍffffeÌeedËddddcÊccccbÉbbbbaÈaaaa`Ç````_Æ____^Å^Å^^^^]Ä]]]]]]\Ã\\\\\\[Â[[[[[[ZÁZZZZZZYÀYÀYYYYYYX¿X¿XXXXXXW¾W¾WWWWWWWWV½VVVVVVVVVVU¼UUUUUUUUUUT»T»TTTTTTTTTTSºSºSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHG®G®G®G®G®GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG·········································································„·„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±~±~°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|¯|®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««x«xªªªªªªªªªªªw©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨u¨u§§§§§§§t§t¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤q£££££££p¢¢¢¢¢¢¢o¡¡¡¡¡¡¡n     mŸŸŸŸŸŸŸlžžžžžkjœœœœœi››››šššššg™™™™™f˜˜˜˜—————d–––c••••””””“““““`’’’_‘‘‘^]\ŽŽŽ[ŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††††…………„„ƒƒƒƒ‚‚‚‚€€€€~~}}}}||||{{{{zzyyyyxxxxwwv©vvuuuuttttssssrrq¤qqp£ppo¢oon¡nnm mmlŸllkžkkkkjjjjiiiih›hhhhggggf™ffffeeeed—ddddc–ccccb•bbbba”aaaa`“````_’______^‘^^^^]]]]]]]\\\\\\\[Ž[[[[[[ZZZZZZZZZYŒYYYYYYYYX‹XXXXXXXXWŠWWWWWWWWV‰V‰VVVVVVVVUˆUˆUUUUUUUUUUT‡T‡TTTTTTTTTTS†S†SSSSSSSSSSSSR…R…RRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQPƒPƒPƒPPPPPPPPPPPPPPPPO‚O‚OOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGzGzGzGzGzGGGGGGGGGGGGGGGGGGGGGGGGGGGG········································································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššš™™™™™™˜˜˜˜˜˜————––––••••••””””““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆ‡‡‡‡††††…………„„ƒƒƒƒ‚‚‚‚€€€€~~}}}}||||{{{{zzyyyyxxxxwwwwvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllkkkkjjjjiiiiiihhhhggggggffffeeeeeeddddddccccccbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGGGGGGGGGGGG······························································¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡      ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™˜˜˜˜˜˜————––––––••••””””““““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††……„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}||||{{{{zzyyyyxxxxwwwwvvvvuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiihhhhhhggggffffffeeeeeeddddddccccbbbbbbbbaaaaaa``````________^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHGGGGGGGGGGGGGGGGGG······················································¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««ªÝªÝªªªªªªªªªª©Ü©Ü©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤£Ö£Ö££££££¢Õ¢¢¢¢¢¢¡Ô¡¡¡¡¡¡ Ó    ŸÒŸŸŸŸŸŸžÑžžžžÐœÏœœœœ›Î››››šÍšššš™Ì™™™™˜˜˜˜—Ê————––––•È••••””””““““’Å’’‘Ä‘‘ÃÂŽŽŽŽŒ¿ŒŒ‹¾‹‹Š½ŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††…¸……„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}||||{{{{zzzGyyxxxxwwwwvvvvuuuBtttAsss@rrr?qqq>ppp=ooo¤¤¤¤¤¤¤¤¤=£££££££<¢¢¢¢¢¢¢;¢;¡¡¡¡¡:       9ŸŸŸŸŸŸŸ8žžžžž76œœœœœœ››››››ššššš3™™™™™2˜˜˜˜˜1———0–––––/•••.””””“““““,’’’+‘‘‘*ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆ!‡‡‡ ††…………„„„„ƒƒƒƒ‚‚‚€€€€~~~~}}||||{{{{zzzzyyyyxxwÞwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmÔmmlÓllllkkkkjjjjiÐiiiihhhhgÎggggfÍffffeeeedËddddcÊccccbÉbbbbaÈaaaaaa`Ç````_Æ______^Å^^^^]Ä]Ä]]]]]]\Ã\\\\\\[Â[[[[[[ZÁZÁZZZZZZYÀYYYYYYYYX¿X¿XXXXXXXXW¾WWWWWWWWWWV½VVVVVVVVVVU¼U¼UUUUUUUUUUT»T»TTTTTTTTTTTTSºSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQP·P·P·PPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOONµNµNµNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH···························„·„·„·„¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y«««««««««««««««x«xªªªªªªªªªªªwªw©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤q£££££££p¢¢¢¢¢¢¢o¡¡¡¡¡¡¡n       mŸŸŸŸŸŸŸlžžžžžkœœœœœœœi›››››hšššš™™™™™f˜˜˜˜˜e————–––––c••••”””””a““““’’’’‘‘‘‘]\ŽŽŽ[ZŒŒŒY‹‹‹XŠŠŠW‰‰‰Vˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚€€€€~~~~}}|¯||{{{{zzzzyyyyxxxxwwv©vvu¨uut§tts¦ssr¥rrq¤qqp£ppo¢oon¡nnnnmmmmllllkžkkjjjjjiiiih›hhhhgšggggffffe˜eeeed—ddddc–ccccb•bbbba”aaaaaa`“````_’______^‘^^^^^^]]]]]]]\\\\\\\[Ž[[[[[[[[ZZZZZZZYŒYŒYYYYYYYYX‹XXXXXXXXWŠWŠWWWWWWWWV‰V‰VVVVVVVVVVUˆUUUUUUUUUUUUT‡T‡TTTTTTTTTTS†S†SSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPO‚O‚OOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{H{HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH··························¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™˜˜˜˜˜˜——————––––••••••””””““““““’’’’‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚€€€€~~~~}}}}||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnmmmmllllllkkkkjjjjiiiiiihhhhhhggggffffffeeeeeeddddddccccccbbbbbbaaaaaaaa``````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH················¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžœœœœœœ››››››šššššš™™™™™™˜˜˜˜——————––––––••••””””””““““’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒ‚‚‚‚€€€€~~~~}}}}||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppoooonnnnnnmmmmllllkkkkkkjjjjiiiiiihhhhggggggffffffeeeeeeddddddccccccbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH······¶é¶é¶é¶é¶é¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®­à­à­à­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««ªÝªÝªªªªªªªªªªªª©Ü©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤£Ö££££££££¢Õ¢¢¢¢¢¢¡Ô¡¡¡¡¡¡ Ó      ŸÒŸŸŸŸŸŸžÑžžžžÐœÏœœœœ›Î››››šÍšššš™Ì™™™™˜Ë˜˜˜˜————–É––––•È••••””””“Æ““’Å’’’’‘‘‘‘ÃÂŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒ‚‚‚‚€€€€~~~~}}}}|||I{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqppppp=ooo¤¤¤¤¤¤¤¤¤=£££££££<£<¢¢¢¢¢¢¢;¡¡¡¡¡¡¡:       9ŸŸŸŸŸŸŸ8žžžžžž6œœœœœ5››››››šššššš™™™™™™˜˜˜˜˜1—————0––––•••••.”””””-“““,’’’’‘‘‘‘‘*)ŽŽŽŽŒŒŒŒŒ%‹‹‹$ŠŠŠ#‰‰‰"ˆˆˆ!‡‡‡ †††……„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzyàyyxßxxwÞwwvÝvvuÜuutÛttsÚssssrrrrqqqqppppoooonÕnnnnmmmmllllkÒkkkkjjjjiÐiiiihhhhhhggggfÍffffeÌeeeedËddddcÊccccbÉbbbbbbaÈaaaa`Ç``````_Æ______^Å^^^^^^]Ä]]]]]]\Ã\\\\\\\\[Â[[[[[[[[ZÁZZZZZZZZYÀYYYYYYYYX¿X¿XXXXXXXXW¾W¾WWWWWWWWWWV½VVVVVVVVVVU¼U¼UUUUUUUUUUUUT»T»TTTTTTTTTTTTSºSºSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOONµNµNµNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH¯H¯H¯H¯H¯HHHHHHHHHHHHHHHHHHHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««xªªªªªªªªªªªªªªªw©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤q¤q£££££££p¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡n       mŸŸŸŸŸŸŸlžžžžžžžkjœœœœœœ›››››››hšššššg™™™™™f˜˜˜˜——————–––––c••••”””””a““““’’’’’_‘‘‘‘\ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqp£ppo¢oooonnnnmmmmlŸllllkkkkjjjjjiiiih›hhhhgšggggf™ffffe˜eeeed—ddddc–ccccb•bbbbbba”aaaa`“``````_’______^‘^^^^^^]]]]]]]\\\\\\\\[Ž[[[[[[[[ZZZZZZZZZYŒYYYYYYYYYYX‹XXXXXXXXXXWŠWWWWWWWWWWV‰V‰VVVVVVVVVVUˆUˆUUUUUUUUUUUUT‡TTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPO‚O‚OOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH{H{H{H{HHHHHHHHHHHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜——————––––••••••””””““““““’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssrrrrqqqqqqppppoooonnnnmmmmmmllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHHHHHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››šššššš™™™™™™˜˜˜˜˜˜——————––––––••••””””””““““’’’’’’‘‘‘‘ŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuuttttssssssrrrrqqqqppppoooooonnnnmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaa````````________^^^^^^^^]]]]]]]]]]\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHHHHHHHH¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®á®®®®®®®®®®®®®®®®®®­à­à­à­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««ªÝªÝªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤£Ö££££££££¢Õ¢¢¢¢¢¢¡Ô¡Ô¡¡¡¡¡¡ Ó      ŸÒŸŸŸŸŸŸžÑžžžžžžÐœÏœœœœœœ›Î››››šÍšššš™Ì™™™™˜Ë˜˜˜˜—Ê————–É––––•È••••””””“Æ““““’’’’‘Ä‘‘‘‘ÂŽŽŽŽŒŒŒŒ‹¾‹‹Š½ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvuuuutttttAssssrrrrqqqqppppp=oooonnnnmmmmm:llllkkkkk8jjjjj7iiiihhhhhhggggg4fffff3eeeee2ddddddd1ccccc0bbbbbbaaaaaaa.```````-_______,^^^^^^^+]]]]]]]]]*\\\\\\\)\)[[[[[[[([(ZZZZZZZZZ'YYYYYYYYYYY&XXXXXXXXXXX%WWWWWWWWWWW$W$VVVVVVVVVVV#V#UUUUUUUUUUUUU"U"TTTTTTTTTTTTTTT!SSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥ ¥ ¤¤¤¤¤¤¤¤¤ £££££££££ ¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡       ŸŸŸŸŸŸŸžžžžžžžœœœœœœœ›››››šššššš™™™™™™˜˜˜˜˜˜——————––––•ü••••”û””””““““’ù’’’’‘‘‘‘÷ŽõŽŽôŒŒŒŒ‹‹‹‹ŠŠŠŠ‰ð‰‰ˆïˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwwwwvvvvvuuuttttssssrrrrqqqqq ppppoooonnnnnmmmmlllllkkkkkjjjjiiiiihhhhhgggggffffeÿeeeedþddddddccccccbübbbbaûaaaaaa`ú``````_ù______^ø^^^^^^]÷]]]]]]]]\ö\\\\\\\\[õ[[[[[[[[ZôZZZZZZZZYóYóYYYYYYYYXòXXXXXXXXXXWñWñWWWWWWWWWWVðVVVVVVVVVVVVUïUïUUUUUUUUUUUUTîTîTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQPêPêPêPPPPPPPPPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOOOOOOOOOOOONèNèNNNNNNNNNNNNNNNNNNNNNNNNNNNNMçMçMçMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKåKåKåKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIãIãIãIãIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶O¶O¶O¶OµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµNµNµNµN´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´M´M´M³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³L³L³L²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²K²K²K±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±J±J±J°°°°°°°°°°°°°°°°°°°°°°°°°°°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««D«DªªªªªªªªªªªªªªªCªC©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦?¦?¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤=¤=£££££££<£<¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡:       9ŸŸŸŸŸŸŸ8žžžžžžž7œœœœœœœ5››››››ššššššš3™™™™™2˜˜˜˜˜1—————0––––••••••”””””-““““’’’’’+‘‘‘‘)ŽŽŽŽ&ŒŒŒŒ‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆˆ!‡‡‡‡††††…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyxxxxwÞwwvÝvvvvuuuuttttssssrrrrqØqqqqppppoooonÕnnnnmmmmlÓllllkkkkkkjjjjiÐiiiihÏhhhhgÎggggfÍffffeÌeeeedËddddcÊccccccbÉbbbbaÈaaaaaa`Ç``````_Æ______^Å^^^^^^]Ä]Ä]]]]]]\Ã\\\\\\\\[Â[[[[[[[[ZÁZZZZZZZZZZYÀYYYYYYYYX¿X¿XXXXXXXXXXW¾WWWWWWWWWWV½V½VVVVVVVVVVVVU¼UUUUUUUUUUUUUUT»TTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOONµNµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶ƒ¶ƒ¶ƒ¶ƒ¶ƒµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|¯|®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««xªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤q£££££££££p¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡n       mŸŸŸŸŸŸŸlžžžžžžžkjœœœœœi›››››››hšššššg™™™™™f˜˜˜˜˜e—————d–––––c•••••b””””“““““`’’’’‘‘‘‘‘^\ŽŽŽŽŒŒŒŒŒY‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡T†††S…………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyx«xxxxwwwwvvvvuuuutttts¦ssr¥rrrrqqqqppppo¢oooonnnnm mmmmllllkžkkkkjjjjjiiiiiihhhhhhggggggffffffeeeeeed—ddddc–ccccccbbbbbba”aaaaaa`“``````_’______^‘^^^^^^^^]]]]]]]\\\\\\\\\[Ž[[[[[[[[ZZZZZZZZZZYŒYYYYYYYYYYX‹XXXXXXXXXXWŠWŠWWWWWWWWWWV‰V‰VVVVVVVVVVUˆUˆUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPO‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœ››››››››šššššš™™™™™™˜˜˜˜˜˜——————––––––••••••””””””““““’’’’’’‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††………………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvuuuuttttttssssrrrrqqqqppppppoooonnnnnnmmmmllllllkkkkkkjjjjiiiiiihhhhhhggggggffffffeeeeeeeeddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII¶¶¶¶¶¶¶¶µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡        ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœ››››››››šššššš™™™™™™™™˜˜˜˜˜˜——————––––––••••””””””““““““’’’’‘‘‘‘‘‘ŽŽŽŽŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{{{zzzzyyyyxxxxwwwwvvvvuuuuuuttttssssrrrrqqqqqqppppoooooonnnnmmmmmmllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffeeeeeeddddddddccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIµèµèµèµèµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ««««««««««««««««ªÝªÝªªªªªªªªªªªªªª©Ü©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤£Ö££££££££¢Õ¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡ Ó Ó      ŸÒŸŸŸŸŸŸžÑžžžžžžÐœÏœœœœœœ››››››šÍšššššš™™™™™™˜Ë˜˜˜˜—Ê————–É––––•È••••””””””““““’Å’’’’‘‘‘‘‘‘ÂŽŽŽŽŒ¿ŒŒŒŒ‹‹‹‹ŠŠŠŠ‰¼‰‰‰‰ˆˆˆˆ‡‡‡‡††††…………„„„„ƒ¶ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||{{{{{{zzzzyyyyxxxxwwwwvvvvvCuuuuttttssssrrrrr?qqqqppppp=oooonnnnn;mmmmlllll9kkkkk8jjjjj7iiiihhhhhhh5ggggg4fffff3eeeee2ddddddd1ccccc0bbbbbbb/aaaaaaa.```````-_______,^^^^^^^^^+]]]]]]]]]*\\\\\\\\\)[[[[[[[[[(ZZZZZZZZZZZ'YYYYYYYYYYY&XXXXXXXXXXX%X%WWWWWWWWWWW$W$VVVVVVVVVVVVV#UUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤ ¤ £££££££££ ¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡         ŸŸŸŸŸŸŸžžžžžžžœœœœœœœ›››››ššššššš™™™™˜ÿ˜˜˜˜—þ————–ý––––•ü••••”û””””“ú““““’’’’’’‘‘‘‘÷ŽõŽŽŽŽŒŒŒŒ‹ò‹‹‹‹ŠŠŠŠ‰‰‰‰ˆïˆˆˆˆ‡‡‡‡††††…………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}|||||{{{{zzzzyyyyxxxxwwwwwvvvvuuuuttttsssss rrrrqqqqq ppppooooonnnnmmmmmlllllkkkkjjjjjjiiiiihhhhhhggggggffffffeÿeeeedþddddddccccccbübbbbbbaûaaaaaa`ú``````_ù______^ø^^^^^^^^]÷]]]]]]]]\ö\\\\\\\\[õ[[[[[[[[ZôZZZZZZZZZZYóYYYYYYYYYYXòXXXXXXXXXXXXWñWWWWWWWWWWWWVðVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUTîTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQQQPêPêPPPPPPPPPPPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOOOOOOOOOOOOOONèNèNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMçMçMçMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKåKåKåKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIãIãIãIãIIIIIIIIIIIIIIIIIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµNµNµNµN´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´M´M´M´M³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³L³L³L²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²K²K²K±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±J±J±J°°°°°°°°°°°°°°°°°°°°°°°°°°°I°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««D«DªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦?¦?¥¥¥¥¥¥¥¥¥>¥>¤¤¤¤¤¤¤¤¤=£££££££££££<¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡:         9ŸŸŸŸŸŸŸ8žžžžžžž76œœœœœœœ5››››››ššššššš3™™™™™2˜˜˜˜˜˜——————––––––••••••””””””“““““,’’’’’+‘‘‘‘)ŽŽŽŽŽ'ŒŒŒŒ‹‹‹‹‹$ŠŠŠŠ‰‰‰‰ˆˆˆˆˆ!‡‡‡‡††††…………„„„„ƒƒƒƒƒ‚‚‚‚€€€€~~~~}}}}||||||{{{{zzzzyyyyxxxxwÞwwwwvvvvuuuuttttsÚssssrrrrqØqqqqppppoooooonnnnmÔmmmmllllllkkkkjÑjjjjiÐiiiihÏhhhhgÎggggfÍffffffeeeeeedËddddcÊccccccbÉbbbbbbaÈaaaaaa`Ç``````_Æ______^Å^^^^^^^^]Ä]]]]]]]]\Ã\\\\\\\\[Â[[[[[[[[ZÁZÁZZZZZZZZYÀYÀYYYYYYYYX¿X¿XXXXXXXXXXW¾W¾WWWWWWWWWWV½V½VVVVVVVVVVVVU¼U¼UUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTSºSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOONµNµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI°I°I°I°IIIIIIIIIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««xªªªªªªªªªªªªªªªªªw©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤q£££££££££p£p¢¢¢¢¢¢¢o¢o¡¡¡¡¡¡¡n         mŸŸŸŸŸŸŸlžžžžžžžkjœœœœœœœi›››››››hšššššg™™™™™™˜˜˜˜˜˜˜e—————d–––––c•••••b”””””a““““’’’’’’‘‘‘‘‘^\ŽŽŽŽZŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡‡T††††…………„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€~~~~}}}}|¯||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuut§ttttssssrrrrrrqqqqppppo¢oooonnnnnnmmmmlŸllllkžkkkkjjjjjjiiiiiihhhhhhggggggf™ffffe˜eeeeeeddddddc–ccccccb•bbbbbba”aaaaaa`“``````_’______^‘^^^^^^^^]]]]]]]]]\\\\\\\\\[Ž[[[[[[[[[[ZZZZZZZZZZZYŒYYYYYYYYYYX‹XXXXXXXXXXXXWŠWWWWWWWWWWWWV‰VVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI|I|I|I|IIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššš™™™™™™™™˜˜˜˜˜˜——————––––––••••••””””””““““““’’’’’’‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆ‡‡‡‡††††††…………„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€~~~~}}}}}}||||{{{{zzzzyyyyxxxxxxwwwwvvvvuuuuuuttttssssrrrrrrqqqqppppppoooonnnnnnmmmmmmllllllkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIIIIIIIIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™˜˜˜˜˜˜——————––––––––••••••””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡††††††…………„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€~~~~}}}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvvvuuuuttttssssssrrrrqqqqqqppppoooooonnnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggffffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa````````________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´ç´ç´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®á®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««ªÝªÝªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤£Ö£Ö££££££££¢Õ¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡ Ó        ŸÒŸŸŸŸŸŸžÑžžžžžžžžœœœœœœ›Î››››››šÍšššššš™Ì™™™™˜Ë˜˜˜˜˜˜——————––––––•È••••”Ç””””““““““’’’’’’‘‘‘‘ÃŽŽŽŽÀŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰ˆ»ˆˆˆˆ‡‡‡‡††††…¸…………„„„„ƒƒƒƒ‚‚‚‚´€€€€~~~~}}}}}}||||{{{{zzzzyyyyyyxxxxwwwwvvvvvvuuuutttttAssssrrrrr?qqqqppppp=oooonnnnnnmmmmm:lllll9kkkkk8jjjjj7iiiii6hhhhh5ggggg4ffffffeeeeeee2ddddddd1ccccccbbbbbbbbaaaaaaaaa.```````-_______,_,^^^^^^^+]]]]]]]]]*\\\\\\\\\\\)[[[[[[[[[([(ZZZZZZZZZ'Z'YYYYYYYYYYY&XXXXXXXXXXXXX%WWWWWWWWWWWWW$W$VVVVVVVVVVVVV#V#UUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««ªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥ ¥ ¤¤¤¤¤¤¤¤¤ £££££££££££ ¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡         ŸŸŸŸŸŸŸžžžžžžžžžœœœœœœ›››››››ššššššš™™™™™™˜˜˜˜˜˜—þ————–ý––––•ü••••”û””””“ú““““’ù’’’’‘ø‘‘‘‘ŽõŽŽŽŽŒóŒŒŒŒ‹‹‹‹ŠñŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††………………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}}}}}}||||{{{{zzzzzyyyyxxxxwwwwwvvvvuuuuttttttssssrrrrrrqqqqq ppppooooonnnnnmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhgggggggffffeÿeeeeeedþddddddccccccbübbbbbbaûaaaaaaaa`ú``````_ù________^ø^^^^^^]÷]]]]]]]]\ö\ö\\\\\\\\[õ[[[[[[[[[[ZôZZZZZZZZZZYóYYYYYYYYYYXòXXXXXXXXXXXXWñWñWWWWWWWWWWWWVðVVVVVVVVVVVVVVUïUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSRìRRRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQQQQQPêPêPPPPPPPPPPPPPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOOOOOOOOOOOOOONèNèNèNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMçMçMçMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKåKåKåKåKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJäJäJäJäJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµµµµµµµµµµNµNµNµN´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´M´M´M´M³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³L³L³L²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²K²K²K±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±J±J±J°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°I°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®®®®®®®G®G®G­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««D«DªªªªªªªªªªªªªªªªªC©©©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤=£££££££££££<¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡:         9ŸŸŸŸŸŸŸ8žžžžžžžžž76œœœœœœœ5››››››ššššššš3™™™™™™™2˜˜˜˜˜1——————––––––••••••””””””““““““’’’’’’‘‘‘‘‘*)ŽŽŽŽŽ'ŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆ!‡‡‡‡††††……………„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~}ä}}}}||||{{{{zzzzzzyyyyxxxxwwwwwwvvvvuuuutÛttttssssrÙrrrrqqqqqqppppoÖoooonnnnnnmmmmlÓllllkÒkkkkjÑjjjjiÐiiiihÏhhhhgÎggggggffffffeÌeeeeeedËddddcÊccccccbÉbbbbbbaÈaaaaaaaa`Ç``````_Æ________^Å^^^^^^]Ä]Ä]]]]]]]]\Ã\\\\\\\\[Â[[[[[[[[[[ZÁZZZZZZZZZZYÀYYYYYYYYYYX¿X¿XXXXXXXXXXXXW¾WWWWWWWWWWWWV½V½VVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµµ‚µ‚µ‚µ‚´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|¯|®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««xªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤q£££££££££p£p¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡n         mŸŸŸŸŸŸŸlžžžžžžžžžkjœœœœœœœi›››››››hšššššš™™™™™™™f˜˜˜˜˜˜———————d–––––c•••••b”””””a“““““`’’’’’_‘‘‘‘\ŽŽŽŽŒŒŒŒŒY‹‹‹‹ŠŠŠŠŠW‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††…………„„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||{{{{z­zzzzyyyyxxxxwªwwwwvvvvu¨uuuutttts¦ssssrrrrq¤qqqqppppppoooon¡nnnnmmmmmmllllllkkkkkkjjjjjjiiiiiih›hhhhgšggggf™ffffffe˜eeeeeeddddddc–ccccccb•bbbbbba”aaaaaaaa````````_’________^‘^^^^^^^^]]]]]]]]]\\\\\\\\\[Ž[[[[[[[[[[ZZZZZZZZZZZYŒYŒYYYYYYYYYYX‹XXXXXXXXXXXXWŠWŠWWWWWWWWWWWWV‰VVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQPƒPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµµµµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™˜˜˜˜˜˜˜˜——————––––––••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††…………„„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqppppppoooooonnnnmmmmmmllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggffffffffeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJµµµµ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™˜˜˜˜˜˜˜˜——————––––––––••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††…………„„„„„„ƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||{{{{{{zzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssrrrrrrqqqqqqppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ´ç´ç´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªª©Ü©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤£Ö££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡ Ó        ŸÒŸŸŸŸŸŸžÑžžžžžžžžÐœÏœœœœœœ›Î››››››šÍšššššš™Ì™™™™™™˜˜˜˜˜˜—Ê——————––––––•È••••”Ç””””“Æ““““’Å’’’’‘Ä‘‘‘‘ÃŽŽŽŽÀŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††…………„„„„ƒ¶ƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||{{{{{{zzzzyyyyyFxxxxwwwwwDvvvvuuuuuBttttsssss@rrrrqqqqqqppppp=ooooo¥>¤¤¤¤¤¤¤¤¤¤¤=£££££££££££<¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡:         9ŸŸŸŸŸŸŸ8žžžžžžžžž76œœœœœœœœ››››››››šššššššš™™™™™™™2˜˜˜˜˜˜˜1—————0––––––•••••••.”””””-“““““,’’’’’+‘‘‘‘‘*ŽŽŽŽŽŽ&ŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆ!‡‡‡‡††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€~~~~~~}}}}||||||{{{{zzzzyyyyyyxxxxwwwwwwvvvvuÜuuuuttttsÚssssrrrrrrqqqqp×ppppoÖoooonnnnnnmmmmmmllllllkkkkkkjjjjjjiÐiiiihÏhhhhhhggggggfÍffffffeÌeeeeeeddddddddccccccccbbbbbbbbaÈaaaaaa`Ç````````_Æ________^Å^^^^^^^^]Ä]]]]]]]]\Ã\\\\\\\\\\[Â[[[[[[[[[[ZÁZZZZZZZZZZZZYÀYYYYYYYYYYYYX¿XXXXXXXXXXXXW¾WWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVU¼UUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQP·P·P·PPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ±J±J±JJJJJJJJJJJJJJ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®{®{®{­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««xªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££p¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡n         mŸŸŸŸŸŸŸŸžžžžžžžžžkœœœœœœœœœi›››››››hšššššššg™™™™™™˜˜˜˜˜˜˜e——————–––––––c•••••b””””””““““““’’’’’’‘‘‘‘‘‘\ŽŽŽŽŽ[ŒŒŒŒŒŒ‹‹‹‹‹XŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{zzzzy¬yyyyxxxxwªwwwwvvvvvvuuuuttttttssssr¥rrrrqqqqqqppppppoooon¡nnnnm mmmmlŸllllkžkkkkjjjjjjjiiiiiih›hhhhgšggggggf™ffffffeeeeeed—ddddddc–ccccccb•bbbbbbbba”aaaaaa`“````````_’________^‘^^^^^^^^]]]]]]]]]\\\\\\\\\\[Ž[Ž[[[[[[[[ZZZZZZZZZZZZYŒYYYYYYYYYYYYX‹XXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUT‡TTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJ}J}J}J}JJJJJJ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜————————––––––••••••””””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwvvvvvvuuuuttttttssssssrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸžžžžžžžžœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜————————––––––••••••••””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††………………„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppoooooonnnnnnmmmmmmllllllkkkkkkkkjjjjjjiiiiiihhhhhhhhggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaa``````````__________^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK´´´´´´´´´´´´´´´´´´´´´´´´´´´´³æ³æ³æ³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤£Ö££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡ Ó Ó        ŸÒŸŸŸŸŸŸŸŸžÑžžžžžžÐœÏœœœœœœ›Î››››››šÍšššššš™Ì™™™™™™˜Ë˜˜˜˜˜˜——————–É––––––••••••”Ç””””“Æ““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒ¿ŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰¼‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡††††††…………„·„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{zzzzzzyyyyxxxxxxwwwwwDvvvvuuuuuutttttAssssrrrrrrqqqqqqppppp=ooooo¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££<¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡:           9ŸŸŸŸŸŸŸŸŸ8žžžžžžž76œœœœœœœ5››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜1———————0––––––•••••••.”””””-““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒ%‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡‡‡‡‡‡††††††…………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}|ã||||{{{{{{zzzzyyyyyyxxxxwwwwwwvvvvvvuuuuttttttssssssrrrrqØqqqqp×ppppoÖoooonÕnnnnmÔmmmmlÓllllkÒkkkkkkjjjjjjiÐiiiiiihhhhhhgÎggggggffffffffeeeeeeeeddddddddccccccccbÉbbbbbbaÈaaaaaaaa`Ç````````_Æ________^Å^^^^^^^^]Ä]Ä]]]]]]]]\Ã\\\\\\\\\\[Â[[[[[[[[[[[[ZÁZZZZZZZZZZYÀYÀYYYYYYYYYYYYX¿XXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTSºSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK´´´´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤q¤q£££££££££££p¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡n           mŸŸŸŸŸŸŸŸŸlžžžžžžžžjœœœœœœœœ›››››››››hšššššššg™™™™™™™f˜˜˜˜˜˜———————d–––––––c•••••b””””””“““““““`’’’’’_‘‘‘‘‘^]\ŽŽŽŽŽ[ZŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰Vˆˆˆˆ‡‡‡‡‡‡†††††S…………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxwªwwwwvvvvvvuuuut§ttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllkžkkkkjjjjjjjiiiiiih›hhhhhhggggggf™ffffffe˜eeeeeed—ddddddc–ccccccccb•bbbbbba”aaaaaaaa`“````````_’________^‘^^^^^^^^^^]]]]]]]]]\\\\\\\\\\\[Ž[Ž[[[[[[[[[[ZZZZZZZZZZZZZYŒYYYYYYYYYYYYX‹XXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUUUUUT‡TTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK´´´´´´´´³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜——————––––––––••••••””””””””““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††………………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––••••••••””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqppppppoooooonnnnnnnnmmmmmmllllllkkkkkkjjjjjjjjiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªª©Ü©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡ Ó          ŸÒŸŸŸŸŸŸŸŸžÑžžžžžžžžÐœÏœœœœœœœœ›Î››››››šÍšššššš™Ì™™™™™™˜Ë˜˜˜˜˜˜—Ê——————–É––––––••••••”Ç””””””““““““’Å’’’’‘Ä‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹Š½ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡†¹††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{{{{{{zzzzzGyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqq>ppppp=ooooo¥>¤¤¤¤¤¤¤¤¤¤¤=¤=£££££££££££<¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡:           9ŸŸŸŸŸŸŸŸŸ8žžžžžžžžž76œœœœœœœ5››››››››ššššššššš3™™™™™™™2˜˜˜˜˜˜˜1——————––––––––•••••••.””””””“““““““,’’’’’+‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰"ˆˆˆˆ‡‡‡‡‡‡††††††……………„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~å~~~~}}}}}}||||{â{{{{zzzzzzyyyyxßxxxxwwwwwwvvvvvvuuuuuuttttsÚssssrÙrrrrqØqqqqp×ppppoÖoooonÕnnnnnnmmmmmmllllllkÒkkkkkkjjjjjjiÐiiiiiihhhhhhgÎggggggfÍffffffeÌeeeeeedËddddddddcÊccccccbÉbbbbbbbbaÈaaaaaaaa`Ç````````_Æ________^Å^^^^^^^^^^]Ä]]]]]]]]]]\Ã\\\\\\\\\\[Â[[[[[[[[[[[[ZÁZZZZZZZZZZZZYÀYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUT»TTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK²K²K²KKKKKKKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££p¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡n         m mŸŸŸŸŸŸŸŸŸlžžžžžžžžžkjœœœœœœœi›››››››››hšššššššg™™™™™™™f˜˜˜˜˜˜˜e———————d–––––––c••••••”””””””a““““““’’’’’’‘‘‘‘‘‘‘^]ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠW‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††…………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚O€€€€€€~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxwªwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmlŸllllllkkkkkkjjjjjjjiiiiiih›hhhhhhgšggggggf™ffffffe˜eeeeeed—ddddddddccccccccb•bbbbbbbba”aaaaaaaa`“````````_’________^‘^^^^^^^^^^]]]]]]]]]]]\\\\\\\\\\\[Ž[Ž[[[[[[[[[[ZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLK~K~K~KKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››››šššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””““““““““’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††…………„„„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKK³³³³³³³³³³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››šššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜————————––––––••••••••””””””””““““““’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoooooooonnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa``````````__________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³³³³³³³³³³³³³³³³³³³³³²å²å²å²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££¢Õ¢Õ¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡ Ó        ŸÒŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžÐœÏœœœœœœœœ›Î››››››šÍšššššššš™™™™™™™™˜Ë˜˜˜˜˜˜—Ê——————–É––––––••••••”Ç””””””“Æ““““““’’’’’’‘‘‘‘‘‘ÃŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡†¹††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{HzzzzyyyyyyxxxxxxwwwwwwvvvvvCuuuuuBtttttAsssss@rrrrr?qqqqq>ppppp=oooooonnnnnnmmmmmmm:llllllkkkkkkk8jjjjjjiiiiiiiihhhhhhh5ggggggg4fffffff3eeeeeeeee2ddddddd1ccccccccc0bbbbbbbbaaaaaaaaaaa.`````````-_________,^^^^^^^^^^^+]]]]]]]]]]]*\\\\\\\\\\\\\)[[[[[[[[[[[[[(ZZZZZZZZZZZZZ'YYYYYYYYYYYYYYY&XXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³³³³³³³³³³³³³³³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦¦¦ ¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤ ¤ £££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡         ŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžœœœœœœœœœ››››››››ššššššššš™™™™™™˜ÿ˜˜˜˜˜˜—þ——————–ý––––––•ü••••••””””””“ú““““““’ù’’’’‘ø‘‘‘‘‘‘öŽõŽŽŽŽôŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆ‡î‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppooooooonnnnnmmmmmmlllllllkkkkkkjjjjjjjiiiiiiihhhhhhhgggggggffffffeÿeeeeeeeeddddddddcýccccccccbbbbbbbbaûaaaaaaaaaa``````````_ù________^ø^^^^^^^^^^]÷]]]]]]]]]]\ö\ö\\\\\\\\\\[õ[[[[[[[[[[[[ZôZZZZZZZZZZZZYóYóYYYYYYYYYYYYXòXòXXXXXXXXXXXXXXWñWWWWWWWWWWWWWWWWVðVðVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUTîTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQQQQQQQQQQQPêPêPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONèNèNèNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMçMçMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³³³³³³³³L³L³L³L²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²K²K±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±J±J±J°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°I°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªC©©©©©©©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££<¢¢¢¢¢¢¢¢¢¢¢;¢;¡¡¡¡¡¡¡¡¡¡¡:         9ŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžž76œœœœœœœœœ5›››››››››4ššššššš3™™™™™™™2˜˜˜˜˜˜˜˜————————––––––––•••••••.””””””““““““““’’’’’’‘‘‘‘‘‘‘*ŽŽŽŽŽŽ&ŒŒŒŒŒ%‹‹‹‹‹$ŠŠŠŠŠ#‰‰‰‰‰"ˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒ‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzyàyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppoÖoooooonnnnnnmmmmmmlÓllllllkkkkkkjÑjjjjjjiÐiiiiiihhhhhhhhggggggggffffffffeÌeeeeeedËddddddddcÊccccccbÉbbbbbbbbaÈaaaaaaaa`Ç``````````_Æ________^Å^Å^^^^^^^^]Ä]]]]]]]]]]]]\Ã\\\\\\\\\\[Â[[[[[[[[[[[[ZÁZÁZZZZZZZZZZZZYÀYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVU¼UUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³³€³€³€²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££p¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡n         mŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžkjœœœœœœœœœi›››››››››hšššššššg™™™™™™™™˜˜˜˜˜˜˜˜˜e———————d–––––––c••••••”””””””a“““““““`’’’’’_‘‘‘‘‘‘]ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzzzyyyyx«xxxxwªwwwwvvvvvvuuuuuuttttttssssssr¥rrrrq¤qqqqp£ppppppoooooonnnnnnm mmmmmmllllllkžkkkkkkjjjjjjjjiiiiiih›hhhhhhgšggggggf™ffffffffeeeeeeeed—ddddddddccccccccb•bbbbbbbba”aaaaaaaa`“``````````_’__________^‘^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\[Ž[Ž[[[[[[[[[[[[ZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRQ„Q„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL³³³³²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡          ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””““““““““’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœ››››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqppppppppoooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤£Ö£Ö££££££££££¢Õ¢Õ¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡ Ó          ŸÒŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžÐœÏœœœœœœœœ››››››››šÍšššššššš™Ì™™™™™™˜Ë˜˜˜˜˜˜˜˜————————––––––––••••••••””””””“Æ““““““’’’’’’’’‘‘‘‘‘‘ÂŽŽŽŽŽŽŒ¿ŒŒŒŒ‹¾‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††…………„·„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~K}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqq>ppppppoooooonnnnnnn;mmmmmmlllllll9kkkkkkk8jjjjjjiiiiiiiihhhhhhhhggggggggg4fffffff3eeeeeeeeddddddddd1ccccccccc0bbbbbbbbb/aaaaaaaaa.```````````-___________,^^^^^^^^^^^+]]]]]]]]]]]*\\\\\\\\\\\\\)[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLLLLLLLLLLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡           ŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžœœœœœœœœœ››››››››ššššššššš™™™™™™™™˜˜˜˜˜˜˜˜—þ——————–ý––––––•ü••••••”û””””””““““““’ù’’’’’’‘ø‘‘‘‘‘‘ŽõŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠñŠŠŠŠ‰ð‰‰‰‰ˆïˆˆˆˆ‡î‡‡‡‡†í††††…ì…………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttsssssss rrrrr qqqqqqppppppooooooonnnnnnmmmmmmmllllllkkkkkkkkjjjjjjjiiiiiiihhhhhhhggggggggffffffffeÿeeeeeedþddddddddcýccccccccbübbbbbbbbaûaaaaaaaa`ú``````````_ù__________^ø^^^^^^^^^^]÷]]]]]]]]]]\ö\\\\\\\\\\\\[õ[[[[[[[[[[[[ZôZôZZZZZZZZZZZZYóYYYYYYYYYYYYYYXòXòXXXXXXXXXXXXXXWñWñWWWWWWWWWWWWWWWWVðVVVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTSíSSSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQQQQQQQQQQQPêPêPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONèNèNèNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMçMçMçMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLæLæLæLLLLLLLLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²K²K²K±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±J±J°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¦?¥¥¥¥¥¥¥¥¥¥¥¥¥>¥>¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡:           9ŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžž76œœœœœœœœœ5›››››››››4ššššššš3™™™™™™™™™2˜˜˜˜˜˜˜1———————0–––––––/•••••••.”””””””-““““““’’’’’’’’‘‘‘‘‘‘‘*ŽŽŽŽŽŽŽ'ŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}|ã||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuutÛttttsÚssssssrrrrrrqqqqqqppppppoÖoooooonnnnnnmÔmmmmmmllllllkÒkkkkkkjÑjjjjjjiÐiiiiiihÏhhhhhhgÎggggggfÍffffffffeeeeeeeedËddddddddcÊccccccccbÉbbbbbbbbaÈaaaaaaaa`Ç``````````_Æ__________^Å^^^^^^^^^^]Ä]]]]]]]]]]\Ã\Ã\\\\\\\\\\[Â[Â[[[[[[[[[[[[ZÁZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVU¼UUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMML³L³L³LLLLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡n           mŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžkjœœœœœœœœœi›››››››››hšššššššš™™™™™™™™™f˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””“““““““`’’’’’’’_‘‘‘‘‘‘]ŽŽŽŽŽŽZŒŒŒŒŒY‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||{®{{{{z­zzzzyyyyyyxxxxxxwªwwwwv©vvvvu¨uuuuuuttttttssssssrrrrrrqqqqqqp£ppppppoooooon¡nnnnnnmmmmmmlŸllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggf™ffffffe˜eeeeeeeed—ddddddddccccccccccbbbbbbbbbba”aaaaaaaa`“``````````_’__________^‘^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\[Ž[[[[[[[[[[[[ZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLLLLL²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMLL²²²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggffffffffffeeeeeeeeddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaa````````````____________^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²²²²²²²²²²²²²²²²²±ä±ä±ä±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¡Ô¡Ô¡¡¡¡¡¡¡¡¡¡ Ó          ŸÒŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžÐœÏœœœœœœœœ›Î››››››››šÍšššššššš™Ì™™™™™™™™˜˜˜˜˜˜˜˜—Ê——————–É––––––––••••••••””””””””““““““’Å’’’’’’‘Ä‘‘‘‘‘‘ÂŽŽŽŽŽŽÀŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰¼‰‰‰‰ˆ»ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuutttttttAssssssrrrrrrqqqqqqq>ppppppoooooonnnnnnnnmmmmmmm:lllllll9kkkkkkjjjjjjjjiiiiiiiihhhhhhhhh5ggggggg4fffffffff3eeeeeee2ddddddddd1ccccccccc0bbbbbbbbbbaaaaaaaaaaa.```````````-___________,^^^^^^^^^^^+]]]]]]]]]]]]]*\\\\\\\\\\\\\)[[[[[[[[[[[[[([(ZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££ £ ¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡           ŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžœœœœœœœœœœ››››››››››ššššššššš™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜————————––––––––•ü••••••”û””””””“ú““““““’’’’’’’’‘‘‘‘‘‘÷ŽõŽŽŽŽŽŽŒóŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡î‡‡‡‡†í††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuuuuuuuttttttssssssrrrrrrr qqqqqqppppppooooooonnnnnnnmmmmmmllllllllkkkkkkkjjjjjjjiiiiiiihhhhhhhhgggggggggffffffffeeeeeeeedþddddddddcýccccccccbübbbbbbbbaûaaaaaaaaaa`ú``````````_ù__________^ø^^^^^^^^^^]÷]]]]]]]]]]]]\ö\\\\\\\\\\\\[õ[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYXòXòXXXXXXXXXXXXXXXXWñWWWWWWWWWWWWWWWWWWVðVVVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPêPêPêPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOéOéOéOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONèNèNèNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMçMçMçMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²²²²²²²²K²K²K±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±J±J±J°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°I°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªªªC©©©©©©©©©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡:           9ŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžž76œœœœœœœœœœœ5›››››››››4šššššššš™™™™™™™™™2˜˜˜˜˜˜˜˜˜1———————0–––––––/••••••••”””””””-“““““““,’’’’’’’+‘‘‘‘‘‘)ŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒ%‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡†††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwvvvvvvuÜuuuuuuttttttssssssrrrrrrrrqqqqqqppppppoÖoooooonnnnnnnnmmmmmmlÓllllllkÒkkkkkkjÑjjjjjjiÐiiiiiihÏhhhhhhgÎggggggggffffffffeÌeeeeeeeedËddddddddcÊccccccccbÉbbbbbbbbaÈaaaaaaaaaa`Ç``````````_Æ__________^Å^^^^^^^^^^]Ä]]]]]]]]]]]]\Ã\\\\\\\\\\\\[Â[Â[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤q¤q£££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡n           mŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžkœœœœœœœœœœœi›››››››››hšššššššššg™™™™™™™™˜˜˜˜˜˜˜˜˜e————————––––––––•••••••••b””””””““““““““’’’’’’’’‘‘‘‘‘‘‘^ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹XŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††…………………R„„„„„Qƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwv©vvvvvvuuuuuuttttttssssssr¥rrrrrrqqqqqqp£ppppppoooooon¡nnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhgšggggggf™ffffffffe˜eeeeeeeed—ddddddddc–ccccccccb•bbbbbbbba”aaaaaaaaaa`“``````````_’__________^‘^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUUUUUUUUUT‡TTTTTTTTTTTTTTTTTTTTTTTTS†SSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€MMMMMMMMMMMMMMMMMMMMMMMMMMMM²²²²±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜——————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppoooooooonnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMMMMMMMM±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡            ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜——————————––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeddddddddddccccccccccccbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMMMMMMMMMMMMMMMMMMMMMM±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°ã°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡ Ó          ŸÒŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžÐœÏœœœœœœœœ›Î››››››››šÍšššššššš™Ì™™™™™™™™˜Ë˜˜˜˜˜˜˜˜————————–É––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘ÃÂŽŽŽŽŽŽÀŒŒŒŒŒŒ‹¾‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆ»ˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zzzzzzyyyyyyyFxxxxxxwwwwwwvvvvvvuuuuuuuBttttttssssssrrrrrrrrqqqqqqppppppppooooooo¥>¤¤¤¤¤¤¤¤¤¤¤¤¤=¤=£££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡:           9ŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžž76œœœœœœœœœœ›››››››››››4ššššššššš3™™™™™™™™˜˜˜˜˜˜˜˜˜1—————————0–––––––/••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘)ŽŽŽŽŽŽŽŽ&ŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠ#‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡ ††††††………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{zázzzzzzyyyyyyxxxxxxwwwwwwvÝvvvvvvuuuuuuttttttsÚssssssrrrrrrqØqqqqqqppppppoÖoooooonÕnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiÐiiiiiihÏhhhhhhhhggggggggfÍffffffffeÌeeeeeeeedËddddddddcÊccccccccbÉbbbbbbbbbbaÈaaaaaaaaaa`Ç``````````_Æ__________^Å^^^^^^^^^^^^]Ä]]]]]]]]]]]]\Ã\\\\\\\\\\\\[Â[Â[[[[[[[[[[[[ZÁZÁZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUT»TTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM´M´M´MMMMMM±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±~±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢o¢o¡¡¡¡¡¡¡¡¡¡¡n¡n           mŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžkjœœœœœœœœœœœi›››››››››hšššššššššg™™™™™™™™™f˜˜˜˜˜˜˜˜—————————d––––––––•••••••••b”””””””a“““““““`’’’’’’’_‘‘‘‘‘‘‘^ŽŽŽŽŽŽŽ[ŒŒŒŒŒŒŒY‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡†††††††S………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuut§ttttttssssssrrrrrrrrqqqqqqp£ppppppoooooooonnnnnnm mmmmmmlŸllllllkžkkkkkkjjjjjjjjjiiiiiiiihhhhhhhhgšggggggggf™ffffffffeeeeeeeeeeddddddddddc–ccccccccb•bbbbbbbbbba”aaaaaaaaaa`“``````````_’__________^‘^^^^^^^^^^^^]]]]]]]]]]]]]\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNM€M€M€±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––––••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssrrrrrrrrqqqqqqqqppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±±±±±±±±±±±±±±±±±±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––••••••••””””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrqqqqqqqqppppppppoooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggffffffffffeeeeeeeeeeddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa````````````____________^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±±±±±±±±±±±±±±±±±°ã°ã°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««ªÝªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡ Ó            ŸÒŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžÐœÏœœœœœœœœœœ›Î››››››››šÍšššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜—Ê————————–É––––––––••••••••””””””””“Æ““““““’Å’’’’’’‘Ä‘‘‘‘‘‘ÃŽŽŽŽŽŽŽŽŒ¿ŒŒŒŒŒŒ‹‹‹‹‹‹Š½ŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††………………„·„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€³€€€€€€~~~~~~}}}}}}||||||||{{{{{{zzzzzzyyyyyyyFxxxxxxwwwwwwvvvvvvvCuuuuuutttttttAssssssrrrrrrr?qqqqqqppppppppooooooo¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=¤=£££££££££££££<£<¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡:             9ŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžž76œœœœœœœœœ5›››››››››››4ššššššššš3™™™™™™™™™2˜˜˜˜˜˜˜˜˜1————————–––––––––/••••••••”””””””””-“““““““,’’’’’’’+‘‘‘‘‘‘‘*)ŽŽŽŽŽŽŽŽ&ŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}|ã||||||{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwvÝvvvvvvuuuuuutÛttttttssssssrÙrrrrrrqqqqqqqqppppppoÖoooooonÕnnnnnnmÔmmmmmmlÓllllllkÒkkkkkkkkjjjjjjjjiÐiiiiiihÏhhhhhhhhgÎggggggggffffffffffeeeeeeeeeedËddddddddcÊccccccccccbbbbbbbbbbaÈaaaaaaaaaa`Ç````````````_Æ__________^Å^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVU¼UUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNµNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±±~±~°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªw©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡n             mŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžkjœœœœœœœœœœ›››››››››››hšššššššššg™™™™™™™™™f˜˜˜˜˜˜˜˜˜e—————————d––––––––•••••••••b”””””””a““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽ[ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹XŠŠŠŠŠŠ‰‰‰‰‰‰‰Vˆˆˆˆˆˆ‡‡‡‡‡‡‡T††††††………………„„„„„„„Qƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{zzzzzzy¬yyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrq¤qqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjiiiiiiiih›hhhhhhhhggggggggf™ffffffffe˜eeeeeeeeeeddddddddddc–ccccccccb•bbbbbbbbbba”aaaaaaaaaa`“````````````_’__________^‘^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNN±±±±°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————––––––––––••••••••••””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````____________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««ªÝªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡ Ó            ŸÒŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžÐœÏœœœœœœœœœœ›Î››››››››šÍšššššššššš™Ì™™™™™™™™˜Ë˜˜˜˜˜˜˜˜—Ê————————––––––––•È••••••••”Ç””””””“Æ““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒ¿ŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰¼‰‰‰‰‰‰ˆˆˆˆˆˆ‡º‡‡‡‡‡‡††††††…¸………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{{Hzzzzzzyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttsssssss@rrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkkkkkkkkk8jjjjjjjjiiiiiiiii6hhhhhhhhh5ggggggggg4fffffffff3eeeeeeeee2ddddddddddccccccccccc0bbbbbbbbbbb/aaaaaaaaaaa.`````````````-___________,_,^^^^^^^^^^^^^+]]]]]]]]]]]]]*\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWW$VVVVVVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ ¤ £££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡             ŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžœœœœœœœœœœœ››››››››››ššššššššššš™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜—þ————————–ý––––––––••••••••”û””””””””““““““““’ù’’’’’’‘ø‘‘‘‘‘‘‘‘ŽõŽŽŽŽŽŽôŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyxxxxxxwwwwwwwvvvvvvuuuuuuuttttttssssssssrrrrrrr qqqqqqq ppppppp ooooooonnnnnnnmmmmmmmlllllllkkkkkkkkjjjjjjjjjiiiiiiiiihhhhhhhhggggggggggffffffffffeeeeeeeeeedþddddddddcýccccccccccbübbbbbbbbbbaûaaaaaaaaaa`ú````````````_ù____________^ø^^^^^^^^^^^^]÷]]]]]]]]]]]]\ö\\\\\\\\\\\\\\[õ[õ[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYYYYYXòXXXXXXXXXXXXXXXXXXWñWñWWWWWWWWWWWWWWWWWWVðVðVVVVVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUUUUUUUTîTTTTTTTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPêPêPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOéOéOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONèNèNNNNNNNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°I°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©©©©©©©©©©©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££<£<¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:             9ŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžž76œœœœœœœœœœœ5›››››››››››4ššššššššš3™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————–––––––––/••••••••”””””””””-““““““““’’’’’’’’‘‘‘‘‘‘‘‘‘*)ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹$ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvuuuuuuuuttttttsÚssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllkÒkkkkkkjÑjjjjjjjjiiiiiiiiiihhhhhhhhgÎggggggggfÍffffffffeÌeeeeeeeeeeddddddddddcÊccccccccccbÉbbbbbbbbbbaÈaaaaaaaaaa`Ç````````````_Æ____________^Å^^^^^^^^^^^^]Ä]]]]]]]]]]]]\Ã\Ã\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONµNµNµNNNNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n             mŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžkjœœœœœœœœœœœi›››››››››››hšššššššššš™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜e—————————d––––––––•••••••••b””””””””“““““““““`’’’’’’’_‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽZŒŒŒŒŒŒŒY‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰Vˆˆˆˆˆˆ‡‡‡‡‡‡‡T††††††…………………R„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~}°}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvu¨uuuuuuttttttttssssssr¥rrrrrrq¤qqqqqqppppppppoooooooonnnnnnnnm mmmmmmlŸllllllllkkkkkkkkjjjjjjjiœiiiiiiiih›hhhhhhhhgšggggggggf™ffffffffe˜eeeeeeeed—ddddddddddc–ccccccccccb•bbbbbbbbbba”aaaaaaaaaa`“````````````_’____________^‘^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTS†SSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNN°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••””””””””””““““““““’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONN°°°°°°°°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡              ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO°°°°°°°°°°°°°°°°°°°°¯â¯â¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««ªÝªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö£Ö££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó            ŸÒŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžÐœÏœœœœœœœœœœ›Î››››››››šÍšššššššššš™Ì™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜——————————––––––––•È••••••••”Ç””””””””““““““““’Å’’’’’’‘Ä‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹Š½ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡º‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}|||||||I{{{{{{zzzzzzzGyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuBtttttttAssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmm:llllllllkkkkkkkkk8jjjjjjjjiiiiiiiii6hhhhhhhhh5ggggggggg4ffffffffffeeeeeeeeeee2ddddddddddccccccccccc0bbbbbbbbbbbbaaaaaaaaaaaaa.`````````````-_____________,^^^^^^^^^^^^^+]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWW$VVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO°°°°°°°°°°°°°°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡             ŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžœœœœœœœœœœœ››››››››››ššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜—þ————————–ý––––––––••••••••••””””””””“ú““““““““’’’’’’’’‘‘‘‘‘‘‘‘÷öŽŽŽŽŽŽŽŽŒóŒŒŒŒŒŒ‹ò‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††…ì………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyxxxxxxxxwwwwwwwvvvvvvuuuuuuuuttttttttsssssss rrrrrrr qqqqqqq ppppppp ooooooonnnnnnnmmmmmmmmlllllllllkkkkkkkkjjjjjjjjjiiiiiiiiihhhhhhhhhgggggggggffffffffeÿeeeeeeeeeedþddddddddcýccccccccccbübbbbbbbbbbaûaaaaaaaaaaaa`ú````````````_ù____________^ø^^^^^^^^^^^^]÷]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\[õ[õ[[[[[[[[[[[[[[ZôZôZZZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYYYYYXòXXXXXXXXXXXXXXXXXXXXWñWWWWWWWWWWWWWWWWWWWWVðVðVVVVVVVVVVVVVVVVVVVVVVUïUUUUUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPêPêPêPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOéOéOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO°°°°°°°°°°°I°I¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H¯H®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©©©©©©©©©©©©©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:             9ŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžž6œœœœœœœœœœœ5›››››››››››4šššššššššš™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜1—————————0–––––––––/•••••••••.””””””””“““““““““,’’’’’’’’‘‘‘‘‘‘‘‘(ŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰"ˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyxßxxxxxxwwwwwwwwvvvvvvuÜuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmlÓllllllllkkkkkkkkjÑjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeÌeeeeeeeeeeddddddddddcÊccccccccccbÉbbbbbbbbbbaÈaaaaaaaaaaaa`Ç````````````_Æ____________^Å^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO°°°°°}°}°}¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªw©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££p£p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡n¡n             mŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžkjœœœœœœœœœœœi›››››››››››hšššššššššššg™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜——————————––––––––––•••••••••b”””””””””a““““““““’’’’’’’’’_‘‘‘‘‘‘‘^ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠW‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuut§ttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooon¡nnnnnnm mmmmmmmmllllllllkžkkkkkkkkjjjjjjjjiœiiiiiiiih›hhhhhhhhgšggggggggf™ffffffffffe˜eeeeeeeed—ddddddddddc–ccccccccccb•bbbbbbbbbba”aaaaaaaaaaaa`“````````````_’____________^‘^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OOOOOOOOOOOOOOOOOOOOOOOOOO°°°°¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••””””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••””””””””””““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««««ªÝªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó              ŸÒŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžÐœÏœœœœœœœœœœœœ››››››››››››šššššššššš™Ì™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜—Ê————————–É––––––––•È••••••••”Ç””””””””“Æ““““““““’’’’’’’’‘Ä‘‘‘‘‘‘ÃŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆ‡º‡‡‡‡‡‡††††††††……………………„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooonnnnnnnnn;mmmmmmmmlllllllll9kkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggg4fffffffff3eeeeeeeeeee2ddddddddddd1ccccccccccc0bbbbbbbbbbb/aaaaaaaaaaaaa.`````````````-_____________,^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTT!SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡               ŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžœœœœœœœœœœœœœ›››››››››››šššššššššš™™™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜—þ——————————––––––––––••••••••••””””””””“ú““““““““’ù’’’’’’’’‘‘‘‘‘‘‘‘öŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆïˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††………………„ë„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwvvvvvvvuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppp ooooooonnnnnnnnmmmmmmmmmllllllllkkkkkkkkkjjjjjjjjjiiiiiiiiihhhhhhhhhggggggggggffffffffffeÿeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbaûaaaaaaaaaaaa`ú````````````_ù____________^ø^^^^^^^^^^^^^^]÷]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\[õ[õ[[[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZYóYóYYYYYYYYYYYYYYYYYYXòXXXXXXXXXXXXXXXXXXXXWñWWWWWWWWWWWWWWWWWWWWWWVðVðVVVVVVVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPêPêPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOéOéOOOOOOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯H¯H®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©©©©©©©©©©©©©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:               9ŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžž7œœœœœœœœœœœœœ5›››››››››››4ššššššššššš3™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜———————————0–––––––––/•••••••••.””””””””““““““““““’’’’’’’’’+‘‘‘‘‘‘‘‘(ŽŽŽŽŽŽŽ'&ŒŒŒŒŒŒŒ%‹‹‹‹‹‹‹$ŠŠŠŠŠŠŠ#‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}|ã||||||{{{{{{{{zzzzzzyàyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuutÛttttttsÚssssssrÙrrrrrrqØqqqqqqp×ppppppppoooooooonnnnnnnnmÔmmmmmmmmllllllllkÒkkkkkkkkjÑjjjjjjjjiiiiiiiiiihhhhhhhhhhgÎggggggggfÍffffffffffeeeeeeeeeedËddddddddddcÊccccccccccbÉbbbbbbbbbbbbaÈaaaaaaaaaaaa`Ç````````````_Æ____________^Å^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVVVU¼UUUUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO¶O¶OOOOOOOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q¤q£££££££££££££££p£p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n               mŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžkjœœœœœœœœœœœi›››››››››››hšššššššššššg™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜e—————————d––––––––––•••••••••b”””””””””a“““““““““`’’’’’’’’‘‘‘‘‘‘‘‘‘^]ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡†††††††S………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyx«xxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooon¡nnnnnnnnmmmmmmmmlŸllllllllkkkkkkkkkkjjjjjjjjiœiiiiiiiih›hhhhhhhhhhggggggggggf™ffffffffe˜eeeeeeeeeed—ddddddddddc–ccccccccccb•bbbbbbbbbbbba”aaaaaaaaaaaa``````````````_’____________^‘^^^^^^^^^^^^^^]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUUUT‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPO‚O‚O‚OO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜——————————––––––––––––••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOO¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqqqppppppppoooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯®á®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««««ªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó              ŸÒŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžÐœÏœœœœœœœœœœ›Î››››››››››››šššššššššš™Ì™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜——————————–É––––––––•È••••••••”Ç””””””””“Æ““““““““’Å’’’’’’’’‘‘‘‘‘‘‘‘ÃŽŽŽŽŽŽŽŽÀŒ¿ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰ˆ»ˆˆˆˆˆˆ‡º‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}|||||||I{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwDvvvvvvvCuuuuuutttttttttAsssssss@rrrrrrr?qqqqqqqqppppppppooooooooo¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=¤=£££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:               9ŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžž76œœœœœœœœœœœœœ5›››››››››››4ššššššššššš3™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜1——————————–––––––––––/•••••••••.”””””””””-““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘*(ŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„ƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{zázzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqp×ppppppppoooooooonÕnnnnnnnnmmmmmmmmlÓllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihÏhhhhhhhhgÎggggggggggffffffffffeÌeeeeeeeeeedËddddddddddcÊccccccccccccbÉbbbbbbbbbbaÈaaaaaaaaaaaa`Ç``````````````_Æ____________^Å^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[ZÁZÁZZZZZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUT»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯¯¯¯¯¯|¯|®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªw©©©©©©©©©©©©©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n               mŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžkjœœœœœœœœœœœœœi›››››››››››hšššššššššššg™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜———————————d–––––––––c••••••••••””””””””””“““““““““`’’’’’’’’’_‘‘‘‘‘‘‘‘]ŽŽŽŽŽŽŽŽZŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††…………………R„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssr¥rrrrrrq¤qqqqqqqqppppppppoooooooooonnnnnnnnm mmmmmmmmllllllllkžkkkkkkkkjjjjjjjjjiœiiiiiiiiiihhhhhhhhhhgšggggggggf™ffffffffffe˜eeeeeeeeeed—ddddddddddc–ccccccccccccbbbbbbbbbbbba”aaaaaaaaaaaa`“``````````````_’____________^‘^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS†SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPƒPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯¯¯¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––••••••••••••””””””””””““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppoooooooooonnnnnnnnnnmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPP¯¯®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````______________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPP®á®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó              ŸÒŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžœœœœœœœœœœœœ›Î››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜—Ê——————————–É––––––––•È••••••••”Ç””””””””””““““““““’Å’’’’’’’’‘Ä‘‘‘‘‘‘‘‘ÂŽŽŽŽŽŽŽŽÀŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰¼‰‰‰‰‰‰ˆ»ˆˆˆˆˆˆ‡º‡‡‡‡‡‡†¹††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxEwwwwwwwDvvvvvvvCuuuuuuuuttttttttssssssssrrrrrrrrqqqqqqqqq>ppppppppooooooooo¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:¡:               9ŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžž76œœœœœœœœœœœœ›››››››››››››4ššššššššššš3™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜1——————————–––––––––––/•••••••••.”””””””””-“““““““““,’’’’’’’’’+‘‘‘‘‘‘‘‘(ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒ%‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttsÚssssssssrrrrrrrrqqqqqqqqp×ppppppppoooooooonÕnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihÏhhhhhhhhhhggggggggggfÍffffffffffeÌeeeeeeeeeedËddddddddddcÊccccccccccccbÉbbbbbbbbbbbbaÈaaaaaaaaaaaa`Ç``````````````_Æ____________^Å^Å^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQP·P·PPPPPPPPPP®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y«««««««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                 mŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœi›››››››››››hšššššššššššg™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜e———————————d–––––––––c••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘]ŽŽŽŽŽŽŽŽŽ[ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹XŠŠŠŠŠŠŠW‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuut§ttttttttssssssssrrrrrrrrq¤qqqqqqqqppppppppo¢oooooooonnnnnnnnm mmmmmmmmlŸllllllllkžkkkkkkkkjjjjjjjjjiœiiiiiiiiiihhhhhhhhhhgšggggggggggf™ffffffffffe˜eeeeeeeeeed—ddddddddddc–ccccccccccccb•bbbbbbbbbbbba”aaaaaaaaaaaa`“``````````````_’______________^‘^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPƒPƒPƒPPPP®®®®®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPP®®®®®®®®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————––––––––––––••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®®®®®®®®®®®®®®®­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö£Ö££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                ŸÒŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœ›Î››››››››››››šššššššššššš™Ì™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜—Ê——————————––––––––––•È••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ÂŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹¾‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡º‡‡‡‡‡‡†¹††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyFxxxxxxxEwwwwwwwwvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqq>ppppppppoooooooooonnnnnnnnn;mmmmmmmmm:lllllllll9kkkkkkkkk8jjjjjjjjjjiiiiiiiiiii6hhhhhhhhhhggggggggggg4fffffffffff3eeeeeeeeeee2ddddddddddddd1ccccccccccccbbbbbbbbbbbbbbb/aaaaaaaaaaaaa.```````````````-_______________,^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\)\)[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®®®®®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                 ŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžœœœœœœœœœœœœœ›››››››››››››ššššššššššš™™™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜˜˜—þ——————————–ý––––––––––••••••••••”û””””””””“ú““““““““’ù’’’’’’’’‘ø‘‘‘‘‘‘‘‘÷ŽŽŽŽŽŽŽŽôŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠñŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††…ì………………„ë„„„„„„ƒêƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~}}}}}}}|||||||{{{{{{{zzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuttttttttt ssssssssrrrrrrrrr qqqqqqqqppppppppp oooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhgggggggggggffffffffffeÿeeeeeeeeeedþddddddddddddcýccccccccccbübbbbbbbbbbbbbbaaaaaaaaaaaaaa`ú``````````````_ù______________^ø^^^^^^^^^^^^^^]÷]]]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\\\\\[õ[[[[[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYYYYYYYXòXXXXXXXXXXXXXXXXXXXXXXWñWñWWWWWWWWWWWWWWWWWWWWWWWWVðVVVVVVVVVVVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQëQëQëQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®®®®®®G®G­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©©©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                 9ŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžž76œœœœœœœœœœœœœ5›››››››››››››4ššššššššššš3™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜———————————0–––––––––––/•••••••••.””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘(ŽŽŽŽŽŽŽŽ&ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠ#‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuutÛttttttttssssssssrrrrrrrrrrqqqqqqqqp×ppppppppoooooooonÕnnnnnnnnmÔmmmmmmmmlÓllllllllkÒkkkkkkkkjÑjjjjjjjjjjiiiiiiiiiihÏhhhhhhhhhhgÎggggggggggffffffffffffeeeeeeeeeeeedËddddddddddddccccccccccccbÉbbbbbbbbbbbbaÈaaaaaaaaaaaaaa`Ç``````````````_Æ______________^Å^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVVVVVVVU¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®®{®{­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n               m mŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœi›››››››››››››hšššššššššššš™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜e——————————–––––––––––c••••••••••”””””””””””a“““““““““`’’’’’’’’’_‘‘‘‘‘‘‘‘‘^]ŽŽŽŽŽŽŽŽŽ[ŒŒŒŒŒŒŒŒŒY‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰Vˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvu¨uuuuuuuuttttttttssssssssr¥rrrrrrrrqqqqqqqqqqppppppppo¢oooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjiœiiiiiiiiiihhhhhhhhhhhhggggggggggf™ffffffffffe˜eeeeeeeeeeeed—ddddddddddc–ccccccccccccb•bbbbbbbbbbbba”aaaaaaaaaaaaaa`“``````````````_’______________^‘^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••••””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQQQQQ®®®®­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••””””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQQQQQ­à­à­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö£Ö££££££££££££££££¢Õ¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó              ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžÐœÏœœœœœœœœœœœœ›Î››››››››››››šššššššššššš™Ì™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜——————————–É––––––––––•È••••••••••””””””””””“Æ““““““““’Å’’’’’’’’‘Ä‘‘‘‘‘‘‘‘ÃÂŽŽŽŽŽŽŽŽŽŽŒ¿ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹Š½ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡º‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€²~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxEwwwwwwwwvvvvvvvvuuuuuuuuuBttttttttssssssssrrrrrrrrrrqqqqqqqqq>ppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkk8jjjjjjjjjjiiiiiiiiiii6hhhhhhhhhhh5ggggggggggg4fffffffffff3eeeeeeeeeee2ddddddddddddd1ccccccccccccc0bbbbbbbbbbbbb/aaaaaaaaaaaaaaa.```````````````-_______________,^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWWWWWW$VVVVVVVVVVVVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQQQQQQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡               ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžœœœœœœœœœœœœœ›››››››››››››ššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜—þ——————————––––––––––––••••••••••”û””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽôŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰ð‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡†í††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttsssssssss rrrrrrrrqqqqqqqqqqppppppppp ooooooooonnnnnnnnnmmmmmmmmmlllllllllkkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeedþddddddddddddcýccccccccccccbübbbbbbbbbbbbaûaaaaaaaaaaaaaa`ú``````````````_ù______________^ø^^^^^^^^^^^^^^^^]÷]]]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\\\\\[õ[[[[[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZZZZZYóYóYYYYYYYYYYYYYYYYYYYYXòXXXXXXXXXXXXXXXXXXXXXXXXWñWWWWWWWWWWWWWWWWWWWWWWWWVðVðVVVVVVVVVVVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQëQëQQQQQQQQQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªC©©©©©©©©©©©©©©©©©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§§§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:               9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžž76œœœœœœœœœœœœœ5›››››››››››››4šššššššššššš™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜1———————————0–––––––––––/••••••••••”””””””””””-“““““““““,’’’’’’’’’+‘‘‘‘‘‘‘‘‘*)(ŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒ%‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡†††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwvvvvvvvvuÜuuuuuuuuttttttttsÚssssssssrrrrrrrrqØqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjÑjjjjjjjjjjiiiiiiiiiihÏhhhhhhhhhhgÎggggggggggfÍffffffffffeÌeeeeeeeeeeeedËddddddddddddccccccccccccccbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaa````````````````_Æ______________^Å^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[ZÁZÁZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ¸Q¸QQQQQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n               mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžjœœœœœœœœœœœœœi›››››››››››››hšššššššššššššg™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜————————————–––––––––––c•••••••••••b”””””””””a““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽZŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹XŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰Vˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzy¬yyyyyyyyxxxxxxxxwwwwwwwwv©vvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqp£ppppppppo¢oooooooon¡nnnnnnnnm mmmmmmmmlŸllllllllkžkkkkkkkkkkjjjjjjjjjjiœiiiiiiiiiih›hhhhhhhhhhgšggggggggggf™ffffffffffe˜eeeeeeeeeeeeddddddddddddc–ccccccccccccb•bbbbbbbbbbbbbba”aaaaaaaaaaaa`“````````````````_’______________^‘^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQ„Q„QQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––••••••••••••””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa``````````````````________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQQQQ­­­­­­­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••””””””””””””““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQ­­­­­­­­­­­­­­­­­­­­­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ««««««««««««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££¢Õ¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó              ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœ››››››››››››››šššššššššššš™Ì™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜—Ê——————————–É––––––––––•È••••••••••””””””””””“Æ““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ÃÂŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹¾‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰¼‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxEwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrr?qqqqqqqqq>ppppppppoooooooooonnnnnnnnnnmmmmmmmmmmm:lllllllll9kkkkkkkkkkjjjjjjjjjjj7iiiiiiiiiii6hhhhhhhhhhh5ggggggggggg4ffffffffffffeeeeeeeeeeeee2ddddddddddddd1ccccccccccccc0bbbbbbbbbbbbbbb/aaaaaaaaaaaaa.```````````````-_________________,^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWW$VVVVVVVVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS S RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­­­­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡               ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœ›››››››››››››šššššššššššš™™™™™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜˜˜—þ————————————––––––––––•ü••••••••••”û””””””””””““““““““““’ù’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒóŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡†í††††††††……………………„„„„„„„„ƒêƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvvvvvvvvvuuuuuuuuttttttttt ssssssssrrrrrrrrrrqqqqqqqqqqppppppppp ooooooooonnnnnnnnnmmmmmmmmmmlllllllllllkkkkkkkkkjjjjjjjjjjjiiiiiiiiiihhhhhhhhhhhhhgggggggggggffffffffffeÿeeeeeeeeeeeedþddddddddddddcýccccccccccccbübbbbbbbbbbbbbbaaaaaaaaaaaaaa`ú``````````````_ù________________^ø^^^^^^^^^^^^^^^^]÷]]]]]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\\\\\[õ[[[[[[[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYYYYYYYYYXòXòXXXXXXXXXXXXXXXXXXXXXXWñWñWWWWWWWWWWWWWWWWWWWWWWWWVðVðVVVVVVVVVVVVVVVVVVVVVVVVVVVVUïUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRìRìRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­­­­­­­­­­F­F¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬E¬E«««««««««««««««««««««««««««««««««««D«DªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©©©©©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§§§§§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                 9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžž7œœœœœœœœœœœœœœœ5›››››››››››››4ššššššššššššš3™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜—————————————0––––––––––••••••••••••”””””””””””-““““““““““’’’’’’’’’’’+‘‘‘‘‘‘‘‘‘*)(ŽŽŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹$ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰"ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||{â{{{{{{{{zzzzzzzzyyyyyyyyxßxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssrÙrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmlÓllllllllllkkkkkkkkkkjÑjjjjjjjjjjiiiiiiiiiihÏhhhhhhhhhhhhggggggggggggffffffffffffeÌeeeeeeeeeeeedËddddddddddddcÊccccccccccccbÉbbbbbbbbbbbbaÈaaaaaaaaaaaaaa`Ç``````````````_Æ________________^Å^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­­­­­­z­z¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q¤q£££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                 mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœi›››››››››››››hšššššššššššššg™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜e———————————d–––––––––––c•••••••••••b””””””””””“““““““““““`’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒY‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwv©vvvvvvvvuuuuuuuut§ttttttttssssssssssrrrrrrrrq¤qqqqqqqqp£ppppppppo¢oooooooon¡nnnnnnnnm mmmmmmmmmmllllllllllkžkkkkkkkkkkjjjjjjjjjjiœiiiiiiiiiih›hhhhhhhhhhgšggggggggggf™ffffffffffffe˜eeeeeeeeeeeeddddddddddddddccccccccccccccb•bbbbbbbbbbbba”aaaaaaaaaaaaaa`“``````````````_’________________^‘^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””””““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­­­­­¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRR­­­­¬ß¬ß¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœ›Î››››››››››››››šššššššššššššš™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜————————————–É––––––––––•È••••••••••””””””””””””““““““““““’Å’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹Š½ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzGyyyyyyyyxxxxxxxxxEwwwwwwwwvvvvvvvvvCuuuuuuuuttttttttttsssssssss@rrrrrrrrr?qqqqqqqqppppppppppooooooooooo¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=¤=£££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                 9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœ5›››››››››››››4ššššššššššššš3™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜1————————————––––––––––––••••••••••••””””””””””””“““““““““““,’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘)(ŽŽŽŽŽŽŽŽŽ'&ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡ ††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwvÝvvvvvvvvuuuuuuuuuuttttttttsÚssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmÔmmmmmmmmmmllllllllllkÒkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggfÍffffffffffffeÌeeeeeeeeeeeeddddddddddddddcÊccccccccccccbÉbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaa`Ç``````````````_Æ________________^Å^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR¹R¹RRRRRRRRRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                 mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœi›››››››››››››hšššššššššššššg™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜—————————————d–––––––––––c•••••••••••b”””””””””””a““““““““““’’’’’’’’’’’_‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠW‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{z­zzzzzzzzyyyyyyyyx«xxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssr¥rrrrrrrrq¤qqqqqqqqp£ppppppppo¢oooooooon¡nnnnnnnnnnmmmmmmmmmmlŸllllllllllkkkkkkkkkkjjjjjjjjjjjiœiiiiiiiiiih›hhhhhhhhhhgšggggggggggggf™ffffffffffffeeeeeeeeeeeed—ddddddddddddddccccccccccccccb•bbbbbbbbbbbbbba”aaaaaaaaaaaaaa`“``````````````_’________________^‘^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR…R…RRRRRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa````````````````__________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRR¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžÐœœœœœœœœœœœœœœ›Î››››››››››››››šÍšššššššššššš™Ì™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜—Ê————————————––––––––––––•È••••••••••”Ç””””””””””““““““““““““’’’’’’’’’’‘Ä‘‘‘‘‘‘‘‘‘‘ŽÁŽŽŽŽŽŽŽŽÀŒ¿ŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…¸……………………„„„„„„„„ƒ¶ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚´€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxEwwwwwwwwvvvvvvvvvvuuuuuuuuuBttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppp=ooooooooo¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                 9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœ›››››››››››››››4ššššššššššššš3™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜˜1———————————0––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’+‘‘‘‘‘‘‘‘‘‘(ŽŽŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹$ŠŠŠŠŠŠŠŠŠ#‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡ ††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzyàyyyyyyyyxxxxxxxxxxwwwwwwwwvÝvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooonÕnnnnnnnnnnmmmmmmmmmmlÓllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhgÎggggggggggggffffffffffffeÌeeeeeeeeeeeeeeddddddddddddddcÊccccccccccccbÉbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaa`Ç``````````````_Æ__________________^Å^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]\Ã\Ã\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬y¬y«««««««««««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                 mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœi›››››››››››››hšššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜e————————————–––––––––––––c•••••••••••b”””””””””””a“““““““““““`’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘^ŽŽŽŽŽŽŽŽŽŽZŒŒŒŒŒŒŒŒŒY‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||{®{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvu¨uuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqp£ppppppppo¢oooooooooonnnnnnnnnnm mmmmmmmmmmllllllllllkžkkkkkkkkkkjjjjjjjjjjjiœiiiiiiiiiih›hhhhhhhhhhhhggggggggggggf™ffffffffffffe˜eeeeeeeeeeeed—ddddddddddddddccccccccccccccb•bbbbbbbbbbbbbba”aaaaaaaaaaaaaaaa`“``````````````_’__________________^‘^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\[Ž[Ž[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                  ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````____________________^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬¬¬«Þ«Þ««««««««««««««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö£Ö££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó Ó                ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœ›Î››››››››››››››šÍšššššššššššš™Ì™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜—Ê————————————––––––––––––•È••••••••••”Ç””””””””””“Æ““““““““““’Å’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘ÃŽÁŽŽŽŽŽŽŽŽÀŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰ˆ»ˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppp=oooooooooonnnnnnnnnnmmmmmmmmmmmmlllllllllll9kkkkkkkkkkk8jjjjjjjjjjj7iiiiiiiiiiiihhhhhhhhhhhhh5ggggggggggggffffffffffffffeeeeeeeeeeeeeee2ddddddddddddd1ccccccccccccccc0bbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaa.```````````````-___________________,^^^^^^^^^^^^^^^^^+^+]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSSSSSSSSSSSSSSSSSSSS¬¬¬¬¬¬««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                   ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœ›››››››››››››››ššššššššššššš™™™™™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜˜˜˜˜—þ————————————–ý––––––––––•ü••••••••••••””””””””””””““““““““““““’’’’’’’’’’‘ø‘‘‘‘‘‘‘‘‘‘öŽŽŽŽŽŽŽŽŽŽŒóŒŒŒŒŒŒŒŒ‹ò‹‹‹‹‹‹‹‹ŠñŠŠŠŠŠŠŠŠ‰ð‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…ì……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqq ppppppppppoooooooooonnnnnnnnnnnmmmmmmmmmmmllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhhgggggggggggggffffffffffffeÿeeeeeeeeeeeeeeddddddddddddddcýccccccccccccccbübbbbbbbbbbbbbbaûaaaaaaaaaaaaaaaa````````````````_ù__________________^ø^^^^^^^^^^^^^^^^^^]÷]]]]]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\\\\\\\[õ[[[[[[[[[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYYYYYYYYYYYXòXòXXXXXXXXXXXXXXXXXXXXXXXXXXWñWWWWWWWWWWWWWWWWWWWWWWWWWWWWVðVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSíSíSSSSSSSSSSSSSSSSSSSSSS¬E¬E«««««««««««««««««««««««««««««««««««««««DªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªC©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§§§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££<£<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                   9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœ5›››››››››››››››4šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜—————————————0––––––––––––•••••••••••••.”””””””””””-“““““““““““,’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘*(ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwvÝvvvvvvvvuÜuuuuuuuutÛttttttttsÚssssssssrÙrrrrrrrrqØqqqqqqqqqqppppppppppoooooooooonÕnnnnnnnnnnmmmmmmmmmmmmllllllllllkÒkkkkkkkkkkjÑjjjjjjjjjjjjiiiiiiiiiiiihÏhhhhhhhhhhgÎggggggggggggfÍffffffffffffeÌeeeeeeeeeeeedËddddddddddddddcÊccccccccccccccbÉbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaa`Ç````````````````_Æ__________________^Å^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSºSSSSSSSSSSSSSSSSSS«««««««««««««««««««««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                   mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžkœœœœœœœœœœœœœœœœ›››››››››››››››hšššššššššššššššg™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜e————————————–––––––––––––c•••••••••••b””””””””””””“““““““““““`’’’’’’’’’’’_‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽ[ŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡T††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppo¢oooooooooonnnnnnnnnnm mmmmmmmmmmlŸllllllllllkžkkkkkkkkkkjjjjjjjjjjjiœiiiiiiiiiiiihhhhhhhhhhhhgšggggggggggggf™ffffffffffffe˜eeeeeeeeeeeed—ddddddddddddddc–ccccccccccccccbbbbbbbbbbbbbbbba”aaaaaaaaaaaaaa`“````````````````_’__________________^‘^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[Ž[Ž[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTS†S†SSSSSSSSSSSSSS««««««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••””””””””””””””““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeddddddddddddddddccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSS««««««««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSS««««««««««««««««««««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                  ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœ›Î››››››››››››››šÍšššššššššššš™Ì™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————–É––––––––––––•È••••••••••”Ç””””””””””“Æ““““““““““’Å’’’’’’’’’’‘Ä‘‘‘‘‘‘‘‘‘‘ÃŽÁŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„ƒ¶ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyFxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttsssssssssss@rrrrrrrrr?qqqqqqqqqqppppppppppp=oooooooooonnnnnnnnnnn;mmmmmmmmmmm:lllllllllll9kkkkkkkkkkk8jjjjjjjjjjj7iiiiiiiiiiiihhhhhhhhhhhhh5ggggggggggggg4fffffffffffff3eeeeeeeeeeeeeee2ddddddddddddd1ccccccccccccccc0bbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaa.`````````````````-___________________,^^^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT!T!SSSSSSSS««««««««««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                   ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœ›››››››››››››››šššššššššššššš™™™™™™™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜˜˜˜˜—þ————————————––––––––––––•ü••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘öŽŽŽŽŽŽŽŽŽŽôŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxwwwwwwwwwvvvvvvvvvuuuuuuuuuttttttttt ssssssssssrrrrrrrrrrqqqqqqqqqqq ppppppppppooooooooooonnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiihhhhhhhhhhhhhgggggggggggggffffffffffffeÿeeeeeeeeeeeeeeddddddddddddddcýccccccccccccccbübbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaa`ú````````````````_ù__________________^ø^^^^^^^^^^^^^^^^^^]÷]]]]]]]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\\\\\\\[õ[[[[[[[[[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYYYYYYYYYYYXòXòXXXXXXXXXXXXXXXXXXXXXXXXXXWñWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVðVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUïUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSíSíSSSS«««««««««««««««««««««««««DªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§§§§§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££<£<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                   9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœ5›››››››››››››››4ššššššššššššššš3™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜˜1—————————————0––––––––––––•••••••••••••.”””””””””””-“““““““““““,’’’’’’’’’’’+‘‘‘‘‘‘‘‘‘‘‘*(ŽŽŽŽŽŽŽŽŽŽ&ŒŒŒŒŒŒŒŒŒ%‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††………………………„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrqØqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnmÔmmmmmmmmmmlÓllllllllllkÒkkkkkkkkkkjÑjjjjjjjjjjjjiiiiiiiiiiiihÏhhhhhhhhhhhhggggggggggggggffffffffffffffeÌeeeeeeeeeeeedËddddddddddddddcÊccccccccccccccbÉbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaa`Ç````````````````_Æ__________________^Å^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[[[ZÁZÁZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT»T»TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSºSº«««««««««««««««««««««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q¤q£££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                   mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœi›››››››››››››››hšššššššššššššššg™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜˜—————————————d–––––––––––––c••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘]ŽŽŽŽŽŽŽŽŽŽŽ[ŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹XŠŠŠŠŠŠŠŠŠW‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡T†††††††††S……………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||{®{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssr¥rrrrrrrrrrqqqqqqqqqqppppppppppo¢oooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiœiiiiiiiiiiiihhhhhhhhhhhhgšggggggggggggf™ffffffffffffffeeeeeeeeeeeeeed—ddddddddddddddc–ccccccccccccccb•bbbbbbbbbbbbbba”aaaaaaaaaaaaaaaa`“````````````````_’__________________^‘^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\[Ž[Ž[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT««««««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————––––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT««««««««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT««««««««««««ªÝªÝªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                  ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœœœ›Î››››››››››››››šÍšššššššššššššš™™™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜—Ê————————————–É––––––––––––••••••••••••”Ç””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽÁŽŽŽŽŽŽŽŽŽŽŒ¿ŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€³€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{HzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvCuuuuuuuuuBttttttttttssssssssssrrrrrrrrrrr?qqqqqqqqqqppppppppppp=oooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjj7iiiiiiiiiiiii6hhhhhhhhhhhhggggggggggggggfffffffffffffff3eeeeeeeeeeeeeeddddddddddddddd1ccccccccccccccccbbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaa.`````````````````-___________________,^^^^^^^^^^^^^^^^^^^+^+]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYYYYYY&XXXXXXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#V#UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"U"TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT««««««««««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                   ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœ›››››››››››››››ššššššššššššššš™™™™™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜˜˜˜˜—þ————————————–ý––––––––––––•ü••••••••••••””””””””””””“ú““““““““““’ù’’’’’’’’’’‘ø‘‘‘‘‘‘‘‘‘‘÷öŽŽŽŽŽŽŽŽŽŽôŒŒŒŒŒŒŒŒŒŒ‹ò‹‹‹‹‹‹‹‹ŠñŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzyyyyyyyyyxxxxxxxxxwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttsssssssssss rrrrrrrrrrqqqqqqqqqqppppppppppppooooooooooonnnnnnnnnnnmmmmmmmmmmmlllllllllllkkkkkkkkkkkjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhgggggggggggggffffffffffffffeÿeeeeeeeeeeeedþddddddddddddddcýccccccccccccccbübbbbbbbbbbbbbbbbaûaaaaaaaaaaaaaaaa`ú````````````````_ù__________________^ø^^^^^^^^^^^^^^^^^^^^]÷]]]]]]]]]]]]]]]]]]\ö\ö\\\\\\\\\\\\\\\\\\\\[õ[[[[[[[[[[[[[[[[[[[[[[ZôZôZZZZZZZZZZZZZZZZZZZZZZYóYóYYYYYYYYYYYYYYYYYYYYYYYYXòXòXXXXXXXXXXXXXXXXXXXXXXXXXXWñWñWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVðVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUïUïUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTîTîTTTTTTTTTTTTTTTTTTTTTTTTTT«««««««DªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªCªC©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§§§§§§§§§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                   9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœ5›››››››››››››››4ššššššššššššššš3™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––•••••••••••••.””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘(ŽŽŽŽŽŽŽŽŽŽ&ŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠ#‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttsÚssssssssssrrrrrrrrrrqqqqqqqqqqp×ppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjÑjjjjjjjjjjjjiiiiiiiiiiiihÏhhhhhhhhhhhhgÎggggggggggggfÍffffffffffffffeeeeeeeeeeeeeedËddddddddddddddcÊccccccccccccccbÉbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaa`Ç````````````````_Æ__________________^Å^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\[Â[Â[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT»TTTTTTTTTTTTTTTTTTTTTTTT«««x«xªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q¤q£££££££££££££££££££££p£p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                   mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœi›››››››››››››››hšššššššššššššššg™™™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜e—————————————d–––––––––––––c••••••••••••”””””””””””””a“““““““““““`’’’’’’’’’’’_‘‘‘‘‘‘‘‘‘‘‘^]ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰Vˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚O€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuut§ttttttttttssssssssssrrrrrrrrrrq¤qqqqqqqqqqppppppppppo¢oooooooooon¡nnnnnnnnnnm mmmmmmmmmmlŸllllllllllkžkkkkkkkkkkkkjjjjjjjjjjjjiœiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggf™ffffffffffffe˜eeeeeeeeeeeeeed—ddddddddddddddc–ccccccccccccccb•bbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaa`“````````````````_’__________________^‘^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTTTTTTTTTTTTTTTTTTT««ªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                    ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––••••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa``````````````````____________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó Ó                  ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœœœ››››››››››››››››šÍšššššššššššššš™Ì™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————–É––––––––––––••••••••••••”Ç””””””””””””“Æ““““““““““’Å’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ÂŽÁŽŽŽŽŽŽŽŽŽŽŒ¿ŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹Š½ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„·„„„„„„„„ƒ¶ƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwDvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppp=ooooooooooo¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=¤=£££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                     9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœœœ5›››››››››››››››4ššššššššššššššš3™™™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜˜1——————————————––––––––––––––•••••••••••••.””””””””””””“““““““““““““,’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘(ŽŽŽŽŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒŒŒŒŒ%‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰"ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxßxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuutÛttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqp×ppppppppppoÖoooooooooonnnnnnnnnnnnmmmmmmmmmmmmlÓllllllllllkÒkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiihÏhhhhhhhhhhhhgÎggggggggggggggffffffffffffffeÌeeeeeeeeeeeeeedËddddddddddddddcÊccccccccccccccccbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaaaa`Ç````````````````_Æ__________________^Å^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]\Ã\Ã\\\\\\\\\\\\\\\\\\\\[Â[Â[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT»TTTTTTªªªªªªªªªªªªªªªªªªªªªªªªªwªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££££p£p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                     mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœœœi›››››››››››››››hšššššššššššššššg™™™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜˜———————————————d–––––––––––––c••••••••••••”””””””””””””a““““““““““““’’’’’’’’’’’’’_‘‘‘‘‘‘‘‘‘‘‘^]ŽŽŽŽŽŽŽŽŽŽŽŽZŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹XŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆU‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{z­zzzzzzzzy¬yyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvu¨uuuuuuuuuuttttttttttssssssssssr¥rrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooon¡nnnnnnnnnnm mmmmmmmmmmmmllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiœiiiiiiiiiiiih›hhhhhhhhhhhhgšggggggggggggf™ffffffffffffffe˜eeeeeeeeeeeeeed—ddddddddddddddc–ccccccccccccccb•bbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaaaa`“````````````````_’__________________^‘^‘^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUT‡T‡TTªªªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````______________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTªªªªªªªªªªªªªªªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUªªªªªªªªªªªªªªªªªª©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                    ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžÐœœœœœœœœœœœœœœœœ›Î››››››››››››››››šÍšššššššššššššš™Ì™™™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜—Ê——————————————––––––––––––––••••••••••••••””””””””””””“Æ““““““““““““’’’’’’’’’’’’‘Ä‘‘‘‘‘‘‘‘‘‘ÃÂŽÁŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹¾‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰¼‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡†¹††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}J||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwDvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqq>ppppppppppp=ooooooooooo¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                     9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžžž6œœœœœœœœœœœœœœœœœ5›››››››››››››››4ššššššššššššššš3™™™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜1—————————————0––––––––––––––•••••••••••••.”””””””””””””-““““““““““““’’’’’’’’’’’’’+‘‘‘‘‘‘‘‘‘‘‘*)(ŽŽŽŽŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹$ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰"ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††……………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}ä}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrqØqqqqqqqqqqp×ppppppppppoÖoooooooooonÕnnnnnnnnnnmÔmmmmmmmmmmmmllllllllllllkÒkkkkkkkkkkkkjjjjjjjjjjjjiÐiiiiiiiiiiiihÏhhhhhhhhhhhhhhggggggggggggggfÍffffffffffffffeÌeeeeeeeeeeeeeedËddddddddddddddcÊccccccccccccccccbÉbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaa`Ç``````````````````_Æ____________________^Å^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\\\[Â[Â[[[[[[[[[[[[[[[[[[[[[[ZÁZÁZZZZZZZZZZZZZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼U¼UUUUUUUUUUUUUUUUUUUUUUUUUUUUªªªªªªªªªw©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                     mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœœœi›››››››››››››››hšššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜e——————————————–––––––––––––––c••••••••••••””””””””””””””“““““““““““““`’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒY‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆU‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyx«xxxxxxxxxxwwwwwwwwwwvvvvvvvvvvu¨uuuuuuuuuutttttttttts¦ssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmlŸllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjiœiiiiiiiiiiiih›hhhhhhhhhhhhgšggggggggggggggf™ffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddc–ccccccccccccccccb•bbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaa`“``````````````````_’____________________^‘^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUUUUUUUUUUUUUUUUUUªªªªªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUªªªª©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUU©Ü©Ü©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö£Ö££££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                    ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœœœ›Î››››››››››››››››šÍšššššššššššššš™Ì™™™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜˜˜—Ê————————————–É––––––––––––––••••••••••••••””””””””””””””““““““““““““’Å’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ÂŽÁŽŽŽŽŽŽŽŽŽŽÀŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹Š½ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡†¹††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zzzzzzzzzzzGyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuBttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqq>ppppppppppp=ooooooooooo¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                     9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœœœœ›››››››››››››››››4šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜———————————————0–––––––––––––/••••••••••••••”””””””””””””-“““““““““““““,’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘*)ŽŽŽŽŽŽŽŽŽŽŽŽ&ŒŒŒŒŒŒŒŒŒŒŒ%‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡†††††††††††…………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{zázzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvuÜuuuuuuuuuuttttttttttttssssssssssrÙrrrrrrrrrrqØqqqqqqqqqqp×ppppppppppoÖoooooooooonÕnnnnnnnnnnnnmmmmmmmmmmmmlÓllllllllllllkkkkkkkkkkkkjÑjjjjjjjjjjjjiÐiiiiiiiiiiiiiihhhhhhhhhhhhhhgÎggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeedËddddddddddddddcÊccccccccccccccccbÉbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaaaa`Ç``````````````````_Æ____________________^Å^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\\\[Â[Â[[[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYYYYYX¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU¼UUUUUUUUUUUU©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                     mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžžžkœœœœœœœœœœœœœœœœœœœi›››››››››››››››hšššššššššššššššššg™™™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜e—————————————d––––––––––––––•••••••••••••••b””””””””””””““““““““““““““’’’’’’’’’’’’’_‘‘‘‘‘‘‘‘‘‘‘‘\ŽŽŽŽŽŽŽŽŽŽŽ[ŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠW‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆU‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuut§ttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnm mmmmmmmmmmmmllllllllllllkžkkkkkkkkkkkkjjjjjjjjjjjjjiœiiiiiiiiiiiih›hhhhhhhhhhhhhhggggggggggggggf™ffffffffffffffe˜eeeeeeeeeeeeeeeeddddddddddddddddc–ccccccccccccccccb•bbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaaaa`“``````````````````_’____________________^‘^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYYYYYYYYYYYYYYYX‹X‹XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUˆUˆUUUUUUUU©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUU©©©©©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUU©©©©©©©©©©©©©©©©©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                      ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœœœ›Î››››››››››››››››šÍšššššššššššššš™Ì™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————–É––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘Ä‘‘‘‘‘‘‘‘‘‘‘‘ÂŽÁŽŽŽŽŽŽŽŽŽŽÀŒ¿ŒŒŒŒŒŒŒŒŒŒ‹¾‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆ»ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…………………………„·„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}|||||||||||I{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttsssssssssss@rrrrrrrrrrr?qqqqqqqqqqq>ppppppppppp=oooooooooooonnnnnnnnnnnnn;mmmmmmmmmmmmlllllllllllll9kkkkkkkkkkkkk8jjjjjjjjjjjjj7iiiiiiiiiiiii6hhhhhhhhhhhhhhh5ggggggggggggggfffffffffffffffff3eeeeeeeeeeeeeee2ddddddddddddddddd1ccccccccccccccccc0bbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaaaa.```````````````````-_____________________,^^^^^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]]]]]*]*\\\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#UU©©©©©©©©©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                       ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœ›››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜˜˜˜˜˜˜—þ——————————————––––––––––––––•ü••••••••••••”û””””””””””””“ú““““““““““““’ù’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘÷ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡†í††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚耀€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhgggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeedþddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbaûaaaaaaaaaaaaaaaaaa`ú``````````````````_ù____________________^ø^^^^^^^^^^^^^^^^^^^^]÷]÷]]]]]]]]]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\\\\\\\\\[õ[õ[[[[[[[[[[[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZZZZZZZZZZZYóYóYYYYYYYYYYYYYYYYYYYYYYYYYYYYXòXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWñWñWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVðVðVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUï©©©©©©©©©©©©©©©©©©©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A¨A§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                     9 9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœœœ5›››››››››››››››››4ššššššššššššššššš3™™™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜1———————————————0–––––––––––––/••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’+‘‘‘‘‘‘‘‘‘‘‘‘)ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠ#‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzyàyyyyyyyyyyxxxxxxxxxxwÞwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuutÛttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoÖoooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkÒkkkkkkkkkkkkjÑjjjjjjjjjjjjiÐiiiiiiiiiiiiiihhhhhhhhhhhhhhgÎggggggggggggggfÍffffffffffffffeÌeeeeeeeeeeeeeeeeddddddddddddddddcÊccccccccccccccccbÉbbbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaaaa`Ç``````````````````_Æ____________________^Å^^^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV½VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV©©©©©©©©©©©©©©©v©v¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q¤q£££££££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                     mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššg™™™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜———————————————d––––––––––––––•••••••••••••••b”””””””””””””a“““““““““““““`’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘^\ŽŽŽŽŽŽŽŽŽŽŽ[ZŒŒŒŒŒŒŒŒŒŒŒY‹‹‹‹‹‹‹‹‹‹‹XŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆU‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuutttttttttts¦ssssssssssr¥rrrrrrrrrrq¤qqqqqqqqqqp£ppppppppppppoooooooooooonnnnnnnnnnnnm mmmmmmmmmmmmlŸllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiœiiiiiiiiiiiih›hhhhhhhhhhhhhhgšggggggggggggggf™ffffffffffffffe˜eeeeeeeeeeeeeed—ddddddddddddddddc–ccccccccccccccccb•bbbbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaaaa`“``````````````````_’____________________^‘^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV©©©©©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV©©©©©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVVVVVVVVVVVVVVVV©©©©©©©©¨Û¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££££££¢Õ¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                    ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžžžžžœÏœœœœœœœœœœœœœœœœœœ›Î››››››››››››››››šÍšššššššššššššššš™™™™™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜˜˜—Ê——————————————–É––––––––––––––••••••••••••••”Ç””””””””””””“Æ““““““““““““’Å’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽÁŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰¼‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}J||||||||||{{{{{{{{{{{HzzzzzzzzzzyyyyyyyyyyyFxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvCuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqq>ppppppppppp=oooooooooooonnnnnnnnnnnnn;mmmmmmmmmmmmm:llllllllllllkkkkkkkkkkkkkkk8jjjjjjjjjjjjj7iiiiiiiiiiiiiihhhhhhhhhhhhhhh5ggggggggggggggg4fffffffffffffff3eeeeeeeeeeeeeeeee2ddddddddddddddddd1ccccccccccccccccc0bbbbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaaaa.```````````````````-_____________________,^^^^^^^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[[[[[(ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ'YYYYYYYYYYYYYYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%X%WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVVVVVVVVVVVVVVVVVV©©©©©©¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§ ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ £££££££££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                     ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœ›››››››››››››››››ššššššššššššššššš™™™™™™™™™™™™™™˜ÿ˜˜˜˜˜˜˜˜˜˜˜˜˜˜—þ——————————————–ý––––––––––––––•ü••••••••••••”û””””””””””””””““““““““““““““’’’’’’’’’’’’‘ø‘‘‘‘‘‘‘‘‘‘‘‘÷ŽŽŽŽŽŽŽŽŽŽŽŽôŒóŒŒŒŒŒŒŒŒŒŒ‹ò‹‹‹‹‹‹‹‹‹‹ŠñŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡†í††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuttttttttttt sssssssssss rrrrrrrrrrr qqqqqqqqqqqqppppppppppppooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhgggggggggggggggffffffffffffffeÿeeeeeeeeeeeeeeeedþddddddddddddddddccccccccccccccccccbübbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa`ú``````````````````_ù____________________^ø^^^^^^^^^^^^^^^^^^^^^^]÷]]]]]]]]]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\\\\\\\\\\\[õ[õ[[[[[[[[[[[[[[[[[[[[[[[[ZôZôZZZZZZZZZZZZZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXòXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWñWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVðVVVVVVVVVVVVVVVVVVVVVV©B©B¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§@§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=¤=£££££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                     9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœœœ5›››››››››››››››››4ššššššššššššššššš3™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————–––––––––––––––/••••••••••••••”””””””””””””””-“““““““““““““,’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘)ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwÞwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoÖoooooooooooonnnnnnnnnnnnmÔmmmmmmmmmmmmlÓllllllllllllkÒkkkkkkkkkkkkjÑjjjjjjjjjjjjiÐiiiiiiiiiiiiiihÏhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeÌeeeeeeeeeeeeeeeeddddddddddddddddcÊccccccccccccccccccbÉbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaaaaaa`Ç``````````````````_Æ____________________^Å^^^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]]]\Ã\Ã\\\\\\\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV½V½VVVVVVVVVVVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                     mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœœœi››››››››››››››››››šššššššššššššššššg™™™™™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜e———————————————d––––––––––––––•••••••••••••••b”””””””””””””a““““““““““““““’’’’’’’’’’’’’_‘‘‘‘‘‘‘‘‘‘‘‘‘^\ŽŽŽŽŽŽŽŽŽŽŽ[ŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆU‡‡‡‡‡‡‡‡‡‡††††††††††††……………………………R„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwv©vvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqp£ppppppppppppoooooooooooon¡nnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiœiiiiiiiiiiiiiihhhhhhhhhhhhhhgšggggggggggggggf™ffffffffffffffffeeeeeeeeeeeeeeeed—ddddddddddddddddc–ccccccccccccccccccbbbbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaaaaaa````````````````````_’____________________^‘^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰V‰VVVVVVVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••””””””””””””””““““““““““““““““’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                      ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````______________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVVVVVVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££££££¢Õ¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                    ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœœœœœ›Î››››››››››››››››šÍšššššššššššššššš™Ì™™™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••”Ç””””””””””””“Æ““““““““““““““’’’’’’’’’’’’‘Ä‘‘‘‘‘‘‘‘‘‘‘‘ÃŽÁŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹Š½ŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††…¸…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjj7iiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggfffffffffffffffff3eeeeeeeeeeeeeeeee2ddddddddddddddddd1ccccccccccccccccc0bbbbbbbbbbbbbbbbbbb/aaaaaaaaaaaaaaaaaaa.`````````````````````-_____________________,_,^^^^^^^^^^^^^^^^^^^^^+]]]]]]]]]]]]]]]]]]]]]]]]]*\\\\\\\\\\\\\\\\\\\\\\\\\)[[[[[[[[[[[[[[[[[[[[[[[[[[[([(ZZZZZZZZZZZZZZZZZZZZZZZZZZZ'Z'YYYYYYYYYYYYYYYYYYYYYYYYYYYYY&Y&XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW$W$VVVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§ § ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ ¦ ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥ ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ ¤ £££££££££££££££££££££££££ ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                     ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœ›››››››››››››››››ššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜—þ——————————————–ý––––––––––––––•ü••••••••••••”û””””””””””””””““““““““““““““’ù’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘öŽŽŽŽŽŽŽŽŽŽŽŽôŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}|||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrr qqqqqqqqqqq ppppppppppppooooooooooooonnnnnnnnnnnnnmmmmmmmmmmmmmlllllllllllllkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiihhhhhhhhhhhhhhhgggggggggggggggffffffffffffffffeÿeeeeeeeeeeeeeeeeddddddddddddddddddcýccccccccccccccccbübbbbbbbbbbbbbbbbbbaûaaaaaaaaaaaaaaaaaa`ú````````````````````_ù______________________^ø^^^^^^^^^^^^^^^^^^^^]÷]÷]]]]]]]]]]]]]]]]]]]]]]\ö\\\\\\\\\\\\\\\\\\\\\\\\[õ[õ[[[[[[[[[[[[[[[[[[[[[[[[[[ZôZZZZZZZZZZZZZZZZZZZZZZZZZZZZYóYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXòXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWñWñWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVðVVVVVV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨A§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§@¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦?¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥>¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                     9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžžžžžž76œœœœœœœœœœœœœœœœœœœ5›››››››››››››››››4ššššššššššššššššš3™™™™™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜1———————————————0–––––––––––––––/••••••••••••••”””””””””””””””-“““““““““““““,’’’’’’’’’’’’’+‘‘‘‘‘‘‘‘‘‘‘‘‘*ŽŽŽŽŽŽŽŽŽŽŽŽ&ŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡ ††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxwÞwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrÙrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppoÖoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjiÐiiiiiiiiiiiiiihÏhhhhhhhhhhhhhhgÎggggggggggggggfÍffffffffffffffffeeeeeeeeeeeeeeeedËddddddddddddddddddccccccccccccccccccbÉbbbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaaaa`Ç````````````````````_Æ______________________^Å^^^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV½V½VV¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨u¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n¡n                     mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœœœœœi››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––•••••••••••••••b””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘\ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒŒY‹‹‹‹‹‹‹‹‹‹‹XŠŠŠŠŠŠŠŠŠŠŠW‰‰‰‰‰‰‰‰‰‰‰VˆˆˆˆˆˆˆˆˆˆˆU‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„Qƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚N€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwv©vvvvvvvvvvu¨uuuuuuuuuut§tttttttttts¦ssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqp£ppppppppppppoooooooooooon¡nnnnnnnnnnnnm mmmmmmmmmmmmlŸllllllllllllkžkkkkkkkkkkkkjjjjjjjjjjjjjjjiœiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggf™ffffffffffffffe˜eeeeeeeeeeeeeeeed—ddddddddddddddddc–ccccccccccccccccccb•bbbbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaaaa`“````````````````````_’______________________^‘^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYŒYŒYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWV‰¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““’’’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨¨¨¨¨¨¨¨¨§Ú§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö££££££££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                      ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœœœœœ››››››››››››››››››šÍšššššššššššššššš™Ì™™™™™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜˜˜—Ê————————————————––––––––––––––•È••••••••••••••”Ç””””””””””””””““““““““““““““’’’’’’’’’’’’’’‘Ä‘‘‘‘‘‘‘‘‘‘‘‘ŽÁŽŽŽŽŽŽŽŽŽŽŽŽŒ¿ŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††…………………………„·„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttsssssssssssss@rrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppooooooooooooo¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                       9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžžžžž6œœœœœœœœœœœœœœœœœœœ5›››››››››››››››››4šššššššššššššššššš™™™™™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜1———————————————0–––––––––––––––/•••••••••••••••.””””””””””””””““““““““““““““’’’’’’’’’’’’’’’+‘‘‘‘‘‘‘‘‘‘‘‘‘*ŽŽŽŽŽŽŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹$ŠŠŠŠŠŠŠŠŠŠŠ#‰‰‰‰‰‰‰‰‰‰‰"ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡ †††††††††††…………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyxßxxxxxxxxxxwÞwwwwwwwwwwvÝvvvvvvvvvvuÜuuuuuuuuuutÛttttttttttttssssssssssssrrrrrrrrrrrrqØqqqqqqqqqqqqppppppppppppoÖoooooooooooonÕnnnnnnnnnnnnmmmmmmmmmmmmmmlÓllllllllllllkÒkkkkkkkkkkkkkkjjjjjjjjjjjjjjiÐiiiiiiiiiiiiiihÏhhhhhhhhhhhhhhgÎggggggggggggggggffffffffffffffffeÌeeeeeeeeeeeeeeeedËddddddddddddddddddccccccccccccccccccbÉbbbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaaaaaa`Ç````````````````````_Æ______________________^Å^^^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[[[[[[[ZÁZÁZZZZZZZZZZZZZZZZZZZZZZZZZZZZYÀYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾W¾WWWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨¨u§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                       mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœœœœœi››››››››››››››››››šššššššššššššššššššg™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜e————————————————––––––––––––––––•••••••••••••••b”””””””””””””””a“““““““““““““`’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘]\ŽŽŽŽŽŽŽŽŽŽŽŽZŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆˆU‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssr¥rrrrrrrrrrrrqqqqqqqqqqqqp£ppppppppppppoooooooooooooonnnnnnnnnnnnm mmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjiœiiiiiiiiiiiiiihhhhhhhhhhhhhhhhgšggggggggggggggf™ffffffffffffffffe˜eeeeeeeeeeeeeeeed—ddddddddddddddddc–ccccccccccccccccccb•bbbbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaaaaaa`“````````````````````_’______________________^‘^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWŠWWWWWWWWWWWWWWWWWWWWWW¨¨¨¨¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜——————————————————––––––––––––––––••••••••••••••””””””””””””””””““““““““““““““’’’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWWWWWW¨¨§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppppoooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkjjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffffeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWWWWWWWWWWWWW§Ú§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦Ù¦Ù¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥Ø¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤×¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤£Ö£Ö££££££££££££££££££££££££££¢Õ¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡Ô¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡ Ó                      ŸÒŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžÑžžžžžžžžžžžžžžžžžžžžÐœÏœœœœœœœœœœœœœœœœœœ›Î››››››››››››››››››šÍšššššššššššššššš™Ì™™™™™™™™™™™™™™™™˜Ë˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜—Ê——————————————–É––––––––––––––•È••••••••••••••”Ç””””””””””””””““““““““““““““’Å’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽÁŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹Š½ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}}J||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuuBttttttttttttssssssssssssrrrrrrrrrrrrr?qqqqqqqqqqqqppppppppppppppooooooooooooo¥>¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤=£££££££££££££££££££££££££££<¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢;¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡:                       9ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸ8žžžžžžžžžžžžžžžžžžžžž7œœœœœœœœœœœœœœœœœœœœœ5›››››››››››››››››4ššššššššššššššššššš3™™™™™™™™™™™™™™™™™2˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜—————————————————0–––––––––––––––/•••••••••••••••.””””””””””””””“““““““““““““““,’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽ'ŒŒŒŒŒŒŒŒŒŒŒŒŒ%‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰‰"ˆˆˆˆˆˆˆˆˆˆˆ!‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}}}||||||||||{â{{{{{{{{{{zázzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwÞwwwwwwwwwwvÝvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttssssssssssssssrrrrrrrrrrrrqqqqqqqqqqqqqqppppppppppppoÖoooooooooooonÕnnnnnnnnnnnnmÔmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiÐiiiiiiiiiiiiiihÏhhhhhhhhhhhhhhhhggggggggggggggggfÍffffffffffffffffeÌeeeeeeeeeeeeeeeedËddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbaÈaaaaaaaaaaaaaaaaaaaa`Ç````````````````````_Æ______________________^Å^^^^^^^^^^^^^^^^^^^^^^^^]Ä]]]]]]]]]]]]]]]]]]]]]]]]\Ã\\\\\\\\\\\\\\\\\\\\\\\\\\[Â[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZÁZZZZZZZZZZZZZZZZZZZZZZZZZZZZYÀYÀYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX¿X¿XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXW¾WWWWWWWWWW§§§§§§§§§§§§§§§§§§§§§§§§§§§§§t¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦s¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥r¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤q£££££££££££££££££££££££££££p¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢o¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡n                       mŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸlžžžžžžžžžžžžžžžžžžžžžkjœœœœœœœœœœœœœœœœœœœi››››››››››››››››››šššššššššššššššššššg™™™™™™™™™™™™™™™™™f˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜e———————————————d––––––––––––––––••••••••••••••••”””””””””””””””a““““““““““““““’’’’’’’’’’’’’’’_‘‘‘‘‘‘‘‘‘‘‘‘‘^]\ŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡‡T†††††††††††S……………………………R„„„„„„„„„„„Qƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzy¬yyyyyyyyyyx«xxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuutttttttttttts¦ssssssssssssrrrrrrrrrrrrq¤qqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmlŸllllllllllllkžkkkkkkkkkkkkkkjjjjjjjjjjjjjjjiiiiiiiiiiiiiiiih›hhhhhhhhhhhhhhgšggggggggggggggggf™ffffffffffffffffeeeeeeeeeeeeeeeeeed—ddddddddddddddddc–ccccccccccccccccccb•bbbbbbbbbbbbbbbbbbbba”aaaaaaaaaaaaaaaaaaaa`“````````````````````_’______________________^‘^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\[Ž[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYŒYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYX‹XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWŠWŠWWWWWW§§§§§§§§§§§§§§§§§§§§§§§§§§§§¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤££££££££££££££££££££££££££££¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡                        ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸžžžžžžžžžžžžžžžžžžžžžžœœœœœœœœœœœœœœœœœœœœ››››››››››››››››››››šššššššššššššššššš™™™™™™™™™™™™™™™™™™˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜˜————————————————––––––––––––––––––••••••••••••••••””””””””””””””““““““““““““““““’’’’’’’’’’’’’’‘‘‘‘‘‘‘‘‘‘‘‘‘‘ŽŽŽŽŽŽŽŽŽŽŽŽŽŽŒŒŒŒŒŒŒŒŒŒŒŒ‹‹‹‹‹‹‹‹‹‹‹‹‹‹ŠŠŠŠŠŠŠŠŠŠŠŠ‰‰‰‰‰‰‰‰‰‰‰‰ˆˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡‡‡‡‡‡‡‡‡††††††††††††………………………………„„„„„„„„„„„„ƒƒƒƒƒƒƒƒƒƒƒƒ‚‚‚‚‚‚‚‚‚‚‚‚€€€€€€€€€€€€~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||{{{{{{{{{{{{zzzzzzzzzzzzyyyyyyyyyyyyxxxxxxxxxxxxwwwwwwwwwwwwvvvvvvvvvvvvuuuuuuuuuuuuttttttttttttttssssssssssssrrrrrrrrrrrrrrqqqqqqqqqqqqppppppppppppppoooooooooooooonnnnnnnnnnnnnnmmmmmmmmmmmmmmmmllllllllllllllkkkkkkkkkkkkkkkkjjjjjjjjjjjjjjiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhggggggggggggggggggffffffffffffffffeeeeeeeeeeeeeeeeeeeeddddddddddddddddddccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaa``````````````````````________________________^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXWWWWWWmlt-6.20.0/src/tests/common.pri000066400000000000000000000011221362234133600163270ustar00rootroot00000000000000QT += testlib QT -= gui CONFIG += console CONFIG -= app_bundle CONFIG += testcase CONFIG += c++11 QMAKE_CXXFLAGS += -std=c++11 TEMPLATE = app DEFINES += SRCDIR=\\\"$$PWD/\\\" win32 { INCLUDEPATH += $$PWD/.. LIBS += -L$$PWD/../framework -L$$PWD/../mlt++ -lmlt++ -lmlt isEmpty(PREFIX) { message("Install PREFIX not set; using C:\\Projects\\Shotcut. You can change this with 'qmake PREFIX=...'") PREFIX = C:\\Projects\\Shotcut } target.path = $$PREFIX INSTALLS += target } else { CONFIG += link_pkgconfig PKGCONFIG += mlt++ } mlt-6.20.0/src/tests/setenv000066400000000000000000000003071362234133600155560ustar00rootroot00000000000000export MLT_REPOSITORY=`pwd`/../modules export LD_LIBRARY_PATH=`pwd`/../framework:\ `pwd`/../modules/bluefish:\ `pwd`/../../../bluefish/lib:\ `pwd`/../../../mpeg_sdk_demo/bin:\ `pwd`/../../../dv_sdk mlt-6.20.0/src/tests/test_animation/000077500000000000000000000000001362234133600173455ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_animation/test_animation.cpp000066400000000000000000000232271362234133600230750ustar00rootroot00000000000000/* * Copyright (C) 2015-2018 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 consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestAnimation : public QObject { Q_OBJECT private Q_SLOTS: void DefaultConstructorIsInvalid() { Animation a; QVERIFY(!a.is_valid()); } void ConstructFromProperties() { Properties p; p.anim_set("foo", "bar", 10); Animation* a = p.get_anim("foo"); QVERIFY(a); QVERIFY(a->is_valid()); delete a; a = p.get_anim("bar"); QVERIFY(a); QVERIFY(!a->is_valid()); } void ConstructFromCType() { Properties p; p.anim_set("foo", "bar", 10); Animation a1(p.get_animation("foo")); QVERIFY(a1.is_valid()); Animation a2(a1.get_animation()); QVERIFY(a2.is_valid()); QVERIFY(a1.get_animation() == a2.get_animation()); } void CopyConstructor() { Properties p; p.anim_set("foo", "bar", 10); Animation a1(p.get_animation("foo")); QVERIFY(a1.is_valid()); Animation a2(a1); QVERIFY(a2.is_valid()); QVERIFY(a1.get_animation() == a2.get_animation()); } void Assignment() { Properties p; p.anim_set("foo", "bar", 10); Animation a1 = p.get_animation("foo"); QVERIFY(a1.is_valid()); Animation a2; a2 = a1; QVERIFY(a2.is_valid()); QVERIFY(a1.get_animation() == a2.get_animation()); } void LengthIsCorrect() { Properties p; p.anim_set("foo", "bar", 10); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.length(), 10); } void IsAKeyFrame() { Properties p; p.anim_set("foo", "bar", 10); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QVERIFY(!a.is_key(5)); QVERIFY(a.is_key(10)); } void KeyFrameTypeIsDiscrete() { Properties p; p.anim_set("foo", "bar", 10); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.keyframe_type(0), mlt_keyframe_discrete); QCOMPARE(a.keyframe_type(10), mlt_keyframe_discrete); QCOMPARE(a.keyframe_type(11), mlt_keyframe_discrete); } void KeyFrameTypeIsLinear() { Properties p; p.anim_set("foo", 1, 10); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.keyframe_type(0), mlt_keyframe_linear); QCOMPARE(a.keyframe_type(10), mlt_keyframe_linear); QCOMPARE(a.keyframe_type(11), mlt_keyframe_linear); } void KeyFrameTypeIsSmooth() { Properties p; int pos = 10; p.anim_set("foo", 1, pos, pos, mlt_keyframe_smooth); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.keyframe_type(0), mlt_keyframe_smooth); QCOMPARE(a.keyframe_type(10), mlt_keyframe_smooth); QCOMPARE(a.keyframe_type(11), mlt_keyframe_smooth); } void GetItem() { Properties p; int pos = 10; p.anim_set("foo", 1, pos, pos, mlt_keyframe_smooth); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); bool is_key = true; mlt_keyframe_type type = mlt_keyframe_linear; int error = a.get_item(10, is_key, type); QVERIFY(!error); QVERIFY(is_key); QCOMPARE(type, mlt_keyframe_smooth); error = a.get_item(1, is_key, type); QVERIFY(!error); QVERIFY(!is_key); QCOMPARE(type, mlt_keyframe_smooth); } void AnimationFromStringProperty() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); Animation a = p.get_animation("foo"); QVERIFY(!a.is_valid()); // Cause the string to be interpreted as animated value. p.anim_get("foo", 0); a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.length(), 100); } void SetLength() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.length(), 100); a.set_length(200); QCOMPARE(a.length(), 200); a.set_length(60); QCOMPARE(a.length(), 60); QCOMPARE(a.serialize_cut(), "50=100;60=60"); } void RemoveMiddleKeyframe() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int error = a.remove(60); QVERIFY(!error); QCOMPARE(a.serialize_cut(), "50=100;100=0"); QCOMPARE(a.length(), 100); QCOMPARE(p.anim_get_int("foo", 50), 100); QCOMPARE(p.anim_get_int("foo", 75), 50); QCOMPARE(p.anim_get_int("foo", 100), 0); } void RemoveFirstKeyframe() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int error = a.remove(0); QVERIFY(error); error = a.remove(50); QVERIFY(!error); QCOMPARE(a.serialize_cut(), "60=60;100=0"); QCOMPARE(a.length(), 100); QCOMPARE(p.anim_get_int("foo", 50), 60); QCOMPARE(p.anim_get_int("foo", 80), 30); QCOMPARE(p.anim_get_int("foo", 100), 0); } void RemoveLastKeyframe() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int error = a.remove(101); QVERIFY(error); error = a.remove(100); QVERIFY(!error); QCOMPARE(a.serialize_cut(), "50=100;60=60"); QCOMPARE(a.length(), 60); QCOMPARE(p.anim_get_int("foo", 50), 100); QCOMPARE(p.anim_get_int("foo", 55), 80); QCOMPARE(p.anim_get_int("foo", 60), 60); } void EmptyAnimationIsInvalid() { Properties p; p.set("foo", ""); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(!a.is_valid()); } void NonEmptyAnimationKeyCount() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.key_count(), 3); } void RemoveKeyframeCount() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); a.remove(50); QCOMPARE(a.key_count(), 2); } void GetKeyFrame() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); int frame = -1; mlt_keyframe_type type = mlt_keyframe_smooth; int error = a.key_get(0, frame, type); QVERIFY(!error); QCOMPARE(frame, 50); QCOMPARE(type, mlt_keyframe_linear); QCOMPARE(a.key_count(), 3); QCOMPARE(a.key_get_frame(0), 50); QCOMPARE(a.key_get_frame(1), 60); QCOMPARE(a.key_get_frame(2), 100); QCOMPARE(a.key_get_frame(3), -1); QCOMPARE(a.keyframe_type(0), mlt_keyframe_linear); QCOMPARE(a.keyframe_type(1), mlt_keyframe_linear); QCOMPARE(a.keyframe_type(2), mlt_keyframe_linear); } void SerializesInTimeFormat() { Profile profile; Properties p; p.set("_profile", profile.get_profile(), 0); p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); QCOMPARE(a.serialize_cut(mlt_time_clock), "00:00:02.000=100;00:00:02.400=60;00:00:04.000=0"); QCOMPARE(a.serialize_cut(mlt_time_smpte_ndf), "00:00:02:00=100;00:00:02:10=60;00:00:04:00=0"); } void GetPropertyInTimeFormat() { Profile profile; Properties p; p.set("_profile", profile.get_profile(), 0); p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); for (int i = 0; i < p.count(); i++) { if (!qstrcmp(p.get_name(i), "foo")) { QCOMPARE(p.get(i, mlt_time_clock), "00:00:02.000=100;00:00:02.400=60;00:00:04.000=0"); QCOMPARE(p.get(i, mlt_time_smpte_ndf), "00:00:02:00=100;00:00:02:10=60;00:00:04:00=0"); break; } } } void AnimationClears() { Properties p; p.set("foo", "50=100; 60=60; 100=0"); // Cause the string to be interpreted as animated value. p.anim_get_int("foo", 0); Animation a = p.get_animation("foo"); QVERIFY(a.is_valid()); p.clear("foo"); QCOMPARE(p.get_animation("foo"), mlt_animation(0)); } void CanBeEscapedWithQuotes() { Properties p; p.set("foo", "\"50=100; 60=60; 100=0\""); // Quotes are retained when using the non-anim getter. QCOMPARE(p.get("foo"), "\"50=100; 60=60; 100=0\""); // Quotes are removed when using the anim getter. QCOMPARE(p.anim_get("foo", 0), "50=100; 60=60; 100=0"); // Anim strings may contain delimiters and equal signs if quoted. p.set("foo", "50=100; 60=\"60; 100=0\";\"hello=world\""); QCOMPARE(p.anim_get("foo", 0), "hello=world"); QCOMPARE(p.anim_get("foo", 50), "100"); QCOMPARE(p.anim_get("foo", 60), "60; 100=0"); } }; QTEST_APPLESS_MAIN(TestAnimation) #include "test_animation.moc" mlt-6.20.0/src/tests/test_animation/test_animation.pro000066400000000000000000000001151362234133600231020ustar00rootroot00000000000000include(../common.pri) TARGET = test_animation SOURCES += test_animation.cpp mlt-6.20.0/src/tests/test_events/000077500000000000000000000000001362234133600166725ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_events/test_events.cpp000066400000000000000000000053141362234133600217440ustar00rootroot00000000000000/* * Copyright (C) 2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestEvents : public QObject { Q_OBJECT private: mlt_properties m_properties; public: TestEvents() : m_properties(nullptr) { Factory::init(); } private: void checkOwner(mlt_properties owner) { QVERIFY(owner == m_properties); } static void onPropertyChanged(mlt_properties owner, TestEvents* self, char* name) { QVERIFY(self != nullptr); QCOMPARE(name, "foo"); self->checkOwner(owner); } private Q_SLOTS: void ListenToPropertyChanged() { Profile profile; Producer producer(profile, "noise"); QVERIFY(producer.is_valid()); Event* event = producer.listen("property-changed", this, (mlt_transmitter) onPropertyChanged); QVERIFY(event != nullptr); QVERIFY(event->is_valid()); m_properties = producer.get_properties(); producer.set("foo", 1); delete event; } void BlockEvent() { Profile profile; Producer producer(profile, "noise"); m_properties = nullptr; Event* event = producer.listen("property-changed", nullptr, (mlt_transmitter) onPropertyChanged); QVERIFY(event != nullptr); QVERIFY(event->is_valid()); event->block(); producer.set("bar", 1); delete event; } void UnblockEvent() { Profile profile; Producer producer(profile, "noise"); m_properties = producer.get_properties(); Event* event = producer.listen("property-changed", this, (mlt_transmitter) onPropertyChanged); QVERIFY(event != nullptr); QVERIFY(event->is_valid()); event->block(); producer.set("bar", 1); event->unblock(); producer.set("foo", 1); delete event; } }; QTEST_APPLESS_MAIN(TestEvents) #include "test_events.moc" mlt-6.20.0/src/tests/test_events/test_events.pro000066400000000000000000000001071362234133600217550ustar00rootroot00000000000000include(../common.pri) TARGET = test_events SOURCES += test_events.cpp mlt-6.20.0/src/tests/test_filter/000077500000000000000000000000001362234133600166535ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_filter/test_filter.cpp000066400000000000000000000042621362234133600217070ustar00rootroot00000000000000/* * Copyright (C) 2015 Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestFilter: public QObject { Q_OBJECT locale_t locale; public: TestFilter() { #if defined(__linux__) || defined(__APPLE__) locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL ); #endif Factory::init(); } ~TestFilter() { #if defined(__linux__) || defined(__APPLE__) freelocale(locale); #endif } private Q_SLOTS: void ProcessModifiesFrame() { Profile profile("dv_ntsc"); Producer producer(profile, "noise", NULL); Filter filter(profile, "resize"); int width = 0; int height = 0; mlt_image_format format = mlt_image_rgb24; // Get a frame from the producer Frame* frame = producer.get_frame(); // Get the default image size: width should match profile frame->get_image(format, width, height, 0); QCOMPARE(width, 720); // Without applying the filter, the width request is not honored. width = 360; frame->get_image(format, width, height, 0); QCOMPARE(width, 720); // Apply the filter and the requested width will be provided width = 360; filter.process(*frame); frame->get_image(format, width, height, 0); QCOMPARE(width, 360); delete frame; } }; QTEST_APPLESS_MAIN(TestFilter) #include "test_filter.moc" mlt-6.20.0/src/tests/test_filter/test_filter.pro000066400000000000000000000001101362234133600217110ustar00rootroot00000000000000include(../common.pri) TARGET = test_filter SOURCES += test_filter.cpp mlt-6.20.0/src/tests/test_frame/000077500000000000000000000000001362234133600164605ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_frame/test_frame.cpp000066400000000000000000000057061362234133600213250ustar00rootroot00000000000000/* * Copyright (C) 2015 Brian Matherly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestFrame: public QObject { Q_OBJECT public: TestFrame() {} private Q_SLOTS: void FrameConstructorAddsReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f(frame); QCOMPARE(f.ref_count(), 2); mlt_frame_close(frame); } void CopyConstructorAddsReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count( MLT_FRAME_PROPERTIES(frame) ), 1); Frame f1(frame); QCOMPARE(f1.ref_count(), 2); Frame f2(f1); QCOMPARE(f1.ref_count(), 3); mlt_frame_close(frame); } void ConstCopyConstructorAddsReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f1(frame); QCOMPARE(f1.ref_count(), 2); const Frame& cf1 = f1; // Force const to avoid non-const constructor. Frame f2(cf1); QCOMPARE(f1.ref_count(), 3); QCOMPARE(f2.ref_count(), 3); mlt_frame_close(frame); } void DefaultConstructorIsNotValid() { Frame f1; QCOMPARE(f1.is_valid(), false); QCOMPARE(f1.ref_count(), 0); } void OperatorEqualsAddsReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f1(frame); QCOMPARE(f1.ref_count(), 2); Frame f2; f2 = f1; QCOMPARE(f1.ref_count(), 3); QCOMPARE(f2.ref_count(), 3); mlt_frame_close(frame); } void DestructionRemovesReference() { mlt_frame frame = mlt_frame_init(NULL); QCOMPARE(mlt_properties_ref_count(MLT_FRAME_PROPERTIES(frame)), 1); Frame f1(frame); QCOMPARE(f1.ref_count(), 2); Frame* f2 = new Frame(f1); QCOMPARE(f2->ref_count(), 3); delete f2; QCOMPARE(f1.ref_count(), 2); mlt_frame_close(frame); } }; QTEST_APPLESS_MAIN(TestFrame) #include "test_frame.moc" mlt-6.20.0/src/tests/test_frame/test_frame.pro000066400000000000000000000001061362234133600213300ustar00rootroot00000000000000include(../common.pri) TARGET = test_frame SOURCES += test_frame.cpp mlt-6.20.0/src/tests/test_playlist/000077500000000000000000000000001362234133600172275ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_playlist/test_playlist.cpp000066400000000000000000000170111362234133600226330ustar00rootroot00000000000000/* * Copyright (C) 2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestPlaylist : public QObject { Q_OBJECT Profile profile; public: TestPlaylist() : profile("dv_pal") { Factory::init(); } private Q_SLOTS: void Append() { Playlist pl(profile); QVERIFY(pl.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(pl.count(), 0); int result = pl.append(p); QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 1); } void Remove() { Playlist pl(profile); QVERIFY(pl.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(pl.count(), 0); pl.append(p); QCOMPARE(pl.count(), 1); int result = pl.remove(0); QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 0); } void RemoveErrorOnInvalidIndex() { Playlist pl(profile); QVERIFY(pl.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(pl.count(), 0); pl.append(p); QCOMPARE(pl.count(), 1); int result = pl.remove(10); // Invalid index QCOMPARE(result, 1); // Fail QCOMPARE(pl.count(), 1); } void Move() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Move first to last int result = pl.move(0, 2); QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 3); // New order: 2, 3, 1 Producer* pp1 = pl.get_clip(0); Producer* pp2 = pl.get_clip(1); Producer* pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 2); QCOMPARE(pp2->parent().get_int("id"), 3); QCOMPARE(pp3->parent().get_int("id"), 1); delete pp1; delete pp2; delete pp3; } void MoveCorrectInvalidIndex() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Move first to an invalid index int result = pl.move(0, 10); // 10 is invalid // This is still successful because move() corrects the index to be the last entry. QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 3); // The first will be moved to the last after move() corrects the index. Producer* pp1 = pl.get_clip(0); Producer* pp2 = pl.get_clip(1); Producer* pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 2); QCOMPARE(pp2->parent().get_int("id"), 3); QCOMPARE(pp3->parent().get_int("id"), 1); delete pp1; delete pp2; delete pp3; } void Reorder() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Reverse the order of the clips. int new_order[] = {2, 1, 0}; int result = pl.reorder(new_order); QCOMPARE(result, 0); // Success QCOMPARE(pl.count(), 3); // The order will be reversed. Producer* pp1 = pl.get_clip(0); Producer* pp2 = pl.get_clip(1); Producer* pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 3); QCOMPARE(pp2->parent().get_int("id"), 2); QCOMPARE(pp3->parent().get_int("id"), 1); delete pp1; delete pp2; delete pp3; } void ReorderErrorOnDuplicate() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Provide an order with duplicate indices. int new_order[] = {2, 2, 2}; int result = pl.reorder(new_order); QCOMPARE(result, 1); // Fail QCOMPARE(pl.count(), 3); // The order will unchanged. Producer* pp1 = pl.get_clip(0); Producer* pp2 = pl.get_clip(1); Producer* pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 1); QCOMPARE(pp2->parent().get_int("id"), 2); QCOMPARE(pp3->parent().get_int("id"), 3); delete pp1; delete pp2; delete pp3; } void ReorderErrorOnInvalidIndex() { Playlist pl(profile); QVERIFY(pl.is_valid()); // Initial order: 1, 2, 3 Producer p1(profile, "noise"); p1.set("id", 1); QVERIFY(p1.is_valid()); pl.append(p1); Producer p2(profile, "noise"); p2.set("id", 2); QVERIFY(p2.is_valid()); pl.append(p2); Producer p3(profile, "noise"); p3.set("id", 3); QVERIFY(p3.is_valid()); pl.append(p3); QCOMPARE(pl.count(), 3); // Provide an order with an invalid index. int new_order[] = {0, 1, 10}; int result = pl.reorder(new_order); QCOMPARE(result, 1); // Fail QCOMPARE(pl.count(), 3); // The order will unchanged. Producer* pp1 = pl.get_clip(0); Producer* pp2 = pl.get_clip(1); Producer* pp3 = pl.get_clip(2); QCOMPARE(pp1->parent().get_int("id"), 1); QCOMPARE(pp2->parent().get_int("id"), 2); QCOMPARE(pp3->parent().get_int("id"), 3); delete pp1; delete pp2; delete pp3; } }; QTEST_APPLESS_MAIN(TestPlaylist) #include "test_playlist.moc" mlt-6.20.0/src/tests/test_playlist/test_playlist.pro000066400000000000000000000001131362234133600226440ustar00rootroot00000000000000include(../common.pri) TARGET = test_playlist SOURCES += test_playlist.cpp mlt-6.20.0/src/tests/test_properties/000077500000000000000000000000001362234133600175625ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_properties/test_properties.cpp000066400000000000000000001073741362234133600235350ustar00rootroot00000000000000/* * Copyright (C) 2013-2018 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 consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include using namespace Mlt; extern "C" { #define __APPLE__ #include #include } #include static const bool kRunLongTests = true; class TestProperties: public QObject { Q_OBJECT locale_t locale; public: TestProperties() { #if !defined(_WIN32) && (defined(__GLIBC__) || defined(__APPLE__)) locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL ); #else locale = 0; #endif Factory::init(); } ~TestProperties() { #if !defined(_WIN32) && (defined(__GLIBC__) || defined(__APPLE__)) freelocale(locale); #endif } private Q_SLOTS: void InstantiationIsAReference() { Properties p; QCOMPARE(p.ref_count(), 1); } void CopyAddsReference() { Properties p; Properties q = p; QCOMPARE(p.ref_count(), 2); } void DestructionRemovesReference() { Properties p; Properties* q = new Properties(p); QCOMPARE(p.ref_count(), 2); delete q; QCOMPARE(p.ref_count(), 1); } void SetAndGetString() { Properties p; p.set("key", "value"); QVERIFY(p.get("key")); QVERIFY(QString(p.get("key")) != QString("")); QCOMPARE(p.get("key"), "value"); } void SetAndGetInt() { Properties p; int i = 1; p.set("key", i); QCOMPARE(p.get_int("key"), i); } void SetAndGetDouble() { Properties p; double d = 1.0; p.set("key", d); QCOMPARE(p.get_double("key"), d); } void SetAndGetInt64() { Properties p; int64_t i = 1LL << 32; p.set("key", i); QCOMPARE(p.get_int64("key"), i); } void SetAndGetData() { Properties p; const char *value = "value"; char* const s = strdup(value); p.set("key", s, strlen(s), free); int size = 0; QCOMPARE((char*) p.get_data("key", size), value); QCOMPARE(size, int(strlen(value))); } void IntFromString() { Properties p; const char *s = "-1"; int i = -1; p.set("key", i); QCOMPARE(p.get("key"), s); p.set("key", s); QCOMPARE(p.get_int("key"), i); } void Int64FromString() { Properties p; const char *s = "-1"; int64_t i = -1; p.set("key", i); QCOMPARE(p.get("key"), s); p.set("key", s); QCOMPARE(p.get_int64("key"), i); } void DoubleFromString() { Properties p; const char *s = "-1.23456"; double d = -1.23456; p.set("key", d); QCOMPARE(p.get("key"), s); p.set("key", s); QCOMPARE(p.get_double("key"), d); } void SetNullRemovesProperty() { Properties p; const char *s = NULL; p.set("key", "value"); p.set("key", s); QCOMPARE(p.get("key"), s); } void SetAndGetHexColor() { Properties p; const char *hexColorString = "0xaabbccdd"; int hexColorInt = 0xaabbccdd; p.set("key", hexColorString); QCOMPARE(p.get_int("key"), hexColorInt); } void SetAndGetCssColor() { Properties p; const char *cssColorString = "#aabbcc"; int cssColorInt = 0xaabbccff; p.set("key", cssColorString); QCOMPARE(p.get_int("key"), cssColorInt); const char *cssColorAlphaString = "#00aabbcc"; int cssColorAlphaInt = 0xaabbcc00; p.set("key", cssColorAlphaString); QCOMPARE(p.get_int("key"), cssColorAlphaInt); } void SetAndGetTimeCode() { Profile profile; Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33:04"; p.set("key", timeString); QCOMPARE(p.get_int("key"), 1023829); p.set("key", 1023829); QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); } void SetAndGetTimeCodeNtscFps() { Profile profile("atsc_720p_2997"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "00:00:17;09"; // Looking just add seconds: // 510 f / 29.97 fps = 17.017017017 s // 29.97 fps * 17 s = 509.49 f // round(509.49) = 509 f, which it not equal to 510, the original amount. // However, ceiling(509.49) = 510 f // Now, adding on the frames part: // 509.49 f + 9 f = 518.49 f // ceiling(518.49) = 519 f const int frames = 519; p.set("key", timeString); QCOMPARE(p.get_int("key"), frames); p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); } void SetAndGetTimeCodeNonIntFps() { Profile profile("atsc_720p_2398"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33:04"; // 11 * 3600 + 22 * 60 + 33 = 40953 s // floor(23.98 fps * 40953 s) = 981890 f // 981890 f + 4 f = 981894 f const int frames = 981894; p.set("key", timeString); QCOMPARE(p.get_int("key"), frames); p.set("key", frames); // floor(981894 f / (24000/1001 * 3600)) = 11 h // 981894 f - floor(11 * 3600 * 24000/1001) = 32444 f // floor(32444 f / (24000/1001 * 60)) = 22 m // 981894 f - floor((11 * 3600 + 22 * 60) * 24000/1001) = 796 f // floor(796 f / (24000/1001)) = 33 s // 981894 f - ceil((11 * 3600 + 22 * 60 + 33) * 24000/1001) = 4f QCOMPARE(p.get_time("key", mlt_time_smpte_df), timeString); QCOMPARE(p.get_time("key", mlt_time_clock), "11:22:33.200"); if (kRunLongTests) for (int i = 0; i < 9999999; ++i) { p.set("key", i); // QWARN(p.get_time("key", mlt_time_smpte_df)); p.set("test", p.get_time("key", mlt_time_smpte_df)); QCOMPARE(p.get_int("test"), i); } } void SetAndGetTimeCodeNonDropFrame() { Profile profile("dv_ntsc"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33:04"; const int frames = 1228594; p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_smpte_ndf), timeString); } void SetAndGetTimeClock() { Profile profile; Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33.400"; p.set("key", timeString); QCOMPARE(p.get_int("key"), 1023835); p.set("key", 1023835); QCOMPARE(p.get_time("key", mlt_time_clock), timeString); } void SetAndGetTimeClockNtscFps() { Profile profile("dv_ntsc"); Properties p; p.set("_profile", profile.get_profile(), 0); const char *timeString = "11:22:33.400"; const int frames = 1227374; p.set("key", timeString); QCOMPARE(p.get_int("key"), frames); p.set("key", frames); QCOMPARE(p.get_time("key", mlt_time_clock), timeString); if (kRunLongTests) for (int i = 0; i < 9999999; ++i) { p.set("key", i); // QWARN(p.get_time("key", mlt_time_clock)); p.set("test", p.get_time("key", mlt_time_clock)); QCOMPARE(p.get_int("test"), i); } } void SetAndGetTimeClockNonIntFps() { Profile profile("atsc_720p_2398"); Properties p; p.set("_profile", profile.get_profile(), 0); if (kRunLongTests) for (int i = 0; i < 9999999; ++i) { p.set("key", i); // QWARN(p.get_time("key", mlt_time_clock)); p.set("test", p.get_time("key", mlt_time_clock)); QCOMPARE(p.get_int("test"), i); } } void SetSimpleMathExpression() { Properties p; p.set("key", "@16.0/9.0 *2 +3 -1"); QCOMPARE(p.get_int("key"), 5); QCOMPARE(p.get_double("key"), 16.0/9.0 *2 +3 -1); } void SetMathExpressionWithProperty() { Properties p; p.set("width", 100); p.set("key", "@16.0/9.0 *width"); QCOMPARE(p.get_int("key"), 177); } void PassOneProperty() { Properties p[2]; const char *s = "value"; p[0].set("key", s); QCOMPARE(p[1].get("key"), (void*) 0); p[1].pass_property(p[0], "key"); QCOMPARE(p[1].get("key"), s); } void PassMultipleByPrefix() { Properties p[2]; const char *s = "value"; p[0].set("key.one", s); p[0].set("key.two", s); QCOMPARE(p[1].get("key.one"), (void*) 0); QCOMPARE(p[1].get("key.two"), (void*) 0); p[1].pass_values(p[0], "key."); QCOMPARE(p[1].get("one"), s); QCOMPARE(p[1].get("two"), s); } void PassMultipleByList() { Properties p[2]; const char *s = "value"; p[0].set("key.one", s); p[0].set("key.two", s); QCOMPARE(p[1].get("key.one"), (void*) 0); QCOMPARE(p[1].get("key.two"), (void*) 0); p[1].pass_list(p[0], "key.one key.two"); QCOMPARE(p[1].get("key.one"), s); QCOMPARE(p[1].get("key.two"), s); } void MirrorProperties() { Properties p[2]; p[0].mirror(p[1]); p[0].set("key", "value"); QCOMPARE(p[1].get("key"), "value"); } void InheritProperties() { Properties p[2]; p[0].set("key", "value"); QVERIFY(p[1].get("key") == 0); p[1].inherit(p[0]); QCOMPARE(p[1].get("key"), "value"); } void ParseString() { Properties p; QCOMPARE(p.get("key"), (void*) 0); p.parse("key=value"); QCOMPARE(p.get("key"), "value"); p.parse("key=\"new value\""); QCOMPARE(p.get("key"), "new value"); } void RenameProperty() { Properties p; p.set("key", "value"); QVERIFY(p.get("new key") == 0); p.rename("key", "new key"); QCOMPARE(p.get("new key"), "value"); } void SequenceDetected() { Properties p; p.set("1", 1); p.set("2", 2); p.set("3", 3); QVERIFY(p.is_sequence()); p.set("four", 4); QVERIFY(!p.is_sequence()); } void SerializesToYamlTiny() { Properties p[3]; p[0].set("key1", "value1"); p[0].set("key:2", "value[2]"); p[1].set("1", "value3"); p[1].set("2", "value:4"); p[2].set("1", "value5"); p[2].set("2", "\"value6\""); p[0].set("seq1", p[1].get_properties(), 0); p[0].set("seq'2", p[2].get_properties(), 0); char* serializedYaml = p[0].serialise_yaml(); QCOMPARE(serializedYaml, "---\n" "key1: value1\n" "\"key:2\": \"value[2]\"\n" "seq1:\n" " - value3\n" " - \"value:4\"\n" "\"seq'2\":\n" " - value5\n" " - '\"value6\"'\n" "...\n"); free(serializedYaml); } void ParsesYamlTiny() { QTemporaryFile tempFile; if (tempFile.open()) { tempFile.write( "---\n" "key1: value1\n" "\"key:2\":\"value[2]\"\n" "seq1:\n" " - value3\n" " - \"value:4\"\n" "\"seq'2\":\n" " - value5\n" " - \"value:6\"\n" "...\n"); tempFile.close(); } QScopedPointer p(Properties::parse_yaml(tempFile.fileName().toUtf8().constData())); QVERIFY(!p.isNull()); QVERIFY(p->is_valid()); QCOMPARE(p->get("key1"), "value1"); QCOMPARE(p->get("key:2"), "value[2]"); } void RadixRespondsToLocale() { Properties p; p.set_lcnumeric("en_US.UTF-8"); p.set("key", "0.125"); QCOMPARE(p.get_double("key"), double(1) / double(8)); #if !defined(_WIN32) p.set_lcnumeric("de_DE.UTF-8"); p.set("key", "0,125"); QCOMPARE(p.get_double("key"), double(1) / double(8)); #endif } void AnimationInsert() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; item.is_key = 1; item.keyframe_type = mlt_keyframe_discrete; item.property = mlt_property_init(); item.frame = 0; mlt_property_set_string(item.property, "0"); mlt_animation_insert(a, &item); item.frame = 1; mlt_property_set_string(item.property, "1"); mlt_animation_insert(a, &item); item.frame = 2; mlt_property_set_string(item.property, "2"); mlt_animation_insert(a, &item); QCOMPARE(mlt_animation_get_length(a), 2); char *a_serialized = mlt_animation_serialize(a); mlt_animation_parse(a, a_serialized, 0, fps, locale); QCOMPARE(a_serialized, "0|=0;1|=1;2|=2"); if (a_serialized) free(a_serialized); mlt_property_close(item.property); mlt_animation_close(a); } void DoubleAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50=1; 60=60; 100=0", 100, fps, locale); mlt_animation_remove(a, 60); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=1;100=0"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.5); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void IntAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50=100; 60=60; 100=0", 100, fps, locale); mlt_animation_remove(a, 60); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=100;100=0"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 50); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void AnimationWithTimeValueKeyframes() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, ":2.0=1; :4.0=0", 100, fps, locale); char *a_serialized = mlt_animation_serialize(a); // Time serializes to frame units :-\. QCOMPARE(a_serialized, "50=1;100=0"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 1.0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.5); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_double(item.property, fps, locale), 0.0); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void DiscreteIntAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50|=100; 60|=60; 100|=0", 100, fps, locale); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50|=100;60|=60;100|=0"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 55); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 60); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 60); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 60); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void StringAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "50=hello world; 60=\"good night\"; 100=bar", 100, fps, locale); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=hello world;60=good night;100=bar"); if (a_serialized) free(a_serialized); mlt_animation_parse(a, "50=hello world; 60=\"good; night\"; 100=bar", 100, fps, locale); a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=hello world;60=\"good; night\";100=bar"); if (a_serialized) free(a_serialized); mlt_animation_parse(a, "50=hello world; 60=\"\"good night\"\"; 100=bar", 100, fps, locale); a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "50=hello world;60=\"good night\";100=bar"); if (a_serialized) free(a_serialized); item.property = mlt_property_init(); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_string(item.property), "hello world"); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_string(item.property), "hello world"); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_string(item.property), "\"good night\""); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_string(item.property), "bar"); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_string(item.property), "bar"); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void test_property_anim_get_double() { double fps = 25.0; int len = 0; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=100; 20=200"); QCOMPARE(mlt_property_get_double(p, fps, locale), 10.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 0, len), 100.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 15, len), 150.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 20, len), 200.0); mlt_property_set_string(p, "1.5"); QCOMPARE(mlt_property_get_double(p, fps, locale), 1.5); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 10, 100), 1.5); mlt_property_close(p); } void test_property_anim_get_int() { double fps = 25.0; int len = 100; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=100; 20=200"); QCOMPARE(mlt_property_get_int(p, fps, locale), 10); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 0, len), 100); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 15, len), 150); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 20, len), 200); mlt_property_set_string(p, "1.5"); QCOMPARE(mlt_property_get_int(p, fps, locale), 1); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 10, 100), 1); mlt_property_close(p); } void SmoothIntAnimation() { double fps = 25.0; mlt_animation a = mlt_animation_new(); struct mlt_animation_item_s item; mlt_animation_parse(a, "0=80;10~=80; 20~=30; 30~=40; 40~=28; 50=90; 60=0; 70=60; 80=20", 100, fps, locale); item.property = mlt_property_init(); char *a_serialized = mlt_animation_serialize(a); QCOMPARE(a_serialized, "0=80;10~=80;20~=30;30~=40;40~=28;50=90;60=0;70=60;80=20"); if (a_serialized) free(a_serialized); mlt_animation_get_item(a, &item, 10); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 80); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 50); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 90); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 55); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 45); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 60); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); QCOMPARE(item.is_key, 1); mlt_animation_get_item(a, &item, 75); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 40); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 100); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 20); QCOMPARE(item.is_key, 0); mlt_animation_get_item(a, &item, 110); QCOMPARE(mlt_property_get_int(item.property, fps, locale), 20); QCOMPARE(item.is_key, 0); mlt_property_close(item.property); mlt_animation_close(a); } void test_property_anim_set_double() { double fps = 25.0; int len = 100; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=100; 20=200"); mlt_property_anim_set_double(p, 1.5, fps, locale, 30, len, mlt_keyframe_linear); QCOMPARE(mlt_property_get_double(p, fps, locale), 10.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 0, len), 100.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 15, len), 150.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 20, len), 200.0); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 25, len), 100.75); QCOMPARE(mlt_property_anim_get_double(p, fps, locale, 30, len), 1.5); mlt_property_close(p); } void test_property_anim_set_int() { double fps = 25.0; int len = 0; mlt_property p = mlt_property_init(); mlt_property_set_string(p, "10=100; 20=200"); mlt_property_anim_set_int(p, 300, fps, locale, 30, len, mlt_keyframe_linear); QCOMPARE(mlt_property_get_int(p, fps, locale), 10); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 0, len), 100); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 15, len), 150); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 20, len), 200); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 25, len), 250); QCOMPARE(mlt_property_anim_get_int(p, fps, locale, 30, len), 300); mlt_property_close(p); } void PercentAsRatio() { Properties p; p.set("foo", "12.3%"); QCOMPARE(p.get_double("foo"), 0.123); p.set("foo", "456 %"); QCOMPARE(p.get_double("foo"), 456.0); } void PropertiesAnimInt() { Properties p; p.set_lcnumeric("POSIX"); // Construct animation from scratch p.anim_set("foo", 0, 0); p.anim_set("foo", 100, 50, -1, mlt_keyframe_smooth); QCOMPARE(p.anim_get_int("foo", 0), 0); QCOMPARE(p.anim_get_int("foo", 25), 50); QCOMPARE(p.anim_get_int("foo", 50), 100); QCOMPARE(p.get("foo"), "0=0;50~=100"); // Animation from string value p.set("foo", "10=100;20=200"); QCOMPARE(p.anim_get_int("foo", 0), 100); QCOMPARE(p.anim_get_int("foo", 15), 150); QCOMPARE(p.anim_get_int("foo", 20), 200); // Animation from string using time clock values // Need to set a profile so fps can be used to convert time to frames. Profile profile("dv_pal"); p.set("_profile", profile.get_profile(), 0); p.set("foo", ":0.0=100; :2.0=200"); QCOMPARE(p.anim_get_int("foo", 0), 100); QCOMPARE(p.anim_get_int("foo", 25), 150); QCOMPARE(p.anim_get_int("foo", 50), 200); } void PropertiesAnimDouble() { Properties p; p.set_lcnumeric("POSIX"); // Construct animation from scratch p.anim_set("foo", 0.0, 0); p.anim_set("foo", 100.0, 50, -1, mlt_keyframe_smooth); QCOMPARE(p.anim_get_double("foo", 0), 0.0); QCOMPARE(p.anim_get_double("foo", 25), 50.0); QCOMPARE(p.anim_get_double("foo", 50), 100.0); QCOMPARE(p.get("foo"), "0=0;50~=100"); // Animation from string value p.set("foo", "10=100.2;20=200.8"); QCOMPARE(p.anim_get_double("foo", 0), 100.2); QCOMPARE(p.anim_get_double("foo", 15), 150.5); QCOMPARE(p.anim_get_double("foo", 20), 200.8); // Animation from string using time clock values // Need to set a profile so fps can be used to convert time to frames. Profile profile("dv_pal"); p.set("_profile", profile.get_profile(), 0); p.set("foo", ":0.0=100; :2.0=200"); QCOMPARE(p.anim_get_double("foo", 0), 100.0); QCOMPARE(p.anim_get_double("foo", 25), 150.0); QCOMPARE(p.anim_get_double("foo", 50), 200.0); // Test a non-animation string. p.set("bar", "0.5"); QCOMPARE(p.anim_get_double("bar", 25), 0.5); } void PropertiesStringAnimation() { Properties p; p.anim_set("key", "foo", 10); p.anim_set("key", "bar", 30); QCOMPARE(p.get("key"), "10|=foo;30|=bar"); p.set("key", "0=; 10=foo bar; 30=hello world"); QCOMPARE(p.anim_get("key", 1), ""); QCOMPARE(p.anim_get("key", 15), "foo bar"); QCOMPARE(p.anim_get("key", 45), "hello world"); } void test_mlt_rect() { mlt_property p = mlt_property_init(); mlt_rect r = { 1, 2, 3, 4, 5 }; mlt_property_set_rect( p, r ); QCOMPARE(mlt_property_get_string(p), "1 2 3 4 5"); r.o = DBL_MIN; mlt_property_set_rect( p, r ); QCOMPARE(mlt_property_get_string(p), "1 2 3 4"); r.w = DBL_MIN; r.h = DBL_MIN; mlt_property_set_rect( p, r ); QCOMPARE(mlt_property_get_string(p), "1 2"); mlt_property_set_string(p, "1.1/2.2:3.3x4.4:5.5"); r = mlt_property_get_rect(p, locale); QCOMPARE(r.x, 1.1); QCOMPARE(r.y, 2.2); QCOMPARE(r.w, 3.3); QCOMPARE(r.h, 4.4); QCOMPARE(r.o, 5.5); mlt_property_set_string(p, "1.1 2.2"); r = mlt_property_get_rect(p, locale); QCOMPARE(r.x, 1.1); QCOMPARE(r.y, 2.2); QCOMPARE(r.w, DBL_MIN); mlt_property_set_int64(p, UINT_MAX); r = mlt_property_get_rect(p, locale); QCOMPARE(r.x, double(UINT_MAX)); mlt_property_close(p); } void SetAndGetRect() { Properties p; mlt_rect r; r.x = 1.1; r.y = 2.2; r.w = 3.3; r.h = 4.4; r.o = 5.5; p.set("key", r); mlt_rect q = p.get_rect("key"); QCOMPARE(q.x, 1.1); QCOMPARE(q.y, 2.2); QCOMPARE(q.w, 3.3); QCOMPARE(q.h, 4.4); QCOMPARE(q.o, 5.5); p.set("key", 10, 20, 30, 40); q = p.get_rect("key"); QCOMPARE(q.x, 10.0); QCOMPARE(q.y, 20.0); QCOMPARE(q.w, 30.0); QCOMPARE(q.h, 40.0); } void RectFromString() { Properties p; p.set_lcnumeric("POSIX"); const char *s = "1.1 2.2 3.3 4.4 5.5"; mlt_rect r = { 1.1, 2.2, 3.3, 4.4, 5.5 }; p.set("key", r); QCOMPARE(p.get("key"), s); p.set("key", s); r = p.get_rect("key"); QCOMPARE(r.x, 1.1); QCOMPARE(r.y, 2.2); QCOMPARE(r.w, 3.3); QCOMPARE(r.h, 4.4); QCOMPARE(r.o, 5.5); } void RectAnimation() { mlt_rect r1 = { 0, 0, 200, 200, 0 }; mlt_rect r2 = { 100, 100, 400, 400, 1.0 }; Properties p; p.set_lcnumeric("POSIX"); // Construct animation from scratch p.anim_set("key", r1, 0); p.anim_set("key", r2, 50); QCOMPARE(p.anim_get_rect("key", 0).x, 0.0); QCOMPARE(p.anim_get_rect("key", 25).x, 50.0); QCOMPARE(p.anim_get_rect("key", 25).y, 50.0); QCOMPARE(p.anim_get_rect("key", 25).w, 300.0); QCOMPARE(p.anim_get_rect("key", 25).h, 300.0); QCOMPARE(p.anim_get_rect("key", 25).o, 0.5); QCOMPARE(p.anim_get_rect("key", 50).x, 100.0); QCOMPARE(p.get("key"), "0=0 0 200 200 0;50=100 100 400 400 1"); // Animation from string value QCOMPARE(p.anim_get_rect("key", 0).x, 0.0); QCOMPARE(p.anim_get_rect("key", 0).y, 0.0); QCOMPARE(p.anim_get_rect("key", 0).w, 200.0); QCOMPARE(p.anim_get_rect("key", 0).h, 200.0); QCOMPARE(p.anim_get_rect("key", 0).o, 0.0); QCOMPARE(p.anim_get_rect("key", 50).x, 100.0); QCOMPARE(p.anim_get_rect("key", 50).y, 100.0); QCOMPARE(p.anim_get_rect("key", 50).w, 400.0); QCOMPARE(p.anim_get_rect("key", 50).h, 400.0); QCOMPARE(p.anim_get_rect("key", 50).o, 1.0); QCOMPARE(p.anim_get_rect("key", 15).x, 30.0); QCOMPARE(p.anim_get_rect("key", 15).y, 30.0); QCOMPARE(p.anim_get_rect("key", 15).w, 260.0); QCOMPARE(p.anim_get_rect("key", 15).h, 260.0); QCOMPARE(p.anim_get_rect("key", 15).o, 0.3); // Smooth animation p.set("key", "0~=0/0:200x200:0; 50=100/100:400x400:1"); QCOMPARE(p.anim_get_rect("key", 0).x, 0.0); QCOMPARE(p.anim_get_rect("key", 0).y, 0.0); QCOMPARE(p.anim_get_rect("key", 0).w, 200.0); QCOMPARE(p.anim_get_rect("key", 0).h, 200.0); QCOMPARE(p.anim_get_rect("key", 0).o, 0.0); QCOMPARE(p.anim_get_rect("key", 50).x, 100.0); QCOMPARE(p.anim_get_rect("key", 50).y, 100.0); QCOMPARE(p.anim_get_rect("key", 50).w, 400.0); QCOMPARE(p.anim_get_rect("key", 50).h, 400.0); QCOMPARE(p.anim_get_rect("key", 50).o, 1.0); QCOMPARE(p.anim_get_rect("key", 15).x, 25.8); QCOMPARE(p.anim_get_rect("key", 15).y, 25.8); QCOMPARE(p.anim_get_rect("key", 15).w, 251.6); QCOMPARE(p.anim_get_rect("key", 15).h, 251.6); QCOMPARE(p.anim_get_rect("key", 15).o, 0.258); // Using percentages p.set("key", "0=0 0; 50=100% 200%"); QCOMPARE(p.anim_get_rect("key", 25).x, 0.5); QCOMPARE(p.anim_get_rect("key", 25).y, 1.0); } void ColorFromInt() { Properties p; p.set_lcnumeric("POSIX"); p.set("key", (int) 0xaabbccdd); mlt_color color = p.get_color("key"); QCOMPARE(color.r, quint8(0xaa)); QCOMPARE(color.g, quint8(0xbb)); QCOMPARE(color.b, quint8(0xcc)); QCOMPARE(color.a, quint8(0xdd)); p.set("key", color); QCOMPARE(p.get_int("key"), int(0xaabbccdd)); } void ColorFromString() { Properties p; p.set_lcnumeric("POSIX"); p.set("key", "red"); mlt_color color = p.get_color("key"); QCOMPARE(color.r, quint8(0xff)); QCOMPARE(color.g, quint8(0x00)); QCOMPARE(color.b, quint8(0x00)); QCOMPARE(color.a, quint8(0xff)); p.set("key", "#deadd00d"); color = p.get_color("key"); QCOMPARE(color.r, quint8(0xad)); QCOMPARE(color.g, quint8(0xd0)); QCOMPARE(color.b, quint8(0x0d)); QCOMPARE(color.a, quint8(0xde)); } void SetIntAndGetAnim() { Properties p; p.set_lcnumeric("POSIX"); p.set("key", 123); QCOMPARE(p.anim_get_int("key", 10, 50), 123); p.set("key", "123"); QCOMPARE(p.anim_get_int("key", 10, 50), 123); } void SetDoubleAndGetAnim() { Properties p; p.set_lcnumeric("POSIX"); p.set("key", 123.0); QCOMPARE(p.anim_get_double("key", 10, 50), 123.0); p.set("key", "123"); QCOMPARE(p.anim_get_double("key", 10, 50), 123.0); } void AnimNegativeTimevalue() { Properties p; Profile profile("dv_pal"); p.set("_profile", profile.get_profile(), 0); p.set_lcnumeric("POSIX"); p.set("key", "0=100; -1=200"); QCOMPARE(p.anim_get_int("key", 75, 100), 175); p.set("key", "0=100; -1:=200"); QCOMPARE(p.anim_get_int("key", 75, 125), 175); } void PropertyClears() { Properties p; p.set("key", 1); QCOMPARE(p.get_int("key"), 1); QCOMPARE(p.get("key"), "1"); p.clear("key"); QCOMPARE(p.get("key"), (char*) 0); QCOMPARE(p.get_data("key"), (void*) 0); QCOMPARE(p.get_animation("key"), mlt_animation(0)); QCOMPARE(p.get_int("key"), 0); } void SetString() { Properties p; p.set_string("foo", "123.4"); QCOMPARE(p.get("foo"), "123.4"); QCOMPARE(p.get_int("foo"), 123); QCOMPARE(p.get_double("foo"), 123.4); } }; QTEST_APPLESS_MAIN(TestProperties) #include "test_properties.moc" mlt-6.20.0/src/tests/test_properties/test_properties.pro000066400000000000000000000001221362234133600235320ustar00rootroot00000000000000include (../common.pri) TARGET = test_properties SOURCES = test_properties.cpp mlt-6.20.0/src/tests/test_repository/000077500000000000000000000000001362234133600176055ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_repository/test_repository.cpp000066400000000000000000000031331362234133600235670ustar00rootroot00000000000000/* * Copyright (C) 2013 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 consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestRepository : public QObject { Q_OBJECT public: TestRepository() {} private Q_SLOTS: void ThereAreProducers() { Repository* r = Factory::init(); Properties* producers = r->producers(); QVERIFY(producers->is_valid()); if (producers->is_valid()) QVERIFY(producers->count() > 0); delete producers; } void ThereAreConsumers() { Repository* r = Factory::init(); Properties* consumers = r->consumers(); QVERIFY(consumers->is_valid()); if (consumers->is_valid()) QVERIFY(consumers->count() > 0); delete consumers; } }; QTEST_APPLESS_MAIN(TestRepository) #include "test_repository.moc" mlt-6.20.0/src/tests/test_repository/test_repository.pro000066400000000000000000000001201362234133600235760ustar00rootroot00000000000000include(../common.pri) TARGET = test_repository SOURCES += test_repository.cpp mlt-6.20.0/src/tests/test_service/000077500000000000000000000000001362234133600170265ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_service/test_service.cpp000066400000000000000000000053651362234133600222420ustar00rootroot00000000000000/* * Copyright (C) 2019 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include using namespace Mlt; class TestService : public QObject { Q_OBJECT public: TestService() { repo = Factory::init(); } ~TestService() { Factory::close(); } private Q_SLOTS: void CanIdentifyServicesFromFactory() { Profile profile; Producer producer(profile, "color"); QCOMPARE(mlt_service_identify(producer.get_service()), producer_type); Filter filter(profile, "resize"); QCOMPARE(mlt_service_identify(filter.get_service()), filter_type); Transition transition(profile, "mix"); QCOMPARE(mlt_service_identify(transition.get_service()), transition_type); Consumer consumer(profile, "null"); QCOMPARE(mlt_service_identify(consumer.get_service()), consumer_type); } void CanIdentifyServicesFromAPI() { mlt_profile profile = mlt_profile_init(NULL); mlt_playlist playlist = mlt_playlist_new(profile); QCOMPARE(mlt_service_identify(MLT_PLAYLIST_SERVICE(playlist)), playlist_type); mlt_tractor tractor = mlt_tractor_new(); QCOMPARE(mlt_service_identify(MLT_TRACTOR_SERVICE(tractor)), tractor_type); QCOMPARE(mlt_service_identify(MLT_MULTITRACK_SERVICE(mlt_tractor_multitrack(tractor))), multitrack_type); mlt_producer producer = mlt_producer_new(profile); QCOMPARE(mlt_service_identify(MLT_PRODUCER_SERVICE(producer)), producer_type); mlt_filter filter = mlt_filter_new(); QCOMPARE(mlt_service_identify(MLT_FILTER_SERVICE(filter)), filter_type); mlt_transition transition = mlt_transition_new(); QCOMPARE(mlt_service_identify(MLT_TRANSITION_SERVICE(transition)), transition_type); mlt_consumer consumer = mlt_consumer_new(profile); QCOMPARE(mlt_service_identify(MLT_CONSUMER_SERVICE(consumer)), consumer_type); } private: Repository* repo; }; QTEST_APPLESS_MAIN(TestService) #include "test_service.moc" mlt-6.20.0/src/tests/test_service/test_service.pro000066400000000000000000000001201362234133600222400ustar00rootroot00000000000000include(../common.pri) TARGET = test_service SOURCES += \ test_service.cpp mlt-6.20.0/src/tests/test_tractor/000077500000000000000000000000001362234133600170445ustar00rootroot00000000000000mlt-6.20.0/src/tests/test_tractor/test_tractor.cpp000066400000000000000000000273471362234133600223020ustar00rootroot00000000000000/* * Copyright (C) 2015 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 consumer library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include using namespace Mlt; class TestTractor : public QObject { Q_OBJECT Profile profile; public: TestTractor() : profile("dv_pal") { Factory::init(); } private Q_SLOTS: void CreateSingleTrack() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p, t.count()); QCOMPARE(t.count(), 1); } void FailSameProducerNewTrack() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p(profile, "noise"); QVERIFY(p.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p, t.count()); QCOMPARE(t.count(), 1); int result = t.set_track(p, t.count()); QCOMPARE(result, 3); QCOMPARE(t.count(), 1); } void CreateMultipleTracks() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); } void PlantTransitionWorks() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); } void InsertTrackBelowAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.insert_track(p3, 0); QCOMPARE(t.count(), 3); QCOMPARE(trans.get_int("a_track"), 1); QCOMPARE(trans.get_int("b_track"), 2); } void InsertTrackMiddleAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.insert_track(p3, 1); QCOMPARE(t.count(), 3); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 2); } void InsertTrackAboveDoesNotAffectTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.insert_track(p3, 2); QCOMPARE(t.count(), 3); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); } void RemoveTrackBelowAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.set_track(p3, t.count()); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 1); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); t.remove_track(0); QCOMPARE(t.count(), 2); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 0); // This transition is a candidate for removal. } void RemoveMiddleTrackAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.set_track(p3, t.count()); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 0, 2); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 2); t.remove_track(1); QCOMPARE(t.count(), 2); QCOMPARE(trans.get_int("a_track"), 0); QCOMPARE(trans.get_int("b_track"), 1); } void RemoveTrackAboveAdjustsTransition() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Producer p3(profile, "noise"); QVERIFY(p3.is_valid()); t.set_track(p3, t.count()); Transition trans(profile, "mix"); QVERIFY(trans.is_valid()); t.plant_transition(trans, 1, 2); QCOMPARE(trans.get_int("a_track"), 1); QCOMPARE(trans.get_int("b_track"), 2); t.remove_track(2); QCOMPARE(t.count(), 2); QCOMPARE(trans.get_int("a_track"), 1); QCOMPARE(trans.get_int("b_track"), 1); // This transition is a candidate for removal. } void PlantFilterWorks() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 0); QCOMPARE(filter.get_track(), 0); } void InsertTrackBelowAdjustsFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 0); QCOMPARE(filter.get_track(), 0); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.insert_track(p2, 0); QCOMPARE(t.count(), 2); QCOMPARE(filter.get_track(), 1); } void InsertTrackAboveDoesNotAffectFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 0); QCOMPARE(filter.get_track(), 0); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.insert_track(p2, 1); QCOMPARE(t.count(), 2); QCOMPARE(filter.get_track(), 0); } void RemoveTrackBelowAdjustsFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 1); QCOMPARE(filter.get_track(), 1); t.remove_track(0); QCOMPARE(t.count(), 1); QCOMPARE(filter.get_track(), 0); } void RemoveFilteredTrackAdjustsFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 1); QCOMPARE(filter.get_track(), 1); t.remove_track(1); QCOMPARE(t.count(), 1); QCOMPARE(filter.get_track(), 0); // This filter is a candidate for removal. } void RemoveTrackAboveDoesNotAffectFilter() { Tractor t(profile); QVERIFY(t.is_valid()); Producer p1(profile, "noise"); QVERIFY(p1.is_valid()); QCOMPARE(t.count(), 0); t.set_track(p1, t.count()); QCOMPARE(t.count(), 1); Producer p2(profile, "noise"); QVERIFY(p2.is_valid()); t.set_track(p2, t.count()); QCOMPARE(t.count(), 2); Filter filter(profile, "crop"); QVERIFY(filter.is_valid()); t.plant_filter(filter, 0); QCOMPARE(filter.get_track(), 0); t.remove_track(1); QCOMPARE(t.count(), 1); QCOMPARE(filter.get_track(), 0); } }; QTEST_APPLESS_MAIN(TestTractor) #include "test_tractor.moc" mlt-6.20.0/src/tests/test_tractor/test_tractor.pro000066400000000000000000000001111362234133600222740ustar00rootroot00000000000000include(../common.pri) TARGET = test_tractor SOURCES += test_tractor.cpp mlt-6.20.0/src/tests/tests.pro000066400000000000000000000003071362234133600162130ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = test_filter \ test_events \ test_frame \ test_playlist \ test_properties \ test_repository \ test_animation \ test_tractor \ test_service mlt-6.20.0/src/win32/000077500000000000000000000000001362234133600141275ustar00rootroot00000000000000mlt-6.20.0/src/win32/fnmatch.c000066400000000000000000000134301362234133600157140ustar00rootroot00000000000000/* * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Guido van Rossum. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * From FreeBSD fnmatch.c 1.11 * $Id$ */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94"; #endif /* LIBC_SCCS and not lint */ /* * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. * Compares a filename or pathname to a pattern. */ #include #include #include #include "fnmatch.h" #define EOS '\0' static const char *rangematch(const char *, char, int); int fnmatch(const char *pattern, const char *string, int flags) { const char *stringstart; char c, test; for (stringstart = string;;) switch (c = *pattern++) { case EOS: if ((flags & FNM_LEADING_DIR) && *string == '/') return (0); return (*string == EOS ? 0 : FNM_NOMATCH); case '?': if (*string == EOS) return (FNM_NOMATCH); if (*string == '/' && (flags & FNM_PATHNAME)) return (FNM_NOMATCH); if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); ++string; break; case '*': c = *pattern; /* Collapse multiple stars. */ while (c == '*') c = *++pattern; if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) return (FNM_NOMATCH); /* Optimize for pattern with * at end or before /. */ if (c == EOS) if (flags & FNM_PATHNAME) return ((flags & FNM_LEADING_DIR) || strchr(string, '/') == NULL ? 0 : FNM_NOMATCH); else return (0); else if (c == '/' && flags & FNM_PATHNAME) { if ((string = strchr(string, '/')) == NULL) return (FNM_NOMATCH); break; } /* General case, use recursion. */ while ((test = *string) != EOS) { if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) return (0); if (test == '/' && flags & FNM_PATHNAME) break; ++string; } return (FNM_NOMATCH); case '[': if (*string == EOS) return (FNM_NOMATCH); if (*string == '/' && flags & FNM_PATHNAME) return (FNM_NOMATCH); if ((pattern = rangematch(pattern, *string, flags)) == NULL) return (FNM_NOMATCH); ++string; break; case '\\': if (!(flags & FNM_NOESCAPE)) { if ((c = *pattern++) == EOS) { c = '\\'; --pattern; } } /* FALLTHROUGH */ default: if (c == *string) ; else if ((flags & FNM_CASEFOLD) && (tolower((unsigned char)c) == tolower((unsigned char)*string))) ; else if ((flags & FNM_PREFIX_DIRS) && *string == EOS && ((c == '/' && string != stringstart) || (string == stringstart+1 && *stringstart == '/')) ) return (0); else return (FNM_NOMATCH); string++; break; } /* NOTREACHED */ } static const char * rangematch(const char *pattern, char test, int flags) { int negate, ok; char c, c2; /* * A bracket expression starting with an unquoted circumflex * character produces unspecified results (IEEE 1003.2-1992, * 3.13.2). This implementation treats it like '!', for * consistency with the regular expression syntax. * J.T. Conklin (conklin@ngai.kaleida.com) */ if ( (negate = (*pattern == '!' || *pattern == '^')) ) ++pattern; if (flags & FNM_CASEFOLD) test = tolower((unsigned char)test); for (ok = 0; (c = *pattern++) != ']';) { if (c == '\\' && !(flags & FNM_NOESCAPE)) c = *pattern++; if (c == EOS) return (NULL); if (flags & FNM_CASEFOLD) c = tolower((unsigned char)c); if (*pattern == '-' && (c2 = *(pattern+1)) != EOS && c2 != ']') { pattern += 2; if (c2 == '\\' && !(flags & FNM_NOESCAPE)) c2 = *pattern++; if (c2 == EOS) return (NULL); if (flags & FNM_CASEFOLD) c2 = tolower((unsigned char)c2); if ((unsigned char)c <= (unsigned char)test && (unsigned char)test <= (unsigned char)c2) ok = 1; } else if (c == test) ok = 1; } return (ok == negate ? NULL : pattern); } mlt-6.20.0/src/win32/fnmatch.h000066400000000000000000000046571362234133600157340ustar00rootroot00000000000000/*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)fnmatch.h 8.1 (Berkeley) 6/2/93 * * From FreeBSD fnmatch.h 1.7 * $Id$ */ #ifndef _FNMATCH_H_ #define _FNMATCH_H_ #define FNM_NOMATCH 1 /* Match failed. */ #define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ #define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ #define FNM_PERIOD 0x04 /* Period must be matched by period. */ #define FNM_LEADING_DIR 0x08 /* Ignore / after Imatch. */ #define FNM_CASEFOLD 0x10 /* Case insensitive search. */ #define FNM_PREFIX_DIRS 0x20 /* Directory prefixes of pattern match too. */ int fnmatch(const char *pattern, const char *string, int flags); #endif /* !_FNMATCH_H_ */ mlt-6.20.0/src/win32/strptime.c000066400000000000000000000326421362234133600161510ustar00rootroot00000000000000/* $NetBSD: strptime.c,v 1.36 2012/03/13 21:13:48 christos Exp $ */ /*- * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. * All rights reserved. * * This code was contributed to The NetBSD Foundation by Klaus Klein. * Heavily optimised by David Laight * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* #include #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: strptime.c,v 1.36 2012/03/13 21:13:48 christos Exp $"); #endif #include "namespace.h" #include */ #include #include #include #include #include /* #include #include "private.h" #ifdef __weak_alias __weak_alias(strptime,_strptime) #endif */ typedef unsigned char u_char; typedef unsigned int uint; typedef unsigned __int64 uint64_t; #define _ctloc(x) (_CurrentTimeLocale->x) /* * We do not implement alternate representations. However, we always * check whether a given modifier is allowed for a certain conversion. */ #define ALT_E 0x01 #define ALT_O 0x02 #define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } static int TM_YEAR_BASE = 1900; static char gmt[] = { "GMT" }; static char utc[] = { "UTC" }; /* RFC-822/RFC-2822 */ static const char * const nast[5] = { "EST", "CST", "MST", "PST", "\0\0\0" }; static const char * const nadt[5] = { "EDT", "CDT", "MDT", "PDT", "\0\0\0" }; static const char * const am_pm[2] = { "am", "pm" }; static const char * const day[7] = { "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" }; static const char * const abday[7] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; static const char * const mon[12] = { "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" }; static const char * const abmon[12] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; static const u_char *conv_num(const unsigned char *, int *, uint, uint); static const u_char *find_string(const u_char *, int *, const char * const *, const char * const *, int); char * strptime(const char *buf, const char *fmt, struct tm *tm) { unsigned char c; const unsigned char *bp, *ep; int alt_format, i, split_year = 0, neg = 0, offs; const char *new_fmt; bp = (const u_char *)buf; while (bp != NULL && (c = *fmt++) != '\0') { /* Clear `alternate' modifier prior to new conversion. */ alt_format = 0; i = 0; /* Eat up white-space. */ if (isspace(c)) { while (isspace(*bp)) bp++; continue; } if (c != '%') goto literal; again: switch (c = *fmt++) { case '%': /* "%%" is converted to "%". */ literal: if (c != *bp++) return NULL; LEGAL_ALT(0); continue; /* * "Alternative" modifiers. Just set the appropriate flag * and start over again. */ case 'E': /* "%E?" alternative conversion modifier. */ LEGAL_ALT(0); alt_format |= ALT_E; goto again; case 'O': /* "%O?" alternative conversion modifier. */ LEGAL_ALT(0); alt_format |= ALT_O; goto again; /* * "Complex" conversion rules, implemented through recursion. */ /* we do not need 'c' case 'c': Date and time, using the locale's format. new_fmt = _ctloc(d_t_fmt); goto recurse; */ case 'D': /* The date as "%m/%d/%y". */ new_fmt = "%m/%d/%y"; LEGAL_ALT(0); goto recurse; case 'F': /* The date as "%Y-%m-%d". */ new_fmt = "%Y-%m-%d"; LEGAL_ALT(0); goto recurse; case 'R': /* The time as "%H:%M". */ new_fmt = "%H:%M"; LEGAL_ALT(0); goto recurse; case 'r': /* The time in 12-hour clock representation. */ new_fmt = "%I:%M:S %p";//_ctloc(t_fmt_ampm); LEGAL_ALT(0); goto recurse; case 'T': /* The time as "%H:%M:%S". */ new_fmt = "%H:%M:%S"; LEGAL_ALT(0); goto recurse; /* we don't use 'X' case 'X': The time, using the locale's format. new_fmt =_ctloc(t_fmt); goto recurse; */ /* we do not need 'x' case 'x': The date, using the locale's format. new_fmt =_ctloc(d_fmt);*/ recurse: bp = (const u_char *)strptime((const char *)bp, new_fmt, tm); LEGAL_ALT(ALT_E); continue; /* * "Elementary" conversion rules. */ case 'A': /* The day of week, using the locale's form. */ case 'a': bp = find_string(bp, &tm->tm_wday, day, abday, 7); LEGAL_ALT(0); continue; case 'B': /* The month, using the locale's form. */ case 'b': case 'h': bp = find_string(bp, &tm->tm_mon, mon, abmon, 12); LEGAL_ALT(0); continue; case 'C': /* The century number. */ i = 20; bp = conv_num(bp, &i, 0, 99); i = i * 100 - TM_YEAR_BASE; if (split_year) i += tm->tm_year % 100; split_year = 1; tm->tm_year = i; LEGAL_ALT(ALT_E); continue; case 'd': /* The day of month. */ case 'e': bp = conv_num(bp, &tm->tm_mday, 1, 31); LEGAL_ALT(ALT_O); continue; case 'k': /* The hour (24-hour clock representation). */ LEGAL_ALT(0); /* FALLTHROUGH */ case 'H': bp = conv_num(bp, &tm->tm_hour, 0, 23); LEGAL_ALT(ALT_O); continue; case 'l': /* The hour (12-hour clock representation). */ LEGAL_ALT(0); /* FALLTHROUGH */ case 'I': bp = conv_num(bp, &tm->tm_hour, 1, 12); if (tm->tm_hour == 12) tm->tm_hour = 0; LEGAL_ALT(ALT_O); continue; case 'j': /* The day of year. */ i = 1; bp = conv_num(bp, &i, 1, 366); tm->tm_yday = i - 1; LEGAL_ALT(0); continue; case 'M': /* The minute. */ bp = conv_num(bp, &tm->tm_min, 0, 59); LEGAL_ALT(ALT_O); continue; case 'm': /* The month. */ i = 1; bp = conv_num(bp, &i, 1, 12); tm->tm_mon = i - 1; LEGAL_ALT(ALT_O); continue; case 'p': /* The locale's equivalent of AM/PM. */ bp = find_string(bp, &i, am_pm, NULL, 2); if (tm->tm_hour > 11) return NULL; tm->tm_hour += i * 12; LEGAL_ALT(0); continue; case 'S': /* The seconds. */ bp = conv_num(bp, &tm->tm_sec, 0, 61); LEGAL_ALT(ALT_O); continue; #ifndef TIME_MAX #define TIME_MAX INT64_MAX #endif case 's': /* seconds since the epoch */ { time_t sse = 0; uint64_t rulim = TIME_MAX; if (*bp < '0' || *bp > '9') { bp = NULL; continue; } do { sse *= 10; sse += *bp++ - '0'; rulim /= 10; } while ((sse * 10 <= TIME_MAX) && rulim && *bp >= '0' && *bp <= '9'); if (sse < 0 || (uint64_t)sse > TIME_MAX) { bp = NULL; continue; } tm = localtime(&sse); if (tm == NULL) bp = NULL; } continue; case 'U': /* The week of year, beginning on sunday. */ case 'W': /* The week of year, beginning on monday. */ /* * XXX This is bogus, as we can not assume any valid * information present in the tm structure at this * point to calculate a real value, so just check the * range for now. */ bp = conv_num(bp, &i, 0, 53); LEGAL_ALT(ALT_O); continue; case 'w': /* The day of week, beginning on sunday. */ bp = conv_num(bp, &tm->tm_wday, 0, 6); LEGAL_ALT(ALT_O); continue; case 'u': /* The day of week, monday = 1. */ bp = conv_num(bp, &i, 1, 7); tm->tm_wday = i % 7; LEGAL_ALT(ALT_O); continue; case 'g': /* The year corresponding to the ISO week * number but without the century. */ bp = conv_num(bp, &i, 0, 99); continue; case 'G': /* The year corresponding to the ISO week * number with century. */ do bp++; while (isdigit(*bp)); continue; case 'V': /* The ISO 8601:1988 week number as decimal */ bp = conv_num(bp, &i, 0, 53); continue; case 'Y': /* The year. */ i = TM_YEAR_BASE; /* just for data sanity... */ bp = conv_num(bp, &i, 0, 9999); tm->tm_year = i - TM_YEAR_BASE; LEGAL_ALT(ALT_E); continue; case 'y': /* The year within 100 years of the epoch. */ /* LEGAL_ALT(ALT_E | ALT_O); */ bp = conv_num(bp, &i, 0, 99); if (split_year) /* preserve century */ i += (tm->tm_year / 100) * 100; else { split_year = 1; if (i <= 68) i = i + 2000 - TM_YEAR_BASE; else i = i + 1900 - TM_YEAR_BASE; } tm->tm_year = i; continue; case 'Z': _tzset(); if (strncasecmp((const char *)bp, gmt, 3) == 0 || strncasecmp((const char *)bp, utc, 3) == 0) { tm->tm_isdst = 0; #ifdef TM_GMTOFF tm->TM_GMTOFF = 0; #endif #ifdef TM_ZONE tm->TM_ZONE = gmt; #endif bp += 3; } else { ep = find_string(bp, &i, (const char * const *)tzname, NULL, 2); if (ep != NULL) { tm->tm_isdst = i; #ifdef TM_GMTOFF tm->TM_GMTOFF = -(timezone); #endif #ifdef TM_ZONE tm->TM_ZONE = tzname[i]; #endif } bp = ep; } continue; case 'z': /* * We recognize all ISO 8601 formats: * Z = Zulu time/UTC * [+-]hhmm * [+-]hh:mm * [+-]hh * We recognize all RFC-822/RFC-2822 formats: * UT|GMT * North American : UTC offsets * E[DS]T = Eastern : -4 | -5 * C[DS]T = Central : -5 | -6 * M[DS]T = Mountain: -6 | -7 * P[DS]T = Pacific : -7 | -8 * Military * [A-IL-M] = -1 ... -9 (J not used) * [N-Y] = +1 ... +12 */ while (isspace(*bp)) bp++; switch (*bp++) { case 'G': if (*bp++ != 'M') return NULL; /*FALLTHROUGH*/ case 'U': if (*bp++ != 'T') return NULL; /*FALLTHROUGH*/ case 'Z': tm->tm_isdst = 0; #ifdef TM_GMTOFF tm->TM_GMTOFF = 0; #endif #ifdef TM_ZONE tm->TM_ZONE = utc; #endif continue; case '+': neg = 0; break; case '-': neg = 1; break; default: --bp; ep = find_string(bp, &i, nast, NULL, 4); if (ep != NULL) { #ifdef TM_GMTOFF tm->TM_GMTOFF = -5 - i; #endif #ifdef TM_ZONE tm->TM_ZONE = __UNCONST(nast[i]); #endif bp = ep; continue; } ep = find_string(bp, &i, nadt, NULL, 4); if (ep != NULL) { tm->tm_isdst = 1; #ifdef TM_GMTOFF tm->TM_GMTOFF = -4 - i; #endif #ifdef TM_ZONE tm->TM_ZONE = __UNCONST(nadt[i]); #endif bp = ep; continue; } if ((*bp >= 'A' && *bp <= 'I') || (*bp >= 'L' && *bp <= 'Y')) { #ifdef TM_GMTOFF /* Argh! No 'J'! */ if (*bp >= 'A' && *bp <= 'I') tm->TM_GMTOFF = ('A' - 1) - (int)*bp; else if (*bp >= 'L' && *bp <= 'M') tm->TM_GMTOFF = 'A' - (int)*bp; else if (*bp >= 'N' && *bp <= 'Y') tm->TM_GMTOFF = (int)*bp - 'M'; #endif #ifdef TM_ZONE tm->TM_ZONE = NULL; /* XXX */ #endif bp++; continue; } return NULL; } offs = 0; for (i = 0; i < 4; ) { if (isdigit(*bp)) { offs = offs * 10 + (*bp++ - '0'); i++; continue; } if (i == 2 && *bp == ':') { bp++; continue; } break; } switch (i) { case 2: offs *= 100; break; case 4: i = offs % 100; if (i >= 60) return NULL; /* Convert minutes into decimal */ offs = (offs / 100) * 100 + (i * 50) / 30; break; default: return NULL; } if (neg) offs = -offs; tm->tm_isdst = 0; /* XXX */ #ifdef TM_GMTOFF tm->TM_GMTOFF = offs; #endif #ifdef TM_ZONE tm->TM_ZONE = NULL; /* XXX */ #endif continue; /* * Miscellaneous conversions. */ case 'n': /* Any kind of white-space. */ case 't': while (isspace(*bp)) bp++; LEGAL_ALT(0); continue; default: /* Unknown/unsupported conversion. */ return NULL; } } return (char *)(bp); } static const u_char * conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim) { uint result = 0; unsigned char ch; /* The limit also determines the number of valid digits. */ uint rulim = ulim; ch = *buf; if (ch < '0' || ch > '9') return NULL; do { result *= 10; result += ch - '0'; rulim /= 10; ch = *++buf; } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); if (result < llim || result > ulim) return NULL; *dest = result; return buf; } static const u_char * find_string(const u_char *bp, int *tgt, const char * const *n1, const char * const *n2, int c) { int i; size_t len; /* check full name - then abbreviated ones */ for (; n1 != NULL; n1 = n2, n2 = NULL) { for (i = 0; i < c; i++, n1++) { len = strlen(*n1); if (strncasecmp(*n1, (const char *)bp, len) == 0) { *tgt = i; return bp + len; } } } /* Nothing matched */ return NULL; } mlt-6.20.0/src/win32/win32.c000066400000000000000000000165711362234133600152470ustar00rootroot00000000000000/** * \file win32.c * \brief Miscellaneous utility functions for Windows. * * Copyright (C) 2003-2016 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "../framework/mlt_properties.h" #include "../framework/mlt_log.h" int usleep(unsigned int useconds) { HANDLE timer; LARGE_INTEGER due; due.QuadPart = -(10 * (__int64)useconds); timer = CreateWaitableTimer(NULL, TRUE, NULL); SetWaitableTimer(timer, &due, 0, NULL, NULL, 0); WaitForSingleObject(timer, INFINITE); CloseHandle(timer); return 0; } int nanosleep( const struct timespec * rqtp, struct timespec * rmtp ) { if (rqtp->tv_nsec > 999999999) { /* The time interval specified 1,000,000 or more microseconds. */ errno = EINVAL; return -1; } return usleep( rqtp->tv_sec * 1000000 + rqtp->tv_nsec / 1000 ); } int setenv(const char *name, const char *value, int overwrite) { int result = 1; if (overwrite == 0 && getenv (name)) { result = 0; } else { result = SetEnvironmentVariable (name,value); } return result; } static int iconv_from_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out, const char* encoding ) { char *text = mlt_properties_get( properties, prop_name ); int result = 0; if ( text ) { iconv_t cd = iconv_open( encoding, "UTF-8" ); if ( cd != (iconv_t) -1 ) { 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, &text, &inbuf_n, &outbuf_p, &outbuf_n ) != -1 ) mlt_properties_set( properties, prop_name_out, outbuf ); else mlt_properties_set( properties, prop_name_out, "" ); mlt_pool_release( outbuf ); result = iconv_close( cd ); } else { result = -1; } } return result; } static int iconv_to_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out, const char* encoding ) { char *text = mlt_properties_get( properties, prop_name ); int result = 0; if ( text ) { iconv_t cd = iconv_open( "UTF-8", encoding ); if ( cd != (iconv_t) -1 ) { 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, &text, &inbuf_n, &outbuf_p, &outbuf_n ) != -1 ) mlt_properties_set( properties, prop_name_out, outbuf ); else mlt_properties_set( properties, prop_name_out, "" ); mlt_pool_release( outbuf ); result = iconv_close( cd ); } else { result = -1; } } return result; } int mlt_properties_from_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out ) { int result = -1; UINT codepage = GetACP(); if ( codepage > 0 ) { // numeric code page char codepage_str[10]; snprintf( codepage_str, sizeof(codepage_str), "CP%u", codepage ); codepage_str[sizeof(codepage_str) - 1] = '\0'; result = iconv_from_utf8( properties, prop_name, prop_name_out, codepage_str ); } if ( result < 0 ) { result = mlt_properties_set( properties, prop_name_out, mlt_properties_get( properties, prop_name ) ); mlt_log_warning( NULL, "iconv failed to convert \"%s\" from UTF-8 to code page %u: %s\n", prop_name, codepage, mlt_properties_get( properties, prop_name ) ); } return result; } int mlt_properties_to_utf8( mlt_properties properties, const char *prop_name, const char *prop_name_out ) { int result = -1; UINT codepage = GetACP(); if ( codepage > 0 ) { // numeric code page char codepage_str[10]; snprintf( codepage_str, sizeof(codepage_str), "CP%u", codepage ); codepage_str[sizeof(codepage_str) - 1] = '\0'; result = iconv_to_utf8( properties, prop_name, prop_name_out, codepage_str ); } if ( result < 0 ) { result = mlt_properties_set( properties, prop_name_out, mlt_properties_get( properties, prop_name ) ); mlt_log_warning( NULL, "iconv failed to convert \"%s\" from code page %u to UTF-8\n", prop_name, codepage ); } return result; } /* Adapted from g_win32_getlocale() - free() the result */ char* getlocale() { LCID lcid; LANGID langid; char *ev; int primary, sub; char iso639[10]; char iso3166[10]; const char *script = ""; char result[33]; /* Let the user override the system settings through environment * variables, as on POSIX systems. */ if (((ev = getenv ("LC_ALL")) != NULL && ev[0] != '\0') || ((ev = getenv ("LC_MESSAGES")) != NULL && ev[0] != '\0') || ((ev = getenv ("LANG")) != NULL && ev[0] != '\0')) return strdup (ev); lcid = GetThreadLocale (); if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, sizeof (iso639)) || !GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166))) return strdup ("C"); /* Strip off the sorting rules, keep only the language part. */ langid = LANGIDFROMLCID (lcid); /* Split into language and territory part. */ primary = PRIMARYLANGID (langid); sub = SUBLANGID (langid); /* Handle special cases */ switch (primary) { case LANG_AZERI: switch (sub) { case SUBLANG_AZERI_LATIN: script = "@Latn"; break; case SUBLANG_AZERI_CYRILLIC: script = "@Cyrl"; break; } break; case LANG_SERBIAN: /* LANG_CROATIAN == LANG_SERBIAN */ switch (sub) { case SUBLANG_SERBIAN_LATIN: case 0x06: /* Serbian (Latin) - Bosnia and Herzegovina */ script = "@Latn"; break; } break; case LANG_UZBEK: switch (sub) { case SUBLANG_UZBEK_LATIN: script = "@Latn"; break; case SUBLANG_UZBEK_CYRILLIC: script = "@Cyrl"; break; } break; } snprintf (result, sizeof(result), "%s_%s%s", iso639, iso3166, script); result[sizeof(result) - 1] = '\0'; return strdup (result); } FILE* win32_fopen(const char *filename_utf8, const char *mode_utf8) { // Convert UTF-8 to wide chars. int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename_utf8, -1, NULL, 0); if (n > 0) { wchar_t *filename_w = (wchar_t *) calloc(n, sizeof(wchar_t)); if (filename_w) { int m = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode_utf8, -1, NULL, 0); if (m > 0) { wchar_t *mode_w = (wchar_t *) calloc(m, sizeof(wchar_t)); if (mode_w) { MultiByteToWideChar(CP_UTF8, 0, filename_utf8, -1, filename_w, n); MultiByteToWideChar(CP_UTF8, 0, mode_utf8, -1, mode_w, n); FILE *fh = _wfopen(filename_w, mode_w); free(filename_w); if (fh) return fh; } } } } // Try with regular old fopen. return fopen(filename_utf8, mode_utf8); }