summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Auras <yak54@gmx.net>2010-02-08 16:53:24 +0100
committerAndreas Auras <yak54@gmx.net>2010-02-08 16:53:24 +0100
commit82687e40db5d89aaff644fe7a34ae3c058b80255 (patch)
tree0df3b9e872779a4e4f98d48e67ac72d34e658eb6
downloadxine-lib-atmolight-82687e40db5d89aaff644fe7a34ae3c058b80255.tar.gz
xine-lib-atmolight-82687e40db5d89aaff644fe7a34ae3c058b80255.tar.bz2
Import of unmanaged version 0.3 of plugin
Signed-off-by: Andreas Auras <yak54@gmx.net>
-rw-r--r--.cproject144
-rw-r--r--.project77
-rw-r--r--.settings/org.eclipse.cdt.core.prefs3
-rw-r--r--COPYING340
-rw-r--r--Makefile33
-rw-r--r--README177
-rw-r--r--debian/changelog25
-rw-r--r--debian/compat1
-rw-r--r--debian/control14
-rw-r--r--debian/copyright26
-rw-r--r--debian/docs1
-rw-r--r--debian/rules91
-rw-r--r--df10ch_usb_proto.h116
-rw-r--r--output_driver.h1025
-rw-r--r--xine_post_atmo.c1272
15 files changed, 3345 insertions, 0 deletions
diff --git a/.cproject b/.cproject
new file mode 100644
index 0000000..3e3e00d
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?>
+
+<cproject>
+<storageModule moduleId="org.eclipse.cdt.core.settings">
+<cconfiguration id="cdt.managedbuild.toolchain.gnu.base.522923725">
+<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.522923725" moduleId="org.eclipse.cdt.core.settings" name="Linux GCC">
+<externalSettings/>
+<extensions>
+<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+</extensions>
+</storageModule>
+<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+<configuration artifactName="atmo" buildProperties="" id="cdt.managedbuild.toolchain.gnu.base.522923725" name="Linux GCC" parent="org.eclipse.cdt.build.core.emptycfg">
+<folderInfo id="cdt.managedbuild.toolchain.gnu.base.522923725.1299807297" name="/" resourcePath="">
+<toolChain id="cdt.managedbuild.toolchain.gnu.base.1765852131" name="cdt.managedbuild.toolchain.gnu.base" superClass="cdt.managedbuild.toolchain.gnu.base">
+<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.1949347644" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
+<builder id="cdt.managedbuild.target.gnu.builder.base.1108692234" managedBuildOn="false" name="Gnu Make Builder.Linux GCC" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+<tool id="cdt.managedbuild.tool.gnu.archiver.base.1982832721" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
+<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.2064905689" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/>
+<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1226506188" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
+<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.758137729" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.c.linker.base.314775202" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base">
+<inputType id="cdt.managedbuild.tool.gnu.c.linker.input.2084718394" superClass="cdt.managedbuild.tool.gnu.c.linker.input">
+<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+<additionalInput kind="additionalinput" paths="$(LIBS)"/>
+</inputType>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1209638415" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"/>
+<tool id="cdt.managedbuild.tool.gnu.assembler.base.891915511" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
+<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1190003687" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+</tool>
+</toolChain>
+</folderInfo>
+</configuration>
+</storageModule>
+<storageModule moduleId="scannerConfiguration">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</storageModule>
+<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
+<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
+<buildTargets>
+<target name="install" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+<buildCommand>make</buildCommand>
+<buildArguments/>
+<buildTarget>install</buildTarget>
+<stopOnError>true</stopOnError>
+<useDefaultCommand>true</useDefaultCommand>
+<runAllBuilders>false</runAllBuilders>
+</target>
+</buildTargets>
+</storageModule>
+</cconfiguration>
+</storageModule>
+<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+<project id="atmo.null.2145325042" name="atmo"/>
+</storageModule>
+</cproject>
diff --git a/.project b/.project
new file mode 100644
index 0000000..54774f4
--- /dev/null
+++ b/.project
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>xine-lib-atmolight</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+ <triggers>clean,full,incremental,</triggers>
+ <arguments>
+ <dictionary>
+ <key>?name?</key>
+ <value></value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.append_environment</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.autoBuildTarget</key>
+ <value>all</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.buildArguments</key>
+ <value></value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.buildCommand</key>
+ <value>make</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
+ <value>clean</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.contents</key>
+ <value>org.eclipse.cdt.make.core.activeConfigSettings</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableAutoBuild</key>
+ <value>false</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableCleanBuild</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.enableFullBuild</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.fullBuildTarget</key>
+ <value>all</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.stopOnError</key>
+ <value>true</value>
+ </dictionary>
+ <dictionary>
+ <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
+ <value>true</value>
+ </dictionary>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+ <nature>org.eclipse.cdt.core.cnature</nature>
+ </natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs
new file mode 100644
index 0000000..9d7778c
--- /dev/null
+++ b/.settings/org.eclipse.cdt.core.prefs
@@ -0,0 +1,3 @@
+#Tue Jul 07 22:40:27 CEST 2009
+eclipse.preferences.version=1
+environment/project/cdt.managedbuild.toolchain.gnu.base.522923725=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?>\n<environment append\="true" appendContributed\="true">\n<variable delimiter\=";" name\="PKG_CONFIG_PATH" operation\="append" value\="/home/andy/build/lib/pkgconfig"/>\n</environment>\n
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f90922e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 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.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..44db1ba
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,33 @@
+#
+# Makefile for xine atmo post plugin
+#
+INSTALL ?= install
+XINEPLUGINDIR ?= $(shell pkg-config --variable=plugindir libxine)
+CFLAGS_XINE ?= $(shell pkg-config --cflags libxine )
+LIBS_XINE ?= $(shell pkg-config --libs libxine)
+CFLAGS_USB ?= $(shell pkg-config --cflags libusb-1.0)
+LIBS_USB ?= $(shell pkg-config --libs libusb-1.0)
+LDFLAGS_SO ?= -shared -fvisibility=hidden
+CFLAGS ?= -O3 -pipe -Wall -fPIC -g
+
+XINEPOSTATMO = xineplug_post_atmo.so
+
+.PHONY: all install clean
+
+all: $(XINEPOSTATMO)
+
+install: all
+ @echo Installing $(XINEPLUGINDIR)/post/$(XINEPOSTATMO)
+ @-rm -rf $(XINEPLUGINDIR)/post/$(XINEPOSTATMO)
+ @$(INSTALL) -m 0644 $(XINEPOSTATMO) $(XINEPLUGINDIR)/post/$(XINEPOSTATMO)
+
+clean:
+ @-rm -f *.so* *.o
+
+xine_post_atmo.o: xine_post_atmo.c output_driver.h
+ $(CC) $(CFLAGS) $(CFLAGS_XINE) $(CFLAGS_USB) -c -o $@ $<
+
+$(XINEPOSTATMO): xine_post_atmo.o
+ $(CC) $(CFLAGS) $(LDFLAGS_SO) $(LIBS_XINE) $(LIBS_USB) -lm -o $@ $<
+
+
diff --git a/README b/README
new file mode 100644
index 0000000..4b7c0d4
--- /dev/null
+++ b/README
@@ -0,0 +1,177 @@
+This is a "post plugin" for xine based media players.
+
+Written by: Andreas Auras (yak54@gmx.net)
+
+See the file COPYING for license information.
+
+Many ideas and algorithm for this plugin had been derived from the fantastic
+Atmolight-plugin for the Video Disk Recorder (VDR) that was developed by
+Eike Edener.
+
+
+Description:
+------------
+The "atmo" post plugin analyze the video picture and generates
+output data for so called "atmolight" controllers.
+
+For detailed informations about "atmolight" visit the VDR-Wiki:
+http://www.vdr-wiki.de/wiki/index.php/Atmo-plugin
+
+!!!NOTE!!!!
+Currently this plugin supports only grabbing of video from VDPAU
+enabled devices!
+
+
+Requirements:
+-------------
+
+xine-lib-vdpau or xine-lib-1.2 with vdpau support
+Both version must have the vdpau extension patches from here:
+http://www.vdr-portal.de/board/thread.php?threadid=86804
+
+For compiling the plugin the xine-lib and libusb-1.0 development
+files have to be installed. On debian based systems these are
+packages libxine-dev and libusb-1.0-0-dev.
+
+
+Compiling and installation:
+---------------------------
+
+make
+make install
+
+
+Configuration:
+--------------
+
+Load this plugin as a post plugin in your xine based media player.
+For the VDR xineliboutput player or xine-ui player add a command line option like this:
+
+--post=atmo:driver=classic,driver_param=/dev/ttyS0,top=1,bottom=1,left=1,right=1,center=1
+
+When using DF10CH controller(s):
+
+--post atmo:driver=df10ch
+
+
+Parameters understood by this plugin:
+-------------------------------------
+
+Parameter Default Description
+
+driver none Selects output driver. Currently supported are:
+
+ file Write output data to file. Usefull for
+ testing and debuging.
+
+ classic Send output data to serial port. Used
+ Protokoll is for "classic" Atmolight
+ 2 channel controller. See:
+ http://www.vdr-wiki.de/wiki/index.php/Atmo-plugin
+
+ df4ch Send output data to serial port. Used
+ Protokoll is for my own designed 4 channel
+ controller.
+
+ df10ch Send output data via libusb to my own designed
+ DF10CH 10ch Controller(s).
+
+driver_param Parameter for output driver:
+
+ file: File name of output file. If not specified
+ "xine_atmo_data.out" is used.
+
+ classic, df4ch:
+ Path of serial device e.g. /dev/ttyS0
+ For users with usb-to-serial converters the
+ device could be specified as a regular expression
+ that is looked up in /proc/tty/driver/usbserial to
+ evaluate the /dev/ttyUSB device. The parameter must
+ start with "usb:" followed by the regular expression
+ e.g.: usb:pl2303.*usb-.*-[^6]
+
+ df10ch:
+ All connected controllers are scanned automatically. No
+ parameter required here.
+
+top
+bottom
+left
+right
+center
+top_left
+top_right
+bottom_left
+bottom_right 0 Number of RGB channel groups per Area.
+ For top, bottom, left and right area more then one group could be
+ specified. This will be usefull when "next generation" controllers
+ like DF10CH are available. See the "aurora" project at:
+ http://www.vdr-portal.de/board/thread.php?threadid=71741&hilight=aurora
+ Valid values: 0 ... 25 for top, bottom, left, right
+ Valid values: 0 ... 1 for center, top_left, top_right, bottom_left, bottom_right
+
+ NOTE!!!: Starting from plugin version 0.3 for "classic" controllers you must define one
+ group for area top, bottom, left, right and center as plugin parameter!!!
+
+ For DF10CH controller you do not have to specify these parameters here because they are read
+ from the controller configuration data. Use the DF10CH setup program to configure your
+ desired layout.
+
+
+analyze_rate 40 Rate of frame grabbing and video analysis. Unit milliseconds.
+ Valid values: 10 ... 500
+
+analyze_size 1 Size of analyze window. The window width is calculated by (analyze_size+1)*64 pixel.
+ The window height is calcuated aspect correct according to the grabbed video
+ window size. So a analyze size of 1 for a 16:9 video will give us a 128x72 pixel
+ analyze window.
+ Valid values: 0 ... 3
+
+overscan 30 Ignored overscan border of grabbed video frame.
+ Unit is percentage of 1000. e.g. 30 -> 3%
+ Valid values: 0 ... 200
+
+darkness_limit 50 Minimum brightness of pixel. Value's below are interpreted as black pixel.
+ Used to detect and skip "black borders" in video.
+ Valid values are 0 ... 100
+
+edge_weighting 8 Power of edge weighting. Valid values 1 ... 30
+
+hue_win_size 3 Windowing size for HUE. Valid values 0 ... 5
+
+sat_win_size 3 Windowing size for saturation. Valid values 0 ... 5
+
+filter combined Select's smoothness filter. Currently there are two filters
+ supported: percentage and combined.
+ Valid values: off, percentage, combined
+
+filter_smoothness 50 Controls filter smoothness of percentage filter.
+ Unit percentage of 100. Valid values 1 ... 100
+
+filter_length 500 Controls filter length of combined filter.
+ Unit milliseconds. Valid values 300 ... 5000
+
+filter_threshold 40 Controls filter threshold of combined filter.
+ Unit percentage of 100. Valid values 1 ... 100
+
+brightness 100 Controls brightness of generated color values.
+ Unit percentage of 100. Valid values 50 ... 300
+ Note: When using DF10CH controller(s) it is better to use the white calibration or
+ common brightness feature of the DF10CH setup program instead of this
+ parameter because you will lose lesser or no PWM resolution.
+
+wc_red
+wc_green
+wc_blue 255 White calibration values for red, green and blue channel.
+ Valid values 0 ... 255.
+ Note: When using DF10CH controller(s) it is better to use the white calibration
+ feature of the DF10CH setup program instead of this parameter because you will
+ lose lesser PWM resolution.
+
+gamma 0 Gamma correction factor for red, green and blue channel.
+ Value is divided by 10 e.g. 25 -> gamma 2.5
+ Valid values 0 ... 50. 0 means no gamma correction
+ Note: When using DF10CH controller(s) it is better to use the gamma calibration
+ feature of the DF10CH setup program instead of this parameter because you will
+ lose no PWM resolution.
+ \ No newline at end of file
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..ce329aa
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,25 @@
+libxine-atmo-plugin (0.3-0) karmic; urgency=low
+
+ * New upstream version 0.3
+ * added dependency for libusb-1.0
+ * control file fixed so that compilation against libxine1 or libxine2 is possible
+
+ -- durchflieger <yak54@gmx.net> Thu, 04 Feb 2010 11:49:15 +0100
+
+libxine-atmo-plugin (0.2-3tvt1) karmic; urgency=low
+
+ * changed debian/control+copyright
+
+ -- Holger Schvestka <hotzenplotz5@gmx.de> Mon, 28 Dec 2009 21:09:18 +0100
+
+libxine-atmo-plugin (0.2-2tvt1) karmic; urgency=low
+
+ * recompile with -fPIC
+
+ -- Gerald Dachs <gda@dachsweb.de> Mon, 28 Dec 2009 12:45:24 +0100
+
+libxine-atmo-plugin (0.2-1tvt1) karmic; urgency=low
+
+ * Initial release
+
+ -- Gerald Dachs <gda@dachsweb.de> Mon, 28 Dec 2009 00:08:17 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..59bc583
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,14 @@
+Source: libxine-atmo-plugin
+Section: libs
+Priority: extra
+Maintainer: Gerald Dachs <gda@dachsweb.de>
+Build-Depends: debhelper (>= 7), libxine-dev, libusb-1.0-0-dev
+Standards-Version: 3.8.3
+Homepage: http://www.vdrportal.de/board/thread.php?threadid=88205&hilightuser=18772
+
+Package: libxine-atmo-plugin
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: xine post plugin needed for atmolight controllers
+ The "atmo" post plugin analyze the video picture and generates
+ output data for so called "atmolight" controllers.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..71ac9ee
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,26 @@
+This package was debianized by Gerald Dachs <Gerald Dachs <gda@dachsweb.de>> on
+Mon, 28 Dec 2009 00:08:17 +0100.
+
+ It was downloaded from http://www.vdrportal.de/board/thread.php?threadid=88205&hilightuser=18772
+
+Upstream Author:
+
+ Andreas Auras <yak54@gmx.net>
+
+Copyright:
+
+ (C) 2009 Andreas Auras
+
+License:
+
+ and is licensed under the GPL version 2,
+ see `/usr/share/common-licenses/GPL-2'.
+
+The Debian packaging is:
+
+ Copyright (C) 2009 Gerald Dachs <Gerald Dachs <gda@dachsweb.de>>
+
+ and is licensed under the GPL version 2,
+ see `/usr/share/common-licenses/GPL-2'.
+
+
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..e845566
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1 @@
+README
diff --git a/debian/rules b/debian/rules
new file mode 100644
index 0000000..75b5e91
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,91 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+CFLAGS+=-O3 -fPIC
+XINEPLUGINDIR = $(shell pkg-config --variable=plugindir libxine)
+
+
+
+configure: configure-stamp
+configure-stamp:
+ dh_testdir
+ # Add here commands to configure the package.
+
+ touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp
+ dh_testdir
+
+ # Add here commands to compile the package.
+ $(MAKE)
+ #docbook-to-man debian/atmo.sgml > atmo.1
+
+ touch $@
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp configure-stamp
+
+ # Add here commands to clean up after the build process.
+ $(MAKE) clean
+
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_prep
+ dh_installdirs
+
+ # Add here commands to install the package into debian/atmo.
+ dh_install xineplug_post_atmo.so $(XINEPLUGINDIR)/post/
+
+# Build architecture-independent files here.
+binary-indep: install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs
+ dh_installdocs
+ dh_installexamples
+# dh_install
+# dh_installmenu
+# dh_installdebconf
+# dh_installlogrotate
+# dh_installemacsen
+# dh_installpam
+# dh_installmime
+# dh_python
+# dh_installinit
+# dh_installcron
+# dh_installinfo
+ dh_installman
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+# dh_perl
+# dh_makeshlibs
+ dh_installdeb
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/df10ch_usb_proto.h b/df10ch_usb_proto.h
new file mode 100644
index 0000000..d1b7310
--- /dev/null
+++ b/df10ch_usb_proto.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 Andreas Auras
+ *
+ * This file is part of the atmo post plugin, a plugin for the free xine video player.
+ *
+ * atmo post plugin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * atmo post plugin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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, USA
+ *
+ */
+
+/* ---
+ * Communication Protokoll related defines for DF10CH 10 channel RGB Controller.
+ */
+
+#define REQ_USB_START 0 // Start of usb controller requests
+#define REQ_USB_BL_START 64 // Start of usb controller boot loader requests
+#define REQ_PWM_START 128 // Start of pwm controller requests
+#define REQ_PWM_BL_START 192 // Start of pwm controller boot loader requests
+
+enum
+{
+ // usb controller requests
+ REQ_START_BOOTLOADER = REQ_USB_START, // start boot loader of usb controller
+ REQ_READ_EE_DATA, // read eeprom data (wLength: number of bytes, wIndex: eeprom start address)
+ REQ_WRITE_EE_DATA, // write eeprom data (wLength: number of bytes, wIndex: eeprom start address)
+
+ REQ_STOP_PWM_CTRL, // stop PWM controller
+ REQ_RESET_PWM_CTRL, // reset PWM controller
+ REQ_BOOTLOADER_RESET_PWM_CTRL, // reset PWM controller and signal bootloader start
+
+ REQ_SET_REPLY_TIMEOUT, // set reply timeout values (wValue: start timeout [ms], wIndex: timeout [ms])
+
+ REQ_GET_REPLY_ERR_STATUS, // get reply error status (COMM_ERR_...)
+
+ // usb controller boot loader requests
+ BL_REQ_WRITE_PAGE = REQ_USB_BL_START, // write flash page
+ BL_REQ_LEAVE_BOOT, // leave boot loader and start application
+ BL_REQ_GET_PAGE_SIZE, // return flash page size of device
+ BL_REQ_READ_FLASH, // return flash contents
+
+ // pwm controller requests
+ PWM_REQ_GET_VERSION = REQ_PWM_START, // Get firmware version (returns 1 byte)
+ PWM_REQ_SET_BRIGHTNESS, // Set channel brightness values (wLenght: number of bytes, wIndex: start channel)
+ PWM_REQ_SET_BRIGHTNESS_SYNCED, // Same as above but wait until buffer flip
+ PWM_REQ_GET_BRIGHTNESS,
+
+ PWM_REQ_SET_CHANNEL_MAP, // Set channel to port mapping (wLength: number of bytes, wIndex: start channel)
+ PWM_REQ_GET_CHANNEL_MAP,
+
+ PWM_REQ_SET_COMMON_PWM, // Set common pwm value (wValue.low: pwm value)
+ PWM_REQ_GET_COMMON_PWM,
+
+ PWM_REQ_STORE_SETUP, // Store actual calibration values
+ PWM_REQ_RESET_SETUP, // Reset calibration values to default
+
+ PWM_REQ_GET_REQUEST_ERR_STATUS, // Get request error status (COMM_ERR_...)
+
+ PWM_REQ_GET_MAX_PWM, // Get maximum internal PWM value
+
+ PWM_REQ_SET_PWM_FREQ, // Set pwm frequency (wValue: frequency [hz])
+ PWM_REQ_GET_PWM_FREQ, // Get pwm frequency (returns word)
+
+ PWM_REQ_ECHO_TEST, // Reply 8 byte header
+
+ // pwm controller boot loader requests
+ BL_PWM_REQ_WRITE_PAGE = REQ_PWM_BL_START, // write flash page
+ BL_PWM_REQ_GET_PAGE_SIZE, // return flash page size of device
+ BL_PWM_REQ_READ_FLASH, // return flash contents
+ BL_PWM_REQ_GET_REQUEST_ERR_STATUS // Get request error status (COMM_ERR_...)
+};
+
+// Data payload related
+#define MAX_REQ_PAYLOAD_SIZE 128
+#define MAX_REPLY_PAYLOAD_SIZE 128
+
+// Error flag definition for communication error's of usb and pwm controller
+#define COMM_ERR_OVERRUN 0
+#define COMM_ERR_FRAME 1
+#define COMM_ERR_TIMEOUT 2
+#define COMM_ERR_START 3
+#define COMM_ERR_OVERFLOW 4
+#define COMM_ERR_CRC 5
+#define COMM_ERR_DUPLICATE 6
+#define COMM_ERR_DEBUG 7
+
+// Port channel mapping related
+#define NCHANNELS 30 // Number of supported Channels
+
+#define NPORTS 4
+#define PA_IDX 0
+#define PB_IDX 1
+#define PC_IDX 2
+#define PD_IDX 3
+
+#define CM_CODE(port, channel) (((channel) << 2) | (port))
+#define CM_CHANNEL(code) ((code) >> 2)
+#define CM_PORT(code) ((code) & 0x03)
+
+// PWM frequency related
+#define MIN_PWM_FREQ 50
+#define MAX_PWM_FREQ 400
+
+// PWM controller version request related
+#define PWM_VERS_APPL 0 // Is application firmware
+#define PWM_VERS_BOOT 1 // Is bootloader firmware
diff --git a/output_driver.h b/output_driver.h
new file mode 100644
index 0000000..3d88b4f
--- /dev/null
+++ b/output_driver.h
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (C) 2009, 2010 Andreas Auras
+ *
+ * This file is part of the atmo post plugin, a plugin for the free xine video player.
+ *
+ * atmo post plugin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * atmo post plugin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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, USA
+ *
+ */
+
+#include <termios.h>
+#include <regex.h>
+#include <fcntl.h>
+#include <math.h>
+
+#define NUM_AREAS 9 // Number of different areas (top, bottom ...)
+
+typedef struct { int sum, top, bottom, left, right, center, top_left, top_right, bottom_left, bottom_right; } num_channels_t;
+
+
+/*
+ * abstraction for output drivers
+ */
+typedef struct output_driver_s output_driver_t;
+struct output_driver_s {
+ /* open device and configure for number of channels */
+ int (*open)(output_driver_t *this, const char *param, num_channels_t *channels);
+
+ /* configure device for number of channels */
+ int (*configure)(output_driver_t *this, num_channels_t *channels);
+
+ /* close device */
+ void (*close)(output_driver_t *this);
+
+ /*
+ * send RGB color values to device
+ * last_colors is NULL when first initial color packet is send
+ * order for 'colors' is: top 1,2,3..., bottom 1,2,3..., left 1,2,3..., right 1,2,3..., center, top left, top right, bottom left, bottom right
+ */
+ void (*output_colors)(output_driver_t *this, rgb_color_t *new_colors, rgb_color_t *last_colors);
+
+ /* provide detailed error message here if open of device fails */
+ char errmsg[128];
+};
+
+
+
+/**********************************************************************************************************
+ * File output driver
+ **********************************************************************************************************/
+
+typedef struct {
+ output_driver_t output_driver;
+ num_channels_t num_channels;
+ FILE *fd;
+ int id;
+} file_output_driver_t;
+
+
+static int file_driver_open(output_driver_t *this_gen, const char *param, num_channels_t *n) {
+ file_output_driver_t *this = (file_output_driver_t *) this_gen;
+
+ this->num_channels = *n;
+ this->id = 0;
+
+ if (param && strlen(param))
+ this->fd = fopen(param, "a");
+ else
+ this->fd = fopen("xine_atmo_data.out", "a");
+
+ if (this->fd == NULL) {
+ strerror_r(errno, this->output_driver.errmsg, sizeof(this->output_driver.errmsg));
+ return -1;
+ }
+ return 0;
+}
+
+static int file_driver_configure(output_driver_t *this_gen, num_channels_t *n) {
+ file_output_driver_t *this = (file_output_driver_t *) this_gen;
+ this->num_channels = *n;
+ return 0;
+}
+
+static void file_driver_close(output_driver_t *this_gen) {
+ file_output_driver_t *this = (file_output_driver_t *) this_gen;
+
+ if (this->fd) {
+ fclose(this->fd);
+ this->fd = NULL;
+ }
+}
+
+
+static void file_driver_output_colors(output_driver_t *this_gen, rgb_color_t *colors, rgb_color_t *last_colors) {
+ file_output_driver_t *this = (file_output_driver_t *) this_gen;
+ FILE *fd = this->fd;
+ struct timeval tvnow;
+ int c;
+
+ if (fd) {
+ gettimeofday(&tvnow, NULL);
+ fprintf(fd, "%d: %ld.%03ld ---\n", this->id++, tvnow.tv_sec, tvnow.tv_usec / 1000);
+
+ for (c = 1; c <= this->num_channels.top; ++c, ++colors)
+ fprintf(fd," top %2d: %3d %3d %3d\n", c, colors->r, colors->g, colors->b);
+
+ for (c = 1; c <= this->num_channels.bottom; ++c, ++colors)
+ fprintf(fd," bottom %2d: %3d %3d %3d\n", c, colors->r, colors->g, colors->b);
+
+ for (c = 1; c <= this->num_channels.left; ++c, ++colors)
+ fprintf(fd," left %2d: %3d %3d %3d\n", c, colors->r, colors->g, colors->b);
+
+ for (c = 1; c <= this->num_channels.right; ++c, ++colors)
+ fprintf(fd," right %2d: %3d %3d %3d\n", c, colors->r, colors->g, colors->b);
+
+ if (this->num_channels.center) {
+ fprintf(fd," center: %3d %3d %3d\n", colors->r, colors->g, colors->b);
+ ++colors;
+ }
+ if (this->num_channels.top_left) {
+ fprintf(fd," top left: %3d %3d %3d\n", colors->r, colors->g, colors->b);
+ ++colors;
+ }
+ if (this->num_channels.top_right) {
+ fprintf(fd," top right: %3d %3d %3d\n", colors->r, colors->g, colors->b);
+ ++colors;
+ }
+ if (this->num_channels.bottom_left) {
+ fprintf(fd," bottom left: %3d %3d %3d\n", colors->r, colors->g, colors->b);
+ ++colors;
+ }
+ if (this->num_channels.bottom_right) {
+ fprintf(fd," bottom right: %3d %3d %3d\n", colors->r, colors->g, colors->b);
+ }
+ fflush(fd);
+ }
+}
+
+
+
+/*************************************************************************************************
+ * Serial output driver
+ *************************************************************************************************/
+
+typedef struct {
+ output_driver_t output_driver;
+ num_channels_t num_channels;
+ int devfd;
+} serial_output_driver_t;
+
+
+static int serial_driver_open(output_driver_t *this_gen, const char *param, num_channels_t *n) {
+ serial_output_driver_t *this = (serial_output_driver_t *) this_gen;
+ char buf[256], buf1[64], *s;
+ const char *devname = NULL;
+ regex_t preg;
+ int rc;
+
+ this->num_channels = *n;
+ this->devfd = -1;
+
+ if (!param || !strlen(param))
+ {
+ strcpy(this->output_driver.errmsg, "no device parameter");
+ return -1;
+ }
+
+ if (strncmp(param, "usb:", 4) == 0) {
+ /* Lookup serial USB device name */
+ rc = regcomp(&preg, param + 4, REG_EXTENDED | REG_NOSUB);
+ if (rc) {
+ regerror(rc, &preg, buf, sizeof(buf));
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "illegal device identification pattern '%s': %s", param + 4, buf);
+ regfree(&preg);
+ return -1;
+ }
+
+ FILE *procfd = fopen("/proc/tty/driver/usbserial", "r");
+ if (!procfd) {
+ strerror_r(errno, buf, sizeof(buf));
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "could not open /proc/tty/driver/usbserial: %s", buf);
+ regfree(&preg);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), procfd)) {
+ if (!regexec(&preg, buf, 0, NULL, 0) && (s = index(buf, ':'))) {
+ *s = 0;
+ snprintf(buf1, sizeof(buf1), "/dev/ttyUSB%s", buf);
+ devname = buf1;
+ break;
+ }
+ }
+ fclose(procfd);
+ regfree(&preg);
+ if (!devname) {
+ strcpy(this->output_driver.errmsg, "could not find usb device in /proc/tty/driver/usbserial");
+ return -1;
+ }
+ llprintf(LOG_1, "USB tty device for '%s' is '%s'\n", param + 4, devname);
+ } else {
+ devname = param;
+ }
+
+ /* open serial port */
+ int devfd = open(devname, O_RDWR | O_NOCTTY);
+ if (devfd < 0) {
+ strerror_r(errno, buf, sizeof(buf));
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "could not open serial port: %s", buf);
+ return -1;
+ }
+
+ /* configure serial port */
+ const int bconst = B38400;
+ struct termios tio;
+ memset(&tio, 0, sizeof(tio));
+ tio.c_cflag = (CS8 | CREAD | CLOCAL);
+ tio.c_cc[VMIN] = 0;
+ tio.c_cc[VTIME] = 1;
+ cfsetispeed(&tio, bconst);
+ cfsetospeed(&tio, bconst);
+ if (tcsetattr(devfd, TCSANOW, &tio)) {
+ strerror_r(errno, buf, sizeof(buf));
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "configuration of serial port failed: %s", buf);
+ close(devfd);
+ return -1;
+ }
+ tcflush(devfd, TCIOFLUSH);
+
+ this->devfd = devfd;
+ return 0;
+}
+
+
+static int serial_driver_configure(output_driver_t *this_gen, num_channels_t *n) {
+ serial_output_driver_t *this = (serial_output_driver_t *) this_gen;
+ this->num_channels = *n;
+ return 0;
+}
+
+
+static void serial_driver_close(output_driver_t *this_gen) {
+ serial_output_driver_t *this = (serial_output_driver_t *) this_gen;
+
+ if (this->devfd < 0)
+ return;
+
+ close(this->devfd);
+ this->devfd = -1;
+}
+
+
+
+/**************************************************************************************
+ * Protocol for "classic" atmolight serial 2ch controller
+ **************************************************************************************/
+
+static void classic_driver_output_colors(output_driver_t *this_gen, rgb_color_t *colors, rgb_color_t *last_colors) {
+ serial_output_driver_t *this = (serial_output_driver_t *) this_gen;
+ uint8_t msg[19];
+
+ if (this->devfd < 0)
+ return;
+
+ /* create command */
+ memset(msg, 0, sizeof(msg));
+ msg[0] = 0xFF; /* start byte */
+ msg[1] = msg[2] = 0; /* start channel */
+ msg[3] = 15; /* number of channels (5 * RGB) */
+
+ /* top channel */
+ if (this->num_channels.top)
+ {
+ msg[13] = colors->r;
+ msg[14] = colors->g;
+ msg[15] = colors->b;
+ colors += this->num_channels.top;
+ }
+
+ /* bottom channel */
+ if (this->num_channels.bottom)
+ {
+ msg[16] = colors->r;
+ msg[17] = colors->g;
+ msg[18] = colors->b;
+ colors += this->num_channels.bottom;
+ }
+
+ /* left channel */
+ if (this->num_channels.left)
+ {
+ msg[7] = colors->r;
+ msg[8] = colors->g;
+ msg[9] = colors->b;
+ colors += this->num_channels.left;
+ }
+
+ /* right channel */
+ if (this->num_channels.right)
+ {
+ msg[10] = colors->r;
+ msg[11] = colors->g;
+ msg[12] = colors->b;
+ colors += this->num_channels.right;
+ }
+
+ /* center channel */
+ if (this->num_channels.center)
+ {
+ msg[4] = colors->r;
+ msg[5] = colors->g;
+ msg[6] = colors->b;
+ }
+
+ /* send data to target */
+ if (write(this->devfd, msg, sizeof(msg)) > 0)
+ tcdrain(this->devfd); /* flush buffer */
+}
+
+
+
+/********************************************************************************
+ * Protocol for my own designed 4ch serial controller
+ ********************************************************************************/
+
+static void df4ch_driver_output_colors(output_driver_t *this_gen, rgb_color_t *colors, rgb_color_t *last_colors) {
+ serial_output_driver_t *this = (serial_output_driver_t *) this_gen;
+ uint8_t msg[15];
+
+ if (this->devfd < 0)
+ return;
+
+ /* create command */
+ memset(msg, 0, sizeof(msg));
+ msg[0] = 0xFF; /* start byte */
+ msg[1] = 0; /* start channel */
+ msg[2] = 12; /* number of channels (4 * RGB) */
+
+ /* top channel */
+ if (this->num_channels.top)
+ {
+ msg[9] = colors->r;
+ msg[10] = colors->g;
+ msg[11] = colors->b;
+ colors += this->num_channels.top;
+ }
+
+ /* bottom channel */
+ if (this->num_channels.bottom)
+ {
+ msg[12] = colors->r;
+ msg[13] = colors->g;
+ msg[14] = colors->b;
+ colors += this->num_channels.bottom;
+ }
+
+ /* left channel */
+ if (this->num_channels.left)
+ {
+ msg[3] = colors->r;
+ msg[4] = colors->g;
+ msg[5] = colors->b;
+ colors += this->num_channels.left;
+ }
+
+ /* right channel */
+ if (this->num_channels.right)
+ {
+ msg[6] = colors->r;
+ msg[7] = colors->g;
+ msg[8] = colors->b;
+ }
+
+ /* send data to target */
+ if (write(this->devfd, msg, sizeof(msg)) > 0)
+ tcdrain(this->devfd); /* flush buffer */
+}
+
+
+
+/***************************************************************************************************
+ * DF10CH output driver for my own designed "next generation" 10ch RGB Controller
+ ***************************************************************************************************/
+
+#include <libusb.h>
+#include "df10ch_usb_proto.h"
+
+#define DF10CH_USB_CFG_VENDOR_ID 0x16c0
+#define DF10CH_USB_CFG_PRODUCT_ID 0x05dc
+#define DF10CH_USB_CFG_VENDOR_NAME "yak54@gmx.net"
+#define DF10CH_USB_CFG_PRODUCT "DF10CH"
+#define DF10CH_USB_CFG_SERIAL "AP"
+#define DF10CH_USB_DEFAULT_TIMEOUT 100
+
+#define DF10CH_MAX_CHANNELS 30
+#define DF10CH_SIZE_CONFIG (14 + DF10CH_MAX_CHANNELS * 6)
+#define DF10CH_CONFIG_VALID_ID 0xA0A1
+
+enum { DF10CH_AREA_TOP, DF10CH_AREA_BOTTOM, DF10CH_AREA_LEFT, DF10CH_AREA_RIGHT, DF10CH_AREA_CENTER, DF10CH_AREA_TOP_LEFT, DF10CH_AREA_TOP_RIGHT, DF10CH_AREA_BOTTOM_LEFT, DF10CH_AREA_BOTTOM_RIGHT };
+
+typedef struct df10ch_gamma_tab_s {
+ struct df10ch_gamma_tab_s *next;
+ uint8_t gamma;
+ uint16_t white_cal;
+ uint16_t tab[256];
+} df10ch_gamma_tab_t;
+
+typedef struct {
+ int req_channel; // Channel number in request
+ int area; // Source area
+ int area_num; // Source area number
+ int color; // Source color
+ df10ch_gamma_tab_t *gamma_tab; // Corresponding gamma table
+} df10ch_channel_config_t;
+
+typedef struct df10ch_ctrl_s {
+ struct df10ch_ctrl_s *next;
+ libusb_device_handle *dev;
+ int idx_serial_number;
+ uint16_t config_version; // Version number of configuration data
+ uint16_t pwm_res; // PWM resolution
+ int num_req_channels; // Number of channels in request
+ df10ch_channel_config_t *channel_config; // List of channel configurations
+ char id[32]; // ID of Controller
+ struct libusb_transfer *transfer;
+ uint8_t *transfer_data;
+ int pending_submit;
+} df10ch_ctrl_t;
+
+typedef struct {
+ output_driver_t output_driver;
+ libusb_context *ctx;
+ num_channels_t num_channels; // Global channel layout
+ df10ch_ctrl_t *ctrls; // List of found controllers
+ df10ch_gamma_tab_t *gamma_tabs; // List of calculated gamma tables
+ int max_transmit_latency;
+ int avg_transmit_latency;
+} df10ch_output_driver_t;
+
+
+static const char *df10ch_usb_errmsg(int rc) {
+ switch (rc) {
+ case LIBUSB_SUCCESS:
+ return ("Success (no error)");
+ case LIBUSB_ERROR_IO:
+ return ("Input/output error");
+ case LIBUSB_ERROR_INVALID_PARAM:
+ return ("Invalid parameter");
+ case LIBUSB_ERROR_ACCESS:
+ return ("Access denied (insufficient permissions)");
+ case LIBUSB_ERROR_NO_DEVICE:
+ return ("No such device (it may have been disconnected)");
+ case LIBUSB_ERROR_NOT_FOUND:
+ return ("Entity not found");
+ case LIBUSB_ERROR_BUSY:
+ return ("Resource busy");
+ case LIBUSB_ERROR_TIMEOUT:
+ return ("Operation timed out");
+ case LIBUSB_ERROR_OVERFLOW:
+ return ("Overflow");
+ case LIBUSB_ERROR_PIPE:
+ return ("Pipe error");
+ case LIBUSB_ERROR_INTERRUPTED:
+ return ("System call interrupted (perhaps due to signal)");
+ case LIBUSB_ERROR_NO_MEM:
+ return ("Insufficient memory");
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ return ("Operation not supported or unimplemented on this platform");
+ case LIBUSB_ERROR_OTHER:
+ return ("Other error");
+ }
+ return ("?");
+}
+
+
+static const char * df10ch_usb_transfer_errmsg(int s) {
+ switch (s) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ return ("Transfer completed without error");
+ case LIBUSB_TRANSFER_ERROR:
+ return ("Transfer failed");
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ return ("Transfer timed out");
+ case LIBUSB_TRANSFER_CANCELLED:
+ return ("Transfer was cancelled");
+ case LIBUSB_TRANSFER_STALL:
+ return ("Control request stalled");
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ return ("Device was disconnected");
+ case LIBUSB_TRANSFER_OVERFLOW:
+ return ("Device sent more data than requested");
+ }
+ return ("?");
+}
+
+
+static int df10ch_control_in_transfer(df10ch_ctrl_t *ctrl, uint8_t req, uint16_t val, uint16_t index, unsigned int timeout, uint8_t *buf, uint16_t buflen)
+{
+ // Use a return buffer always so that the controller is able to send a USB reply status
+ // This is special for VUSB at controller side
+ unsigned char rcbuf[1];
+ int len = buflen;
+ if (!len)
+ {
+ buf = rcbuf;
+ len = 1;
+ }
+
+ // Because VUSB at controller sends ACK reply before CRC check of received data we have to retry sending request our self if data is corrupted
+ int n = 0, retrys = 0;
+ while (retrys < 3)
+ {
+ n = libusb_control_transfer(ctrl->dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, req, val, index, buf, len, timeout);
+ if (n != LIBUSB_ERROR_INTERRUPTED)
+ {
+ if (n >= 0 || n != LIBUSB_ERROR_PIPE)
+ break;
+ ++retrys;
+ }
+ }
+
+ if (n < 0)
+ {
+ llprintf(LOG_1, "%s: sending USB control transfer message %d failed: %s\n", ctrl->id, req, df10ch_usb_errmsg(n));
+ return -1;
+ }
+
+ if (n != buflen)
+ {
+ llprintf(LOG_1, "%s: sending USB control transfer message %d failed: read %d bytes but expected %d bytes\n", ctrl->id, req, n, buflen);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void df10ch_dispose(df10ch_output_driver_t *this) {
+ df10ch_ctrl_t *ctrl = this->ctrls;
+ while (ctrl) {
+ libusb_free_transfer(ctrl->transfer);
+ libusb_release_interface(ctrl->dev, 0);
+ libusb_close(ctrl->dev);
+
+ df10ch_ctrl_t *next = ctrl->next;
+ free(ctrl->transfer_data);
+ free(ctrl->channel_config);
+ free(ctrl);
+ ctrl = next;
+ }
+
+ if (this->ctx)
+ libusb_exit(this->ctx);
+
+ df10ch_gamma_tab_t *gt = this->gamma_tabs;
+ while (gt) {
+ df10ch_gamma_tab_t *next = gt->next;
+ free(gt);
+ gt = next;
+ }
+
+ this->ctrls = NULL;
+ this->ctx = NULL;
+ this->gamma_tabs = NULL;
+}
+
+
+static void df10ch_wait_for_replys(df10ch_output_driver_t *this) {
+ // wait for end of all pending transfers
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = (DF10CH_USB_DEFAULT_TIMEOUT + 50) * 1000;
+ df10ch_ctrl_t *ctrl = this->ctrls;
+ while (ctrl) {
+ if (ctrl->pending_submit) {
+ int rc = libusb_handle_events_timeout(this->ctx, &timeout);
+ if (rc && rc != LIBUSB_ERROR_INTERRUPTED) {
+ llprintf(LOG_1, "handling USB events failed: %s\n", df10ch_usb_errmsg(rc));
+ break;
+ }
+ }
+ else
+ ctrl = ctrl->next;
+ }
+}
+
+
+static void df10ch_reply_cb(struct libusb_transfer *transfer) {
+ df10ch_ctrl_t *ctrl = (df10ch_ctrl_t *) transfer->user_data;
+ ctrl->pending_submit = 0;
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED && transfer->status != LIBUSB_TRANSFER_CANCELLED)
+ llprintf(LOG_1, "%s: submitting USB control transfer message failed: %s\n", ctrl->id, df10ch_usb_transfer_errmsg(transfer->status));
+}
+
+
+static int df10ch_driver_open(output_driver_t *this_gen, const char *param, num_channels_t *num_channels) {
+ df10ch_output_driver_t *this = (df10ch_output_driver_t *) this_gen;
+
+ if (libusb_init(&this->ctx) < 0) {
+ strcpy(this->output_driver.errmsg, "can't initialize USB library");
+ return -1;
+ }
+
+ libusb_device **list = NULL;
+ size_t cnt = libusb_get_device_list(this->ctx, &list);
+ if (cnt < 0) {
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "getting list of USB devices failed: %s", df10ch_usb_errmsg(cnt));
+ df10ch_dispose(this);
+ return -1;
+ }
+
+ // Note: Because controller uses obdev's free USB product/vendor ID's we have to do special lookup for finding
+ // the controllers. See file "USB-IDs-for-free.txt" of VUSB distribution.
+ int rc;
+ size_t i;
+ for (i = 0; i < cnt; i++) {
+ libusb_device *d = list[i];
+ struct libusb_device_descriptor desc;
+
+ int busnum = libusb_get_bus_number(d);
+ int devnum = libusb_get_device_address(d);
+
+ rc = libusb_get_device_descriptor(d, &desc);
+ if (rc < 0)
+ llprintf(LOG_1, "USB[%d,%d]: getting USB device descriptor failed: %s\n", busnum, devnum, df10ch_usb_errmsg(rc));
+ else if (desc.idVendor == DF10CH_USB_CFG_VENDOR_ID && desc.idProduct == DF10CH_USB_CFG_PRODUCT_ID) {
+ libusb_device_handle *hdl = NULL;
+ rc = libusb_open(d, &hdl);
+ if (rc < 0)
+ llprintf(LOG_1, "USB[%d,%d]: open of USB device failed: %s\n", busnum, devnum, df10ch_usb_errmsg(rc));
+ else {
+ unsigned char buf[256];
+ rc = libusb_get_string_descriptor_ascii(hdl, desc.iManufacturer, buf, sizeof(buf));
+ if (rc < 0)
+ llprintf(LOG_1, "USB[%d,%d]: getting USB manufacturer string failed: %s\n", busnum, devnum, df10ch_usb_errmsg(rc));
+ else if (rc == sizeof(DF10CH_USB_CFG_VENDOR_NAME) - 1 && !memcmp(buf, DF10CH_USB_CFG_VENDOR_NAME, rc)) {
+ rc = libusb_get_string_descriptor_ascii(hdl, desc.iProduct, buf, sizeof(buf));
+ if (rc < 0)
+ llprintf(LOG_1, "USB[%d,%d]: getting USB product string failed: %s\n", busnum, devnum, df10ch_usb_errmsg(rc));
+ else if (rc == sizeof(DF10CH_USB_CFG_PRODUCT) - 1 && !memcmp(buf, DF10CH_USB_CFG_PRODUCT, rc)) {
+ char id[32];
+ snprintf(id, sizeof(id), "DF10CH[%d,%d]", busnum, devnum);
+ rc = libusb_set_configuration(hdl, 1);
+ if (rc < 0)
+ llprintf(LOG_1, "%s: setting USB configuration failed: %s\n", id, df10ch_usb_errmsg(rc));
+ else {
+ rc = libusb_claim_interface(hdl, 0);
+ if (rc < 0)
+ llprintf(LOG_1, "%s: claiming USB interface failed: %s\n", id, df10ch_usb_errmsg(rc));
+ else {
+ df10ch_ctrl_t *ctrl = (df10ch_ctrl_t *) calloc(1, sizeof(df10ch_ctrl_t));
+ ctrl->next = this->ctrls;
+ this->ctrls = ctrl;
+ ctrl->dev = hdl;
+ ctrl->idx_serial_number = desc.iSerialNumber;
+ strcpy(ctrl->id, id);
+ llprintf(LOG_1, "%s: device opened\n", id);
+ continue;
+ }
+ }
+ }
+ }
+ libusb_close(hdl);
+ }
+ }
+ }
+
+ libusb_free_device_list(list, 1);
+
+ if (!this->ctrls) {
+ strcpy(this->output_driver.errmsg, "USB: no DF10CH devices found!");
+ df10ch_dispose(this);
+ return -1;
+ }
+
+ // Read controller configuration
+ df10ch_ctrl_t *ctrl = this->ctrls;
+ while (ctrl) {
+ uint8_t data[256];
+
+ // Check that USB controller is running application firmware and not bootloader
+ rc = libusb_get_string_descriptor_ascii(ctrl->dev, ctrl->idx_serial_number, data, sizeof(data) - 1);
+ if (rc < 0) {
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "%s: getting USB serial number string failed: %s", ctrl->id, df10ch_usb_errmsg(rc));
+ df10ch_dispose(this);
+ return -1;
+ }
+ if (rc != sizeof(DF10CH_USB_CFG_SERIAL) - 1 || memcmp(data, DF10CH_USB_CFG_SERIAL, rc)) {
+ data[rc] = 0;
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "%s: application firmware of USB controller is not running! Current mode is: %s", ctrl->id, data);
+ df10ch_dispose(this);
+ return -1;
+ }
+
+ // check that PWM controller is running application firmware and not bootloader
+ if (df10ch_control_in_transfer(ctrl, PWM_REQ_GET_VERSION, 0, 0, DF10CH_USB_DEFAULT_TIMEOUT, data, 2)) {
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "%s: reading PWM controller version fails!", ctrl->id);
+ df10ch_dispose(this);
+ return -1;
+ }
+ if (data[0] != PWM_VERS_APPL) {
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "%s: application firmware of PWM controller is not running! Current mode is: %d", ctrl->id, data[0]);
+ df10ch_dispose(this);
+ return -1;
+ }
+
+ // read eeprom configuration data
+ uint8_t eedata[DF10CH_SIZE_CONFIG];
+ if (df10ch_control_in_transfer(ctrl, REQ_READ_EE_DATA, 0, 1, DF10CH_USB_DEFAULT_TIMEOUT, eedata, sizeof(eedata))) {
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "%s: reading eeprom config data fails!", ctrl->id);
+ df10ch_dispose(this);
+ return -1;
+ }
+
+ // check that configuration data is valid
+ int cfg_valid_id = eedata[0] + (eedata[1] << 8);
+ if (cfg_valid_id != DF10CH_CONFIG_VALID_ID) {
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "%s: controller is not configured! Please run setup program first", ctrl->id);
+ df10ch_dispose(this);
+ return -1;
+ }
+
+ ctrl->config_version = eedata[2] + (eedata[3] << 8);
+
+ // Determine channel layout
+ int n;
+ n = eedata[4 + DF10CH_AREA_TOP];
+ if (n > num_channels->top)
+ num_channels->top = n;
+ n = eedata[4 + DF10CH_AREA_BOTTOM];
+ if (n > num_channels->bottom)
+ num_channels->bottom = n;
+ n = eedata[4 + DF10CH_AREA_LEFT];
+ if (n > num_channels->left)
+ num_channels->left = n;
+ n = eedata[4 + DF10CH_AREA_RIGHT];
+ if (n > num_channels->right)
+ num_channels->right = n;
+ n = eedata[4 + DF10CH_AREA_CENTER];
+ if (n > num_channels->center)
+ num_channels->center = n;
+ n = eedata[4 + DF10CH_AREA_TOP_LEFT];
+ if (n > num_channels->top_left)
+ num_channels->top_left = n;
+ n = eedata[4 + DF10CH_AREA_TOP_RIGHT];
+ if (n > num_channels->top_right)
+ num_channels->top_right = n;
+ n = eedata[4 + DF10CH_AREA_BOTTOM_LEFT];
+ if (n > num_channels->bottom_left)
+ num_channels->bottom_left = n;
+ n = eedata[4 + DF10CH_AREA_BOTTOM_RIGHT];
+ if (n > num_channels->bottom_right)
+ num_channels->bottom_right = n;
+
+ ctrl->num_req_channels = eedata[13];
+ if (ctrl->num_req_channels > DF10CH_MAX_CHANNELS)
+ ctrl->num_req_channels = DF10CH_MAX_CHANNELS;
+
+ // Read PWM resolution
+ if (df10ch_control_in_transfer(ctrl, PWM_REQ_GET_MAX_PWM, 0, 0, DF10CH_USB_DEFAULT_TIMEOUT, data, 2)) {
+ snprintf(this->output_driver.errmsg, sizeof(this->output_driver.errmsg), "%s: reading PWM resolution data fails!", ctrl->id);
+ df10ch_dispose(this);
+ return -1;
+ }
+ ctrl->pwm_res = data[0] + (data[1] << 8);
+
+ // Build channel configuration list
+ int nch = ctrl->num_req_channels;
+ df10ch_channel_config_t *ccfg = (df10ch_channel_config_t *) calloc(nch, sizeof(df10ch_channel_config_t));
+ ctrl->channel_config = ccfg;
+ int eei = 14;
+ while (nch) {
+ ccfg->req_channel = eedata[eei];
+ ccfg->area = eedata[eei + 1] >> 2;
+ ccfg->color = eedata[eei + 1] & 0x03;
+ ccfg->area_num = eedata[eei + 2];
+ uint8_t gamma = eedata[eei + 3];
+ if (gamma < 10)
+ gamma = 10;
+ uint16_t white_cal = eedata[eei + 4] + (eedata[eei + 5] << 8);
+
+ // Lookup gamma table for gamma and white calibration value
+ df10ch_gamma_tab_t *gt = this->gamma_tabs;
+ while (gt && gamma != gt->gamma && white_cal != gt->white_cal)
+ gt = gt->next;
+ if (!gt) {
+ // Calculate new gamma table
+ gt = (df10ch_gamma_tab_t *) calloc(1, sizeof(df10ch_gamma_tab_t));
+ gt->next = this->gamma_tabs;
+ this->gamma_tabs = gt;
+ gt->gamma = gamma;
+ gt->white_cal = white_cal;
+ double dgamma = gamma / 10.0;
+ double dwhite_cal = white_cal;
+ int v;
+ for (v = 0; v < 256; ++v) {
+ gt->tab[v] = (uint16_t) (lround(pow((v / 255.0), dgamma) * dwhite_cal));
+ if (gt->tab[v] > ctrl->pwm_res)
+ gt->tab[v] = ctrl->pwm_res;
+ }
+ }
+ ccfg->gamma_tab = gt;
+
+ ++ccfg;
+ eei += 6;
+ --nch;
+ }
+
+ // Prepare USB request for sending brightness values
+ ctrl->transfer_data = calloc(1, (LIBUSB_CONTROL_SETUP_SIZE + ctrl->num_req_channels * 2));
+ libusb_fill_control_setup(ctrl->transfer_data, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, PWM_REQ_SET_BRIGHTNESS, 0, 0, ctrl->num_req_channels * 2);
+ ctrl->transfer = libusb_alloc_transfer(0);
+ libusb_fill_control_transfer(ctrl->transfer, ctrl->dev, ctrl->transfer_data, df10ch_reply_cb, ctrl, DF10CH_USB_DEFAULT_TIMEOUT);
+ ctrl->pending_submit = 0;
+
+ ctrl = ctrl->next;
+ }
+
+ this->num_channels = *num_channels;
+ this->max_transmit_latency = 0;
+ this->avg_transmit_latency = 0;
+ return 0;
+}
+
+
+static int df10ch_driver_configure(output_driver_t *this_gen, num_channels_t *num_channels) {
+ df10ch_output_driver_t *this = (df10ch_output_driver_t *) this_gen;
+
+ if (num_channels->top < this->num_channels.top)
+ num_channels->top = this->num_channels.top;
+ if (num_channels->bottom < this->num_channels.bottom)
+ num_channels->bottom = this->num_channels.bottom;
+ if (num_channels->left < this->num_channels.left)
+ num_channels->left = this->num_channels.left;
+ if (num_channels->right < this->num_channels.right)
+ num_channels->right = this->num_channels.right;
+ if (num_channels->center < this->num_channels.center)
+ num_channels->center = this->num_channels.center;
+ if (num_channels->top_left < this->num_channels.top_left)
+ num_channels->top_left = this->num_channels.top_left;
+ if (num_channels->top_right < this->num_channels.top_right)
+ num_channels->top_right = this->num_channels.top_right;
+ if (num_channels->bottom_left < this->num_channels.bottom_left)
+ num_channels->bottom_left = this->num_channels.bottom_left;
+ if (num_channels->bottom_right < this->num_channels.bottom_right)
+ num_channels->bottom_right = this->num_channels.bottom_right;
+
+ this->num_channels = *num_channels;
+ return 0;
+}
+
+
+static void df10ch_driver_close(output_driver_t *this_gen) {
+ df10ch_output_driver_t *this = (df10ch_output_driver_t *) this_gen;
+
+ llprintf(LOG_1, "average transmit latency: %d [us]\n", this->avg_transmit_latency);
+
+ // Cancel all pending requests
+ df10ch_ctrl_t *ctrl = this->ctrls;
+ while (ctrl) {
+ if (ctrl->pending_submit)
+ libusb_cancel_transfer(ctrl->transfer);
+ ctrl = ctrl->next;
+ }
+
+ df10ch_wait_for_replys(this);
+ df10ch_dispose(this);
+}
+
+
+static void df10ch_driver_output_colors(output_driver_t *this_gen, rgb_color_t *colors, rgb_color_t *last_colors) {
+ df10ch_output_driver_t *this = (df10ch_output_driver_t *) this_gen;
+ struct timeval tvnow, tvlast, tvdiff;
+
+ // Build area mapping table
+ rgb_color_t *area_map[9];
+ rgb_color_t *c = colors;
+ area_map[DF10CH_AREA_TOP] = c;
+ c += this->num_channels.top;
+ area_map[DF10CH_AREA_BOTTOM] = c;
+ c += this->num_channels.bottom;
+ area_map[DF10CH_AREA_LEFT] = c;
+ c += this->num_channels.left;
+ area_map[DF10CH_AREA_RIGHT] = c;
+ c += this->num_channels.right;
+ area_map[DF10CH_AREA_CENTER] = c;
+ c += this->num_channels.center;
+ area_map[DF10CH_AREA_TOP_LEFT] = c;
+ c += this->num_channels.top_left;
+ area_map[DF10CH_AREA_TOP_RIGHT] = c;
+ c += this->num_channels.top_right;
+ area_map[DF10CH_AREA_BOTTOM_LEFT] = c;
+ c += this->num_channels.bottom_left;
+ area_map[DF10CH_AREA_BOTTOM_RIGHT] = c;
+
+ if (LOG_1)
+ gettimeofday(&tvlast, NULL);
+
+ // Generate transfer messages and send it to controllers
+ df10ch_ctrl_t *ctrl = this->ctrls;
+ while (ctrl) {
+ // Generate payload data (brightness values)
+ int do_submit = 0;
+ uint8_t *payload = ctrl->transfer_data + LIBUSB_CONTROL_SETUP_SIZE;
+ df10ch_channel_config_t *cfg = ctrl->channel_config;
+ int nch = ctrl->num_req_channels;
+ while (nch) {
+ c = area_map[cfg->area] + cfg->area_num;
+ rgb_color_t *lc = last_colors ? last_colors + (c - colors): NULL;
+ int v = 0;
+ switch (cfg->color) {
+ case 0: // Red
+ v = c->r;
+ if (!lc || v != lc->r)
+ do_submit = 1;
+ break;
+ case 1: // Green
+ v = c->g;
+ if (!lc || v != lc->g)
+ do_submit = 1;
+ break;
+ case 2: // Blue
+ v = c->b;
+ if (!lc || v != lc->b)
+ do_submit = 1;
+ }
+
+ // gamma and white calibration correction
+ uint16_t bv = cfg->gamma_tab->tab[v];
+ payload[cfg->req_channel * 2] = bv;
+ payload[cfg->req_channel * 2 + 1] = bv >> 8;
+
+ ++cfg;
+ --nch;
+ }
+
+ // initiate asynchron data transfer to controller
+ if (do_submit) {
+ int rc = libusb_submit_transfer(ctrl->transfer);
+ if (rc)
+ llprintf(LOG_1, "%s: submitting USB control transfer message failed: %s\n", ctrl->id, df10ch_usb_errmsg(rc));
+ else
+ ctrl->pending_submit = 1;
+ }
+
+ ctrl = ctrl->next;
+ }
+
+ // wait for end of all pending transfers
+ df10ch_wait_for_replys(this);
+
+ if (LOG_1) {
+ gettimeofday(&tvnow, NULL);
+ timersub(&tvnow, &tvlast, &tvdiff);
+ this->avg_transmit_latency = (this->avg_transmit_latency + tvdiff.tv_usec) / 2;
+ if (tvdiff.tv_usec > this->max_transmit_latency) {
+ this->max_transmit_latency = tvdiff.tv_usec;
+ llprintf(LOG_1, "max/avg transmit latency: %d/%d [us]\n", this->max_transmit_latency, this->avg_transmit_latency);
+ }
+ }
+}
+
+
+/*
+ * Output Drivers
+ */
+
+#define NUM_DRIVERS 4 /* Number of registered drivers */
+static char *driver_enum[NUM_DRIVERS+1] = { "none", "file", "classic", "df4ch", "df10ch" };
+
+typedef union {
+ output_driver_t output_driver;
+ file_output_driver_t file_output_driver;
+ serial_output_driver_t serial_output_driver;
+ df10ch_output_driver_t df10ch_output_driver;
+} output_drivers_t;
+
+
+static output_driver_t * get_output_driver(output_drivers_t *output_drivers, int driver) {
+ memset(output_drivers, 0, sizeof(output_drivers_t));
+
+ output_driver_t *output_driver = &output_drivers->output_driver;
+ switch(driver) {
+ case 1: /* file */
+ output_driver->open = file_driver_open;
+ output_driver->configure = file_driver_configure;
+ output_driver->close = file_driver_close;
+ output_driver->output_colors = file_driver_output_colors;
+ break;
+ case 2: /* classic */
+ output_driver->open = serial_driver_open;
+ output_driver->configure = serial_driver_configure;
+ output_driver->close = serial_driver_close;
+ output_driver->output_colors = classic_driver_output_colors;
+ output_drivers->serial_output_driver.devfd = -1;
+ break;
+ case 3: /* df4ch */
+ output_driver->open = serial_driver_open;
+ output_driver->configure = serial_driver_configure;
+ output_driver->close = serial_driver_close;
+ output_driver->output_colors = df4ch_driver_output_colors;
+ output_drivers->serial_output_driver.devfd = -1;
+ break;
+ case 4: /* df10ch */
+ output_driver->open = df10ch_driver_open;
+ output_driver->configure = df10ch_driver_configure;
+ output_driver->close = df10ch_driver_close;
+ output_driver->output_colors = df10ch_driver_output_colors;
+ break;
+ default: /* none */
+ output_driver = NULL;
+ }
+ return output_driver;
+}
diff --git a/xine_post_atmo.c b/xine_post_atmo.c
new file mode 100644
index 0000000..622c1bc
--- /dev/null
+++ b/xine_post_atmo.c
@@ -0,0 +1,1272 @@
+/*
+ * Copyright (C) 2009, 2010 Andreas Auras
+ *
+ * This file is part of the atmo post plugin, a plugin for the free xine video player.
+ *
+ * atmo post plugin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * atmo post plugin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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, USA
+ *
+ *
+ * Many ideas and algorithm in this module had been derived from the fantastic
+ * Atmolight-plugin for the Video Disk Recorder (VDR) that was developed by
+ * Eike Edener.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <math.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include <xine/post.h>
+
+#undef LOG_MODULE
+#define LOG_MODULE "atmo"
+#define LOG_1 1
+#define LOG_2 0
+
+
+#define OUTPUT_RATE 20 /* rate of output loop [ms] */
+#define OUTPUT_START_DELAY 1000 /* delay after video open for first send output packet [ms] */
+#define GRAB_TIMEOUT 100 /* max. time waiting for next grab image [ms] */
+
+
+/* accuracy of color calculation */
+#define h_MAX 255
+#define s_MAX 255
+#define v_MAX 255
+
+/* macros */
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+#define POS_DIV(a, b) ( (a)/(b) + ( ((a)%(b) >= (b)/2 ) ? 1 : 0) )
+
+
+typedef struct { uint8_t h, s, v; } hsv_color_t;
+typedef struct { uint8_t r, g, b; } rgb_color_t;
+typedef struct { uint64_t r, g, b; } rgb_color_sum_t;
+
+#include "output_driver.h"
+
+/*
+ * Plugin
+ */
+
+typedef struct {
+ int driver;
+ char driver_param[256];
+ int top;
+ int bottom;
+ int left;
+ int right;
+ int center;
+ int top_left;
+ int top_right;
+ int bottom_left;
+ int bottom_right;
+ int analyze_rate;
+ int analyze_size;
+ int overscan;
+ int darkness_limit;
+ int edge_weighting;
+ int hue_win_size;
+ int sat_win_size;
+ int brightness;
+ int filter;
+ int filter_smoothness;
+ int filter_length;
+ int filter_threshold;
+ int wc_red;
+ int wc_green;
+ int wc_blue;
+ int gamma;
+} atmo_parameters_t;
+
+
+#define NUM_FILTERS 2
+static char *filter_enum[NUM_FILTERS+1] = { "off", "percentage", "combined" };
+
+
+START_PARAM_DESCR(atmo_parameters_t)
+PARAM_ITEM(POST_PARAM_TYPE_INT, driver, driver_enum, 0, NUM_DRIVERS, 0,
+ "output driver")
+PARAM_ITEM(POST_PARAM_TYPE_CHAR, driver_param, NULL, 0, 0, 0,
+ "parameters for output driver")
+PARAM_ITEM(POST_PARAM_TYPE_INT, top, NULL, 0, 25, 0,
+ "number of areas at top border")
+PARAM_ITEM(POST_PARAM_TYPE_INT, bottom, NULL, 0, 25, 0,
+ "number of areas at bottom border")
+PARAM_ITEM(POST_PARAM_TYPE_INT, left, NULL, 0, 25, 0,
+ "number of areas at left border")
+PARAM_ITEM(POST_PARAM_TYPE_INT, right, NULL, 0, 25, 0,
+ "number of areas at right border")
+PARAM_ITEM(POST_PARAM_TYPE_INT, center, NULL, 0, 1, 0,
+ "activate center area")
+PARAM_ITEM(POST_PARAM_TYPE_INT, top_left, NULL, 0, 1, 0,
+ "activate top_left area")
+PARAM_ITEM(POST_PARAM_TYPE_INT, top_right, NULL, 0, 1, 0,
+ "activate top_right area")
+PARAM_ITEM(POST_PARAM_TYPE_INT, bottom_left, NULL, 0, 1, 0,
+ "activate bottom_left area")
+PARAM_ITEM(POST_PARAM_TYPE_INT, bottom_right, NULL, 0, 1, 0,
+ "activate bottom right area")
+PARAM_ITEM(POST_PARAM_TYPE_INT, analyze_rate, NULL, 10, 500, 0,
+ "analyze rate [ms]")
+PARAM_ITEM(POST_PARAM_TYPE_INT, analyze_size, NULL, 0, 3, 0,
+ "size of analyze image")
+PARAM_ITEM(POST_PARAM_TYPE_INT, overscan, NULL, 0, 200, 0,
+ "ignored overscan border of grabbed image [%1000]")
+PARAM_ITEM(POST_PARAM_TYPE_INT, darkness_limit, NULL, 0, 100, 0,
+ "limit for black pixel")
+PARAM_ITEM(POST_PARAM_TYPE_INT, edge_weighting, NULL, 1, 30, 0,
+ "power of edge weighting")
+PARAM_ITEM(POST_PARAM_TYPE_INT, hue_win_size, NULL, 0, 5, 0,
+ "hue windowing size")
+PARAM_ITEM(POST_PARAM_TYPE_INT, sat_win_size, NULL, 0, 5, 0,
+ "saturation windowing size")
+PARAM_ITEM(POST_PARAM_TYPE_INT, brightness, NULL, 50, 300, 0,
+ "brightness [%]")
+PARAM_ITEM(POST_PARAM_TYPE_INT, filter, filter_enum, 0, NUM_FILTERS, 0,
+ "filter mode")
+PARAM_ITEM(POST_PARAM_TYPE_INT, filter_smoothness, NULL, 1, 100, 0,
+ "filter smoothness [%]")
+PARAM_ITEM(POST_PARAM_TYPE_INT, filter_length, NULL, 300, 5000, 0,
+ "filter length [ms]")
+PARAM_ITEM(POST_PARAM_TYPE_INT, filter_threshold, NULL, 1, 100, 0,
+ "filter threshold [%]")
+PARAM_ITEM(POST_PARAM_TYPE_INT, wc_red, NULL, 0, 255, 0,
+ "white calibration correction factor of red color channel")
+PARAM_ITEM(POST_PARAM_TYPE_INT, wc_green, NULL, 0, 255, 0,
+ "white calibration correction factor of green color channel")
+PARAM_ITEM(POST_PARAM_TYPE_INT, wc_blue, NULL, 0, 255, 0,
+ "white calibration correction factor of blue color channel")
+PARAM_ITEM(POST_PARAM_TYPE_INT, gamma, NULL, 0, 30, 0,
+ "gamma correction factor")
+END_PARAM_DESCR(atmo_param_descr)
+
+
+typedef struct atmo_post_plugin_s
+{
+ /* xine related */
+ post_plugin_t post_plugin;
+ xine_post_in_t parameter_input;
+ atmo_parameters_t parm;
+ void (*port_open)(xine_video_port_t *port_gen, xine_stream_t *stream);
+ void (*port_close)(xine_video_port_t *port_gen, xine_stream_t *stream);
+
+ /* channel configuration related */
+ num_channels_t num_channels;
+
+ /* thread related */
+ int *grab_running, *output_running;
+ pthread_t grab_thread, output_thread;
+ pthread_mutex_t lock;
+ pthread_cond_t thread_started;
+
+ /* output related */
+ output_driver_t *output_driver;
+ output_drivers_t output_drivers;
+ int driver_opened;
+ int driver;
+ char driver_param[256];
+ rgb_color_t *output_colors, *last_output_colors;
+
+ /* analyze related */
+ uint64_t *hue_hist, *sat_hist;
+ uint64_t *w_hue_hist, *w_sat_hist;
+ int *most_used_hue, *last_most_used_hue, *most_used_sat;
+ rgb_color_t *analyzed_colors;
+
+ /* filter related */
+ rgb_color_t *filtered_colors;
+ rgb_color_t *mean_filter_values;
+ rgb_color_sum_t *mean_filter_sum_values;
+ int old_mean_length;
+} atmo_post_plugin_t;
+
+
+static inline void rgb_to_hsv(hsv_color_t *hsv, int r, int g, int b) {
+ int min, max, delta;
+ int h = 0;
+
+ min = MIN(MIN(r, g), b);
+ max = MAX(MAX(r, g), b);
+
+ delta = max - min;
+
+ hsv->v = (uint8_t) POS_DIV(max * v_MAX, 255);
+
+ if (delta == 0) {
+ h = 0;
+ hsv->s = 0;
+ } else {
+ hsv->s = (uint8_t) POS_DIV((delta * s_MAX) ,max);
+
+ int dr = (max - r) + 3 * delta;
+ int dg = (max - g) + 3 * delta;
+ int db = (max - b) + 3 * delta;
+ int divisor = 6 * delta;
+
+ if (r == max) {
+ h = POS_DIV(( (db - dg) * h_MAX ) , divisor);
+ } else if (g == max) {
+ h = POS_DIV( ((dr - db) * h_MAX) , divisor) + (h_MAX/3);
+ } else if (b == max) {
+ h = POS_DIV(( (dg - dr) * h_MAX) , divisor) + (h_MAX/3) * 2;
+ }
+
+ if (h < 0) {
+ h += h_MAX;
+ }
+ if (h > h_MAX) {
+ h -= h_MAX;
+ }
+ }
+ hsv->h = (uint8_t) h;
+}
+
+
+static void calc_hsv_image(hsv_color_t *hsv, uint8_t *rgb, int img_size) {
+ while (img_size--) {
+ rgb_to_hsv(hsv, rgb[0], rgb[1], rgb[2]);
+ ++hsv;
+ rgb += 3;
+ }
+}
+
+
+static void calc_weight(atmo_post_plugin_t *this, int *weight, const int width, const int height, const double edge_weighting) {
+ int row, col, c;
+
+ const int top_channels = this->num_channels.top;
+ const int bottom_channels = this->num_channels.bottom;
+ const int left_channels = this->num_channels.left;
+ const int right_channels = this->num_channels.right;
+ const int center_channel = this->num_channels.center;
+ const int top_left_channel = this->num_channels.top_left;
+ const int top_right_channel = this->num_channels.top_right;
+ const int bottom_left_channel = this->num_channels.bottom_left;
+ const int bottom_right_channel = this->num_channels.bottom_right;
+
+ c = top_channels + top_left_channel + top_right_channel;
+ const int top_chunk = c ? width / c: 0;
+
+ c = bottom_channels + bottom_left_channel + bottom_right_channel;
+ const int bottom_chunk = c ? width / c: 0;
+
+ c = left_channels + bottom_left_channel + top_left_channel;
+ const int left_chunk = c ? height / c: 0;
+
+ c = right_channels + bottom_right_channel + top_right_channel;
+ const int right_chunk = c ? height / c: 0;
+
+ for (row = 0; row < height; ++row)
+ {
+ double row_norm = (double)row / (double)(height - 1);
+ int top = (int) (255.0 * pow(1.0 - row_norm, edge_weighting));
+ int bottom = (int) (255.0 * pow(row_norm, edge_weighting));
+
+ for (col = 0; col < width; ++col)
+ {
+ double col_norm = (double)col / (double)(width - 1);
+ int left = (int) (255.0 * pow((1.0 - col_norm), edge_weighting));
+ int right = (int) (255.0 * pow(col_norm, edge_weighting));
+
+ for (c = top_left_channel; c < (top_channels + top_left_channel); ++c)
+ *weight++ = (col >= (c * top_chunk) && col < ((c + 1) * top_chunk)) ? top: 0;
+ for (c = bottom_left_channel; c < (bottom_channels + bottom_left_channel); ++c)
+ *weight++ = (col >= (c * bottom_chunk) && col < ((c + 1) * bottom_chunk)) ? bottom: 0;
+ for (c = top_left_channel; c < (left_channels + top_left_channel); ++c)
+ *weight++ = (row >= (c * left_chunk) && row < ((c + 1) * left_chunk)) ? left: 0;
+ for (c = top_right_channel; c < (right_channels + top_right_channel); ++c)
+ *weight++ = (row >= (c * right_chunk) && row < ((c + 1) * right_chunk)) ? right: 0;
+ if (center_channel)
+ *weight++ = 255;
+ if (top_left_channel)
+ *weight++ = (top + left) / 2;
+ if (top_right_channel)
+ *weight++ = (top + right) / 2;
+ if (bottom_left_channel)
+ *weight++ = (bottom + left) / 2;
+ if (bottom_right_channel)
+ *weight++ = (bottom + right) / 2;
+ }
+ }
+}
+
+
+static void calc_hue_hist(atmo_post_plugin_t *this, hsv_color_t *hsv, int *weight, int img_size) {
+ const int n = this->num_channels.sum;
+ uint64_t * const hue_hist = this->hue_hist;
+ const int darkness_limit = this->parm.darkness_limit;
+
+ memset(hue_hist, 0, (n * (h_MAX+1) * sizeof(uint64_t)));
+
+ while (img_size--) {
+ if (hsv->v > darkness_limit) {
+ int c;
+ for (c = 0; c < n; ++c) {
+ hue_hist[c * (h_MAX+1) + hsv->h] += *weight * hsv->v;
+ ++weight;
+ }
+ } else
+ weight += n;
+ ++hsv;
+ }
+}
+
+
+static void calc_windowed_hue_hist(atmo_post_plugin_t *this) {
+ int i, c, w;
+ const int n = this->num_channels.sum;
+ uint64_t * const hue_hist = this->hue_hist;
+ uint64_t * const w_hue_hist = this->w_hue_hist;
+ const int hue_win_size = this->parm.hue_win_size;
+
+ memset(w_hue_hist, 0, (n * (h_MAX+1) * sizeof(uint64_t)));
+
+ for (i = 0; i < (h_MAX+1); ++i)
+ {
+ for (w = -hue_win_size; w <= hue_win_size; w++)
+ {
+ int iw = i + w;
+
+ if (iw < 0)
+ iw = iw + h_MAX + 1;
+ if (iw > h_MAX)
+ iw = iw - h_MAX - 1;
+
+ uint64_t win_weight = (hue_win_size + 1) - abs(w);
+
+ for (c = 0; c < n; ++c)
+ w_hue_hist[c * (h_MAX+1) + i] += hue_hist[c * (h_MAX+1) + iw] * win_weight;
+ }
+ }
+}
+
+
+static void calc_most_used_hue(atmo_post_plugin_t *this) {
+ int i, c;
+
+ const int n = this->num_channels.sum;
+ uint64_t * const w_hue_hist = this->w_hue_hist;
+ int * const most_used_hue = this->most_used_hue;
+ int * const last_most_used_hue = this->last_most_used_hue;
+
+ memset(most_used_hue, 0, (n * sizeof(int)));
+
+ for (c = 0; c < n; ++c) {
+ uint64_t v = 0;
+ for (i = 0; i < (h_MAX + 1); ++i) {
+ if (w_hue_hist[c * (h_MAX+1) + i] > v) {
+ v = w_hue_hist[c * (h_MAX+1) + i];
+ most_used_hue[c] = i;
+ }
+ }
+ if (((double) w_hue_hist[c * (h_MAX+1) + last_most_used_hue[c]] / (double) v) > 0.93)
+ most_used_hue[c] = last_most_used_hue[c];
+ else
+ last_most_used_hue[c] = most_used_hue[c];
+ }
+}
+
+
+static void calc_sat_hist(atmo_post_plugin_t *this, hsv_color_t *hsv, int *weight, int img_size) {
+ const int n = this->num_channels.sum;
+ uint64_t * const sat_hist = this->sat_hist;
+ int * const most_used_hue = this->most_used_hue;
+ const int darkness_limit = this->parm.darkness_limit;
+ const int hue_win_size = this->parm.hue_win_size;
+
+ memset(sat_hist, 0, (n * (s_MAX+1) * sizeof(uint64_t)));
+
+ while (img_size--) {
+ if (hsv->v > darkness_limit) {
+ int h = hsv->h;
+ int c;
+ for (c = 0; c < n; ++c) {
+ if (h > (most_used_hue[c] - hue_win_size) && h < (most_used_hue[c] + hue_win_size))
+ sat_hist[c * (s_MAX+1) + hsv->s] += *weight * hsv->v;
+ ++weight;
+ }
+ } else
+ weight += n;
+ ++hsv;
+ }
+}
+
+
+static void calc_windowed_sat_hist(atmo_post_plugin_t *this) {
+ int i, c, w;
+ const int n = this->num_channels.sum;
+ uint64_t * const sat_hist = this->sat_hist;
+ uint64_t * const w_sat_hist = this->w_sat_hist;
+ const int sat_win_size = this->parm.sat_win_size;
+
+ memset(w_sat_hist, 0, (n * (s_MAX+1) * sizeof(uint64_t)));
+
+ for (i = 0; i < (s_MAX+1); ++i)
+ {
+ for (w = -sat_win_size; w <= sat_win_size; w++)
+ {
+ int iw = i + w;
+
+ if (iw < 0)
+ iw = iw + s_MAX + 1;
+ if (iw > s_MAX)
+ iw = iw - s_MAX - 1;
+
+ uint64_t win_weight = (sat_win_size + 1) - abs(w);
+
+ for (c = 0; c < n; ++c)
+ w_sat_hist[c * (s_MAX+1) + i] += sat_hist[c * (s_MAX+1) + iw] * win_weight;
+ }
+ }
+}
+
+
+static void calc_most_used_sat(atmo_post_plugin_t *this) {
+ int i, c;
+ const int n = this->num_channels.sum;
+ uint64_t * const w_sat_hist = this->w_sat_hist;
+ int * const most_used_sat = this->most_used_sat;
+
+ memset(most_used_sat, 0, (n * sizeof(int)));
+
+ for (c = 0; c < n; ++c) {
+ uint64_t v = 0;
+ for (i = 0; i < (s_MAX + 1); ++i) {
+ if (w_sat_hist[c * (s_MAX+1) + i] > v) {
+ v = w_sat_hist[c * (s_MAX+1) + i];
+ most_used_sat[c] = i;
+ }
+ }
+ }
+}
+
+
+static int calc_average_brightness(hsv_color_t *hsv, int img_size, const int darkness_limit) {
+ int n = 0;
+ uint64_t v_avg = 0;
+
+ while (img_size--) {
+ if (hsv->v > darkness_limit) {
+ v_avg += hsv->v;
+ ++n;
+ }
+ ++hsv;
+ }
+
+ return (n ? (int)(v_avg / n): darkness_limit);
+}
+
+
+static void hsv_to_rgb(rgb_color_t *rgb, double h, double s, double v) {
+ rgb->r = rgb->g = rgb->b = 0;
+
+ h /= h_MAX;
+ s /= s_MAX;
+ v /= v_MAX;
+
+ if (s == 0.0) {
+ rgb->r = (uint8_t) (v * 255.0 + 0.5);
+ rgb->g = rgb->r;
+ rgb->b = rgb->r;
+ } else {
+ h = h * 6.0;
+ if (h == 6.0)
+ h = 0.0;
+ int i = (int) h;
+
+ double f = h - i;
+ double p = v * (1.0 - s);
+ double q = v * (1.0 - (s * f));
+ double t = v * (1.0 - (s * (1.0 - f)));
+
+ if (i == 0) {
+ rgb->r = (uint8_t) (v * 255.0 + 0.5);
+ rgb->g = (uint8_t) (t * 255.0 + 0.5);
+ rgb->b = (uint8_t) (p * 255.0 + 0.5);
+ } else if (i == 1) {
+ rgb->r = (uint8_t) (q * 255.0 + 0.5);
+ rgb->g = (uint8_t) (v * 255.0 + 0.5);
+ rgb->b = (uint8_t) (p * 255.0 + 0.5);
+ } else if (i == 2) {
+ rgb->r = (uint8_t) (p * 255.0 + 0.5);
+ rgb->g = (uint8_t) (v * 255.0 + 0.5);
+ rgb->b = (uint8_t) (t * 255.0 + 0.5);
+ } else if (i == 3) {
+ rgb->r = (uint8_t) (p * 255.0 + 0.5);
+ rgb->g = (uint8_t) (q * 255.0 + 0.5);
+ rgb->b = (uint8_t) (v * 255.0 + 0.5);
+ } else if (i == 4) {
+ rgb->r = (uint8_t) (t * 255.0 + 0.5);
+ rgb->g = (uint8_t) (p * 255.0 + 0.5);
+ rgb->b = (uint8_t) (v * 255.0 + 0.5);
+ } else {
+ rgb->r = (uint8_t) (v * 255.0 + 0.5);
+ rgb->g = (uint8_t) (p * 255.0 + 0.5);
+ rgb->b = (uint8_t) (q * 255.0 + 0.5);
+ }
+ }
+}
+
+
+static void calc_rgb_values(atmo_post_plugin_t *this, int v_avg)
+{
+ int c;
+ const int n = this->num_channels.sum;
+ double v = (v_avg * this->parm.brightness) / 100.0;
+ if (v > 255.0)
+ v = 255.0;
+
+ for (c = 0; c < n; ++c)
+ hsv_to_rgb(&this->analyzed_colors[c], this->most_used_hue[c], this->most_used_sat[c], v);
+}
+
+
+static void *atmo_grab_loop (void *port_gen) {
+ post_video_port_t *port = (post_video_port_t *) port_gen;
+ atmo_post_plugin_t *this = (atmo_post_plugin_t *) port->post;
+ xine_video_port_t *video_port = port->original_port;
+ xine_ticket_t *ticket = this->post_plugin.running_ticket;
+ xine_grab_frame_t *frame = NULL;
+ int rc;
+ int grab_width, grab_height, analyze_width, analyze_height, overscan, img_size;
+ int last_analyze_width = 0, last_analyze_height = 0;
+ int alloc_img_size = 0;
+ int edge_weighting = 0;
+ int running = 1;
+ hsv_color_t *hsv_img = NULL;
+ int *weight = NULL;
+ struct timeval tvnow, tvlast, tvdiff;
+ useconds_t analyze_rate;
+
+ _x_post_inc_usage(port);
+
+ pthread_mutex_lock(&this->lock);
+ this->grab_running = &running;
+ pthread_mutex_unlock(&this->lock);
+ pthread_cond_broadcast(&this->thread_started);
+
+ llprintf(LOG_1, "grab thread running\n");
+
+ ticket->acquire(ticket, 0);
+
+ while (running) {
+
+ /* allocate grab frame */
+ if (!frame && xine_port_send_gui_data(video_port, XINE_GUI_SEND_ALLOC_GRAB_FRAME, &frame)) {
+ xine_log(this->post_plugin.xine, XINE_LOG_PLUGIN, "atmo: frame grabbing not supported by this video driver!\n");
+ break;
+ }
+
+ if (ticket->ticket_revoked) {
+ /* free grab frame */
+ if (frame) {
+ xine_port_send_gui_data(video_port, XINE_GUI_SEND_FREE_GRAB_FRAME, frame);
+ frame = NULL;
+ }
+ llprintf(LOG_1, "grab thread waiting for new ticket\n");
+ ticket->renew(ticket, 0);
+ llprintf(LOG_1, "grab thread got new ticket\n");
+ continue;
+ }
+
+ gettimeofday(&tvlast, NULL);
+
+ /* get actual displayed image size */
+ grab_width = video_port->get_property(video_port, VO_PROP_WINDOW_WIDTH);
+ grab_height = video_port->get_property(video_port, VO_PROP_WINDOW_HEIGHT);
+ if (grab_width > 0 && grab_height > 0) {
+
+ /* calculate size of analyze image */
+ analyze_width = (this->parm.analyze_size + 1) * 64;
+ analyze_height = (analyze_width * grab_height) / grab_width;
+
+ /* calculate size of grab (sub) window */
+ overscan = this->parm.overscan;
+ if (overscan) {
+ frame->crop_left = frame->crop_right = grab_width * overscan / 1000;
+ frame->crop_top = frame->crop_bottom = grab_height * overscan / 1000;
+ grab_width = grab_width - frame->crop_left - frame->crop_right;
+ grab_height = grab_height - frame->crop_top - frame->crop_bottom;
+ } else {
+ frame->crop_bottom = 0;
+ frame->crop_top = 0;
+ frame->crop_left = 0;
+ frame->crop_right = 0;
+ }
+
+ /* grab displayed video frame */
+ frame->timeout = GRAB_TIMEOUT;
+ frame->width = analyze_width;
+ frame->height = analyze_height;
+ if (!(rc = xine_port_send_gui_data(video_port, XINE_GUI_SEND_GRAB_FRAME, frame))) {
+ if (frame->width == analyze_width && frame->height == analyze_height) {
+ img_size = analyze_width * analyze_height;
+
+ /* allocate hsv and weight images */
+ if (img_size > alloc_img_size) {
+ free(hsv_img);
+ free(weight);
+ alloc_img_size = img_size;
+ hsv_img = (hsv_color_t *) malloc(img_size * sizeof(hsv_color_t));
+ weight = (int *) malloc(img_size * this->num_channels.sum * sizeof(int));
+ if (!hsv_img || !weight)
+ break;
+ last_analyze_width = 0;
+ last_analyze_height = 0;
+ edge_weighting = 0;
+ }
+
+ /* calculate weight image */
+ if (analyze_width != last_analyze_width || analyze_height != last_analyze_height || edge_weighting != this->parm.edge_weighting) {
+ edge_weighting = this->parm.edge_weighting;
+ last_analyze_width = analyze_width;
+ last_analyze_height = analyze_height;
+ calc_weight(this, weight, analyze_width, analyze_height, edge_weighting);
+ llprintf(LOG_1, "analyze size %dx%d, grab %dx%d@%d,%d\n", analyze_width, analyze_height, grab_width, grab_height, frame->crop_left, frame->crop_top);
+ }
+
+ /* analyze grabbed image */
+ calc_hsv_image(hsv_img, frame->img, img_size);
+ calc_hue_hist(this, hsv_img, weight, img_size);
+ calc_windowed_hue_hist(this);
+ calc_most_used_hue(this);
+ calc_sat_hist(this, hsv_img, weight, img_size);
+ calc_windowed_sat_hist(this);
+ calc_most_used_sat(this);
+ int v_avg = calc_average_brightness(hsv_img, img_size, this->parm.darkness_limit);
+ pthread_mutex_lock(&this->lock);
+ calc_rgb_values(this, v_avg);
+ pthread_mutex_unlock(&this->lock);
+
+ llprintf(LOG_2, "grab %ld.%03ld: vpts=%ld\n", tvlast.tv_sec, tvlast.tv_usec / 1000, frame->vpts);
+ }
+ } else {
+ if (rc < 0)
+ llprintf(LOG_1, "grab failed!\n");
+ if (rc > 0)
+ llprintf(LOG_2, "grab timed out!\n");
+ }
+ }
+
+ /* loop with analyze rate duration */
+ analyze_rate = this->parm.analyze_rate * 1000;
+ gettimeofday(&tvnow, NULL);
+ timersub(&tvnow, &tvlast, &tvdiff);
+ if (tvdiff.tv_sec == 0 && tvdiff.tv_usec < analyze_rate)
+ usleep(analyze_rate - tvdiff.tv_usec);
+ }
+
+ /* free grab frame */
+ if (frame)
+ xine_port_send_gui_data(video_port, XINE_GUI_SEND_FREE_GRAB_FRAME, frame);
+
+ ticket->release(ticket, 0);
+
+ free(hsv_img);
+ free(weight);
+
+ pthread_mutex_lock(&this->lock);
+ if (running)
+ this->grab_running = NULL;
+ pthread_mutex_unlock(&this->lock);
+
+ _x_post_dec_usage(port);
+
+ llprintf(LOG_1, "grab thread terminated\n");
+
+ return NULL;
+}
+
+
+static void reset_filters(atmo_post_plugin_t *this) {
+ this->old_mean_length = 0;
+}
+
+
+static void percent_filter(atmo_post_plugin_t *this) {
+ rgb_color_t *act = this->analyzed_colors;
+ rgb_color_t *out = this->filtered_colors;
+ const int old_p = this->parm.filter_smoothness;
+ const int new_p = 100 - old_p;
+ int n = this->num_channels.sum;
+
+ while (n--) {
+ out->r = (act->r * new_p + out->r * old_p) / 100;
+ out->g = (act->g * new_p + out->g * old_p) / 100;
+ out->b = (act->b * new_p + out->b * old_p) / 100;
+ ++act;
+ ++out;
+ }
+}
+
+
+static void mean_filter(atmo_post_plugin_t *this) {
+ rgb_color_t *act = this->analyzed_colors;
+ rgb_color_t *out = this->filtered_colors;
+ rgb_color_t *mean_values = this->mean_filter_values;
+ rgb_color_sum_t *mean_sums = this->mean_filter_sum_values;
+ const int64_t mean_threshold = (int64_t) ((double) this->parm.filter_threshold * 3.6);
+ const int old_p = this->parm.filter_smoothness;
+ const int new_p = 100 - old_p;
+ int n = this->num_channels.sum;
+ const int64_t mean_length = (this->parm.filter_length < OUTPUT_RATE) ? 1: this->parm.filter_length / OUTPUT_RATE;
+ const int reinitialize = ((int)mean_length != this->old_mean_length);
+ this->old_mean_length = (int)mean_length;
+
+ while (n--) {
+ mean_sums->r += (act->r - mean_values->r);
+ mean_values->r = (uint8_t) (mean_sums->r / mean_length);
+
+ mean_sums->g += (act->g - mean_values->g);
+ mean_values->g = (uint8_t) (mean_sums->g / mean_length);
+
+ mean_sums->b += (act->b - mean_values->b);
+ mean_values->b = (uint8_t) (mean_sums->b / mean_length);
+
+ /*
+ * check, if there is a jump -> check if differences between actual values and filter values are too big
+ */
+ int64_t dist = (int64_t)(mean_values->r - act->r) * (int64_t)(mean_values->r - act->r) +
+ (int64_t)(mean_values->g - act->g) * (int64_t)(mean_values->g - act->g) +
+ (int64_t)(mean_values->b - act->b) * (int64_t)(mean_values->b - act->b);
+
+ if (dist > 0)
+ dist = (int64_t) sqrt((double) dist);
+
+ /* compare calculated distance with the filter threshold */
+ if (dist > mean_threshold || reinitialize) {
+ /* filter jump detected -> set the long filters to the result of the short filters */
+ *out = *act;
+ *mean_values = *act;
+ mean_sums->r = act->r * mean_length;
+ mean_sums->g = act->g * mean_length;
+ mean_sums->b = act->b * mean_length;
+ }
+ else
+ {
+ /* apply an additional percent filter */
+ out->r = (mean_values->r * new_p + out->r * old_p) / 100;
+ out->g = (mean_values->g * new_p + out->g * old_p) / 100;
+ out->b = (mean_values->b * new_p + out->b * old_p) / 100;
+ }
+
+ ++act;
+ ++out;
+ ++mean_sums;
+ ++mean_values;
+ }
+}
+
+
+static void apply_white_calibration(atmo_post_plugin_t *this) {
+ const int wc_red = this->parm.wc_red;
+ const int wc_green = this->parm.wc_green;
+ const int wc_blue = this->parm.wc_blue;
+ if (wc_red == 255 && wc_green == 255 && wc_blue == 255)
+ return;
+
+ rgb_color_t *out = this->output_colors;
+ int n = this->num_channels.sum;
+ while (n--) {
+ out->r = (uint8_t)((int)out->r * wc_red / 255);
+ out->g = (uint8_t)((int)out->g * wc_green / 255);
+ out->b = (uint8_t)((int)out->b * wc_blue / 255);
+ ++out;
+ }
+}
+
+
+static void apply_gamma_correction(atmo_post_plugin_t *this) {
+ if (!this->parm.gamma)
+ return;
+
+ const double gamma = (double)this->parm.gamma / 10.0;
+ rgb_color_t *out = this->output_colors;
+ int n = this->num_channels.sum;
+ while (n--) {
+ out->r = (uint8_t)(pow((double)out->r / 255.0, gamma) * 255.0);
+ out->g = (uint8_t)(pow((double)out->g / 255.0, gamma) * 255.0);
+ out->b = (uint8_t)(pow((double)out->b / 255.0, gamma) * 255.0);
+ ++out;
+ }
+}
+
+
+static void *atmo_output_loop (void *port_gen) {
+ post_video_port_t *port = (post_video_port_t *) port_gen;
+ atmo_post_plugin_t *this = (atmo_post_plugin_t *) port->post;
+ xine_ticket_t *ticket = this->post_plugin.running_ticket;
+ output_driver_t *output_driver = this->output_driver;
+ int colors_size = this->num_channels.sum * sizeof(rgb_color_t);
+ int running = 1;
+ struct timeval tvnow, tvlast, tvdiff, tvfirst;
+
+ _x_post_inc_usage(port);
+
+ pthread_mutex_lock(&this->lock);
+ this->output_running = &running;
+ pthread_mutex_unlock(&this->lock);
+ pthread_cond_broadcast(&this->thread_started);
+
+ llprintf(LOG_1, "output thread running\n");
+
+ ticket->acquire(ticket, 0);
+ reset_filters(this);
+
+ gettimeofday(&tvfirst, NULL);
+
+ while (running) {
+
+ if (ticket->ticket_revoked) {
+ reset_filters(this);
+ llprintf(LOG_1, "output thread waiting for new ticket\n");
+ ticket->renew(ticket, 0);
+ llprintf(LOG_1, "output thread got new ticket\n");
+ continue;
+ }
+
+ gettimeofday(&tvlast, NULL);
+
+ /* Transfer analyzed colors into filtered colors */
+ pthread_mutex_lock(&this->lock);
+ switch (this->parm.filter) {
+ case 1:
+ percent_filter(this);
+ break;
+ case 2:
+ mean_filter(this);
+ break;
+ default:
+ /* no filtering */
+ memcpy(this->filtered_colors, this->analyzed_colors, colors_size);
+ }
+ pthread_mutex_unlock(&this->lock);
+
+ /* Transfer filtered colors to output colors */
+ memcpy(this->output_colors, this->filtered_colors, colors_size);
+ apply_gamma_correction(this);
+ apply_white_calibration(this);
+
+ /* Output colors */
+ gettimeofday(&tvnow, NULL);
+ timersub(&tvnow, &tvfirst, &tvdiff);
+ if ((tvdiff.tv_sec * 1000 + tvdiff.tv_usec / 1000) >= OUTPUT_START_DELAY) {
+ if (memcmp(this->output_colors, this->last_output_colors, colors_size)) {
+ output_driver->output_colors(output_driver, this->output_colors, this->last_output_colors);
+ memcpy(this->last_output_colors, this->output_colors, colors_size);
+ }
+ }
+
+ /* Loop with output rate duration */
+ const useconds_t output_rate = OUTPUT_RATE * 1000;
+ gettimeofday(&tvnow, NULL);
+ timersub(&tvnow, &tvlast, &tvdiff);
+ if (tvdiff.tv_sec == 0 && tvdiff.tv_usec < output_rate)
+ usleep(output_rate - tvdiff.tv_usec);
+ }
+
+ /* Turn off Light */
+ memset(this->output_colors, 0, colors_size);
+ if (memcmp(this->output_colors, this->last_output_colors, colors_size))
+ output_driver->output_colors(output_driver, this->output_colors, this->last_output_colors);
+ memset(this->last_output_colors, 0, colors_size);
+
+ ticket->release(ticket, 0);
+
+ pthread_mutex_lock(&this->lock);
+ if (running)
+ this->output_running = NULL;
+ pthread_mutex_unlock(&this->lock);
+
+ _x_post_dec_usage(port);
+
+ llprintf(LOG_1, "output thread terminated\n");
+
+ return NULL;
+}
+
+
+static void config_channels(atmo_post_plugin_t *this) {
+ int n = this->num_channels.top + this->num_channels.bottom + this->num_channels.left + this->num_channels.right +
+ this->num_channels.center +
+ this->num_channels.top_left + this->num_channels.top_right + this->num_channels.bottom_left + this->num_channels.bottom_right;
+ this->num_channels.sum = n;
+
+ if (n)
+ {
+ this->hue_hist = (uint64_t *) calloc(n * (h_MAX + 1), sizeof(uint64_t));
+ this->w_hue_hist = (uint64_t *) calloc(n * (h_MAX + 1), sizeof(uint64_t));
+ this->most_used_hue = (int *) calloc(n, sizeof(int));
+ this->last_most_used_hue = (int *) calloc(n, sizeof(int));
+
+ this->sat_hist = (uint64_t *) calloc(n * (s_MAX + 1), sizeof(uint64_t));
+ this->w_sat_hist = (uint64_t *) calloc(n * (s_MAX + 1), sizeof(uint64_t));
+ this->most_used_sat = (int *) calloc(n, sizeof(int));
+
+ this->analyzed_colors = (rgb_color_t *) calloc(n, sizeof(rgb_color_t));
+ this->filtered_colors = (rgb_color_t *) calloc(n, sizeof(rgb_color_t));
+ this->output_colors = (rgb_color_t *) calloc(n, sizeof(rgb_color_t));
+ this->last_output_colors = (rgb_color_t *) calloc(n, sizeof(rgb_color_t));
+ this->mean_filter_values = (rgb_color_t *) calloc(n, sizeof(rgb_color_t));
+ this->mean_filter_sum_values = (rgb_color_sum_t *) calloc(n, sizeof(rgb_color_sum_t));
+ }
+
+ llprintf(LOG_1, "configure channels top %d, bottom %d, left %d, right %d, center %d, topLeft %d, topRight %d, bottomLeft %d, bottomRight %d\n",
+ this->num_channels.top, this->num_channels.bottom, this->num_channels.left, this->num_channels.right, this->num_channels.center,
+ this->num_channels.top_left, this->num_channels.top_right, this->num_channels.bottom_left, this->num_channels.bottom_right);
+}
+
+
+static void free_channels(atmo_post_plugin_t *this) {
+ if (this->num_channels.sum)
+ {
+ free(this->hue_hist);
+ free(this->w_hue_hist);
+ free(this->most_used_hue);
+ free(this->last_most_used_hue);
+
+ free(this->sat_hist);
+ free(this->w_sat_hist);
+ free(this->most_used_sat);
+
+ free(this->analyzed_colors);
+ free(this->filtered_colors);
+ free(this->output_colors);
+ free(this->last_output_colors);
+ free(this->mean_filter_values);
+ free(this->mean_filter_sum_values);
+ }
+}
+
+
+static void start_threads(atmo_post_plugin_t *this, post_video_port_t *port) {
+ int err;
+ pthread_attr_t pth_attrs;
+
+ pthread_attr_init(&pth_attrs);
+ pthread_attr_setscope(&pth_attrs, PTHREAD_SCOPE_SYSTEM);
+ pthread_attr_setdetachstate(&pth_attrs, PTHREAD_CREATE_DETACHED);
+
+ pthread_mutex_lock(&this->lock);
+ if (this->grab_running == NULL) {
+ if ((err = pthread_create (&this->grab_thread, &pth_attrs, atmo_grab_loop, port)))
+ xprintf(this->post_plugin.xine, XINE_VERBOSITY_LOG, "atmo: can't create grab thread (%s)\n", strerror(err));
+ else
+ pthread_cond_wait(&this->thread_started, &this->lock);
+ }
+
+ if (this->output_running == NULL) {
+ if ((err = pthread_create (&this->output_thread, &pth_attrs, atmo_output_loop, port)))
+ xprintf(this->post_plugin.xine, XINE_VERBOSITY_LOG, "atmo: can't create output thread (%s)\n", strerror(err));
+ else
+ pthread_cond_wait(&this->thread_started, &this->lock);
+ }
+ pthread_mutex_unlock(&this->lock);
+
+ pthread_attr_destroy(&pth_attrs);
+}
+
+
+static void stop_threads(atmo_post_plugin_t *this) {
+ pthread_mutex_lock(&this->lock);
+ if (this->grab_running) {
+ *this->grab_running = 0;
+ this->grab_running = NULL;
+ }
+ if (this->output_running) {
+ *this->output_running = 0;
+ this->output_running = NULL;
+ }
+ pthread_mutex_unlock(&this->lock);
+}
+
+
+static void close_output_driver(atmo_post_plugin_t *this) {
+
+ if (this->driver_opened) {
+ /* Switch all channels off */
+ int colors_size = this->num_channels.sum * sizeof(rgb_color_t);
+ memset(this->output_colors, 0, colors_size);
+ if (memcmp(this->output_colors, this->last_output_colors, colors_size))
+ this->output_driver->output_colors(this->output_driver, this->output_colors, this->last_output_colors);
+
+ this->output_driver->close(this->output_driver);
+ this->driver_opened = 0;
+ llprintf(LOG_1, "output driver closed\n");
+ }
+}
+
+
+/*
+ * Open/Close video port
+ */
+
+static void atmo_video_open(xine_video_port_t *port_gen, xine_stream_t *stream) {
+ post_video_port_t *port = (post_video_port_t *)port_gen;
+ atmo_post_plugin_t *this = (atmo_post_plugin_t *) port->post;
+ num_channels_t num_channels;
+ int start = 1;
+
+ this->port_open(port_gen, stream);
+
+ num_channels.top = this->parm.top;
+ num_channels.bottom = this->parm.bottom;
+ num_channels.left = this->parm.left;
+ num_channels.right = this->parm.right;
+ num_channels.center = this->parm.center;
+ num_channels.top_left = this->parm.top_left;
+ num_channels.top_right = this->parm.top_right;
+ num_channels.bottom_left = this->parm.bottom_left;
+ num_channels.bottom_right = this->parm.bottom_right;
+
+ if (this->driver != this->parm.driver || strcmp(this->driver_param, this->parm.driver_param))
+ close_output_driver(this);
+
+ /* open output driver */
+ if (!this->driver_opened) {
+ this->driver = this->parm.driver;
+ strcpy(this->driver_param, this->parm.driver_param);
+
+ if ((this->output_driver = get_output_driver(&this->output_drivers, this->driver)) == NULL) {
+ xine_log(this->post_plugin.xine, XINE_LOG_PLUGIN, "atmo: no valid output driver selected!\n");
+ start = 0;
+ } else if (this->output_driver->open(this->output_driver, this->driver_param, &num_channels)) {
+ xine_log(this->post_plugin.xine, XINE_LOG_PLUGIN, "atmo: can't open output driver: %s!\n", this->output_driver->errmsg);
+ start = 0;
+ } else {
+ this->driver_opened = 1;
+ llprintf(LOG_1, "output driver opened\n");
+ }
+ } else if (this->output_driver->configure(this->output_driver, &num_channels)) {
+ xine_log(this->post_plugin.xine, XINE_LOG_PLUGIN, "atmo: can't configure output driver: %s!\n", this->output_driver->errmsg);
+ start = 0;
+ }
+
+ if (this->num_channels.top != num_channels.top ||
+ this->num_channels.bottom != num_channels.bottom ||
+ this->num_channels.left != num_channels.left ||
+ this->num_channels.right != num_channels.right ||
+ this->num_channels.center != num_channels.center ||
+ this->num_channels.top_left != num_channels.top_left ||
+ this->num_channels.top_right != num_channels.top_right ||
+ this->num_channels.bottom_left != num_channels.bottom_left ||
+ this->num_channels.bottom_right != num_channels.bottom_right) {
+ free_channels(this);
+ this->num_channels = num_channels;
+ config_channels(this);
+ }
+
+ if (!this->num_channels.sum)
+ start = 0;
+
+ if (start) {
+ /* send first initial color packet */
+ this->output_driver->output_colors(this->output_driver, this->output_colors, NULL);
+
+ start_threads(this, port);
+ }
+}
+
+
+static void atmo_video_close(xine_video_port_t *port_gen, xine_stream_t *stream) {
+ post_video_port_t *port = (post_video_port_t *)port_gen;
+ atmo_post_plugin_t *this = (atmo_post_plugin_t *) port->post;
+
+ stop_threads(this);
+
+ this->port_close(port_gen, stream);
+}
+
+
+/*
+ * Parameter functions
+ */
+
+static xine_post_api_descr_t *atmo_get_param_descr(void)
+{
+ return &atmo_param_descr;
+}
+
+
+static int atmo_set_parameters(xine_post_t *this_gen, void *parm_gen)
+{
+ atmo_post_plugin_t *this = (atmo_post_plugin_t *)this_gen;
+ atmo_parameters_t *parm = (atmo_parameters_t *)parm_gen;
+
+ this->parm = *parm;
+ llprintf(LOG_1, "set parameters\n");
+ return 1;
+}
+
+
+static int atmo_get_parameters(xine_post_t *this_gen, void *parm_gen)
+{
+ atmo_post_plugin_t *this = (atmo_post_plugin_t *)this_gen;
+ atmo_parameters_t *parm = (atmo_parameters_t *)parm_gen;
+
+ *parm = this->parm;
+ return 1;
+}
+
+
+static char *atmo_get_help(void) {
+ return _("The xine atmolight post plugin\n"
+ "Analyze video picture and generate output data for atmolight controllers\n"
+ "\n"
+ );
+}
+
+
+/*
+ * open/close plugin
+ */
+
+static void atmo_dispose(post_plugin_t *this_gen)
+{
+ if (_x_post_dispose(this_gen)) {
+ atmo_post_plugin_t *this = (atmo_post_plugin_t *) this_gen;
+
+ close_output_driver(this);
+ free_channels(this);
+ pthread_mutex_destroy(&this->lock);
+ pthread_cond_destroy(&this->thread_started);
+ free(this);
+ }
+}
+
+
+static post_plugin_t *atmo_open_plugin(post_class_t *class_gen,
+ int inputs,
+ xine_audio_port_t **audio_target,
+ xine_video_port_t **video_target)
+{
+ post_in_t *input;
+ post_out_t *output;
+ post_video_port_t *port;
+ xine_post_in_t *input_param;
+ static xine_post_api_t post_api =
+ { atmo_set_parameters, atmo_get_parameters,
+ atmo_get_param_descr, atmo_get_help };
+
+ if (!video_target || !video_target[0])
+ return NULL;
+
+ atmo_post_plugin_t *this = (atmo_post_plugin_t *) calloc(1, sizeof(atmo_post_plugin_t));
+ if (!this)
+ return NULL;
+
+ _x_post_init(&this->post_plugin, 0, 1);
+
+ port = _x_post_intercept_video_port(&this->post_plugin, video_target[0], &input, &output);
+
+ input->xine_in.name = "video in";
+ output->xine_out.name = "video out";
+
+ this->post_plugin.dispose = atmo_dispose;
+
+ this->port_open = port->new_port.open;
+ this->port_close = port->new_port.close;
+ port->new_port.open = atmo_video_open;
+ port->new_port.close = atmo_video_close;
+
+ this->post_plugin.xine_post.video_input[0] = &port->new_port;
+
+ input_param = &this->parameter_input;
+ input_param->name = "parameters";
+ input_param->type = XINE_POST_DATA_PARAMETERS;
+ input_param->data = &post_api;
+ xine_list_push_back(this->post_plugin.input, input_param);
+
+ this->driver = -1;
+ pthread_mutex_init(&this->lock, NULL);
+ pthread_cond_init(&this->thread_started, NULL);
+
+ /* Set default values for parameters */
+ this->parm.overscan = 30;
+ this->parm.analyze_rate = 40;
+ this->parm.analyze_size = 1;
+ this->parm.brightness = 100;
+ this->parm.darkness_limit = 50;
+ this->parm.edge_weighting = 8;
+ this->parm.filter = 2;
+ this->parm.filter_length = 500;
+ this->parm.filter_smoothness = 50;
+ this->parm.filter_threshold = 40;
+ this->parm.hue_win_size = 3;
+ this->parm.sat_win_size = 3;
+ this->parm.wc_red = 255;
+ this->parm.wc_green = 255;
+ this->parm.wc_blue = 255;
+ this->parm.gamma = 0;
+
+ return &this->post_plugin;
+}
+
+
+/*
+ * Plugin class
+ */
+
+#if POST_PLUGIN_IFACE_VERSION < 10
+static char *atmo_get_identifier(post_class_t *class_gen)
+{
+ return "atmo";
+}
+
+static char *atmo_get_description(post_class_t *class_gen)
+{
+ return "Analyze video picture and generate output data for atmolight controllers";
+}
+
+static void atmo_class_dispose(post_class_t *class_gen)
+{
+ free(class_gen);
+}
+#endif
+
+static void *atmo_init_plugin(xine_t *xine, void *data)
+{
+ post_class_t *class = (post_class_t *) calloc(1, sizeof(post_class_t));
+
+ if(class) {
+ class->open_plugin = atmo_open_plugin;
+#if POST_PLUGIN_IFACE_VERSION < 10
+ class->get_identifier = atmo_get_identifier;
+ class->get_description = atmo_get_description;
+ class->dispose = atmo_class_dispose;
+#else
+ class->identifier = "atmo";
+ class->description = N_("Analyze video picture and generate output data for atmolight controllers");
+ class->dispose = default_post_class_dispose;
+#endif
+ }
+ return class;
+}
+
+
+static post_info_t info = { XINE_POST_TYPE_VIDEO_FILTER };
+
+const plugin_info_t xine_plugin_info[] __attribute__((visibility("default"))) =
+{
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_POST, POST_PLUGIN_IFACE_VERSION, "atmo", XINE_VERSION_CODE, &info, &atmo_init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};