commit 3fba92fb74ed9229cde1d069ce116bb2cfeaa322 Author: Wim Taymans Date: Thu Apr 16 16:58:33 2015 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..6721d6510 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +.tarball-version +.version +.*.swp +ABOUT-NLS +build-aux +intltool-extract.in +intltool-merge.in +intltool-update.in +*~ +*.tar.gz +.deps +.libs +*.la +*.lo +*.o +Makefile +Makefile.in +aclocal.m4 +autom4te.cache +compile +config.guess +config.h +config.h.in +config.log +config.rpath +config.status +config.sub +configure +depcomp +install-sh +libltdl +libtool +ltmain.sh +missing +mkinstalldirs +stamp-* +.dirstamp diff --git a/GPL b/GPL new file mode 100644 index 000000000..b7b5f53df --- /dev/null +++ b/GPL @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/LGPL b/LGPL new file mode 100644 index 000000000..2d2d780e6 --- /dev/null +++ b/LGPL @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 000000000..55486bccc --- /dev/null +++ b/Makefile.am @@ -0,0 +1,62 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, see . + +ACLOCAL_AMFLAGS = -I m4 + +EXTRA_DIST = \ + bootstrap.sh \ + git-version-gen \ + LICENSE \ + GPL \ + LGPL \ + PROTOCOL \ + README + +SUBDIRS = src + +DIST_SUBDIRS = pkgconfig \ + doc \ + man \ + po \ + src + +MAINTAINERCLEANFILES = +noinst_DATA = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = pkgconfig/libpulsevideo.pc + +eolspace: + find \( -name '*.c' -o -name '*.h' -o -name 'Makefile.am' \) -exec perl -i -pe 's/\s+\n$$/\1\n/;' \{\} \; + +untabify: + find \( -name '*.c' -o -name '*.h' \) -exec perl -i -pe 's/\t/ /g;' \{\} \; + +fedora-snapshot: dist + cp $(distdir).tar.gz $$HOME/git.fedora/pulseaudio/$(distdir).tar.gz + +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version + echo $(VERSION) > $(distdir)/.version + +.PHONY: distcleancheck + +# see git-version-gen +BUILT_SOURCES = $(top_srcdir)/.version +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ + +DISTCLEANFILES = \ + po/.intltool-merge-cache diff --git a/NEWS b/NEWS new file mode 100644 index 000000000..b18c4e8b7 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +PulseVideo 0.1 diff --git a/PROTOCOL b/PROTOCOL new file mode 100644 index 000000000..e69de29bb diff --git a/README b/README new file mode 100644 index 000000000..f215d64ba --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +Pulsevideo +---------- + +Pulsevideo is a user space API to deal with video. This includes: + + - Make availabe sources of video (such as from a capture devices or + application provided streams) + - Accessing sources of video for consumption. + +Video providers and consumers are implemented as separate processes +communicating with DBUS and exchanging video frames using fd passing. + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 000000000..2b6f1d546 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# This file is part of PulseVideo +# +# PulseAudio is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, see . + +# Only there to make jhbuild happy + +NOCONFIGURE=1 ./bootstrap.sh + +exec ./configure "$@" diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 000000000..0bb5e3ac7 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# This file is part of PulseVideo +# +# PulseAudio is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, see . + +case $(uname) in + *Darwin*) + LIBTOOLIZE="glibtoolize" + ;; +esac +test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize + +if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then + cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \ + chmod +x .git/hooks/pre-commit && \ + echo "Activated pre-commit hook." +fi + +if [ -f .tarball-version ]; then + echo "Marking tarball version as modified." + echo -n `cat .tarball-version | sed 's/-rebootstrapped$//'`-rebootstrapped >.tarball-version +fi + +# We check for this here, because if pkg-config is not found in the +# system, it's likely that the pkg.m4 macro file is also not present, +# which will make PKG_PROG_PKG_CONFIG be undefined and the generated +# configure file faulty. +if ! pkg-config --version &>/dev/null; then + echo "pkg-config is required to bootstrap this program" &>/dev/null + DIE=1 +fi + +# Other necessary programs +intltoolize --version >/dev/null || DIE=1 +test "$DIE" = 1 && exit 1 + +autopoint --force +AUTOPOINT='intltoolize --automake --copy' autoreconf --force --install --verbose + +if test "x$NOCONFIGURE" = "x"; then + CFLAGS="$CFLAGS -g -O0" ./configure --sysconfdir=/etc --localstatedir=/var --enable-force-preopen "$@" + make clean +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 000000000..c4ca9b6d5 --- /dev/null +++ b/configure.ac @@ -0,0 +1,213 @@ +AC_PREREQ(2.63) + +AC_INIT(pulsevideo, 0.1) +AC_CONFIG_SRCDIR([src/daemon/main.c]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability subdir-objects silent-rules color-tests dist-xz tar-ustar]) + +AS_IF([! test -n "$VERSION"], [ + AC_MSG_ERROR([git-version-gen failed]) +]) + +m4_define(pa_major, `echo $VERSION | cut -d. -f1 | cut -d- -f1`) +m4_define(pa_minor, `echo $VERSION | cut -d. -f2 | cut -d- -f1`) + +AC_SUBST(PV_MAJOR, pa_major) +AC_SUBST(PV_MINOR, pa_minor) +AC_SUBST(PV_MAJORMINOR, pa_major.pa_minor) + +AC_SUBST(PV_API_VERSION, 0) +AC_SUBST(PV_PROTOCOL_VERSION, 0) + +# The stable ABI for client applications, for the version info x:y:z +# always will hold y=z +AC_SUBST(LIBPULSE_VERSION_INFO, [0:0:0]) + +dnl sets host_* variables +AC_CANONICAL_HOST + +AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.]) + +AC_CHECK_PROG([STOW], [stow], [yes], [no]) + +AS_IF([test "x$STOW" = "xyes" && test -d /usr/local/stow], [ + AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***]) + ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}" +]) + +AM_SILENT_RULES([yes]) + +#### Checks for programs. #### + +# mkdir -p + +AC_PROG_MKDIR_P + +# ln -s + +AC_PROG_LN_S + +# CC + +AC_PROG_CC +AC_PROG_CC_C99 +AM_PROG_CC_C_O +# Only required if you want the WebRTC canceller -- no runtime dep on +# libstdc++ otherwise +AC_PROG_CXX +AC_PROG_GCC_TRADITIONAL +AC_USE_SYSTEM_EXTENSIONS + +# M4 + +AC_CHECK_PROGS([M4], gm4 m4, no) +AS_IF([test "x$M4" = "xno"], AC_MSG_ERROR([m4 missing])) + +# pkg-config + +PKG_PROG_PKG_CONFIG + +#### Compiler flags #### + +AX_APPEND_COMPILE_FLAGS( + [-Wall -W -Wextra -pipe -Wno-long-long -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing -Wwrite-strings -Wno-unused-parameter -ffast-math -fno-common -fdiagnostics-show-option -fdiagnostics-color=auto], + [], [-pedantic -Werror]) + +AS_CASE([" $CFLAGS "], [*" -O0 "*], [], [ + # Don't append the flag if it already exists. + # Only enable fastpath asserts when doing a debug build, e.g. from bootstrap.sh. + AX_APPEND_FLAG([-DFASTPATH], [CPPFLAGS]) + + # Cannot use AX_APPEND_FLAG here, as it assumes no space inside the added flags. + # Cannot append flags with AX_APPEND_FLAG one by one, as this would destroy all fortifications + # if CPPFLAGS already contain -D_FORTIFY_SOURCE=2. + + # Warnings to be aware of that appear with -D_FORTIFY_SOURCE=2 but without -U_FORTIFY_SOURCE: + # On Fedora 20 with -O0: #warning _FORTIFY_SOURCE requires compiling with optimization (-O) [-Wcpp] + # On Gentoo with -O2: "_FORTIFY_SOURCE" redefined [enabled by default] + AS_VAR_APPEND([CPPFLAGS],[" -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2"]) +]) + +#### Linker flags #### + +# Check whether the linker supports the -version-script option. +# The Make variable $(srcdir) needs to be in the LDFLAGS in that form, +# so that it is expanded the right way in every subdir. +AX_CHECK_LINK_FLAG(["-Wl,-version-script=${srcdir}/src/map-file"], + [VERSIONING_LDFLAGS='-Wl,-version-script=$(abs_top_srcdir)/src/map-file']) +AC_SUBST([VERSIONING_LDFLAGS]) + +# Use immediate (now) bindings; avoids the funky re-call in itself. +# The -z now syntax is lifted from Sun's linker and works with GNU's too, other linkers might be added later. +AX_APPEND_LINK_FLAGS([-Wl,-z,now], [IMMEDIATE_LDFLAGS]) +AC_SUBST([IMMEDIATE_LDFLAGS]) + +# On ELF systems we don't want the libraries to be unloaded since we don't clean them up properly, +# so we request the nodelete flag to be enabled. +# On other systems, we don't really know how to do that, but it's welcome if somebody can tell. +AX_APPEND_LINK_FLAGS([-Wl,-z,nodelete], [NODELETE_LDFLAGS]) +AC_SUBST([NODELETE_LDFLAGS]) + +# Check for the proper way to build libraries that have no undefined symbols +case $host in + # FreeBSD (et al.) does not complete linking for shared objects when pthreads + # are requested, as different implementations are present. + *-freebsd* | *-openbsd*) ;; + *) + for possible_flag in "-Wl,--no-undefined" "-Wl,-z,defs"; do + AX_CHECK_LINK_FLAG([$possible_flag], [NOUNDEFINED_LDFLAGS="$possible_flag"; break]) + done + ;; +esac +AC_SUBST([NOUNDEFINED_LDFLAGS]) + +#### libtool stuff #### + +LT_PREREQ(2.4) +LT_INIT([dlopen win32-dll disable-static]) + +dnl As an extra safety device, check for lt_dladvise_init() which is +dnl only implemented in libtool 2.x, and refine as we go if we have +dnl refined requirements. +dnl +dnl Check the header files first since the system may have a +dnl libltdl.so for runtime, but no headers, and we want to bail out as +dnl soon as possible. +dnl +dnl We don't need any special variable for this though, since the user +dnl can give the proper place to find libltdl through the standard +dnl variables like LDFLAGS and CPPFLAGS. + +AC_CHECK_HEADER([ltdl.h], + [AC_CHECK_LIB([ltdl], [lt_dladvise_init], [LIBLTDL=-lltdl], [LIBLTDL=])], + [LIBLTDL=]) + +AS_IF([test "x$LIBLTDL" = "x"], + [AC_MSG_ERROR([Unable to find libltdl version 2. Makes sure you have libtool 2.4 or later installed.])]) +AC_SUBST([LIBLTDL]) + +################################### +# External libraries # +################################### + +#### Valgrind (optional) #### + +AC_CHECK_HEADERS_ONCE([valgrind/memcheck.h]) + +#### GLib #### + +PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.32, dummy=yes, + AC_MSG_ERROR(GLib >= 2.32 is required)) +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + +PKG_CHECK_MODULES(GIOUNIX, gio-unix-2.0 >= 2.32, dummy=yes, + AC_MSG_ERROR(GIO-Unix >= 2.32 is required)) +GLIB_CFLAGS="$GLIB_CFLAGS $GIOUNIX_CFLAGS" +GLIB_LIBS="$GLIB_LIBS $GIOUNIX_LIBS" + +AC_MSG_CHECKING([for glib-mkenums script]) +GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` +AS_IF([test "x$GLIB_MKENUMS" = "x"], + [AC_MSG_ERROR([glib-mkenums not listed in glib-2.0 pkg-config file])], + [AC_SUBST([GLIB_MKENUMS]) + AC_MSG_RESULT([$GLIB_MKENUMS])]) + +AC_SUBST([GDBUS_CODEGEN], [`$PKG_CONFIG --variable gdbus_codegen gio-2.0`]) + +#### GStreamer #### + +PKG_CHECK_MODULES(GST, gstreamer-1.0 >= 1.4.0, dummy=yes, + AC_MSG_ERROR(GStreamer >= 1.4.0 is required)) +AC_SUBST(GST_CFLAGS) +AC_SUBST(GST_LIBS) + +#### Build and Install man pages #### + +AC_ARG_ENABLE([manpages], + AS_HELP_STRING([--disable-manpages],[Disable building and installation of man pages])) + +AM_CONDITIONAL([BUILD_MANPAGES], [test "x$enable_manpages" != "xno"]) + +dnl *** output files *** + +dnl keep this alphabetic per directory, please +AC_CONFIG_FILES([ +Makefile +src/Makefile +pkgconfig/libpulsevideo.pc +]) +AC_OUTPUT + +echo " + +Configuration + Version : ${VERSION} + Source code location : ${srcdir} + Prefix : ${prefix} + Compiler : ${CC} + +pulsevideo configured. Type 'make' to build. +" diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 000000000..e1d0f0485 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +org-pulsevideo-*.xml diff --git a/doc/source-api.txt b/doc/source-api.txt new file mode 100644 index 000000000..ed845be6e --- /dev/null +++ b/doc/source-api.txt @@ -0,0 +1,84 @@ +Source +------ + +Service: org.pulsevideo +Interface: org.pulsevideo.Source1 +Object Path: freely definable + + + The Source object defines a source of video. You can query the list of + supported formats and get a SourceOutput object that can be used to + control the video Source. + + +Methods a{a{sv}} GetCapabilities (dict args) + + Get a list of supported formats + + Possible Errors: org.pulsevideo.Error.NotAuthorized + org.pulsevideo.Error.Failed + + object CreateSourceOutput (dict args) + + Create a sourceoutput with given capabilities + + Possible Errors: org.pulsevideo.Error.NotAuthorized + org.pulsevideo.Error.Failed + + void RemoveSourceOutput (object sourceoutput) + + Remove a sourceoutput. + + Possible Errors: org.pulsevideo.Error.NotAuthorized + org.pulsevideo.Error.Failed + +Properties string Name [readonly] + + Name of the source + + strinf Priority [readonly] + + Priority of the source + + + +SourceOutput +------------ + +Service org.pulsevideo +Interface org.pulsevideo.SourceOutput1 +Object Path: freely definable + +Methods fd Acquire() + + Acquire transport file descriptor read and . + + Possible Errors: org.pulsevideo.Error.NotAuthorized + org.pulsevideo.Error.Failed + + void Release () + + Release transport file descriptor + + + +Properties object Source [readonly] + + Path of the Source object + + dict transport [readonly] + + Transport properties + + + +Client +------ + +Service org.pulsevideo +Interface org.pulsevideo.Client1 + + + + + diff --git a/git-version-gen b/git-version-gen new file mode 100755 index 000000000..754688441 --- /dev/null +++ b/git-version-gen @@ -0,0 +1,170 @@ +#!/bin/sh +# Print a version string. +scriptversion=2012-09-25.20 + +# Copyright (C) 2007-2008 Free Software Foundation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version +# echo $(VERSION) > $(distdir)/.version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' +v= + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +# This is presently used by the GNOME-OSTree build system; it +# helps support the case where the meta-build system has already +# determined the git revision, but we may not be able to run "git describe" +# because we're inside a chroot. +if test -n "$GIT_DESCRIBE_FOR_BUILD"; +then + v=$GIT_DESCRIBE_FOR_BUILD +fi + +if test -n "$v" +then + : # use $v +elif test -e .git \ + && v=`git describe --abbrev=4 --match='v[0-9]*' HEAD 2>/dev/null` \ + && [ -n "$v" ] +then + # If we are on a "dev" tag, we need to check that it is not the same + # reference as the a previous version tag (this only happens when we are + # working with a release tag). + # NB The below trick relies on the $v being an exact tag to work which + # will only work when HEAD == tag. When further commits have been made on top + # of the tag, the $v will be supplimented with the number of commits since + # that tag and the commit ref of the most recent commit and thus will + # fail the test below (as intended) + v2=`git describe --abbrev=4 --match='v[0-9]\.[0-9]' --contains $v 2>/dev/null | cut -d'^' -f1` + [ -n "$v2" ] && v=$v2 + + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb +# case $v in +# *-*-*) : git describe is okay three part flavor ;; +# *-*) +# : git describe is older two part flavor +# # Recreate the number of commits and rewrite such that the +# # result is the same as if we were using the newer version +# # of git describe. +# vtag=`echo "$v" | sed 's/-.*//'` +# numcommits=`git rev-list "$vtag"..HEAD | wc -l` +# v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; +# ;; +# esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. +# v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; + : +else + echo 1>&2 "$0: Failed to determine git revision" + exit 1 +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/pkgconfig/libpulsevideo.pc b/pkgconfig/libpulsevideo.pc new file mode 100644 index 000000000..14e531492 --- /dev/null +++ b/pkgconfig/libpulsevideo.pc @@ -0,0 +1,11 @@ +prefix=/usr/local +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include +modlibexecdir=@modlibexecdir@ + +Name: libpulsevideo +Description: PulseVideo Client Interface +Version: 0.1 +Libs: -L${libdir} -lpulsevideo @PTHREAD_LIBS@ +Cflags: -I${includedir} -D_REENTRANT diff --git a/pkgconfig/libpulsevideo.pc.in b/pkgconfig/libpulsevideo.pc.in new file mode 100644 index 000000000..62bd0a316 --- /dev/null +++ b/pkgconfig/libpulsevideo.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +modlibexecdir=@modlibexecdir@ + +Name: libpulsevideo +Description: PulseVideo Client Interface +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lpulsevideo @PTHREAD_LIBS@ +Cflags: -I${includedir} -D_REENTRANT diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 000000000..22385a26f --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,3 @@ +pulsevideo +test-client +test-subscribe diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000..905cdf8d1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,228 @@ +# This file is part of PulseVideo. +# +# Copyright 2015 Wim Taymans +# +# PulseVideo is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseVideo is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseVideo; if not, see . + +################################### +# Extra directories # +################################### + +pulsevideoincludedir=$(includedir)/client +pulsevideoserverincludedir=$(includedir)/server +pulsevideolibexecdir=$(libexecdir)/client +dbuspolicydir=$(sysconfdir)/dbus-1/system.d + +################################### +# Compiler/linker flags # +################################### + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/modules \ + -I$(top_builddir)/src/modules \ + -DPV_SRCDIR=\"$(abs_srcdir)\" \ + -DPV_BUILDDIR=\"$(abs_builddir)\" +AM_CFLAGS = $(GLIB_CFLAGS) $(GST_CFLAGS) +AM_CXXFLAGS = $(AM_CFLAGS) +SERVER_CFLAGS = -D__INCLUDED_FROM_PULSE_VIDEO + +AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS) +AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS) +AM_LDFLAGS = $(NODELETE_LDFLAGS) + +FOREIGN_CFLAGS = -w + +################################### +# Extra files # +################################### + +EXTRA_DIST = \ + src/version.h.in \ + daemon/pulsevideo-system.conf + +dbuspolicy_DATA = \ + daemon/pulsevideo-system.conf + +################################### +# Includes # +################################### + +enumtypesincludes = client/pv-context.h \ + client/pv-stream.h \ + client/pv-subscribe.h + +client/pv-enumtypes.h: $(enumtypesincludes) + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --fhead "#ifndef __PV_ENUM_TYPES_H__\n#define __PV_ENUM_TYPES_H__\n\n#include \n\nG_BEGIN_DECLS\n" \ + --fprod "\n/* enumerations from \"@filename@\" */\n" \ + --vhead "GType @enum_name@_get_type (void);\n#define PV_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --ftail "G_END_DECLS\n\n#endif /* __PV_ENUM_TYPES_H__ */" \ + $^ > client/pv-enumtypes.h + +client/pv-enumtypes.c: $(enumtypesincludes) client/pv-enumtypes.h + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --fhead "#include \"pv-enumtypes.h\"\n#include \n#define C_ENUM(v) ((gint) v)\n#define C_FLAGS(v) ((guint) v)\n " \ + --fprod "\n/* enumerations from \"@filename@\" */" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static gsize id = 0;\n static const G@Type@Value values[] = {" \ + --vprod " { C_@TYPE@(@VALUENAME@), \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n\n if (g_once_init_enter (&id)) {\n GType tmp = g_@type@_register_static (\"@EnumName@\", values);\n g_once_init_leave (&id, tmp);\n }\n\n return (GType) id;\n}" \ + $^ > client/pv-enumtypes.c + + +dbus/org-pulsevideo.c: dbus/org-pulsevideo.h +dbus/org-pulsevideo.h: dbus/org.pulsevideo.xml + $(AM_V_GEN) $(GDBUS_CODEGEN) \ + --c-generate-object-manager \ + --interface-prefix org.pulsevideo. \ + --generate-c-code dbus/org-pulsevideo \ + --generate-docbook ../doc/org-pulsevideo \ + --c-namespace Pv dbus/org.pulsevideo.xml + +built_header_make = client/pv-enumtypes.h dbus/org-pulsevideo.h +built_source_make = client/pv-enumtypes.c dbus/org-pulsevideo.c + +BUILT_SOURCES = $(built_header_make) \ + $(built_source_make) + +CLEANFILES = $(built_header_make) $(built_source_make) + +################################### +# Main daemon # +################################### + +bin_PROGRAMS = pulsevideo + +pulsevideo_SOURCES = \ + daemon/main.c + +pulsevideo_CFLAGS = $(AM_CFLAGS) +pulsevideo_LDADD = $(AM_LDADD) libpulsevideocore-@PV_MAJORMINOR@.la libpulsevideo-@PV_MAJORMINOR@.la $(LIBLTDL) +# This is needed because automake doesn't properly expand the foreach below +pulsevideo_DEPENDENCIES = libpulsevideocore-@PV_MAJORMINOR@.la libpulsevideo-@PV_MAJORMINOR@.la +pulsevideo_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(IMMEDIATE_LDFLAGS) + +################################### +# Test programs # +################################### +noinst_LTLIBRARIES = + +TESTS_default = + +TESTS_norun = test-client \ + test-subscribe + +# These tests need a running pulsevideo daemon +TESTS_daemon = + +noinst_PROGRAMS = $(TESTS_default) $(TESTS_norun) $(TESTS_daemon) + +test_client_SOURCES = tests/test-client.c +test_client_CFLAGS = $(AM_CFLAGS) +test_client_LDADD = $(AM_LDADD) libpulsevideo-@PV_MAJORMINOR@.la +test_client_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +test_subscribe_SOURCES = tests/test-subscribe.c +test_subscribe_CFLAGS = $(AM_CFLAGS) +test_subscribe_LDADD = $(AM_LDADD) libpulsevideo-@PV_MAJORMINOR@.la +test_subscribe_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +################################### +# Client library # +################################### + +pulsevideoinclude_HEADERS = \ + client/pv-context.h \ + client/pv-enumtypes.h \ + client/pv-stream.h \ + client/pv-subscribe.h \ + client/pv-subscribe.h \ + dbus/org-pulsevideo.h + +lib_LTLIBRARIES = \ + libpulsevideo-@PV_MAJORMINOR@.la + +# Public interface +libpulsevideo_@PV_MAJORMINOR@_la_SOURCES = \ + client/pv-context.h client/pv-context.c \ + client/pv-enumtypes.h client/pv-enumtypes.c \ + client/pv-stream.h client/pv-stream.c \ + dbus/org-pulsevideo.c + + +libpulsevideo_@PV_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) +libpulsevideo_@PV_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version +libpulsevideo_@PV_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LTLIBICONV) + +################################### +# Daemon core library # +################################### + +lib_LTLIBRARIES += libpulsevideocore-@PV_MAJORMINOR@.la + +# Pure core stuff +libpulsevideocore_@PV_MAJORMINOR@_la_SOURCES = \ + server/pv-client.c server/pv-client.h \ + server/pv-daemon.c server/pv-daemon.h \ + server/pv-source-output.c server/pv-source-output.h \ + server/pv-source.c server/pv-source.h \ + modules/v4l2/pv-v4l2-source.c + +libpulsevideocore_@PV_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) +libpulsevideocore_@PV_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version +libpulsevideocore_@PV_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LTLIBICONV) + +################################### +# Some minor stuff # +################################### + +DISTCLEANFILES = + +install-exec-hook: + rm -f $(DESTDIR)$(modlibexecdir)/*.la + +uninstall-hook: + rm -f $(DESTDIR)$(modlibexecdir)/*.so + +# Automatically generate linker version script. We use the same one for all public .sos +update-map-file: + ( echo "PULSE_0 {" ; \ + echo "global:" ; \ + ctags -I PV_GCC_MALLOC,PV_GCC_ALLOC_SIZE2,PV_GCC_ALLOC_SIZE,PV_GCC_PURE,PV_GCC_CONST,PV_GCC_DEPRECATED,PV_GCC_PRINTF_ATTR -f - --c-kinds=p $(pulsevideoinclude_HEADERS) | awk '/^pa_/ { print $$1 ";" }' | sort ; \ + echo "local:" ; \ + echo "*;" ; \ + echo "};" ) > $(srcdir)/map-file + +update-all: update-map-file + +# Force installation order of libraries. libtool relinks on install time, in +# which case libpulsecommon has to be installed before others, but the padsp +# preload library has to be done after the normal libraries (e.g. libpulse) +# ... +# Unfortunately automake behaviour means that rules without commands also +# override build-in rules, so it's not trivial to add dependencies. +# See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=7328 for the workaround +# ... +# Isn't libtool/autotools fun! + +installlibLTLIBRARIES = install-libLTLIBRARIES +$(installlibLTLIBRARIES): install-pkglibLTLIBRARIES + +installmodlibexecLTLIBRARIES = install-modlibexecLTLIBRARIES +$(installmodlibexecLTLIBRARIES): install-pkglibLTLIBRARIES + +installpadsplibLTLIBRARIES = install-padsplibLTLIBRARIES +$(installpadsplibLTLIBRARIES): install-libLTLIBRARIES + +.PHONY: update-all update-map-file coverage diff --git a/src/client/.gitignore b/src/client/.gitignore new file mode 100644 index 000000000..3fc13c714 --- /dev/null +++ b/src/client/.gitignore @@ -0,0 +1,2 @@ +pv-enumtypes.c +pv-enumtypes.h diff --git a/src/client/pulsevideo.h b/src/client/pulsevideo.h new file mode 100644 index 000000000..a2b6c5fc9 --- /dev/null +++ b/src/client/pulsevideo.h @@ -0,0 +1,28 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PULSEVIDEO_H__ +#define __PULSEVIDEO_H__ + +#include +#include +#include + +#endif /* __PULSEVIDEO_H__ */ + diff --git a/src/client/pv-context.c b/src/client/pv-context.c new file mode 100644 index 000000000..f56cf57e6 --- /dev/null +++ b/src/client/pv-context.c @@ -0,0 +1,502 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "server/pv-daemon.h" + +#include "client/pv-context.h" +#include "client/pv-enumtypes.h" +#include "client/pv-subscribe.h" + +#include "dbus/org-pulsevideo.h" + +struct _PvContextPrivate +{ + gchar *name; + GVariant *properties; + + guint id; + GDBusConnection *connection; + + PvContextFlags flags; + PvContextState state; + + PvDaemon1 *daemon; + + gchar *client_path; + PvClient1 *client; + + PvSubscriptionFlags subscription_mask; + GDBusObjectManager *client_manager; +}; + + +#define PV_CONTEXT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_CONTEXT, PvContextPrivate)) + +G_DEFINE_TYPE (PvContext, pv_context, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_NAME, + PROP_PROPERTIES, + PROP_STATE, + PROP_SUBSCRIPTION_MASK, + PROP_CONNECTION, + PROP_CLIENT_PATH +}; + +enum +{ + SIGNAL_SUBSCRIPTION_EVENT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +#include "pv-subscribe.c" + +static void +pv_context_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PvContext *context = PV_CONTEXT (_object); + PvContextPrivate *priv = context->priv; + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + + case PROP_PROPERTIES: + g_value_set_variant (value, priv->properties); + break; + + case PROP_STATE: + g_value_set_enum (value, priv->state); + break; + + case PROP_SUBSCRIPTION_MASK: + g_value_set_flags (value, priv->subscription_mask); + break; + + case PROP_CONNECTION: + g_value_set_object (value, priv->connection); + break; + + case PROP_CLIENT_PATH: + g_value_set_string (value, priv->client_path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (context, prop_id, pspec); + break; + } +} + +static void +pv_context_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PvContext *context = PV_CONTEXT (_object); + PvContextPrivate *priv = context->priv; + + switch (prop_id) { + case PROP_NAME: + g_free (priv->name); + priv->name = g_value_dup_string (value); + break; + + case PROP_PROPERTIES: + if (priv->properties) + g_variant_unref (priv->properties); + priv->properties = g_value_dup_variant (value); + break; + + case PROP_SUBSCRIPTION_MASK: + priv->subscription_mask = g_value_get_flags (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (context, prop_id, pspec); + break; + } +} + +static void +pv_context_class_init (PvContextClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PvContextPrivate)); + + gobject_class->set_property = pv_context_set_property; + gobject_class->get_property = pv_context_get_property; + + /** + * PvContext:name + * + * The application name of the context. + */ + g_object_class_install_property (gobject_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "The application name", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * PvContext:properties + * + * Properties of the context. + */ + g_object_class_install_property (gobject_class, + PROP_PROPERTIES, + g_param_spec_variant ("properties", + "Properties", + "Extra properties", + G_VARIANT_TYPE_VARIANT, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * PvContext:state + * + * The state of the context. + */ + g_object_class_install_property (gobject_class, + PROP_STATE, + g_param_spec_enum ("state", + "State", + "The context state", + PV_TYPE_CONTEXT_STATE, + PV_CONTEXT_STATE_UNCONNECTED, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * PvContext:subscription-mask + * + * A mask for what object notifications will be signaled with + * PvContext:subscription-event + */ + g_object_class_install_property (gobject_class, + PROP_SUBSCRIPTION_MASK, + g_param_spec_flags ("subscription-mask", + "Subscription Mask", + "The object to receive subscription events of", + PV_TYPE_SUBSCRIPTION_FLAGS, + 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * PvContext:connection + * + * The connection of the context. + */ + g_object_class_install_property (gobject_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + "Connection", + "The DBus connection", + G_TYPE_DBUS_CONNECTION, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * PvContext:client-path + * + * The client object path of the context. + */ + g_object_class_install_property (gobject_class, + PROP_CLIENT_PATH, + g_param_spec_string ("client-path", + "Client Path", + "The client object path", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * PvContext:subscription-event + * @context: The #PvContext emitting the signal. + * @event: A #PvSubscriptionEvent + * @flags: #PvSubscriptionFlags indicating the object + * @path: the object path + * + * Notify about a new object that was added/removed/modified. + */ + signals[SIGNAL_SUBSCRIPTION_EVENT] = g_signal_new ("subscription-event", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 3, + PV_TYPE_SUBSCRIPTION_EVENT, + PV_TYPE_SUBSCRIPTION_FLAGS, + G_TYPE_STRING); +} + +static void +pv_context_init (PvContext * context) +{ + PvContextPrivate *priv = context->priv = PV_CONTEXT_GET_PRIVATE (context); + + priv->state = PV_CONTEXT_STATE_UNCONNECTED; +} + +/** + * pv_context_new: + * @name: an application name + * @properties: optional properties + * + * Make a new unconnected #PvContext + * + * Returns: a new unconnected #PvContext + */ +PvContext * +pv_context_new (const gchar *name, GVariant *properties) +{ + return g_object_new (PV_TYPE_CONTEXT, "name", name, "properties", properties, NULL); +} + +static void +context_set_state (PvContext *context, PvContextState state) +{ + if (context->priv->state != state) { + context->priv->state = state; + g_object_notify (G_OBJECT (context), "state"); + } +} + +/** + * pv_context_get_state: + * @context: a #PvContext + * + * Get the state of @context. + * + * Returns: the state of @context + */ +PvContextState +pv_context_get_state (PvContext *context) +{ + PvContextPrivate *priv; + + g_return_val_if_fail (PV_IS_CONTEXT (context), PV_CONTEXT_STATE_ERROR); + priv = context->priv; + + return priv->state; +} + +static void +on_client_proxy (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvContext *context = user_data; + PvContextPrivate *priv = context->priv; + GError *error = NULL; + + priv->client = pv_client1_proxy_new_finish (res, &error); + if (priv->client == NULL) { + context_set_state (context, PV_CONTEXT_STATE_ERROR); + g_error ("failed to get client proxy: %s", error->message); + g_clear_error (&error); + return; + } + context_set_state (context, PV_CONTEXT_STATE_READY); +} + +static void +on_client_connected (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvContext *context = user_data; + PvContextPrivate *priv = context->priv; + GError *error = NULL; + + if (!pv_daemon1_call_connect_client_finish (priv->daemon, &priv->client_path, res, &error)) { + context_set_state (context, PV_CONTEXT_STATE_ERROR); + g_error ("failed to connect client: %s", error->message); + g_clear_error (&error); + return; + } + + pv_client1_proxy_new (priv->connection, + G_DBUS_PROXY_FLAGS_NONE, + PV_DBUS_SERVICE, + priv->client_path, + NULL, + on_client_proxy, + context); +} + +static void +on_daemon_connected (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvContext *context = user_data; + PvContextPrivate *priv = context->priv; + GError *error = NULL; + + priv->daemon = pv_daemon1_proxy_new_finish (res, &error); + if (priv->daemon == NULL) { + context_set_state (context, PV_CONTEXT_STATE_ERROR); + g_error ("failed to get daemon: %s", error->message); + g_clear_error (&error); + return; + } + + context_set_state (context, PV_CONTEXT_STATE_REGISTERING); + + { + GVariantBuilder builder; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string ("hello")); + + pv_daemon1_call_connect_client (priv->daemon, + g_variant_builder_end (&builder), /* GVariant *arg_properties */ + NULL, /* GCancellable *cancellable */ + on_client_connected, + context); + + } +} + + +static void +on_name_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + PvContext *context = user_data; + PvContextPrivate *priv = context->priv; + + priv->connection = connection; + + install_subscription (context); + + pv_daemon1_proxy_new (priv->connection, + G_DBUS_PROXY_FLAGS_NONE, + name, + PV_DBUS_OBJECT_SERVER, + NULL, + on_daemon_connected, + user_data); +} + +static void +on_name_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + PvContext *context = user_data; + PvContextPrivate *priv = context->priv; + + uninstall_subscription (context); + + priv->connection = connection; + + if (priv->flags & PV_CONTEXT_FLAGS_NOFAIL) { + context_set_state (context, PV_CONTEXT_STATE_CONNECTING); + } else { + context_set_state (context, PV_CONTEXT_STATE_ERROR); + } +} + +/** + * pv_context_connect: + * @context: a #PvContext + * @flags: #PvContextFlags + * + * Connect to the daemon with @flags + * + * Returns: %TRUE on success. + */ +gboolean +pv_context_connect (PvContext *context, PvContextFlags flags) +{ + PvContextPrivate *priv; + GBusNameWatcherFlags nw_flags; + + g_return_val_if_fail (PV_IS_CONTEXT (context), FALSE); + + priv = context->priv; + g_return_val_if_fail (priv->connection == NULL, FALSE); + + priv->flags = flags; + + context_set_state (context, PV_CONTEXT_STATE_CONNECTING); + + nw_flags = G_BUS_NAME_WATCHER_FLAGS_NONE; + if (!(flags & PV_CONTEXT_FLAGS_NOAUTOSPAWN)) + nw_flags = G_BUS_NAME_WATCHER_FLAGS_AUTO_START; + + priv->id = g_bus_watch_name (G_BUS_TYPE_SESSION, + PV_DBUS_SERVICE, + nw_flags, + on_name_appeared, + on_name_vanished, + context, + NULL); + return TRUE; +} + +/** + * pv_context_get_connection: + * @context: a #PvContext + * + * Get the #GDBusConnection of @context. + * + * Returns: the #GDBusConnection of @context or %NULL when not connected. + */ +GDBusConnection * +pv_context_get_connection (PvContext *context) +{ + g_return_val_if_fail (PV_IS_CONTEXT (context), NULL); + + return context->priv->connection; +} + +/** + * pv_context_get_client_path: + * @context: a #PvContext + * + * Get the client object path that @context is registered with + * + * Returns: the client object path of @context or %NULL when not + * registered. + */ +const gchar * +pv_context_get_client_path (PvContext *context) +{ + g_return_val_if_fail (PV_IS_CONTEXT (context), NULL); + + return context->priv->client_path; +} + diff --git a/src/client/pv-context.h b/src/client/pv-context.h new file mode 100644 index 000000000..81ef3a244 --- /dev/null +++ b/src/client/pv-context.h @@ -0,0 +1,110 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PV_CONTEXT_H__ +#define __PV_CONTEXT_H__ + +#include +#include + +G_BEGIN_DECLS + +#define PV_TYPE_CONTEXT (pv_context_get_type ()) +#define PV_IS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_CONTEXT)) +#define PV_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_CONTEXT)) +#define PV_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_CONTEXT, PvContextClass)) +#define PV_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_CONTEXT, PvContext)) +#define PV_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_CONTEXT, PvContextClass)) +#define PV_CONTEXT_CAST(obj) ((PvContext*)(obj)) +#define PV_CONTEXT_CLASS_CAST(klass) ((PvContextClass*)(klass)) + +typedef struct _PvContext PvContext; +typedef struct _PvContextClass PvContextClass; +typedef struct _PvContextPrivate PvContextPrivate; + +/** + * PvContextFlags: + * @PV_CONTEXT_FLAGS_NONE: no flags + * @PV_CONTEXT_FLAGS_NOAUTOSPAWN: disable autostart of the daemon + * @PV_CONTEXT_FLAGS_NOFAIL: Don't fail if the daemon is not available, + * instead enter PV_CONTEXT_CONNECTING state and wait for the daemon + * to appear. + * + * Context flags passed to pv_context_connect() + */ +typedef enum { + PV_CONTEXT_FLAGS_NONE = 0, + PV_CONTEXT_FLAGS_NOAUTOSPAWN = (1 << 0), + PV_CONTEXT_FLAGS_NOFAIL = (1 << 1) +} PvContextFlags; + +/** + * PvContextState: + * @PV_CONTEXT_STATE_UNCONNECTED: not connected + * @PV_CONTEXT_STATE_CONNECTING: connecting to daemon + * @PV_CONTEXT_STATE_REGISTERING: registering with daemon + * @PV_CONTEXT_STATE_READY: context is ready + * @PV_CONTEXT_STATE_ERROR: context is in error + * + * The state of a #PvContext + */ +typedef enum { + PV_CONTEXT_STATE_UNCONNECTED = 0, + PV_CONTEXT_STATE_CONNECTING = 1, + PV_CONTEXT_STATE_REGISTERING = 2, + PV_CONTEXT_STATE_READY = 3, + PV_CONTEXT_STATE_ERROR = 4 +} PvContextState; + +/** + * PvContext: + * + * Pulsevideo context object class. + */ +struct _PvContext { + GObject object; + + PvContextPrivate *priv; +}; + +/** + * PvContextClass: + * + * Pulsevideo context object class. + */ +struct _PvContextClass { + GObjectClass parent_class; +}; + +/* normal GObject stuff */ +GType pv_context_get_type (void); + +PvContext * pv_context_new (const gchar *name, GVariant *properties); + +gboolean pv_context_connect (PvContext *context, PvContextFlags flags); + +GDBusConnection * pv_context_get_connection (PvContext *context); +const gchar * pv_context_get_client_path (PvContext *context); + +PvContextState pv_context_get_state (PvContext *context); + +G_END_DECLS + +#endif /* __PV_CONTEXT_H__ */ + diff --git a/src/client/pv-stream.c b/src/client/pv-stream.c new file mode 100644 index 000000000..0cadde8fc --- /dev/null +++ b/src/client/pv-stream.c @@ -0,0 +1,731 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include "server/pv-daemon.h" +#include "client/pv-context.h" +#include "client/pv-stream.h" +#include "client/pv-enumtypes.h" + +#include "dbus/org-pulsevideo.h" + +struct _PvStreamPrivate +{ + PvContext *context; + gchar *name; + gchar *target; + PvStreamState state; + + PvCapture1 *capture; + gchar *source_output_path; + PvSourceOutput1 *source_output; + + GSocket *socket; + PvStreamMode mode; + guint socket_id; + + PvBufferInfo info; +}; + +#define PV_STREAM_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_STREAM, PvStreamPrivate)) + +G_DEFINE_TYPE (PvStream, pv_stream, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_CONTEXT, + PROP_NAME, + PROP_STATE, + PROP_SOCKET +}; + +enum +{ + SIGNAL_NEW_BUFFER, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +pv_stream_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PvStream *stream = PV_STREAM (_object); + PvStreamPrivate *priv = stream->priv; + + switch (prop_id) { + case PROP_CONTEXT: + g_value_set_object (value, priv->context); + break; + + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + + case PROP_STATE: + g_value_set_enum (value, priv->state); + break; + + case PROP_SOCKET: + g_value_set_object (value, priv->socket); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (stream, prop_id, pspec); + break; + } +} + +static void +pv_stream_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PvStream *stream = PV_STREAM (_object); + PvStreamPrivate *priv = stream->priv; + + switch (prop_id) { + case PROP_CONTEXT: + priv->context = g_value_dup_object (value); + break; + + case PROP_NAME: + priv->name = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (stream, prop_id, pspec); + break; + } +} + +static void +pv_stream_finalize (GObject * object) +{ + PvStream *stream = PV_STREAM (object); + PvStreamPrivate *priv = stream->priv; + + g_free (priv->name); + + G_OBJECT_CLASS (pv_stream_parent_class)->finalize (object); +} + +static void +pv_stream_class_init (PvStreamClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PvStreamPrivate)); + + gobject_class->finalize = pv_stream_finalize; + gobject_class->set_property = pv_stream_set_property; + gobject_class->get_property = pv_stream_get_property; + + /** + * PvStream:context + * + * The context of the stream. + */ + g_object_class_install_property (gobject_class, + PROP_CONTEXT, + g_param_spec_object ("context", + "Context", + "The context", + PV_TYPE_CONTEXT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * PvStream:name + * + * The name of the stream as specified at construction time. + */ + g_object_class_install_property (gobject_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "The name of the stream", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * PvStream:state + * + * The state of the stream. Use the notify::state signal to be notified + * of state changes. + */ + g_object_class_install_property (gobject_class, + PROP_STATE, + g_param_spec_enum ("state", + "State", + "The stream state", + PV_TYPE_STREAM_STATE, + PV_STREAM_STATE_UNCONNECTED, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * PvStream:socket + * + * The socket of the stream. When doing pv_stream_start() with + * #PV_STREAM_MODE_SOCKET, the socket will contain a data stream with + * meta data and anciliary data containing fds with the data. + */ + g_object_class_install_property (gobject_class, + PROP_SOCKET, + g_param_spec_object ("socket", + "Socket", + "The stream socket", + G_TYPE_SOCKET, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + + /** + * PvStream:new-buffer + * + * When doing pv_stream_start() with #PV_STREAM_MODE_BUFFER, this signal + * will be fired whenever a new buffer can be obtained with + * pv_stream_capture_buffer(). + */ + signals[SIGNAL_NEW_BUFFER] = g_signal_new ("new-buffer", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 0, + G_TYPE_NONE); +} + +static void +pv_stream_init (PvStream * stream) +{ + PvStreamPrivate *priv = stream->priv = PV_STREAM_GET_PRIVATE (stream); + + priv->state = PV_STREAM_STATE_UNCONNECTED; +} + +/** + * pv_stream_new: + * @context: a #PvContext + * @name: a stream name + * + * Make a new unconnected #PvStream + * + * Returns: a new unconnected #PvStream + */ +PvStream * +pv_stream_new (PvContext * context, const gchar *name) +{ + g_return_val_if_fail (PV_IS_CONTEXT (context), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return g_object_new (PV_TYPE_STREAM, "context", context, "name", name, NULL); +} + +static void +stream_set_state (PvStream *stream, PvStreamState state) +{ + if (stream->priv->state != state) { + stream->priv->state = state; + g_object_notify (G_OBJECT (stream), "state"); + } +} + +/** + * pv_stream_get_state: + * @stream: a #PvStream + * + * Get the state of @stream. + * + * Returns: the state of @stream + */ +PvStreamState +pv_stream_get_state (PvStream *stream) +{ + g_return_val_if_fail (PV_IS_STREAM (stream), PV_STREAM_STATE_ERROR); + + return stream->priv->state; +} + +static void +on_source_output1_proxy (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvStream *stream = user_data; + PvStreamPrivate *priv = stream->priv; + GError *error = NULL; + + priv->source_output = pv_source_output1_proxy_new_finish (res, &error); + if (priv->source_output == NULL) { + stream_set_state (stream, PV_STREAM_STATE_ERROR); + g_error ("failed to get source output proxy: %s", error->message); + g_clear_error (&error); + return; + } + stream_set_state (stream, PV_STREAM_STATE_READY); +} + +static void +on_source_output_created (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvStream *stream = user_data; + PvStreamPrivate *priv = stream->priv; + PvContext *context = priv->context; + GError *error = NULL; + + if (!pv_capture1_call_create_source_output_finish (priv->capture, + &priv->source_output_path, res, &error)) { + stream_set_state (stream, PV_STREAM_STATE_ERROR); + g_print ("failed to get connect capture: %s", error->message); + g_clear_error (&error); + return; + } + + pv_source_output1_proxy_new (pv_context_get_connection (context), + G_DBUS_PROXY_FLAGS_NONE, + PV_DBUS_SERVICE, + priv->source_output_path, + NULL, + on_source_output1_proxy, + stream); +} + +static gboolean +create_source_output (PvStream *stream) +{ + PvStreamPrivate *priv = stream->priv; + GVariantBuilder builder; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string ("hello")); + + pv_capture1_call_create_source_output (priv->capture, + priv->target ? priv->target : "/", /* const gchar *arg_source */ + g_variant_builder_end (&builder), /* GVariant *arg_props */ + NULL, /* GCancellable *cancellable */ + on_source_output_created, + stream); + return TRUE; +} + +static void +on_source_output_removed (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvStream *stream = user_data; + PvStreamPrivate *priv = stream->priv; + GError *error = NULL; + + if (!pv_capture1_call_remove_source_output_finish (priv->capture, + res, &error)) { + stream_set_state (stream, PV_STREAM_STATE_ERROR); + g_print ("failed to disconnect: %s", error->message); + g_clear_error (&error); + return; + } + g_clear_pointer (&priv->source_output_path, g_free); + g_clear_object (&priv->source_output); +} + +static gboolean +remove_source_output (PvStream *stream) +{ + PvStreamPrivate *priv = stream->priv; + + pv_capture1_call_remove_source_output (priv->capture, + priv->source_output_path, + NULL, /* GCancellable *cancellable */ + on_source_output_removed, + stream); + return TRUE; +} + +static void +on_capture_proxy (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvStream *stream = user_data; + PvStreamPrivate *priv = stream->priv; + GError *error = NULL; + + priv->capture = pv_capture1_proxy_new_finish (res, &error); + if (priv->capture == NULL) { + stream_set_state (stream, PV_STREAM_STATE_ERROR); + g_error ("failed to get capture proxy: %s", error->message); + g_clear_error (&error); + return; + } + + create_source_output (stream); +} + + +/** + * pv_stream_connect_capture: + * @stream: a #PvStream + * @device: the device name to connect to + * @flags: a #PvStreamFlags + * + * Connect @stream for capturing from @device. + * + * Returns: %TRUE on success. + */ +gboolean +pv_stream_connect_capture (PvStream *stream, + const gchar *source, + PvStreamFlags flags) +{ + PvStreamPrivate *priv; + PvContext *context; + + g_return_val_if_fail (PV_IS_STREAM (stream), FALSE); + priv = stream->priv; + context = priv->context; + g_return_val_if_fail (pv_context_get_state (context) == PV_CONTEXT_STATE_READY, FALSE); + + priv->target = g_strdup (source); + + stream_set_state (stream, PV_STREAM_STATE_CONNECTING); + + if (priv->capture == NULL) { + pv_capture1_proxy_new (pv_context_get_connection (context), + G_DBUS_PROXY_FLAGS_NONE, + PV_DBUS_SERVICE, + pv_context_get_client_path (context), + NULL, + on_capture_proxy, + stream); + + return TRUE; + } else { + return create_source_output (stream); + } +} + +/** + * pv_stream_disconnect: + * @stream: a #PvStream + * + * Disconnect @stream. + * + * Returns: %TRUE on success + */ +gboolean +pv_stream_disconnect (PvStream *stream) +{ + PvStreamPrivate *priv; + PvContext *context; + + g_return_val_if_fail (PV_IS_STREAM (stream), FALSE); + priv = stream->priv; + g_return_val_if_fail (priv->state >= PV_STREAM_STATE_READY, FALSE); + g_return_val_if_fail (priv->capture != NULL, FALSE); + context = priv->context; + g_return_val_if_fail (pv_context_get_state (context) == PV_CONTEXT_STATE_READY, FALSE); + + remove_source_output (stream); + + stream_set_state (stream, PV_STREAM_STATE_UNCONNECTED); + + return TRUE; +} + + +typedef struct { + guint64 offset; + guint64 size; +} FDMessage; + +static gboolean +on_socket_data (GSocket *socket, + GIOCondition condition, + gpointer user_data) +{ + PvStream *stream = user_data; + PvStreamPrivate *priv = stream->priv; + + switch (condition) { + case G_IO_IN: + { + gssize len; + GInputVector ivec; + FDMessage msg; + GSocketControlMessage **messages = NULL; + gint num_messages = 0; + gint flags = 0; + GError *error = NULL; + + ivec.buffer = &msg; + ivec.size = sizeof (msg); + + len = g_socket_receive_message (socket, + NULL, + &ivec, + 1, + &messages, + &num_messages, + &flags, + NULL, + &error); + + g_assert (len == sizeof (msg)); + + if (priv->info.message) + g_object_unref (priv->info.message); + priv->info.offset = msg.offset; + priv->info.size = msg.size; + priv->info.message = num_messages > 0 ? messages[0] : NULL; + + g_signal_emit (stream, SIGNAL_NEW_BUFFER, 0, NULL); + break; + } + + default: + break; + } + return TRUE; +} + + +static void +handle_socket (PvStream *stream, gint fd) +{ + PvStreamPrivate *priv = stream->priv; + GError *error = NULL; + + g_print ("got fd %d\n", fd); + priv->socket = g_socket_new_from_fd (fd, &error); + if (priv->socket == NULL) { + stream_set_state (stream, PV_STREAM_STATE_ERROR); + g_error ("failed to create socket: %s", error->message); + g_clear_error (&error); + return; + } + + switch (priv->mode) { + case PV_STREAM_MODE_SOCKET: + g_object_notify (G_OBJECT (stream), "socket"); + break; + + case PV_STREAM_MODE_BUFFER: + { + GSource *source; + + source = g_socket_create_source (priv->socket, G_IO_IN, NULL); + g_source_set_callback (source, (GSourceFunc) on_socket_data, stream, NULL); + priv->socket_id = g_source_attach (source, NULL); + g_source_unref (source); + break; + } + + default: + break; + } +} + +static void +unhandle_socket (PvStream *stream) +{ + PvStreamPrivate *priv = stream->priv; + + switch (priv->mode) { + case PV_STREAM_MODE_SOCKET: + g_clear_object (&priv->socket); + g_object_notify (G_OBJECT (stream), "socket"); + break; + + default: + break; + } +} + +static void +on_stream_acquired (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvStream *stream = user_data; + PvStreamPrivate *priv = stream->priv; + GUnixFDList *out_fd_list; + gint fd_idx, fd; + GVariant *out_props; + GError *error = NULL; + GVariant *result; + + result = g_dbus_proxy_call_with_unix_fd_list_finish (G_DBUS_PROXY (priv->source_output), &out_fd_list, res, &error); + if (result == NULL) { + stream_set_state (stream, PV_STREAM_STATE_ERROR); + g_error ("failed to acquire: %s", error->message); + g_clear_error (&error); + return; + } + + g_variant_get (result, + "(h@a{sv})", + &fd_idx, + &out_props); + + g_variant_unref (result); + g_variant_unref (out_props); + + if ((fd = g_unix_fd_list_get (out_fd_list, fd_idx, &error)) < 0) { + stream_set_state (stream, PV_STREAM_STATE_ERROR); + g_error ("failed to get FD: %s", error->message); + g_clear_error (&error); + return; + } + + handle_socket (stream, fd); + + stream_set_state (stream, PV_STREAM_STATE_STREAMING); +} + +/** + * pv_stream_start: + * @stream: a #PvStream + * @mode: a #PvStreamMode + * + * Start capturing from @stream. + * + * When @mode is #PV_STREAM_MODE_SOCKET, you should connect to the notify::socket + * signal to obtain a readable socket with metadata and data. + * + * When @mode is ##PV_STREAM_MODE_BUFFER, you should connect to the new-buffer + * signal and use pv_stream_capture_buffer() to get the latest metadata and + * data. + * + * Returns: %TRUE on success. + */ +gboolean +pv_stream_start (PvStream *stream, PvStreamMode mode) +{ + PvStreamPrivate *priv; + + g_return_val_if_fail (PV_IS_STREAM (stream), FALSE); + + priv = stream->priv; + g_return_val_if_fail (priv->state == PV_STREAM_STATE_READY, FALSE); + + priv->mode = mode; + + stream_set_state (stream, PV_STREAM_STATE_STARTING); + { + GVariantBuilder builder; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string ("hello")); + + pv_source_output1_call_acquire (priv->source_output, + g_variant_builder_end (&builder), /* GVariant *arg_properties */ + NULL, /* GCancellable *cancellable */ + on_stream_acquired, + stream); + } + return TRUE; +} + +static void +on_stream_released (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvStream *stream = user_data; + PvStreamPrivate *priv = stream->priv; + GError *error = NULL; + + if (!pv_source_output1_call_release_finish (priv->source_output, + res, &error)) { + stream_set_state (stream, PV_STREAM_STATE_ERROR); + g_error ("failed to release: %s", error->message); + g_clear_error (&error); + return; + } + unhandle_socket (stream); + + stream_set_state (stream, PV_STREAM_STATE_READY); +} + +/** + * pv_stream_stop: + * @stream: a #PvStream + * + * Stop capturing from @stream. + * + * Returns: %TRUE on success. + */ +gboolean +pv_stream_stop (PvStream *stream) +{ + PvStreamPrivate *priv; + + g_return_val_if_fail (PV_IS_STREAM (stream), FALSE); + + priv = stream->priv; + g_return_val_if_fail (priv->state == PV_STREAM_STATE_STREAMING, FALSE); + + pv_source_output1_call_release (priv->source_output, + NULL, /* GCancellable *cancellable */ + on_stream_released, + stream); + + return TRUE; +} + +/** + * pv_stream_capture_buffer: + * @stream: a #PvStream + * @info: a #PvBufferInfo + * + * Capture the next buffer from @stream. This function should be called every + * time after the new-buffer callback has been emitted. + * + * Returns: %TRUE when @info contains valid information + */ +gboolean +pv_stream_capture_buffer (PvStream *stream, PvBufferInfo *info) +{ + PvStreamPrivate *priv; + + g_return_val_if_fail (PV_IS_STREAM (stream), FALSE); + g_return_val_if_fail (info != NULL, FALSE); + + priv = stream->priv; + g_return_val_if_fail (priv->state == PV_STREAM_STATE_STREAMING, FALSE); + + *info = priv->info; + + return TRUE; +} diff --git a/src/client/pv-stream.h b/src/client/pv-stream.h new file mode 100644 index 000000000..7f004600b --- /dev/null +++ b/src/client/pv-stream.h @@ -0,0 +1,113 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PV_STREAM_H__ +#define __PV_STREAM_H__ + +#include +#include + +#include "pv-context.h" + +G_BEGIN_DECLS + +#define PV_TYPE_STREAM (pv_stream_get_type ()) +#define PV_IS_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_STREAM)) +#define PV_IS_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_STREAM)) +#define PV_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_STREAM, PvStreamClass)) +#define PV_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_STREAM, PvStream)) +#define PV_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_STREAM, PvStreamClass)) +#define PV_STREAM_CAST(obj) ((PvStream*)(obj)) +#define PV_STREAM_CLASS_CAST(klass) ((PvStreamClass*)(klass)) + +typedef struct _PvStream PvStream; +typedef struct _PvStreamClass PvStreamClass; +typedef struct _PvStreamPrivate PvStreamPrivate; + +typedef enum { + PV_STREAM_STATE_UNCONNECTED = 0, + PV_STREAM_STATE_CONNECTING = 1, + PV_STREAM_STATE_READY = 2, + PV_STREAM_STATE_STARTING = 3, + PV_STREAM_STATE_STREAMING = 4, + PV_STREAM_STATE_ERROR = 5 +} PvStreamState; + + +typedef enum { + PV_STREAM_FLAGS_NONE = 0, +} PvStreamFlags; + +typedef struct { + gint64 timestamp; + guint64 seq; + guint64 offset; + guint64 size; + GSocketControlMessage *message; +} PvBufferInfo; + +typedef enum { + PV_STREAM_MODE_SOCKET = 0, + PV_STREAM_MODE_BUFFER = 1, +} PvStreamMode; + +/** + * PvStream: + * + * Pulsevideo stream object class. + */ +struct _PvStream { + GObject object; + + PvStreamPrivate *priv; +}; + +/** + * PvStreamClass: + * + * Pulsevideo stream object class. + */ +struct _PvStreamClass { + GObjectClass parent_class; +}; + +/* normal GObject stuff */ +GType pv_stream_get_type (void); + + +PvStream * pv_stream_new (PvContext * context, + const gchar *name); + +PvStreamState pv_stream_get_state (PvStream *stream); + +gboolean pv_stream_connect_capture (PvStream *stream, + const gchar *source, + PvStreamFlags flags); +gboolean pv_stream_disconnect (PvStream *stream); + +gboolean pv_stream_start (PvStream *stream, PvStreamMode mode); +gboolean pv_stream_stop (PvStream *stream); + +gboolean pv_stream_capture_buffer (PvStream *stream, + PvBufferInfo *info); + +G_END_DECLS + +#endif /* __PV_STREAM_H__ */ + diff --git a/src/client/pv-subscribe.c b/src/client/pv-subscribe.c new file mode 100644 index 000000000..e31427b15 --- /dev/null +++ b/src/client/pv-subscribe.c @@ -0,0 +1,183 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +static void +notify_subscription (PvContext *context, + GDBusObject *object, + GDBusInterface *interface, + PvSubscriptionEvent event) +{ + PvContextPrivate *priv = context->priv; + + if (priv->subscription_mask & PV_SUBSCRIPTION_FLAGS_CLIENT) { + if ((interface == NULL && pv_object_peek_client1 (PV_OBJECT (object))) || + PV_IS_CLIENT1_PROXY (interface)) + g_signal_emit (context, signals[SIGNAL_SUBSCRIPTION_EVENT], 0, event, + PV_SUBSCRIPTION_FLAGS_CLIENT, g_dbus_object_get_object_path (object)); + } + if (priv->subscription_mask & PV_SUBSCRIPTION_FLAGS_DEVICE) { + if ((interface == NULL && pv_object_peek_device1 (PV_OBJECT (object))) || + PV_IS_DEVICE1_PROXY (interface)) + g_signal_emit (context, signals[SIGNAL_SUBSCRIPTION_EVENT], 0, event, + PV_SUBSCRIPTION_FLAGS_DEVICE, g_dbus_object_get_object_path (object)); + } + if (priv->subscription_mask & PV_SUBSCRIPTION_FLAGS_SOURCE) { + if ((interface == NULL && pv_object_peek_source1 (PV_OBJECT (object))) || + PV_IS_SOURCE1_PROXY (interface)) + g_signal_emit (context, signals[SIGNAL_SUBSCRIPTION_EVENT], 0, event, + PV_SUBSCRIPTION_FLAGS_SOURCE, g_dbus_object_get_object_path (object)); + } + if (priv->subscription_mask & PV_SUBSCRIPTION_FLAGS_SOURCE_OUTPUT) { + if ((interface == NULL && pv_object_peek_source_output1 (PV_OBJECT (object))) || + PV_IS_SOURCE_OUTPUT1_PROXY (interface)) + g_signal_emit (context, signals[SIGNAL_SUBSCRIPTION_EVENT], 0, event, + PV_SUBSCRIPTION_FLAGS_SOURCE_OUTPUT, g_dbus_object_get_object_path (object)); + } +} + +static void +on_client_manager_interface_added (GDBusObjectManager *manager, + GDBusObject *object, + GDBusInterface *interface, + gpointer user_data) +{ + PvContext *context = user_data; + notify_subscription (context, object, interface, PV_SUBSCRIPTION_EVENT_NEW); +} + +static void +on_client_manager_interface_removed (GDBusObjectManager *manager, + GDBusObject *object, + GDBusInterface *interface, + gpointer user_data) +{ + PvContext *context = user_data; + notify_subscription (context, object, interface, PV_SUBSCRIPTION_EVENT_REMOVE); +} + +static void +on_client_manager_object_added (GDBusObjectManager *manager, + GDBusObject *object, + gpointer user_data) +{ + PvContext *context = user_data; + notify_subscription (context, object, NULL, PV_SUBSCRIPTION_EVENT_NEW); +} + +static void +on_client_manager_object_removed (GDBusObjectManager *manager, + GDBusObject *object, + gpointer user_data) +{ + PvContext *context = user_data; + notify_subscription (context, object, NULL, PV_SUBSCRIPTION_EVENT_REMOVE); +} + +static void +on_client_manager_properties_changed (GDBusObjectManagerClient *manager, + GDBusObjectProxy *object_proxy, + GDBusProxy *interface_proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + gpointer user_data) +{ + g_print ("properties changed\n"); +} + +static void +on_client_manager_signal (GDBusObjectManagerClient *manager, + GDBusObjectProxy *object_proxy, + GDBusProxy *interface_proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + g_print ("proxy signal %s\n", signal_name); +} + +static void +connect_client_signals (PvContext *context) +{ + PvContextPrivate *priv = context->priv; + + g_signal_connect (priv->client_manager, "interface-added", + (GCallback) on_client_manager_interface_added, context); + g_signal_connect (priv->client_manager, "interface-removed", + (GCallback) on_client_manager_interface_removed, context); + g_signal_connect (priv->client_manager, "object-added", + (GCallback) on_client_manager_object_added, context); + g_signal_connect (priv->client_manager, "object-removed", + (GCallback) on_client_manager_object_removed, context); + g_signal_connect (priv->client_manager, "interface-proxy-signal", + (GCallback) on_client_manager_signal, context); + g_signal_connect (priv->client_manager, "interface-proxy-properties-changed", + (GCallback) on_client_manager_properties_changed, context); +} + +static void +on_client_manager_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PvContext *context = user_data; + PvContextPrivate *priv = context->priv; + GError *error = NULL; + + priv->client_manager = pv_object_manager_client_new_finish (res, &error); + if (priv->client_manager == NULL) + goto manager_error; + + connect_client_signals (context); + + return; + + /* ERRORS */ +manager_error: + { + g_warning ("could not create client manager: %s", error->message); + g_clear_error (&error); + return; + } +} + +static void +install_subscription (PvContext *context) +{ + PvContextPrivate *priv = context->priv; + + if (priv->client_manager) + return; + + pv_object_manager_client_new (pv_context_get_connection (context), + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + PV_DBUS_SERVICE, + PV_DBUS_OBJECT_PREFIX, + NULL, + on_client_manager_ready, + context); +} + +static void +uninstall_subscription (PvContext *context) +{ + PvContextPrivate *priv = context->priv; + + g_clear_object (&priv->client_manager); +} diff --git a/src/client/pv-subscribe.h b/src/client/pv-subscribe.h new file mode 100644 index 000000000..f27f1ccd4 --- /dev/null +++ b/src/client/pv-subscribe.h @@ -0,0 +1,45 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PV_SUBSCRIBE_H__ +#define __PV_SUBSCRIBE_H__ + +#include + +G_BEGIN_DECLS + +typedef enum { + PV_SUBSCRIPTION_FLAGS_CLIENT = (1 << 0), + PV_SUBSCRIPTION_FLAGS_DEVICE = (1 << 1), + PV_SUBSCRIPTION_FLAGS_SOURCE = (1 << 2), + PV_SUBSCRIPTION_FLAGS_SOURCE_OUTPUT = (1 << 3), + + PV_SUBSCRIPTION_FLAGS_ALL = 0xf +} PvSubscriptionFlags; + +typedef enum { + PV_SUBSCRIPTION_EVENT_NEW = 0, + PV_SUBSCRIPTION_EVENT_CHANGE = 1, + PV_SUBSCRIPTION_EVENT_REMOVE = 2, +} PvSubscriptionEvent; + +G_END_DECLS + +#endif /* __PV_SUBSCRIBE_H__ */ + diff --git a/src/daemon/main.c b/src/daemon/main.c new file mode 100644 index 000000000..294b3f4af --- /dev/null +++ b/src/daemon/main.c @@ -0,0 +1,44 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "server/pv-daemon.h" + +gint +main (gint argc, gchar *argv[]) +{ + PvDaemon *daemon; + GMainLoop *loop; + + gst_init (&argc, &argv); + + loop = g_main_loop_new (NULL, FALSE); + + daemon = pv_daemon_new (); + pv_daemon_start (daemon); + + g_main_loop_run (loop); + + g_main_loop_unref (loop); + g_object_unref (daemon); + + return 0; +} diff --git a/src/daemon/pulsevideo-system.conf b/src/daemon/pulsevideo-system.conf new file mode 100644 index 000000000..67226a79c --- /dev/null +++ b/src/daemon/pulsevideo-system.conf @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/src/dbus/.gitignore b/src/dbus/.gitignore new file mode 100644 index 000000000..ec02dc3f1 --- /dev/null +++ b/src/dbus/.gitignore @@ -0,0 +1,2 @@ +org-pulsevideo.c +org-pulsevideo.h diff --git a/src/dbus/org.pulsevideo.xml b/src/dbus/org.pulsevideo.xml new file mode 100644 index 000000000..a82cb99f3 --- /dev/null +++ b/src/dbus/org.pulsevideo.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/v4l2/pv-v4l2-source.c b/src/modules/v4l2/pv-v4l2-source.c new file mode 100644 index 000000000..5dc229863 --- /dev/null +++ b/src/modules/v4l2/pv-v4l2-source.c @@ -0,0 +1,130 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "pv-v4l2-source.h" + +#define PV_V4L2_SOURCE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_V4L2_SOURCE, PvV4l2SourcePrivate)) + +struct _PvV4l2SourcePrivate +{ + GstElement *pipeline; + GstElement *sink; + + GSocket *socket; +}; + +G_DEFINE_TYPE (PvV4l2Source, pv_v4l2_source, PV_TYPE_SOURCE); + +static void +setup_pipeline (PvV4l2Source *source) +{ + PvV4l2SourcePrivate *priv = source->priv; + + priv->pipeline = gst_parse_launch ("v4l2src ! video/x-raw,width=320,height=240,framerate=30/1 ! " + "pvfdpay ! multisocketsink buffers-max=2 buffers-soft-max=1 " + "recover-policy=latest sync-method=latest name=sink sync=true " + "enable-last-sample=false", NULL); + priv->sink = gst_bin_get_by_name (GST_BIN (priv->pipeline), "sink"); + + gst_element_set_state (priv->pipeline, GST_STATE_READY); +} + +static void +on_socket_notify (GObject *gobject, + GParamSpec *pspec, + gpointer user_data) +{ + PvV4l2Source *source = user_data; + PvV4l2SourcePrivate *priv = source->priv; + GSocket *socket; + guint num_handles; + + g_object_get (gobject, "socket", &socket, NULL); + + if (socket == NULL) { + if (priv->socket) + g_signal_emit_by_name (priv->sink, "remove", priv->socket); + } else { + g_signal_emit_by_name (priv->sink, "add", socket); + } + priv->socket = socket; + + g_object_get (priv->sink, "num-handles", &num_handles, NULL); + if (num_handles == 0) { + gst_element_set_state (priv->pipeline, GST_STATE_READY); + } else { + gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); + } +} + +static PvSourceOutput * +v4l2_create_source_output (PvSource *source, GVariant *props, const gchar *prefix) +{ + PvSourceOutput *output; + + output = PV_SOURCE_CLASS (pv_v4l2_source_parent_class)->create_source_output (source, props, prefix); + + g_signal_connect (output, "notify::socket", (GCallback) on_socket_notify, source); + + return output; +} + +static gboolean +v4l2_release_source_output (PvSource *source, PvSourceOutput *output) +{ + return PV_SOURCE_CLASS (pv_v4l2_source_parent_class)->release_source_output (source, output); +} + +static void +v4l2_source_finalize (GObject * object) +{ + G_OBJECT_CLASS (pv_v4l2_source_parent_class)->finalize (object); +} + +static void +pv_v4l2_source_class_init (PvV4l2SourceClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + PvSourceClass *source_class = PV_SOURCE_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PvV4l2SourcePrivate)); + + gobject_class->finalize = v4l2_source_finalize; + + source_class->create_source_output = v4l2_create_source_output; + source_class->release_source_output = v4l2_release_source_output; +} + +static void +pv_v4l2_source_init (PvV4l2Source * source) +{ + source->priv = PV_V4L2_SOURCE_GET_PRIVATE (source); + + setup_pipeline (source); +} + +PvSource * +pv_v4l2_source_new (PvDaemon *daemon) +{ + return g_object_new (PV_TYPE_V4L2_SOURCE, "daemon", daemon, NULL); +} diff --git a/src/modules/v4l2/pv-v4l2-source.h b/src/modules/v4l2/pv-v4l2-source.h new file mode 100644 index 000000000..6ea92a00d --- /dev/null +++ b/src/modules/v4l2/pv-v4l2-source.h @@ -0,0 +1,58 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PV_V4L2_SOURCE_H__ +#define __PV_V4L2_SOURCE_H__ + +#include +#include "server/pv-source.h" + +G_BEGIN_DECLS + +#define PV_TYPE_V4L2_SOURCE (pv_v4l2_source_get_type ()) +#define PV_IS_V4L2_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_V4L2_SOURCE)) +#define PV_IS_V4L2_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_V4L2_SOURCE)) +#define PV_V4L2_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_V4L2_SOURCE, PvV4l2SourceClass)) +#define PV_V4L2_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_V4L2_SOURCE, PvV4l2Source)) +#define PV_V4L2_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_V4L2_SOURCE, PvV4l2SourceClass)) +#define PV_V4L2_SOURCE_CAST(obj) ((PvV4l2Source*)(obj)) +#define PV_V4L2_SOURCE_CLASS_CAST(klass) ((PvV4l2SourceClass*)(klass)) + +typedef struct _PvV4l2Source PvV4l2Source; +typedef struct _PvV4l2SourceClass PvV4l2SourceClass; +typedef struct _PvV4l2SourcePrivate PvV4l2SourcePrivate; + +struct _PvV4l2Source { + PvSource object; + + PvV4l2SourcePrivate *priv; +}; + +struct _PvV4l2SourceClass { + PvSourceClass parent_class; +}; + +GType pv_v4l2_source_get_type (void); + +PvSource * pv_v4l2_source_new (PvDaemon *daemon); + +G_END_DECLS + +#endif /* __PV_V4L2_SOURCE_H__ */ + diff --git a/src/server/pv-client.c b/src/server/pv-client.c new file mode 100644 index 000000000..d5a1d55fe --- /dev/null +++ b/src/server/pv-client.c @@ -0,0 +1,279 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "server/pv-client.h" +#include "server/pv-source.h" +#include "server/pv-source-output.h" +#include "client/pv-enumtypes.h" + +#include "dbus/org-pulsevideo.h" + +struct _PvClientPrivate +{ + PvDaemon *daemon; + gchar *object_path; + + PvSource *source; + + GHashTable *source_outputs; +}; + +#define PV_CLIENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_CLIENT, PvClientPrivate)) + +G_DEFINE_TYPE (PvClient, pv_client, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_DAEMON, + PROP_OBJECT_PATH, +}; + +static void +pv_client_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PvClient *client = PV_CLIENT (_object); + PvClientPrivate *priv = client->priv; + + switch (prop_id) { + case PROP_DAEMON: + g_value_set_object (value, priv->daemon); + break; + + case PROP_OBJECT_PATH: + g_value_set_string (value, priv->object_path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (client, prop_id, pspec); + break; + } +} + +static void +pv_client_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PvClient *client = PV_CLIENT (_object); + PvClientPrivate *priv = client->priv; + + switch (prop_id) { + case PROP_DAEMON: + priv->daemon = g_value_dup_object (value); + break; + + case PROP_OBJECT_PATH: + priv->object_path = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (client, prop_id, pspec); + break; + } +} + +static gboolean +handle_create_source_output (PvCapture1 *interface, + GDBusMethodInvocation *invocation, + const gchar *arg_source, + GVariant *arg_properties, + gpointer user_data) +{ + PvClient *client = user_data; + PvClientPrivate *priv = client->priv; + PvDaemon *daemon = priv->daemon; + PvSource *source; + PvSourceOutput *output; + const gchar *object_path; + + source = pv_daemon_get_source (daemon, arg_source); + + output = pv_source_create_source_output (source, NULL, priv->object_path); + + object_path = pv_source_output_get_object_path (output); + g_hash_table_insert (priv->source_outputs, g_strdup (object_path), output); + pv_capture1_complete_create_source_output (interface, invocation, object_path); + + return TRUE; +} + +static gboolean +handle_remove_source_output (PvCapture1 *interface, + GDBusMethodInvocation *invocation, + const gchar *arg_output, + gpointer user_data) +{ + PvClient *client = user_data; + PvClientPrivate *priv = client->priv; + PvSourceOutput *output; + + output = g_hash_table_lookup (priv->source_outputs, arg_output); + if (output) { + pv_source_release_source_output (priv->source, output); + g_hash_table_remove (priv->source_outputs, arg_output); + } + + pv_capture1_complete_remove_source_output (interface, invocation); + + return TRUE; +} + +static void +client_register_object (PvClient *client, const gchar *prefix) +{ + PvClientPrivate *priv = client->priv; + PvDaemon *daemon = priv->daemon; + GDBusObjectSkeleton *skel; + gchar *name; + + name = g_strdup_printf ("%s/client", prefix); + skel = g_dbus_object_skeleton_new (name); + g_free (name); + + { + PvClient1 *iface; + + iface = pv_client1_skeleton_new (); + pv_client1_set_name (iface, "poppy"); + g_dbus_object_skeleton_add_interface (skel, G_DBUS_INTERFACE_SKELETON (iface)); + g_object_unref (iface); + } + { + PvCapture1 *iface; + + iface = pv_capture1_skeleton_new (); + g_signal_connect (iface, "handle-create-source-output", (GCallback) handle_create_source_output, client); + g_signal_connect (iface, "handle-remove-source-output", (GCallback) handle_remove_source_output, client); + g_dbus_object_skeleton_add_interface (skel, G_DBUS_INTERFACE_SKELETON (iface)); + g_object_unref (iface); + } + + g_free (priv->object_path); + priv->object_path = pv_daemon_export_uniquely (daemon, skel); +} + +static void +client_unregister_object (PvClient *client) +{ + PvClientPrivate *priv = client->priv; + PvDaemon *daemon = priv->daemon; + + g_hash_table_unref (priv->source_outputs); + g_clear_object (&priv->source); + + pv_daemon_unexport (daemon, priv->object_path); + g_free (priv->object_path); +} + +static void +pv_client_finalize (GObject * object) +{ + PvClient *client = PV_CLIENT (object); + + client_unregister_object (client); + + G_OBJECT_CLASS (pv_client_parent_class)->finalize (object); +} + +static void +pv_client_constructed (GObject * object) +{ + PvClient *client = PV_CLIENT (object); + PvClientPrivate *priv = client->priv; + + client_register_object (client, priv->object_path); + + G_OBJECT_CLASS (pv_client_parent_class)->constructed (object); +} + +static void +pv_client_class_init (PvClientClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PvClientPrivate)); + + gobject_class->finalize = pv_client_finalize; + gobject_class->set_property = pv_client_set_property; + gobject_class->get_property = pv_client_get_property; + gobject_class->constructed = pv_client_constructed; + + g_object_class_install_property (gobject_class, + PROP_DAEMON, + g_param_spec_object ("daemon", + "Daemon", + "The daemon", + PV_TYPE_DAEMON, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "The object path", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +pv_client_init (PvClient * client) +{ + PvClientPrivate *priv = client->priv = PV_CLIENT_GET_PRIVATE (client); + + priv->source_outputs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); +} + + +/** + * pv_client_new: + * + * Make a new unconnected #PvClient + * + * Returns: a new unconnected #PvClient + */ +PvClient * +pv_client_new (PvDaemon * daemon, const gchar *prefix) +{ + g_return_val_if_fail (PV_IS_DAEMON (daemon), NULL); + g_return_val_if_fail (g_variant_is_object_path (prefix), NULL); + + return g_object_new (PV_TYPE_CLIENT, "daemon", daemon, "object-path", prefix, NULL); +} + +const gchar * +pv_client_get_object_path (PvClient *client) +{ + PvClientPrivate *priv; + + g_return_val_if_fail (PV_IS_CLIENT (client), NULL); + priv = client->priv; + + return priv->object_path; +} + diff --git a/src/server/pv-client.h b/src/server/pv-client.h new file mode 100644 index 000000000..41bac3284 --- /dev/null +++ b/src/server/pv-client.h @@ -0,0 +1,72 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PV_CLIENT_H__ +#define __PV_CLIENT_H__ + +#include + +#include "pv-daemon.h" + +G_BEGIN_DECLS + +#define PV_TYPE_CLIENT (pv_client_get_type ()) +#define PV_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_CLIENT)) +#define PV_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_CLIENT)) +#define PV_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_CLIENT, PvClientClass)) +#define PV_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_CLIENT, PvClient)) +#define PV_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_CLIENT, PvClientClass)) +#define PV_CLIENT_CAST(obj) ((PvClient*)(obj)) +#define PV_CLIENT_CLASS_CAST(klass) ((PvClientClass*)(klass)) + +typedef struct _PvClient PvClient; +typedef struct _PvClientClass PvClientClass; +typedef struct _PvClientPrivate PvClientPrivate; + +/** + * PvClient: + * + * Pulsevideo client object class. + */ +struct _PvClient { + GObject object; + + PvClientPrivate *priv; +}; + +/** + * PvClientClass: + * + * Pulsevideo client object class. + */ +struct _PvClientClass { + GObjectClass parent_class; +}; + +/* normal GObject stuff */ +GType pv_client_get_type (void); + +PvClient * pv_client_new (PvDaemon *daemon, const gchar *prefix); + +const gchar * pv_client_get_object_path (PvClient *client); + +G_END_DECLS + +#endif /* __PV_CLIENT_H__ */ + diff --git a/src/server/pv-daemon.c b/src/server/pv-daemon.c new file mode 100644 index 000000000..ec79d986d --- /dev/null +++ b/src/server/pv-daemon.c @@ -0,0 +1,354 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include "config.h" + +#include "server/pv-daemon.h" +#include "server/pv-client.h" +#include "modules/v4l2/pv-v4l2-source.h" +#include "dbus/org-pulsevideo.h" + +#define PV_DAEMON_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_DAEMON, PvDaemonPrivate)) + +struct _PvDaemonPrivate +{ + guint id; + GDBusConnection *connection; + GDBusObjectManagerServer *server_manager; + + GHashTable *senders; + PvSource *source; +}; + +typedef struct { + PvDaemon *daemon; + gchar *sender; + guint id; + + GHashTable *clients; +} SenderData; + +static void +client_name_appeared_handler (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ +} + +static void +client_name_vanished_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + SenderData *data = user_data; + PvDaemonPrivate *priv = data->daemon->priv; + + g_print ("vanished client %s\n", name); + + g_hash_table_unref (data->clients); + g_hash_table_remove (priv->senders, data->sender); + g_free (data->sender); + g_bus_unwatch_name (data->id); + g_free (data); +} + +static SenderData * +sender_data_new (PvDaemon *daemon, const gchar *sender) +{ + PvDaemonPrivate *priv = daemon->priv; + SenderData *data; + + data = g_new0 (SenderData, 1); + data->daemon = daemon; + data->sender = g_strdup (sender); + data->clients = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + + data->id = g_bus_watch_name_on_connection (priv->connection, + sender, + G_BUS_NAME_WATCHER_FLAGS_NONE, + client_name_appeared_handler, + client_name_vanished_handler, + data, + NULL); + + g_hash_table_insert (priv->senders, data->sender, data); + + return data; +} + +static gboolean +handle_connect_client (PvDaemon1 *interface, + GDBusMethodInvocation *invocation, + GVariant *arg_properties, + gpointer user_data) +{ + PvDaemon *daemon = user_data; + PvDaemonPrivate *priv = daemon->priv; + PvClient *client; + const gchar *sender, *object_path; + SenderData *data; + + sender = g_dbus_method_invocation_get_sender (invocation); + + g_print ("connect client %s\n", sender); + data = g_hash_table_lookup (priv->senders, sender); + if (data == NULL) { + data = sender_data_new (daemon, sender); + } + + client = pv_client_new (daemon, PV_DBUS_OBJECT_PREFIX); + object_path = pv_client_get_object_path (client); + + g_hash_table_insert (data->clients, g_strdup (object_path), client); + + pv_daemon1_complete_connect_client (interface, invocation, object_path); + + return TRUE; +} + +static gboolean +handle_disconnect_client (PvDaemon1 *interface, + GDBusMethodInvocation *invocation, + const gchar *arg_client, + gpointer user_data) +{ + PvDaemon *daemon = user_data; + PvDaemonPrivate *priv = daemon->priv; + const gchar *sender; + SenderData *data; + + sender = g_dbus_method_invocation_get_sender (invocation); + + g_print ("disconnect client %s\n", sender); + data = g_hash_table_lookup (priv->senders, sender); + if (data != NULL) { + g_hash_table_remove (data->clients, arg_client); + } + + pv_daemon1_complete_disconnect_client (interface, invocation); + + return TRUE; +} + +static gboolean +handle_add_provider (PvManager1 *interface, + GDBusMethodInvocation *invocation, + const gchar *arg_provider, + GVariant *arg_properties, + gpointer user_data) +{ + g_print ("add provider\n"); + g_dbus_method_invocation_return_dbus_error (invocation, + "org.pulseaudio.Error.NotImplemented", + "Operation add not yet implemented"); + return TRUE; +} + +static gboolean +handle_remove_provider (PvManager1 *interface, + GDBusMethodInvocation *invocation, + const gchar *arg_provider, + gpointer user_data) +{ + g_print ("remove provider\n"); + g_dbus_method_invocation_return_dbus_error (invocation, + "org.pulseaudio.Error.NotImplemented", + "Operation remove not yet implemented"); + return TRUE; +} + + +static void +export_server_object (PvDaemon *daemon, GDBusObjectManagerServer *manager) +{ + GDBusObjectSkeleton *skel; + + skel = g_dbus_object_skeleton_new (PV_DBUS_OBJECT_SERVER); + { + PvDaemon1 *iface; + + iface = pv_daemon1_skeleton_new (); + g_signal_connect (iface, "handle-connect-client", (GCallback) handle_connect_client, daemon); + g_signal_connect (iface, "handle-disconnect-client", (GCallback) handle_disconnect_client, daemon); + pv_daemon1_set_user_name (iface, g_get_user_name ()); + pv_daemon1_set_host_name (iface, g_get_host_name ()); + pv_daemon1_set_version (iface, PACKAGE_VERSION); + pv_daemon1_set_name (iface, PACKAGE_NAME); + g_dbus_object_skeleton_add_interface (skel, G_DBUS_INTERFACE_SKELETON (iface)); + g_object_unref (iface); + } + { + PvManager1 *iface; + + iface = pv_manager1_skeleton_new (); + g_signal_connect (iface, "handle-add-provider", (GCallback) handle_add_provider, daemon); + g_signal_connect (iface, "handle-remove-provider", (GCallback) handle_remove_provider, daemon); + + g_dbus_object_skeleton_add_interface (skel, G_DBUS_INTERFACE_SKELETON (iface)); + g_object_unref (iface); + } + g_dbus_object_manager_server_export (manager, skel); + g_object_unref (skel); +} + +static void +bus_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + PvDaemon *daemon = user_data; + PvDaemonPrivate *priv = daemon->priv; + + priv->connection = connection; +} + +static void +name_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + PvDaemon *daemon = user_data; + PvDaemonPrivate *priv = daemon->priv; + GDBusObjectManagerServer *manager; + + priv->server_manager = manager = g_dbus_object_manager_server_new (PV_DBUS_OBJECT_PREFIX); + export_server_object (daemon, manager); + g_dbus_object_manager_server_set_connection (manager, connection); +} + +static void +name_lost_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + PvDaemon *daemon = user_data; + PvDaemonPrivate *priv = daemon->priv; + GDBusObjectManagerServer *manager = priv->server_manager; + + g_dbus_object_manager_server_unexport (manager, PV_DBUS_OBJECT_SERVER); + g_clear_object (&priv->server_manager); +} + +PvDaemon * +pv_daemon_new (void) +{ + return g_object_new (PV_TYPE_DAEMON, NULL); +} + +void +pv_daemon_start (PvDaemon *daemon) +{ + PvDaemonPrivate *priv; + + g_return_if_fail (PV_IS_DAEMON (daemon)); + + priv = daemon->priv; + g_return_if_fail (priv->id == 0); + + priv->id = g_bus_own_name (G_BUS_TYPE_SESSION, + PV_DBUS_SERVICE, + G_BUS_NAME_OWNER_FLAGS_REPLACE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + daemon, + NULL); +} + +void +pv_daemon_stop (PvDaemon *daemon) +{ + PvDaemonPrivate *priv = daemon->priv; + + g_return_if_fail (PV_IS_DAEMON (daemon)); + + g_bus_unown_name (priv->id); + priv->id = 0; +} + +gchar * +pv_daemon_export_uniquely (PvDaemon *daemon, GDBusObjectSkeleton *skel) +{ + g_return_val_if_fail (PV_IS_DAEMON (daemon), NULL); + g_return_val_if_fail (G_IS_DBUS_OBJECT_SKELETON (skel), NULL); + + g_dbus_object_manager_server_export_uniquely (daemon->priv->server_manager, skel); + + return g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (skel))); +} + +void +pv_daemon_unexport (PvDaemon *daemon, const gchar *object_path) +{ + g_return_if_fail (PV_IS_DAEMON (daemon)); + g_return_if_fail (g_variant_is_object_path (object_path)); + + g_dbus_object_manager_server_unexport (daemon->priv->server_manager, object_path); +} + +PvSource * +pv_daemon_get_source (PvDaemon *daemon, const gchar *name) +{ + PvDaemonPrivate *priv; + + g_return_val_if_fail (PV_IS_DAEMON (daemon), NULL); + priv = daemon->priv; + + if (priv->source == NULL) { + priv->source = pv_v4l2_source_new (daemon); + } + return priv->source; +} + +G_DEFINE_TYPE (PvDaemon, pv_daemon, G_TYPE_OBJECT); + +static void +pv_daemon_finalize (GObject * object) +{ + PvDaemon *daemon = PV_DAEMON_CAST (object); + PvDaemonPrivate *priv = daemon->priv; + + g_hash_table_unref (priv->senders); + pv_daemon_stop (daemon); + + G_OBJECT_CLASS (pv_daemon_parent_class)->finalize (object); +} + +static void +pv_daemon_class_init (PvDaemonClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PvDaemonPrivate)); + + gobject_class->finalize = pv_daemon_finalize; +} + +static void +pv_daemon_init (PvDaemon * daemon) +{ + PvDaemonPrivate *priv = daemon->priv = PV_DAEMON_GET_PRIVATE (daemon); + + priv->senders = g_hash_table_new (g_str_hash, g_str_equal); +} + diff --git a/src/server/pv-daemon.h b/src/server/pv-daemon.h new file mode 100644 index 000000000..d6d41661e --- /dev/null +++ b/src/server/pv-daemon.h @@ -0,0 +1,85 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PV_DAEMON_H__ +#define __PV_DAEMON_H__ + +#include +#include + +G_BEGIN_DECLS + +#define PV_TYPE_DAEMON (pv_daemon_get_type ()) +#define PV_IS_DAEMON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_DAEMON)) +#define PV_IS_DAEMON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_DAEMON)) +#define PV_DAEMON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_DAEMON, PvDaemonClass)) +#define PV_DAEMON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_DAEMON, PvDaemon)) +#define PV_DAEMON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_DAEMON, PvDaemonClass)) +#define PV_DAEMON_CAST(obj) ((PvDaemon*)(obj)) +#define PV_DAEMON_CLASS_CAST(klass) ((PvDaemonClass*)(klass)) + +typedef struct _PvDaemon PvDaemon; +typedef struct _PvDaemonClass PvDaemonClass; +typedef struct _PvDaemonPrivate PvDaemonPrivate; + +#include "server/pv-source.h" + +#define PV_DBUS_SERVICE "org.pulsevideo" +#define PV_DBUS_OBJECT_PREFIX "/org/pulsevideo" +#define PV_DBUS_OBJECT_SERVER PV_DBUS_OBJECT_PREFIX "/server" +#define PV_DBUS_OBJECT_SOURCE PV_DBUS_OBJECT_PREFIX "/source" +#define PV_DBUS_OBJECT_CLIENT PV_DBUS_OBJECT_PREFIX "/client" + +/** + * PvDaemon: + * + * Pulsevideo daemon object class. + */ +struct _PvDaemon { + GObject object; + + PvDaemonPrivate *priv; +}; + +/** + * PvDaemonClass: + * + * Pulsevideo daemon object class. + */ +struct _PvDaemonClass { + GObjectClass parent_class; +}; + +/* normal GObject stuff */ +GType pv_daemon_get_type (void); + +PvDaemon * pv_daemon_new (void); + +void pv_daemon_start (PvDaemon *daemon); +void pv_daemon_stop (PvDaemon *daemon); + +gchar * pv_daemon_export_uniquely (PvDaemon *daemon, GDBusObjectSkeleton *skel); +void pv_daemon_unexport (PvDaemon *daemon, const gchar *name); + +PvSource * pv_daemon_get_source (PvDaemon *daemon, const gchar *name); + +G_END_DECLS + +#endif /* __PV_DAEMON_H__ */ + diff --git a/src/server/pv-source-output.c b/src/server/pv-source-output.c new file mode 100644 index 000000000..d28e614bd --- /dev/null +++ b/src/server/pv-source-output.c @@ -0,0 +1,273 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include "server/pv-daemon.h" +#include "server/pv-source-output.h" +#include "client/pv-enumtypes.h" + +#include "dbus/org-pulsevideo.h" + +struct _PvSourceOutputPrivate +{ + PvDaemon *daemon; + gchar *object_path; + GSocket *socket; +}; + +#define PV_SOURCE_OUTPUT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_SOURCE_OUTPUT, PvSourceOutputPrivate)) + +G_DEFINE_TYPE (PvSourceOutput, pv_source_output, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_DAEMON, + PROP_OBJECT_PATH, + PROP_SOCKET, +}; + +static void +pv_source_output_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PvSourceOutput *output = PV_SOURCE_OUTPUT (_object); + PvSourceOutputPrivate *priv = output->priv; + + switch (prop_id) { + case PROP_DAEMON: + g_value_set_object (value, priv->daemon); + break; + + case PROP_OBJECT_PATH: + g_value_set_string (value, priv->object_path); + break; + + case PROP_SOCKET: + g_value_set_object (value, priv->socket); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (output, prop_id, pspec); + break; + } +} + +static void +pv_source_output_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PvSourceOutput *output = PV_SOURCE_OUTPUT (_object); + PvSourceOutputPrivate *priv = output->priv; + + switch (prop_id) { + case PROP_DAEMON: + priv->daemon = g_value_dup_object (value); + break; + + case PROP_OBJECT_PATH: + priv->object_path = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (output, prop_id, pspec); + break; + } +} + +static gboolean +handle_acquire (PvSourceOutput1 *interface, + GDBusMethodInvocation *invocation, + GVariant *arg_properties, + gpointer user_data) +{ + PvSourceOutput *output = user_data; + PvSourceOutputPrivate *priv = output->priv; + GUnixFDList *fdlist; + GVariantBuilder props; + gint fd[2]; + + g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&props, "{sv}", "name", g_variant_new_string ("hello")); + + socketpair (AF_UNIX, SOCK_STREAM, 0, fd); + priv->socket = g_socket_new_from_fd (fd[0], NULL); + g_object_notify (G_OBJECT (output), "socket"); + + fdlist = g_unix_fd_list_new (); + g_unix_fd_list_append (fdlist, fd[1], NULL); + g_dbus_method_invocation_return_value_with_unix_fd_list (invocation, + g_variant_new ("(h@a{sv})", 0, g_variant_builder_end (&props)), + fdlist); + + return TRUE; +} + +static void +stop_transfer (PvSourceOutput *output) +{ + PvSourceOutputPrivate *priv = output->priv; + + if (priv->socket) { + g_clear_object (&priv->socket); + g_object_notify (G_OBJECT (output), "socket"); + } +} + +static gboolean +handle_release (PvSourceOutput1 *interface, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + PvSourceOutput *output = user_data; + + stop_transfer (output); + + pv_source_output1_complete_release (interface, invocation); + + return TRUE; +} + +static void +output_register_object (PvSourceOutput *output, const gchar *prefix) +{ + PvSourceOutputPrivate *priv = output->priv; + PvDaemon *daemon = priv->daemon; + GDBusObjectSkeleton *skel; + gchar *name; + + name = g_strdup_printf ("%s/output", prefix); + skel = g_dbus_object_skeleton_new (name); + g_free (name); + + { + PvSourceOutput1 *iface; + + iface = pv_source_output1_skeleton_new (); + g_signal_connect (iface, "handle-acquire", (GCallback) handle_acquire, output); + g_signal_connect (iface, "handle-release", (GCallback) handle_release, output); + g_dbus_object_skeleton_add_interface (skel, G_DBUS_INTERFACE_SKELETON (iface)); + g_object_unref (iface); + } + + g_free (priv->object_path); + priv->object_path = pv_daemon_export_uniquely (daemon, skel); +} + +static void +output_unregister_object (PvSourceOutput *output) +{ + PvSourceOutputPrivate *priv = output->priv; + PvDaemon *daemon = priv->daemon; + + stop_transfer (output); + + pv_daemon_unexport (daemon, priv->object_path); +} + +static void +pv_source_output_finalize (GObject * object) +{ + PvSourceOutput *output = PV_SOURCE_OUTPUT (object); + PvSourceOutputPrivate *priv = output->priv; + + output_unregister_object (output); + g_object_unref (priv->daemon); + g_free (priv->object_path); + + G_OBJECT_CLASS (pv_source_output_parent_class)->finalize (object); +} +static void +pv_source_output_constructed (GObject * object) +{ + PvSourceOutput *output = PV_SOURCE_OUTPUT (object); + PvSourceOutputPrivate *priv = output->priv; + + output_register_object (output, priv->object_path); + + G_OBJECT_CLASS (pv_source_output_parent_class)->constructed (object); +} + +static void +pv_source_output_class_init (PvSourceOutputClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PvSourceOutputPrivate)); + + gobject_class->finalize = pv_source_output_finalize; + gobject_class->set_property = pv_source_output_set_property; + gobject_class->get_property = pv_source_output_get_property; + gobject_class->constructed = pv_source_output_constructed; + + g_object_class_install_property (gobject_class, + PROP_DAEMON, + g_param_spec_object ("daemon", + "Daemon", + "The daemon", + PV_TYPE_DAEMON, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "The object path", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_SOCKET, + g_param_spec_object ("socket", + "Socket", + "The socket with data", + G_TYPE_SOCKET, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +static void +pv_source_output_init (PvSourceOutput * output) +{ + output->priv = PV_SOURCE_OUTPUT_GET_PRIVATE (output); +} + +const gchar * +pv_source_output_get_object_path (PvSourceOutput *output) +{ + PvSourceOutputPrivate *priv; + + g_return_val_if_fail (PV_IS_SOURCE_OUTPUT (output), NULL); + priv = output->priv; + + return priv->object_path; +} + diff --git a/src/server/pv-source-output.h b/src/server/pv-source-output.h new file mode 100644 index 000000000..f40b51feb --- /dev/null +++ b/src/server/pv-source-output.h @@ -0,0 +1,68 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PV_SOURCE_OUTPUT_H__ +#define __PV_SOURCE_OUTPUT_H__ + +#include + +G_BEGIN_DECLS + +#define PV_TYPE_SOURCE_OUTPUT (pv_source_output_get_type ()) +#define PV_IS_SOURCE_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_SOURCE_OUTPUT)) +#define PV_IS_SOURCE_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_SOURCE_OUTPUT)) +#define PV_SOURCE_OUTPUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_SOURCE_OUTPUT, PvSourceOutputClass)) +#define PV_SOURCE_OUTPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_SOURCE_OUTPUT, PvSourceOutput)) +#define PV_SOURCE_OUTPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_SOURCE_OUTPUT, PvSourceOutputClass)) +#define PV_SOURCE_OUTPUT_CAST(obj) ((PvSourceOutput*)(obj)) +#define PV_SOURCE_OUTPUT_CLASS_CAST(klass) ((PvSourceOutputClass*)(klass)) + +typedef struct _PvSourceOutput PvSourceOutput; +typedef struct _PvSourceOutputClass PvSourceOutputClass; +typedef struct _PvSourceOutputPrivate PvSourceOutputPrivate; + +/** + * PvSourceOutput: + * + * Pulsevideo source output object class. + */ +struct _PvSourceOutput { + GObject object; + + PvSourceOutputPrivate *priv; +}; + +/** + * PvSourceOutputClass: + * + * Pulsevideo source output object class. + */ +struct _PvSourceOutputClass { + GObjectClass parent_class; +}; + +/* normal GObject stuff */ +GType pv_source_output_get_type (void); + +const gchar * pv_source_output_get_object_path (PvSourceOutput *output); + +G_END_DECLS + +#endif /* __PV_SOURCE_OUTPUT_H__ */ + diff --git a/src/server/pv-source.c b/src/server/pv-source.c new file mode 100644 index 000000000..fe7e2801a --- /dev/null +++ b/src/server/pv-source.c @@ -0,0 +1,288 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include "server/pv-source.h" +#include "server/pv-daemon.h" + +#include "dbus/org-pulsevideo.h" + + +#define PV_SOURCE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PV_TYPE_SOURCE, PvSourcePrivate)) + +struct _PvSourcePrivate +{ + PvDaemon *daemon; + gchar *object_path; +}; + +G_DEFINE_ABSTRACT_TYPE (PvSource, pv_source, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_DAEMON, + PROP_OBJECT_PATH +}; + +static void +pv_source_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PvSource *source = PV_SOURCE (_object); + PvSourcePrivate *priv = source->priv; + + switch (prop_id) { + case PROP_DAEMON: + g_value_set_object (value, priv->daemon); + break; + + case PROP_OBJECT_PATH: + g_value_set_string (value, priv->object_path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec); + break; + } +} + +static void +pv_source_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PvSource *source = PV_SOURCE (_object); + PvSourcePrivate *priv = source->priv; + + switch (prop_id) { + case PROP_DAEMON: + priv->daemon = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (source, prop_id, pspec); + break; + } +} +static void +source_register_object (PvSource *source) +{ + PvSourcePrivate *priv = source->priv; + PvDaemon *daemon = priv->daemon; + GDBusObjectSkeleton *skel; + + skel = g_dbus_object_skeleton_new (PV_DBUS_OBJECT_SOURCE); + { + PvSource1 *iface; + + iface = pv_source1_skeleton_new (); + g_dbus_object_skeleton_add_interface (skel, G_DBUS_INTERFACE_SKELETON (iface)); + g_object_unref (iface); + } + g_free (priv->object_path); + priv->object_path = pv_daemon_export_uniquely (daemon, skel); +} + +static void +source_unregister_object (PvSource *source) +{ + PvSourcePrivate *priv = source->priv; + PvDaemon *daemon = priv->daemon; + + pv_daemon_unexport (daemon, priv->object_path); +} + +static void +pv_source_finalize (GObject * object) +{ + PvSource *source = PV_SOURCE (object); + PvSourcePrivate *priv = source->priv; + + source_unregister_object (source); + g_object_unref (priv->daemon); + g_free (priv->object_path); + + G_OBJECT_CLASS (pv_source_parent_class)->finalize (object); +} + +static void +pv_source_constructed (GObject * object) +{ + PvSource *source = PV_SOURCE (object); + + source_register_object (source); + + G_OBJECT_CLASS (pv_source_parent_class)->constructed (object); +} + +static PvSourceOutput * +default_create_source_output (PvSource *source, GVariant *props, const gchar *prefix) +{ + PvSourcePrivate *priv = source->priv; + + return g_object_new (PV_TYPE_SOURCE_OUTPUT, "daemon", priv->daemon, "object-path", prefix, NULL); +} + +static gboolean +default_release_source_output (PvSource *source, PvSourceOutput *output) +{ + g_object_unref (output); + return TRUE; +} + + +static void +pv_source_class_init (PvSourceClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PvSourcePrivate)); + + gobject_class->finalize = pv_source_finalize; + gobject_class->set_property = pv_source_set_property; + gobject_class->get_property = pv_source_get_property; + gobject_class->constructed = pv_source_constructed; + + g_object_class_install_property (gobject_class, + PROP_DAEMON, + g_param_spec_object ("daemon", + "Daemon", + "The daemon", + PV_TYPE_DAEMON, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_OBJECT_PATH, + g_param_spec_string ("object-path", + "Object Path", + "The object path", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + klass->create_source_output = default_create_source_output; + klass->release_source_output = default_release_source_output; +} + +static void +pv_source_init (PvSource * source) +{ + source->priv = PV_SOURCE_GET_PRIVATE (source); +} + +GVariant * +pv_source_get_capabilities (PvSource *source, GVariant *props) +{ + PvSourceClass *klass; + GVariant *res; + + g_return_val_if_fail (PV_IS_SOURCE (source), NULL); + + klass = PV_SOURCE_GET_CLASS (source); + + if (klass->get_capabilities) + res = klass->get_capabilities (source, props); + else + res = NULL; + + return res; +} + +gboolean +pv_source_suspend (PvSource *source) +{ + PvSourceClass *klass; + gboolean res; + + g_return_val_if_fail (PV_IS_SOURCE (source), FALSE); + + klass = PV_SOURCE_GET_CLASS (source); + + if (klass->suspend) + res = klass->suspend (source); + else + res = FALSE; + + return res; +} + +gboolean +pv_source_resume (PvSource *source) +{ + PvSourceClass *klass; + gboolean res; + + g_return_val_if_fail (PV_IS_SOURCE (source), FALSE); + + klass = PV_SOURCE_GET_CLASS (source); + + if (klass->resume) + res = klass->resume (source); + else + res = FALSE; + + return res; +} + + +PvSourceOutput * +pv_source_create_source_output (PvSource *source, GVariant *props, const gchar *prefix) +{ + PvSourceClass *klass; + PvSourceOutput *res; + + g_return_val_if_fail (PV_IS_SOURCE (source), NULL); + + klass = PV_SOURCE_GET_CLASS (source); + + if (klass->create_source_output) + res = klass->create_source_output (source, props, prefix); + else + res = NULL; + + return res; +} + +gboolean +pv_source_release_source_output (PvSource *source, PvSourceOutput *output) +{ + PvSourceClass *klass; + gboolean res; + + g_return_val_if_fail (PV_IS_SOURCE (source), FALSE); + g_return_val_if_fail (PV_IS_SOURCE_OUTPUT (output), FALSE); + + klass = PV_SOURCE_GET_CLASS (source); + + if (klass->release_source_output) + res = klass->release_source_output (source, output); + else + res = FALSE; + + return res; +} + diff --git a/src/server/pv-source.h b/src/server/pv-source.h new file mode 100644 index 000000000..fc61a0016 --- /dev/null +++ b/src/server/pv-source.h @@ -0,0 +1,87 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PV_SOURCE_H__ +#define __PV_SOURCE_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _PvSource PvSource; +typedef struct _PvSourceClass PvSourceClass; +typedef struct _PvSourcePrivate PvSourcePrivate; + +#include "server/pv-daemon.h" +#include "server/pv-source-output.h" + +#define PV_TYPE_SOURCE (pv_source_get_type ()) +#define PV_IS_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PV_TYPE_SOURCE)) +#define PV_IS_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PV_TYPE_SOURCE)) +#define PV_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PV_TYPE_SOURCE, PvSourceClass)) +#define PV_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PV_TYPE_SOURCE, PvSource)) +#define PV_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PV_TYPE_SOURCE, PvSourceClass)) +#define PV_SOURCE_CAST(obj) ((PvSource*)(obj)) +#define PV_SOURCE_CLASS_CAST(klass) ((PvSourceClass*)(klass)) + +/** + * PvSource: + * + * Pulsevideo source object class. + */ +struct _PvSource { + GObject object; + + PvSourcePrivate *priv; +}; + +/** + * PvSourceClass: + * + * Pulsevideo source object class. + */ +struct _PvSourceClass { + GObjectClass parent_class; + + GVariant * (*get_capabilities) (PvSource *source, GVariant *props); + + gboolean (*suspend) (PvSource *source); + gboolean (*resume) (PvSource *source); + + PvSourceOutput * (*create_source_output) (PvSource *source, GVariant *props, const gchar *prefix); + gboolean (*release_source_output) (PvSource *source, PvSourceOutput *output); +}; + +/* normal GObject stuff */ +GType pv_source_get_type (void); + +PvSource * pv_source_new (PvDaemon *daemon, const gchar *prefix); + +GVariant * pv_source_get_capabilities (PvSource *source, GVariant *props); + +gboolean pv_source_suspend (PvSource *source); +gboolean pv_source_resume (PvSource *source); + +PvSourceOutput * pv_source_create_source_output (PvSource *source, GVariant *props, const gchar *prefix); +gboolean pv_source_release_source_output (PvSource *source, PvSourceOutput *output); + +G_END_DECLS + +#endif /* __PV_SOURCE_H__ */ + diff --git a/src/tests/test-client.c b/src/tests/test-client.c new file mode 100644 index 000000000..7e30e3f84 --- /dev/null +++ b/src/tests/test-client.c @@ -0,0 +1,128 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include +#include + +#define CAPS "video/x-raw, format=(string)YUY2, width=(int)320, height=(int)240, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction)30/1" + +static GMainLoop *loop; + +static void +on_socket_notify (GObject *gobject, + GParamSpec *pspec, + gpointer user_data) +{ + GSocket *socket; + GstElement *pipeline, *src; + + g_object_get (gobject, "socket", &socket, NULL); + g_print ("got socket %p\n", socket); + + pipeline = gst_parse_launch ("socketsrc name=src ! pvfddepay ! "CAPS" ! videoconvert ! xvimagesink", NULL); + src = gst_bin_get_by_name (GST_BIN (pipeline), "src"); + + g_object_set (src, "socket", socket, NULL); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); +} + +static void +on_stream_notify (GObject *gobject, + GParamSpec *pspec, + gpointer user_data) +{ + PvStreamState state; + PvStream *s = user_data; + + g_object_get (gobject, "state", &state, NULL); + g_print ("got stream state %d\n", state); + + switch (state) { + case PV_STREAM_STATE_ERROR: + g_main_loop_quit (loop); + break; + case PV_STREAM_STATE_READY: + pv_stream_start (s, PV_STREAM_MODE_SOCKET); + break; + case PV_STREAM_STATE_STREAMING: + { + PvBufferInfo info; + + pv_stream_capture_buffer (s, &info); + break; + } + default: + break; + } +} + + +static void +on_state_notify (GObject *gobject, + GParamSpec *pspec, + gpointer user_data) +{ + PvContextState state; + PvContext *c = user_data; + + g_object_get (gobject, "state", &state, NULL); + g_print ("got context state %d\n", state); + + switch (state) { + case PV_CONTEXT_STATE_ERROR: + g_main_loop_quit (loop); + break; + case PV_CONTEXT_STATE_READY: + { + PvStream *stream; + + stream = pv_stream_new (c, "test"); + g_signal_connect (stream, "notify::state", (GCallback) on_stream_notify, stream); + g_signal_connect (stream, "notify::socket", (GCallback) on_socket_notify, stream); + pv_stream_connect_capture (stream, NULL, 0); + break; + } + default: + break; + } +} + +gint +main (gint argc, gchar *argv[]) +{ + PvContext *c; + + gst_init (&argc, &argv); + + loop = g_main_loop_new (NULL, FALSE); + + c = pv_context_new ("test-client", NULL); + g_signal_connect (c, "notify::state", (GCallback) on_state_notify, c); + pv_context_connect(c, PV_CONTEXT_FLAGS_NONE); + + g_main_loop_run (loop); + + g_object_unref (c); + + return 0; +} diff --git a/src/tests/test-subscribe.c b/src/tests/test-subscribe.c new file mode 100644 index 000000000..739394f08 --- /dev/null +++ b/src/tests/test-subscribe.c @@ -0,0 +1,76 @@ +/* Pulsevideo + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +static GMainLoop *loop; + +static void +subscription_cb (PvContext *context, PvSubscriptionEvent type, PvSubscriptionFlags flags, + const gchar *object_path, gpointer user_data) +{ + g_print ("got event %d %d %s\n", type, flags, object_path); +} + +static void +on_state_notify (GObject *gobject, + GParamSpec *pspec, + gpointer user_data) +{ + PvContextState state; + PvContext *c = user_data; + + g_object_get (gobject, "state", &state, NULL); + g_print ("got state %d\n", state); + + switch (state) { + case PV_CONTEXT_STATE_ERROR: + g_main_loop_quit (loop); + break; + case PV_CONTEXT_STATE_READY: + g_object_set (c, "subscription-mask", PV_SUBSCRIPTION_FLAGS_ALL, NULL); + g_signal_connect (c, "subscription-event", (GCallback) subscription_cb, NULL); + break; + default: + break; + } +} + +gint +main (gint argc, gchar *argv[]) +{ + PvContext *c; + + gst_init (&argc, &argv); + + loop = g_main_loop_new (NULL, FALSE); + + c = pv_context_new ("test-client", NULL); + g_signal_connect (c, "notify::state", (GCallback) on_state_notify, c); + pv_context_connect(c, PV_CONTEXT_FLAGS_NOFAIL); + + g_main_loop_run (loop); + + g_object_unref (c); + + return 0; +}