summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Raghavan <arun.raghavan@collabora.co.uk>2010-06-19 23:37:44 +0530
committerArun Raghavan <arun.raghavan@collabora.co.uk>2010-06-20 20:20:15 +0530
commit7dbc61382a2579a34f6dda09a119d896cd9fe093 (patch)
treeba78be4fd56083218b0e325ca07e01772e67a115
parent5dc6021842e6d41fb9f87fcfe225ed8c390b4268 (diff)
downloadgupnp-dlna-7dbc61382a2579a34f6dda09a119d896cd9fe093.tar.gz
Initial commit
-rw-r--r--AUTHORS1
-rw-r--r--COPYING482
-rw-r--r--ChangeLog4
-rw-r--r--INSTALL236
-rw-r--r--Makefile.am23
-rw-r--r--NEWS4
-rw-r--r--README11
-rw-r--r--README.gst-convenience25
-rwxr-xr-xautogen.sh5
-rw-r--r--configure.ac52
-rw-r--r--data/Makefile.am21
-rw-r--r--data/aac.xml115
-rw-r--r--data/ac3.xml23
-rw-r--r--data/amr.xml55
-rw-r--r--data/avc.xml213
-rw-r--r--data/common.xml206
-rw-r--r--data/dlna-profiles.rng159
-rw-r--r--data/jpeg.xml63
-rw-r--r--data/lpcm.xml66
-rw-r--r--data/mp3.xml54
-rw-r--r--data/mpeg-ts.xml347
-rw-r--r--data/mpeg1.xml86
-rw-r--r--data/mpeg4.xml500
-rw-r--r--data/png.xml53
-rw-r--r--data/wma.xml74
-rw-r--r--doc/dlna-profiles-example.xml217
-rw-r--r--gst-convenience/Makefile.am16
-rw-r--r--gst-convenience/gst-libs/Makefile.am1
-rw-r--r--gst-convenience/gst-libs/gst/Makefile.am5
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/Makefile.am87
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-enumtypes.c48
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-enumtypes.h21
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.c87
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.h20
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.list1
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-types.c446
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/gstdiscoverer.c1251
-rw-r--r--gst-convenience/gst-libs/gst/discoverer/gstdiscoverer.h331
-rw-r--r--gst-convenience/gst-libs/gst/gettext.h69
-rw-r--r--gst-convenience/gst-libs/gst/gst-i18n-plugin.h37
-rw-r--r--gst-convenience/gst-libs/gst/profile/Makefile.am82
-rw-r--r--gst-convenience/gst-libs/gst/profile/gstprofile.c553
-rw-r--r--gst-convenience/gst-libs/gst/profile/gstprofile.h234
-rw-r--r--gst-convenience/gst-libs/gst/profile/profile-enumtypes.c29
-rw-r--r--gst-convenience/gst-libs/gst/profile/profile-enumtypes.h19
-rw-r--r--gst-convenience/gstreamer-discoverer-gupnp-dlna-0.10.deps2
-rw-r--r--gst-convenience/gstreamer-discoverer-gupnp-dlna-0.10.vapi106
-rw-r--r--gst-convenience/gstreamer-profile-gupnp-dlna-0.10.deps1
-rw-r--r--gst-convenience/gstreamer-profile-gupnp-dlna-0.10.vapi78
-rw-r--r--gupnp-dlna-1.0-uninstalled.pc.in11
-rw-r--r--gupnp-dlna-1.0.pc.in11
-rw-r--r--libgupnp-dlna/Makefile.am56
-rw-r--r--libgupnp-dlna/gupnp-dlna-discoverer.c140
-rw-r--r--libgupnp-dlna/gupnp-dlna-discoverer.h97
-rw-r--r--libgupnp-dlna/gupnp-dlna-information.c205
-rw-r--r--libgupnp-dlna/gupnp-dlna-information.h73
-rw-r--r--libgupnp-dlna/gupnp-dlna-load.c748
-rw-r--r--libgupnp-dlna/gupnp-dlna-load.h43
-rw-r--r--libgupnp-dlna/gupnp-dlna-marshal.list2
-rw-r--r--libgupnp-dlna/gupnp-dlna-profile.c70
-rw-r--r--libgupnp-dlna/gupnp-dlna-profile.h73
-rw-r--r--libgupnp-dlna/gupnp-dlna-profiles.c442
-rw-r--r--tests/Makefile.am5
-rw-r--r--tests/dlna-profile-parser.c111
-rw-r--r--tests/dlna/xml/dlna-profile-illegal-base.xml10
-rw-r--r--tests/dlna/xml/dlna-profile-illegal-parent.xml11
-rw-r--r--tests/dlna/xml/dlna-profile-missing-attribute.xml8
-rw-r--r--tests/dlna/xml/dlna-profile-parent-order.xml30
-rw-r--r--tests/dlna/xml/dlna-profiles-empty.xml6
-rw-r--r--tests/dlna/xml/dlna-profiles-restrictions-order.xml20
-rw-r--r--tests/dlna/xml/field-no-value.xml12
-rw-r--r--tests/dlna/xml/range-invalid.xml13
-rw-r--r--tests/dlna/xml/restriction-duplicate.xml12
-rw-r--r--tests/dlna/xml/restriction-forward-reference.xml13
-rw-r--r--tests/dlna/xml/restrictions-illegal-parent.xml11
-rw-r--r--tests/dlna/xml/value-empty.xml13
-rw-r--r--tests/dlna/xml/value-invalid.xml13
77 files changed, 8878 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..120697a
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Arun Raghavan <arun.raghavan@collabora.co.uk>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..bf50f20
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,482 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ 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 Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, 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 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 a program 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.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, 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 companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ 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, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+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 compile 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) 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.
+
+ c) 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.
+
+ d) 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 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.
+
+ 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 to
+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 Library 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..9ff8688
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,4 @@
+2010-06-19 Arun Raghavan <arun.raghavan@collabora.co.uk>
+
+ * *: The ChangeLog is auto-generated when releasing. If you are seeing
+ this, use 'git log' for a detailed list of changes.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..5c42247
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,23 @@
+SUBDIRS = gst-convenience libgupnp-dlna tests data
+
+pkgconfig_DATA = gupnp-dlna-1.0.pc
+pkgconfigdir = $(libdir)/pkgconfig
+
+#DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
+
+# Extra clean files so that maintainer-clean removes *everything*
+MAINTAINERCLEANFILES = aclocal.m4 compile config.guess config.sub configure depcomp install-sh ltmain.sh Makefile.in missing config.h.in
+
+dist-hook:
+ @if test -d "$(srcdir)/.git"; \
+ then \
+ echo Creating ChangeLog && \
+ ( cd "$(top_srcdir)" && \
+ echo '# Generated by Makefile. Do not edit.'; echo; \
+ $(top_srcdir)/missing --run git log --stat ) > ChangeLog.tmp \
+ && mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \
+ || ( rm -f ChangeLog.tmp ; \
+ echo Failed to generate ChangeLog >&2 ); \
+ else \
+ echo A git clone is required to generate a ChangeLog >&2; \
+ fi
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..4e2e685
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,4 @@
+0.1
+===
+
+Initial release.
diff --git a/README b/README
new file mode 100644
index 0000000..d877eb1
--- /dev/null
+++ b/README
@@ -0,0 +1,11 @@
+GUPnP DLNA
+==========
+
+GUPnP is an object-oriented open source framework for creating UPnP devices and
+control points, written in C using GObject and libsoup. The GUPnP API is
+intended to be easy to use, efficient and flexible.
+
+GUPnP DLNA is a small utility library that aims to ease the DLNA-related tasks
+such as media profile guessing, transcoding to a given profile, etc.
+
+GUPnP DLNA is free software released under the GNU LGPL.
diff --git a/README.gst-convenience b/README.gst-convenience
new file mode 100644
index 0000000..64a9b6d
--- /dev/null
+++ b/README.gst-convenience
@@ -0,0 +1,25 @@
+Until gstdiscoverer and gstprofile are deemed stable enough to move to
+gst-plugins-base, we are packaging a local copy. See the gupnp mailing list
+archives or drop by #gupnp for more details.
+
+This is a quick HOWTO to help keep the in-tree copy updated.
+
+1. The dependency checks are simplified and copied to configure.ac. These need
+ to be kept up-to-date.
+
+2. gst-convenience/gst-libs is directly synced from the gst-convenience tree,
+ with the following changes:
+
+ a. The marshal and enumtypes generated files are copied to avoid needing
+ the gst common make includes
+
+ b. The libraries are renamed with a -gupnp-dlna suffix to avoid conflicts
+ with gst-convenience if installed (=> do not overwrite Makefile.am in the
+ library directories - use Meld or something similar to diff and update)
+
+ c. gir generation is disabled - it's not needed yet
+
+ d. Include paths are changed to be relative to the gupnp-dlna path
+
+ e. Generated vapi files are copied (renamed with a gupnp-dlna suffix) for
+ sanity preservation purposes
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..cb577b5
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,5 @@
+#! /bin/sh
+gtkdocize --flavour no-tmpl || exit 1
+ACLOCAL="${ACLOCAL-aclocal} $ACLOCAL_FLAGS" autoreconf -v --install || exit 1
+glib-gettextize --force --copy || exit 1
+./configure --enable-debug "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..7a350e0
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,52 @@
+AC_PREREQ(2.53)
+AC_INIT(gupnp-dlna, 0.1.0, http://www.gupnp.org/)
+AM_INIT_AUTOMAKE()
+AC_CONFIG_SRCDIR(libgupnp-dlna/gupnp-dlna-profile.h)
+AM_CONFIG_HEADER(config.h)
+
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
+
+AC_ISC_POSIX
+AC_PROG_CC
+AC_STDC_HEADERS
+AC_LIBTOOL_WIN32_DLL
+AC_PROG_LIBTOOL
+
+PKG_CHECK_MODULES(LIBXML, libxml-2.0 >= 2.5.0)
+
+GST_MAJORMINOR=0.10
+GST_REQ=0.10.25
+GSTVID_REQ=0.10.25
+
+PKG_CHECK_MODULES(GST, gstreamer-$GST_MAJORMINOR >= $GST_REQ)
+PKG_CHECK_MODULES(GST_BASE, gstreamer-base-$GST_MAJORMINOR >= $GST_REQ)
+PKG_CHECK_MODULES(GST_VIDEO, gstreamer-video-$GST_MAJORMINOR >= $GSTVID_REQ)
+AC_SUBST(GST_MAJORMINOR)
+
+# glib-genmarshal
+GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
+AC_SUBST(GLIB_GENMARSHAL)
+
+# Debugging
+AC_ARG_ENABLE(debug,
+ [ --enable-debug enable debugging],,
+ enable_debug=no)
+if test "x$enable_debug" = "xyes"; then
+ CFLAGS="$CFLAGS -g -Wall"
+fi
+
+GTK_DOC_CHECK([1.0])
+
+AC_OUTPUT([
+Makefile
+gst-convenience/Makefile
+gst-convenience/gst-libs/Makefile
+gst-convenience/gst-libs/gst/Makefile
+gst-convenience/gst-libs/gst/discoverer/Makefile
+gst-convenience/gst-libs/gst/profile/Makefile
+libgupnp-dlna/Makefile
+tests/Makefile
+data/Makefile
+gupnp-dlna-1.0.pc
+gupnp-dlna-1.0-uninstalled.pc
+])
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644
index 0000000..550d094
--- /dev/null
+++ b/data/Makefile.am
@@ -0,0 +1,21 @@
+shareddir = $(datadir)/gupnp-dlna
+
+dlnaschemas = dlna-profiles.rng
+dlnaprofiles = mp3.xml \
+ ac3.xml \
+ lpcm.xml \
+ aac.xml \
+ wma.xml \
+ amr.xml \
+ common.xml \
+ mpeg1.xml \
+ avc.xml \
+ mpeg-ts.xml \
+ mpeg4.xml \
+ jpeg.xml \
+ png.xml
+
+dlnadir = $(shareddir)/dlna-profiles
+dlna_DATA = $(dlnaschemas) $(dlnaprofiles)
+
+EXTRA_DIST = $(dlnaschemas) $(dlnaprofiles)
diff --git a/data/aac.xml b/data/aac.xml
new file mode 100644
index 0000000..01defb0
--- /dev/null
+++ b/data/aac.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+
+<!--
+ Notes:
+
+ * There are 2 <dlna-profile>s for each ISO profile because we need to support
+ both MP4 and 3GPP containers, but a <dlna-profile> can have only one
+ container
+-->
+
+<dlna-profiles>
+ <include ref="common.xml" />
+
+ <restrictions>
+ <restriction id="AAC-ADTS" type="audio">
+ <field name="stream-type" type="string">
+ <value>adts</value>
+ </field>
+ </restriction>
+
+ <restriction id="AAC-320" type="audio">
+ <parent name="AAC" />
+
+ <field name="bitrate" type="int">
+ <range min="0" max="320000" />
+ </field>
+ </restriction>
+
+ <restriction id="AAC-576" type="audio">
+ <parent name="AAC" />
+
+ <field name="bitrate" type="int">
+ <range min="0" max="576000" />
+ </field>
+ </restriction>
+
+ <restriction id="AAC-MULT5" type="audio">
+ <parent name="AAC" />
+
+ <field name="channels" type="int">
+ <!-- Technically the profile supports upto 5.1, not 6 -->
+ <range min="1" max="6" />
+ </field>
+ <field name="bitrate" type="int">
+ <range min="0" max="1440000" />
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="AAC_ADTS_320" mime="audio/mpeg">
+ <restriction type="audio">
+ <parent name="AAC-ADTS" />
+ <parent name="AAC-320" />
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="AAC_ISO_320" mime="audio/mpeg">
+ <parent name="MP4" />
+ <parent name="AAC-320" />
+ </dlna-profile>
+
+ <dlna-profile name="AAC_ISO_320" mime="audio/mpeg">
+ <parent name="QT" />
+ <parent name="AAC-320" />
+ </dlna-profile>
+
+ <dlna-profile name="AAC_ISO_320" mime="audio/mpeg">
+ <parent name="3GP" />
+ <parent name="AAC-320" />
+ </dlna-profile>
+
+ <dlna-profile name="AAC_ADTS" mime="audio/mpeg">
+ <restriction type="audio">
+ <parent name="AAC-ADTS" />
+ <parent name="AAC-576" />
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="AAC_ISO" mime="audio/mpeg">
+ <parent name="MP4" />
+ <parent name="AAC-576" />
+ </dlna-profile>
+
+ <dlna-profile name="AAC_ISO" mime="audio/mpeg">
+ <parent name="QT" />
+ <parent name="AAC-576" />
+ </dlna-profile>
+
+ <dlna-profile name="AAC_ISO" mime="audio/mpeg">
+ <parent name="3GP" />
+ <parent name="AAC-576" />
+ </dlna-profile>
+
+ <dlna-profile name="AAC_MULT5_ADTS" mime="audio/mpeg">
+ <restriction type="audio">
+ <parent name="AAC-ADTS" />
+ <parent name="AAC-MULT5" />
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="AAC_MULT5_ISO" mime="audio/mpeg">
+ <parent name="MP4" />
+ <parent name="AAC-MULT5" />
+ </dlna-profile>
+
+ <dlna-profile name="AAC_MULT5_ISO" mime="audio/mpeg">
+ <parent name="QT" />
+ <parent name="AAC-MULT5" />
+ </dlna-profile>
+
+ <dlna-profile name="AAC_MULT5_ISO" mime="audio/mpeg">
+ <parent name="3GP" />
+ <parent name="AAC-MULT5" />
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/ac3.xml b/data/ac3.xml
new file mode 100644
index 0000000..bbe3bba
--- /dev/null
+++ b/data/ac3.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <dlna-profile name="AC3" mime="audio/vnd.dolby.dd-raw">
+ <restriction type="audio">
+ <field name="name" type="string">
+ <value>audio/x-ac3</value>
+ </field>
+ <field name="rate" type="int">
+ <value>32000</value>
+ <value>44100</value>
+ <value>48000</value>
+ </field>
+ <field name="channels" type="int">
+ <!-- XXX: need to verify channel mapping -->
+ <range min="1" max="6"/>
+ </field>
+ <field name="bitrate" type="int">
+ <range min="64000" max="640000"/>
+ </field>
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/amr.xml b/data/amr.xml
new file mode 100644
index 0000000..5175c27
--- /dev/null
+++ b/data/amr.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <include ref="common.xml" />
+
+ <dlna-profile name="AMR_3GPP" mime="audio/mp4" id="AMR_3GPP">
+ <parent name="MP4" />
+
+ <restriction type="audio">
+ <field name="name" type="string">
+ <value>audio/AMR</value>
+ </field>
+ <!--
+ Sample rate, bitrate and number of channels - all values permitted by
+ the AMR spec are also permitted by the DLNA spec
+ -->
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="AMR_3GPP" mime="audio/mp4" base-profile="AMR_3GPP">
+ <parent name="QT" />
+ </dlna-profile>
+
+ <dlna-profile name="AMR_3GPP" mime="audio/3gpp" base-profile="AMR_3GPP">
+ <parent name="3GP" />
+ </dlna-profile>
+
+ <dlna-profile name="AMR_WBplus" mime="audio/mp4">
+ <restriction type="container">
+ <parent name="3GP" />
+
+ <field name="profile" type="string">
+ <value>basic</value>
+ <value>progressive-download</value>
+ </field>
+ </restriction>
+
+ <restriction type="audio">
+ <field name="name" type="string">
+ <value>audio/AMR-WB</value>
+ </field>
+ <field name="rate" type="int">
+ <value>8000</value>
+ <value>16000</value>
+ <value>24000</value>
+ <value>32000</value>
+ <value>48000</value>
+ </field>
+ <!--
+ Bitrate and number of channels - all values permitted by the AMR spec
+ are also permitted by the DLNA spec
+ -->
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/avc.xml b/data/avc.xml
new file mode 100644
index 0000000..36fec11
--- /dev/null
+++ b/data/avc.xml
@@ -0,0 +1,213 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <include ref="common.xml" />
+
+ <restrictions>
+ <restriction id="AVC_AAC_520" type="audio">
+ <parent name="AAC" />
+
+ <field name="maximum-bitrate" type="int">
+ <range min="0" max="128000" />
+ </field>
+ </restriction>
+
+ <restriction id="AVC_BL" type="video">
+ <field name="name" type="string">
+ <value>video/x-h264</value>
+ </field>
+ <field name="profile" type="string">
+ <!-- profile=baseline + constraint_set1_flag=1
+ == constrained-baseline -->
+ <value>constrained-baseline</value>
+ </field>
+ <field name="format" type="fourcc">
+ <value>I420</value>
+ </field>
+ </restriction>
+
+ <restriction id="AVC_BL_384" type="video">
+ <parent name="AVC_BL" />
+
+ <field name="level" type="string">
+ <value>1</value>
+ <value>1b</value>
+ <value>1.1</value>
+ <value>1.2</value>
+ </field>
+ <field name="bitrate" type="int">
+ <range min="0" max="384000" />
+ </field>
+ <field name="maximum-bitrate" type="int">
+ <range min="0" max="384000" />
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>1/1</value>
+ <value>12/21</value>
+ <value>10/11</value>
+ <value>16/11</value>
+ <value>40/33</value>
+ <value>44/33</value>
+ <value>64/33</value>
+ <value>160/99</value>
+ <value>18/11</value>
+ <value>15/11</value>
+ <value>24/11</value>
+ <value>60/33</value>
+ <value>20/11</value>
+ <value>32/11</value>
+ <value>80/33</value>
+ </field>
+ </restriction>
+
+ <restriction id="AVC_BL_CIF" type="video">
+ <parent name="CIF" />
+ <parent name="15fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_525SIF" type="video">
+ <parent name="525SIF" />
+ <field name="framerate" type="fraction">
+ <range min="0/1" max="18/1" />
+ </field>
+ </restriction>
+
+ <restriction id="AVC_BL_QVGA_4:3" type="video">
+ <parent name="QVGA_4:3" />
+ <field name="framerate" type="fraction">
+ <range min="0/1" max="20/1" />
+ </field>
+ </restriction>
+
+ <restriction id="AVC_BL_QVGA_16:9" type="video">
+ <parent name="QVGA_16:9" />
+ <field name="framerate" type="fraction">
+ <range min="0/1" max="26/1" />
+ </field>
+ </restriction>
+
+ <restriction id="AVC_BL_1/7_VGA_4:3" type="video">
+ <parent name="1/7_VGA_4:3" />
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_1/9_VGA_4:3" type="video">
+ <parent name="1/9_VGA_4:3" />
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_QCIF" type="video">
+ <parent name="QCIF" />
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_525QSIF" type="video">
+ <parent name="525QSIF" />
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_SQVGA_4:3" type="video">
+ <parent name="SQVGA_4:3" />
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_1/16_VGA_4:3" type="video">
+ <parent name="1/16_VGA_4:3" />
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_SQVGA_16:9" type="video">
+ <parent name="SQVGA_16:9" />
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_SQCIF" type="video">
+ <parent name="SQCIF" />
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="AVC_BL_1/7_VGA_16:9" type="video">
+ <parent name="1/7_VGA_16:9" />
+ <parent name="30fps" />
+ </restriction>
+
+ </restrictions>
+
+ <dlna-profile id="AVC_MP4_BL_CIF15_AAC_520" name="AVC_MP4_BL_CIF15_AAC_520" mime="video/mp4">
+ <!-- FIXME: we have no way of matching the system bitrate -->
+ <parent name="MP4" />
+
+ <parent name="AVC_AAC_520" />
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_CIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_525SIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_QVGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_QVGA_16:9" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_1/7_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_1/9_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_QCIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_525QSIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_SQVGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_1/16_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_SQVGA_16:9" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_SQCIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="AVC_BL_384" />
+ <parent name="AVC_BL_1/7_VGA_16:9" />
+ </restriction>
+ </dlna-profile>
+
+ <!-- Apply changes to this to remaining <dlna-profiles> as well -->
+ <dlna-profile base-profile="AVC_MP4_BL_CIF15_AAC_520" name="AVC_MP4_BL_CIF15_AAC_520" mime="video/mp4">
+ <!-- FIXME: we have no way of matching the system bitrate -->
+ <parent name="QT" />
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/common.xml b/data/common.xml
new file mode 100644
index 0000000..a0ae71d
--- /dev/null
+++ b/data/common.xml
@@ -0,0 +1,206 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <restrictions>
+ <!-- Common containers -->
+ <restriction id="MP4" type="container">
+ <field name="name" type="string">
+ <value>audio/x-m4a</value>
+ </field>
+ </restriction>
+
+ <restriction id="QT" type="container">
+ <field name="name" type="string">
+ <value>video/quicktime</value>
+ </field>
+ </restriction>
+
+ <restriction id="3GP" type="container">
+ <field name="name" type="string">
+ <value>application/x-3gp</value>
+ </field>
+ </restriction>
+
+ <!-- Audio restrictions -->
+ <restriction id="AAC" type="audio">
+ <field name="name" type="string">
+ <value>audio/mpeg</value>
+ </field>
+ <field name="mpegversion" type="int">
+ <value>2</value>
+ <value>4</value>
+ </field>
+ <field name="profile" type="string">
+ <value>lc</value>
+ </field>
+ <!-- TODO: not exported in Gst yet
+ <field name="level" type="int">
+ <range min="1" max="2" />
+ </field>
+ -->
+ <field name="channels" type="int">
+ <range min="1" max="2" />
+ </field>
+ <field name="rate" type="int">
+ <value>8000</value>
+ <value>11025</value>
+ <value>12000</value>
+ <value>16000</value>
+ <value>22050</value>
+ <value>24000</value>
+ <value>32000</value>
+ <value>44100</value>
+ <value>48000</value>
+ </field>
+ </restriction>
+
+ <!-- Video resolutions -->
+ <restriction id="CIF" type="video">
+ <field name="width" type="int">
+ <value>352</value>
+ </field>
+ <field name="height" type="int">
+ <value>288</value>
+ </field>
+ </restriction>
+
+ <restriction id="525SIF" type="video">
+ <field name="width" type="int">
+ <value>352</value>
+ </field>
+ <field name="height" type="int">
+ <value>240</value>
+ </field>
+ </restriction>
+
+ <restriction id="QVGA_4:3" type="video">
+ <field name="width" type="int">
+ <value>320</value>
+ </field>
+ <field name="height" type="int">
+ <value>240</value>
+ </field>
+ </restriction>
+
+ <restriction id="QVGA_16:9" type="video">
+ <field name="width" type="int">
+ <value>320</value>
+ </field>
+ <field name="height" type="int">
+ <value>180</value>
+ </field>
+ </restriction>
+
+ <restriction id="1/7_VGA_4:3" type="video">
+ <field name="width" type="int">
+ <value>240</value>
+ </field>
+ <field name="height" type="int">
+ <value>180</value>
+ </field>
+ </restriction>
+
+ <restriction id="1/9_VGA_4:3" type="video">
+ <field name="width" type="int">
+ <value>208</value>
+ </field>
+ <field name="height" type="int">
+ <value>160</value>
+ </field>
+ </restriction>
+
+ <restriction id="QCIF" type="video">
+ <field name="width" type="int">
+ <value>176</value>
+ </field>
+ <field name="height" type="int">
+ <value>144</value>
+ </field>
+ </restriction>
+
+ <restriction id="525QSIF" type="video">
+ <field name="width" type="int">
+ <value>176</value>
+ </field>
+ <field name="height" type="int">
+ <value>120</value>
+ </field>
+ </restriction>
+
+ <restriction id="SQVGA_4:3" type="video">
+ <field name="width" type="int">
+ <value>160</value>
+ </field>
+ <field name="height" type="int">
+ <value>120</value>
+ </field>
+ </restriction>
+
+ <restriction id="1/16_VGA_4:3" type="video">
+ <field name="width" type="int">
+ <value>160</value>
+ </field>
+ <field name="height" type="int">
+ <value>112</value>
+ </field>
+ </restriction>
+
+ <restriction id="SQVGA_16:9" type="video">
+ <field name="width" type="int">
+ <value>160</value>
+ </field>
+ <field name="height" type="int">
+ <value>90</value>
+ </field>
+ </restriction>
+
+ <restriction id="SQCIF" type="video">
+ <field name="width" type="int">
+ <value>128</value>
+ </field>
+ <field name="height" type="int">
+ <value>96</value>
+ </field>
+ </restriction>
+
+ <restriction id="1/7_VGA_16:9" type="video">
+ <field name="width" type="int">
+ <value>240</value>
+ </field>
+ <field name="height" type="int">
+ <value>135</value>
+ </field>
+ </restriction>
+
+ <restriction id="VGA" type="video">
+ <field name="width" type="int">
+ <value>640</value>
+ </field>
+ <field name="height" type="int">
+ <value>480</value>
+ </field>
+ </restriction>
+
+ <restriction id="VGA_16:9" type="video">
+ <field name="width" type="int">
+ <value>640</value>
+ </field>
+ <field name="height" type="int">
+ <value>360</value>
+ </field>
+ </restriction>
+
+ <!-- Misc. video restrictions -->
+ <restriction id="15fps" type="video">
+ <field name="framerate" type="fraction">
+ <range min="0/1" max="15/1" />
+ </field>
+ </restriction>
+
+ <restriction id="30fps" type="video">
+ <field name="framerate" type="fraction">
+ <range min="0/1" max="30/1" />
+ </field>
+ </restriction>
+ </restrictions>
+</dlna-profiles>
diff --git a/data/dlna-profiles.rng b/data/dlna-profiles.rng
new file mode 100644
index 0000000..def37bf
--- /dev/null
+++ b/data/dlna-profiles.rng
@@ -0,0 +1,159 @@
+<?xml version="1.0"?>
+
+<!--
+dlna-profiles
+`- zero or more dlna-profile
+`- zero or more restrictions
+
+dlna-profile (name and type mime)
+`- zero or one parent|restriction type="container"
+`- one or more parent|restriction type!="container"
+
+parent (name)
+
+restrictions
+`- zero or more restriction
+
+restriction (type=video|audio|container)
+`- zero-or-more fields
+`- id if in restrictions
+
+field (name and type=string|int|fourcc|fraction|float|boolean
+`- value of appropriate type
+-->
+
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <define name="include">
+ <element name="include">
+ <attribute name="ref">
+ <text />
+ </attribute>
+ </element>
+ </define>
+
+ <define name="field">
+ <element name="field">
+ <attribute name="name">
+ <text />
+ </attribute>
+
+ <attribute name="type">
+ <choice>
+ <value>string</value>
+ <value>int</value>
+ <value>fraction</value>
+ <value>float</value>
+ <value>boolean</value>
+ <value>fourcc</value>
+ </choice>
+ </attribute>
+
+ <choice>
+ <oneOrMore>
+ <element name="value">
+ <text />
+ </element>
+ </oneOrMore>
+ <element name="range">
+ <attribute name="min">
+ <text />
+ </attribute>
+ <attribute name="max">
+ <text />
+ </attribute>
+ </element>
+ </choice>
+ </element>
+ </define> <!-- field -->
+
+ <define name="parent">
+ <element name="parent">
+ <attribute name="name">
+ <text />
+ </attribute>
+ </element>
+ </define> <!-- parent -->
+
+ <define name="restriction">
+ <element name="restriction">
+ <attribute name="type">
+ <choice>
+ <value>container</value>
+ <value>image</value>
+ <value>audio</value>
+ <value>video</value>
+ <value>text</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="id">
+ <text />
+ </attribute>
+ </optional>
+
+ <interleave>
+ <zeroOrMore>
+ <ref name="field" />
+ </zeroOrMore>
+ <zeroOrMore>
+ <ref name="parent" />
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define> <!-- restriction -->
+
+ <define name="restrictions">
+ <element name="restrictions">
+ <zeroOrMore>
+ <ref name="restriction" />
+ </zeroOrMore>
+ </element>
+ </define> <!-- restrictions -->
+
+ <define name="dlna-profile">
+ <element name="dlna-profile">
+ <attribute name="name">
+ <text />
+ </attribute>
+ <attribute name="mime">
+ <text />
+ </attribute>
+
+ <optional>
+ <attribute name="id">
+ <text />
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="base-profile">
+ <text />
+ </attribute>
+ </optional>
+
+ <interleave>
+ <zeroOrMore>
+ <ref name="parent" />
+ </zeroOrMore>
+ <zeroOrMore>
+ <ref name="restriction" />
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define> <!-- dlna-profile -->
+
+ <start>
+ <element name="dlna-profiles">
+ <interleave>
+ <zeroOrMore>
+ <ref name="include" />
+ </zeroOrMore>
+ <zeroOrMore>
+ <ref name="restrictions" />
+ </zeroOrMore>
+ <zeroOrMore>
+ <ref name="dlna-profile" />
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </start>
+</grammar>
diff --git a/data/jpeg.xml b/data/jpeg.xml
new file mode 100644
index 0000000..9c282f6
--- /dev/null
+++ b/data/jpeg.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="JPEG" type="image">
+ <field name="name" type="string">
+ <value>image/jpeg</value>
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="JPEG_TN" mime="image/jpeg">
+ <restriction type="image">
+ <parent name="JPEG" />
+
+ <field name="height" type="int">
+ <range min="1" max="160" />
+ </field>
+ <field name="width" type="int">
+ <range min="1" max="160" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="JPEG_SM" mime="image/jpeg">
+ <restriction type="image">
+ <parent name="JPEG" />
+
+ <field name="height" type="int">
+ <range min="1" max="480" />
+ </field>
+ <field name="width" type="int">
+ <range min="1" max="640" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="JPEG_MED" mime="image/jpeg">
+ <restriction type="image">
+ <parent name="JPEG" />
+
+ <field name="height" type="int">
+ <range min="1" max="768" />
+ </field>
+ <field name="width" type="int">
+ <range min="1" max="1024" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="JPEG_LRG" mime="image/jpeg">
+ <restriction type="image">
+ <parent name="JPEG" />
+
+ <field name="height" type="int">
+ <range min="1" max="4096" />
+ </field>
+ <field name="width" type="int">
+ <range min="1" max="4096" />
+ </field>
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/lpcm.xml b/data/lpcm.xml
new file mode 100644
index 0000000..1cc3c34
--- /dev/null
+++ b/data/lpcm.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="LPCM" type="audio">
+ <field name="name" type="string">
+ <value>audio/x-private1-lpcm</value>
+ </field>
+ <field name="depth" type="int">
+ <value>16</value>
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="LPCM" mime="audio/L16;rate=44100;channels=1">
+ <restriction type="audio">
+ <parent name="LPCM" />
+
+ <field name="rate" type="int">
+ <value>44100</value>
+ </field>
+ <field name="channels" type="int">
+ <value>1</value>
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="LPCM" mime="audio/L16;rate=44100;channels=2">
+ <restriction type="audio">
+ <parent name="LPCM" />
+
+ <field name="rate" type="int">
+ <value>44100</value>
+ </field>
+ <field name="channels" type="int">
+ <value>2</value>
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="LPCM" mime="audio/L16;rate=48000;channels=1">
+ <restriction type="audio">
+ <parent name="LPCM" />
+
+ <field name="rate" type="int">
+ <value>48000</value>
+ </field>
+ <field name="channels" type="int">
+ <value>1</value>
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="LPCM" mime="audio/L16;rate=48000;channels=2">
+ <restriction type="audio">
+ <parent name="LPCM" />
+
+ <field name="rate" type="int">
+ <value>48000</value>
+ </field>
+ <field name="channels" type="int">
+ <value>2</value>
+ </field>
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/mp3.xml b/data/mp3.xml
new file mode 100644
index 0000000..d90e321
--- /dev/null
+++ b/data/mp3.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="MP3" type="audio">
+ <field name="name" type="string">
+ <value>audio/mpeg</value>
+ </field>
+ <field name="mpegversion" type="int">
+ <value>1</value>
+ </field>
+ <field name="layer" type="int">
+ <value>3</value>
+ </field>
+ <field name="channels" type="int">
+ <range min="1" max="2" />
+ </field>
+ <field name="rate" type="int">
+ <value>32000</value>
+ <value>44100</value>
+ <value>48000</value>
+ </field>
+ <field name="bitrate" type="int">
+ <range min="32000" max="320000" />
+ </field>
+ </restriction>
+
+ </restrictions>
+
+ <dlna-profile name="MP3" mime="audio/mpeg">
+ <parent name="MP3" />
+ </dlna-profile>
+
+ <dlna-profile name="MP3X" mime="audio/mpeg">
+ <restriction type="audio">
+ <parent name="MP3" />
+
+ <field name="mpegaudioversion" type="int">
+ <range min="1" max="2"/>
+ </field>
+ <field name="rate" type="int">
+ <value>16000</value>
+ <value>22050</value>
+ <value>24000</value>
+ <value>32000</value>
+ <value>44100</value>
+ <value>48000</value>
+ </field>
+ <field name="bitrate" type="int">
+ <range min="8000" max="320000" />
+ </field>
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/mpeg-ts.xml b/data/mpeg-ts.xml
new file mode 100644
index 0000000..a10e5de
--- /dev/null
+++ b/data/mpeg-ts.xml
@@ -0,0 +1,347 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="MPEG_TS" type="container">
+ <field name="name" type="string">
+ <value>video/mpegts</value>
+ </field>
+ <field name="systemstream" type="boolean">
+ <value>true</value>
+ </field>
+ <field name="packetsize" type="int">
+ <value>188</value>
+ </field>
+ </restriction>
+
+ <restriction id="MPEG_TS_AUDIO" type="audio">
+ <field name="rate" type="int">
+ <value>48000</value>
+ </field>
+ <field name="channels" type="int">
+ <range min="1" max="6" />
+ </field>
+ <field name="bitrate" type="int">
+ <range min="1" max="448000" />
+ </field>
+ </restriction>
+
+ <restriction id="MPEG_TS_VIDEO" type="video">
+ <field name="name" type="string">
+ <value>video/mpeg</value>
+ </field>
+ <field name="systemstream" type="boolean">
+ <value>false</value>
+ </field>
+ <field name="mpegversion" type="int">
+ <value>2</value>
+ </field>
+ <field name="profile" type="string">
+ <value>simple</value>
+ <value>main</value>
+ </field>
+ <field name="level" type="string">
+ <value>low</value>
+ <value>main</value>
+ <value>high-1440</value>
+ <value>high</value>
+ </field>
+ <field name="format" type="fourcc">
+ <value>I420</value>
+ </field>
+ <field name="bitrate" type="int">
+ <!-- Max. system bitrate is 19.3927 Mb/s. Subtracting max. audio
+ bitrate, and ignoring close caption data and other overhead -->
+ <range min="1" max="18881700" />
+ </field>
+ </restriction>
+
+ <restriction id="720x480" type="video">
+ <field name="height" type="int">
+ <value>480</value>
+ </field>
+ <field name="width" type="int">
+ <value>720</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>32/27</value>
+ <value>8/9</value>
+ </field>
+ </restriction>
+
+ <restriction id="704x480" type="video">
+ <field name="height" type="int">
+ <value>480</value>
+ </field>
+ <field name="width" type="int">
+ <value>704</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>24000/1001</value> <!-- Note: only valid for interlaced=false -->
+ <value>24/1</value>
+ <value>30000/1001</value>
+ <value>30/1</value>
+ <value>60000/1001</value> <!-- Note: only valid for interlaced=false -->
+ <value>60/1</value> <!-- Note: only valid for interlaced=false -->
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>40/33</value>
+ <value>10/11</value>
+ </field>
+ </restriction>
+
+ <restriction id="640x480" type="video">
+ <field name="height" type="int">
+ <value>480</value>
+ </field>
+ <field name="width" type="int">
+ <value>640</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>24000/1001</value> <!-- Note: only valid for interlaced=false -->
+ <value>24/1</value>
+ <value>30000/1001</value>
+ <value>30/1</value>
+ <value>60000/1001</value> <!-- Note: only valid for interlaced=false -->
+ <value>60/1</value> <!-- Note: only valid for interlaced=false -->
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>1/1</value>
+ <value>4/3</value>
+ </field>
+ </restriction>
+
+ <restriction id="544x480" type="video">
+ <field name="height" type="int">
+ <value>480</value>
+ </field>
+ <field name="width" type="int">
+ <value>544</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>80/51</value>
+ <value>20/17</value>
+ </field>
+ </restriction>
+
+ <restriction id="480x480" type="video">
+ <field name="height" type="int">
+ <value>480</value>
+ </field>
+ <field name="width" type="int">
+ <value>480</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>16/9</value>
+ <value>4/3</value>
+ </field>
+ </restriction>
+
+ <restriction id="352x480" type="video">
+ <field name="height" type="int">
+ <value>480</value>
+ </field>
+ <field name="width" type="int">
+ <value>352</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>80/33</value>
+ <value>20/11</value>
+ </field>
+ </restriction>
+
+ <!-- HD resolutions -->
+ <restriction id="1920x1080" type="video">
+ <field name="height" type="int">
+ <value>1080</value>
+ </field>
+ <field name="width" type="int">
+ <value>1920</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ <value>30/1</value>
+ <value>24000/1001</value> <!-- Note: only valid for interlaced=false -->
+ <value>24/1</value> <!-- Note: only valid for interlaced=false -->
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>1/1</value>
+ <value>9/16</value>
+ </field>
+ </restriction>
+
+ <restriction id="1280x720" type="video">
+ <field name="height" type="int">
+ <value>720</value>
+ </field>
+ <field name="width" type="int">
+ <value>1280</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ <value>30/1</value>
+ <value>24000/1001</value> <!-- Note: only valid for interlaced=false -->
+ <value>24/1</value> <!-- Note: only valid for interlaced=false -->
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>1/1</value>
+ <value>9/16</value>
+ </field>
+ </restriction>
+
+ <restriction id="1440x1080" type="video">
+ <field name="height" type="int">
+ <value>1080</value>
+ </field>
+ <field name="width" type="int">
+ <value>1440</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ <value>30/1</value>
+ <value>24000/1001</value> <!-- Note: only valid for interlaced=false -->
+ <value>24/1</value> <!-- Note: only valid for interlaced=false -->
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>4/3</value>
+ </field>
+ </restriction>
+
+ <restriction id="1280x1080" type="video">
+ <field name="height" type="int">
+ <value>1080</value>
+ </field>
+ <field name="width" type="int">
+ <value>1280</value>
+ </field>
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ <value>30/1</value>
+ <value>24000/1001</value> <!-- Note: only valid for interlaced=false -->
+ <value>24/1</value> <!-- Note: only valid for interlaced=false -->
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>3/2</value>
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="MPEG_TS_SD_NA" mime="video/mpeg">
+ <!-- Note: We have no way to restrict the system bitrate yet -->
+ <parent name="MPEG_TS" />
+
+ <restriction type="audio">
+ <parent name="MPEG_TS_AUDIO" />
+
+ <field name="name" type="string">
+ <value>audio/x-ac3</value>
+ </field>
+ </restriction>
+
+ <restriction type="audio">
+ <parent name="MPEG_TS_AUDIO" />
+
+ <field name="name" type="string">
+ <value>audio/x-private1-ac3</value>
+ </field>
+ </restriction>
+
+ <restriction type="audio">
+ <parent name="MPEG_TS_AUDIO" />
+
+ <field name="name" type="string">
+ <value>audio/ac3</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="720x480" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="704x480" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="640x480" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="480x480" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="544x480" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="352x480" />
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG_TS_HD_NA" mime="video/mpeg">
+ <parent name="MPEG_TS" />
+
+ <restriction type="audio">
+ <parent name="MPEG_TS_AUDIO" />
+
+ <field name="name" type="string">
+ <value>audio/x-ac3</value>
+ </field>
+ </restriction>
+
+ <restriction type="audio">
+ <parent name="MPEG_TS_AUDIO" />
+
+ <field name="name" type="string">
+ <value>audio/x-private1-ac3</value>
+ </field>
+ </restriction>
+
+ <restriction type="audio">
+ <parent name="MPEG_TS_AUDIO" />
+
+ <field name="name" type="string">
+ <value>audio/ac3</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="1920x1080" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="1280x720" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="1440x1080" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="MPEG_TS_VIDEO" />
+ <parent name="1280x1080" />
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/mpeg1.xml b/data/mpeg1.xml
new file mode 100644
index 0000000..9e130ca
--- /dev/null
+++ b/data/mpeg1.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <include ref="common.xml" />
+
+ <restrictions>
+ <restriction id="mpeg1" type="video">
+ <field name="name" type="string">
+ <value>video/mpeg</value>
+ </field>
+ <field name="mpegversion" type="int">
+ <value>1</value>
+ </field>
+ <field name="bitrate" type="int">
+ <!-- This isn't exactly as in the spec, but should catch more compliant
+ streams -->
+ <range min="1150000" max="1152000" />
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="MPEG1" mime="video/mpeg">
+ <restriction type="container">
+ <field name="name" type="string">
+ <value>video/mpeg</value>
+ </field>
+ <field name="mpegversion" type="int">
+ <value>1</value>
+ </field>
+ <field name="systemstream" type="boolean">
+ <value>true</value>
+ </field>
+ </restriction>
+
+ <restriction type="audio">
+ <field name="name" type="string">
+ <value>audio/mpeg</value>
+ </field>
+ <field name="mpegversion" type="int">
+ <value>1</value>
+ </field>
+ <field name="mpegaudioversion" type="int">
+ <value>1</value>
+ </field>
+ <field name="layer" type="int">
+ <value>2</value>
+ </field>
+ <field name="channels" type="int">
+ <value>2</value>
+ </field>
+ <field name="rate" type="int">
+ <value>44100</value>
+ </field>
+ <field name="bitrate" type="int">
+ <value>224000</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="mpeg1" />
+ <parent name="CIF" />
+
+ <field name="framerate" type="fraction">
+ <value>25/1</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="mpeg1" />
+ <parent name="525SIF" />
+
+ <field name="framerate" type="fraction">
+ <value>30000/1001</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="mpeg1" />
+ <parent name="525SIF" />
+
+ <field name="framerate" type="fraction">
+ <value>24000/1001</value>
+ </field>
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/mpeg4.xml b/data/mpeg4.xml
new file mode 100644
index 0000000..3bd8168
--- /dev/null
+++ b/data/mpeg4.xml
@@ -0,0 +1,500 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <include ref="common.xml" />
+
+ <restrictions>
+ <restriction id="MPEG4V" type="video">
+ <field name="name" type="string">
+ <value>video/mpeg</value>
+ </field>
+ <field name="mpegversion" type="int">
+ <value>4</value>
+ </field>
+ <field name="systemstream" type="boolean">
+ <value>false</value>
+ </field>
+ <field name="format" type="fourcc">
+ <value>I420</value>
+ </field>
+ </restriction>
+
+ <restriction id="H263_P0_L10" type="video">
+ <field name="name" type="string">
+ <value>video/x-h263</value>
+ </field>
+ <field name="profile" type="string">
+ <value>0</value>
+ </field>
+ <field name="level" type="string">
+ <value>10</value>
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>12/11</value>
+ <value>16/11</value>
+ </field>
+ <field name="format" type="fourcc">
+ <value>I420</value>
+ </field>
+ <field name="bitrate" type="int">
+ <range min="1" max="64000" />
+ </field>
+ <parent name="15fps" />
+ </restriction>
+
+ <restriction id="SP" type="video">
+ <parent name="MPEG4V" />
+
+ <field name="profile" type="string">
+ <value>simple</value>
+ </field>
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>1/1</value>
+ <value>12/11</value>
+ <value>10/11</value>
+ <value>16/11</value>
+ <value>40/33</value>
+ </field>
+ <parent name="30fps" />
+ </restriction>
+
+ <restriction id="SP_64" type="video">
+ <parent name="SP" />
+
+ <field name="level" type="string">
+ <value>0</value>
+ <value>1</value>
+ </field>
+ <field name="maximum-bitrate" type="int">
+ <range min="1" max="64000" />
+ </field>
+ </restriction>
+
+ <restriction id="SP_128" type="video">
+ <parent name="SP" />
+
+ <field name="level" type="string">
+ <value>0b</value>
+ <value>2</value>
+ </field>
+ <field name="maximum-bitrate" type="int">
+ <range min="1" max="128000" />
+ </field>
+ </restriction>
+
+ <restriction id="SP_384" type="video">
+ <parent name="SP" />
+
+ <field name="level" type="string">
+ <value>3</value>
+ </field>
+ <field name="maximum-bitrate" type="int">
+ <range min="1" max="384000" />
+ </field>
+ </restriction>
+
+ <restriction id="SP_L2" type="video">
+ <parent name="MPEG4V" />
+
+ <field name="profile" type="string">
+ <value>simple</value>
+ </field>
+ <field name="level" type="string">
+ <value>0</value>
+ <value>0b</value>
+ <value>1</value>
+ <value>2</value>
+ </field>
+ <field name="maximum-bitrate" type="int">
+ <range min="1" max="128000" />
+ </field>
+ </restriction>
+
+ <restriction id="SP_L3_VGA" type="video">
+ <parent name="MPEG4V" />
+
+ <field name="profile" type="string">
+ <value>simple</value>
+ </field>
+ <field name="level" type="string">
+ <value>0</value>
+ <value>0b</value>
+ <value>1</value>
+ <value>2</value>
+ <value>3</value>
+ </field>
+ <field name="maximum-bitrate" type="int">
+ <range min="1" max="3000000" />
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="" mime="" id="SP_L3">
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="CIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="525SIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="QVGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="QVGA_16:9" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="1/7_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="1/9_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="QCIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="525QSIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="SQVGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="1/16_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="SQVGA_16:9" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_64" />
+ <parent name="SQCIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="CIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="525SIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="QVGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="QVGA_16:9" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="1/7_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="1/9_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="QCIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="525QSIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="SQVGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="1/16_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="SQVGA_16:9" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_128" />
+ <parent name="SQCIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="CIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="525SIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="QVGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="QVGA_16:9" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="1/7_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="1/9_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="QCIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="525QSIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="SQVGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="1/16_VGA_4:3" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="SQVGA_16:9" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_384" />
+ <parent name="SQCIF" />
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_P2_MP4_SP_AAC" mime="video/mp4" base-profile="SP_L3" id="SP_L3_MP4">
+ <parent name="MP4" />
+ <restriction type="audio">
+ <parent name="AAC" />
+
+ <!--
+ FIXME: this is a conservative bitrate (system bitrate (600 kbps) -
+ maximum possible video bitrate (384 kbps)), to avoid blowing up the
+ number of possible profiles again. Eventually we need a better way to
+ do this.
+ -->
+ <field name="bitrate" type="int">
+ <range min="1" max="216000" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_P2_MP4_SP_AAC" mime="video/mp4" base-profile="SP_L3_MP4" >
+ <parent name="QT" />
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_P2_MP4_SP_AAC_LTP" mime="video/mp4"
+ base-profile="SP_L3" id="SP_L3_MP4_AAC_LTP">
+ <parent name="MP4" />
+ <restriction type="audio">
+ <parent name="AAC" />
+
+ <field name="profile" type="string">
+ <value>ltp</value>
+ </field>
+ <!-- FIXME: see note for MPEG4_P2_MP4_SP_AAC bitrate -->
+ <field name="bitrate" type="int">
+ <range min="1" max="216000" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_P2_MP4_SP_AAC_LTP" mime="video/mp4"
+ base-profile="SP_L3_MP4_AAC_LTP" >
+ <parent name="QT" />
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_P2_MP4_SP_VGA_AAC" mime="video/mp4" id="SP_L3_VGA">
+ <parent name="MP4" />
+
+ <restriction type="video">
+ <parent name="SP_L3_VGA" />
+ <parent name="VGA" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_L3_VGA" />
+ <parent name="VGA_16:9" />
+ </restriction>
+
+ <restriction type="audio">
+ <parent name="AAC" />
+
+ <field name="bitrate" type="int">
+ <range min="1" max="256000" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_P2_MP4_SP_VGA_AAC" mime="video/mp4"
+ base-profile="SP_L3_VGA">
+ <parent name="QT" />
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_P2_MP4_SP_L2_AAC" mime="video/mp4" id="SP_L2_AAC">
+ <parent name="MP4" />
+
+ <restriction type="audio">
+ <parent name="AAC" />
+ <field name="maximum-bitrate" type="int">
+ <range min="1" max="128000" />
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_L2" />
+ <parent name="CIF" />
+ <parent name="15fps" />
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>12/11</value>
+ <value>16/11</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_L2" />
+ <parent name="QVGA_4:3" />
+ <parent name="15fps" />
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>1/1</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_L2" />
+ <parent name="QVGA_16:9" />
+ <parent name="15fps" />
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>1/1</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_L2" />
+ <parent name="QCIF" />
+ <parent name="30fps" />
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>12/11</value>
+ <value>16/11</value>
+ </field>
+ </restriction>
+
+ <restriction type="video">
+ <parent name="SP_L2" />
+ <parent name="SQCIF" />
+ <parent name="30fps" />
+ <field name="pixel-aspect-ratio" type="fraction">
+ <value>12/11</value>
+ <value>16/11</value>
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_P2_MP4_SP_L2_AAC" mime="video/mp4"
+ base-profile="SP_L2_AAC">
+ <parent name="QT" />
+ </dlna-profile>
+
+ <dlna-profile name="" mime="" id="H263_P0_L10">
+ <restriction type="video">
+ <parent name="H263_P0_L10" />
+ <parent name="QCIF" />
+ </restriction>
+
+ <restriction type="video">
+ <parent name="H263_P0_L10" />
+ <parent name="SQCIF" />
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_H263_MP4_P0_L10_AAC" mime="video/3gpp"
+ base-profile="H263_P0_L10" id="MPEG4_H263_MP4_P0_L10_AAC">
+ <parent name="MP4" />
+
+ <restriction type="audio">
+ <parent name="AAC" />
+ <!-- FIXME: see note for MPEG4_P2_MP4_SP_AAC bitrate, system bitrate
+ here is <= 150 kbps -->
+ <field name="bitrate" type="int">
+ <range min="1" max="86000" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_H263_MP4_P0_L10_AAC" mime="video/3gpp"
+ base-profile="MPEG4_H263_MP4_P0_L10_AAC">
+ <parent name="QT" />
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_H263_MP4_P0_L10_AAC_LTP" mime="video/3gpp"
+ base-profile="H263_P0_L10" id="MPEG4_H263_MP4_P0_L10_AAC_LTP">
+ <parent name="MP4" />
+
+ <restriction type="audio">
+ <parent name="AAC" />
+ <field name="profile" type="string">
+ <value>ltp</value>
+ </field>
+ <!-- FIXME: see note for MPEG4_P2_MP4_SP_AAC bitrate, system bitrate
+ here is <= 150 kbps -->
+ <field name="bitrate" type="int">
+ <range min="1" max="86000" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="MPEG4_H263_MP4_P0_L10_AAC_LTP" mime="video/3gpp"
+ base-profile="MPEG4_H263_MP4_P0_L10_AAC_LTP">
+ <parent name="QT" />
+ </dlna-profile>
+
+</dlna-profiles>
diff --git a/data/png.xml b/data/png.xml
new file mode 100644
index 0000000..091d534
--- /dev/null
+++ b/data/png.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+
+<!--
+ TODO: We don't currently have a way to check if the image is interlaced.
+ Also, we can't restrict what chunks are used in the image - I think this
+ would be overkill, and renderers will hopefully just ignore chunks they don't
+ understand.
+-->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="PNG" type="image">
+ <field name="name" type="string">
+ <value>image/png</value>
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="PNG_TN" mime="image/png">
+ <restriction type="image">
+ <parent name="PNG" />
+
+ <field name="height" type="int">
+ <range min="1" max="160" />
+ </field>
+ <field name="width" type="int">
+ <range min="1" max="160" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="PNG_LRG" mime="image/png">
+ <restriction type="image">
+ <parent name="PNG" />
+
+ <field name="height" type="int">
+ <range min="1" max="4096" />
+ </field>
+ <field name="width" type="int">
+ <range min="1" max="4096" />
+ </field>
+ <field name="depth" type="int">
+ <value>1</value>
+ <value>2</value>
+ <value>4</value>
+ <value>8</value>
+ <value>16</value>
+ <value>24</value>
+ <value>32</value>
+ </field>
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/data/wma.xml b/data/wma.xml
new file mode 100644
index 0000000..d05e66b
--- /dev/null
+++ b/data/wma.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="WMA" type="audio">
+ <field name="name" type="string">
+ <value>audio/x-wma</value>
+ </field>
+ </restriction>
+
+ <restriction id="WMA-ASF" type="container">
+ <field name="name" type="string">
+ <value>video/x-ms-asf</value>
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="WMABASE" mime="audio/x-ms-wma">
+ <parent name="WMA-ASF" />
+
+ <restriction type="audio">
+ <parent name="WMA" />
+
+ <field name="wmaversion" type="int">
+ <value>1</value>
+ <value>2</value>
+ </field>
+ <field name="rate" type="int">
+ <range min="0" max="48000" />
+ </field>
+ <field name="bitrate" type="int">
+ <range min="1" max="192999" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="WMAFULL" mime="audio/x-ms-wma">
+ <parent name="WMA-ASF" />
+
+ <restriction type="audio">
+ <parent name="WMA" />
+
+ <field name="wmaversion" type="int">
+ <value>1</value>
+ <value>2</value>
+ </field>
+ <field name="rate" type="int">
+ <range min="0" max="48000" />
+ </field>
+ </restriction>
+ </dlna-profile>
+
+ <dlna-profile name="WMAPRO" mime="audio/x-ms-wma">
+ <parent name="WMA-ASF" />
+
+ <restriction type="audio">
+ <parent name="WMA" />
+
+ <field name="wmaversion" type="int">
+ <value>3</value>
+ </field>
+ <field name="rate" type="int">
+ <range min="0" max="96000" />
+ </field>
+ <field name="channels" type="int">
+ <!-- FIXME: 8 = 7.1 - we don't have a way to check for LFE channels -->
+ <range min="1" max="8" />
+ </field>
+ <field name="bitrate" type="int">
+ <range min="1" max="1500000" />
+ </field>
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/doc/dlna-profiles-example.xml b/doc/dlna-profiles-example.xml
new file mode 100644
index 0000000..111e07e
--- /dev/null
+++ b/doc/dlna-profiles-example.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+
+<!--
+ General notes:
+
+ 1. You can use the <include ref="..." /> directive to include another file
+
+ 2. Profiles are prioritised in the order that they are read (i.e. if A follows
+ B and a stream matches both A and B, it is assumed to be of profile A.
+-->
+
+<dlna-profiles>
+
+ <!--
+ EXAMPLE 1: MP3
+
+ This is a vanilla example to show what a basic profile would look like.
+ -->
+
+ <!--
+ "name" is the DLNA profile name and "mime", the DLNA MIME type. These 2
+ fields are mandatory for every dlna-profile.
+ -->
+ <dlna-profile name="MP3" mime="audio/mpeg">
+ <!--
+ Here the "type" corresponds to the stream type -
+ video/audio/container/...
+ -->
+ <restriction type="audio">
+ <!-- The name field is mandatory for all restrictions -->
+ <field name="name" type="string">
+ <value>audio/mpeg</value>
+ </field>
+
+ <!-- "name" and "type" are mandatory for every field -->
+ <field name="mpegversion" type="int">
+ <!-- mpegversion = (int) 1 -->
+ <value>1</value>
+ </field>
+
+ <field name="layer" type="int">
+ <!-- layer = (int) 3 -->
+ <value>3</value>
+ </field>
+
+ <field name="channels" type="int">
+ <!-- channels = (int) [ 1, 2 ] -->
+ <range min="1" max="2" />
+ </field>
+
+ <field name="rate" type="int">
+ <!-- rate = (int) { 32000, 44100, 48000 } -->
+ <value>32000</value>
+ <value>44100</value>
+ <value>48000</value>
+ </field>
+
+ <field name="bitrate" type="int">
+ <!-- bitrate = (int) [ 32000, 320000 ] -->
+ <range min="32000" max="320000" />
+ </field>
+
+ </restriction>
+ </dlna-profile>
+
+ <!--
+ EXAMPLE 2: AVC_MP4_BL_CIF15_AAC_520
+
+ This one is a little more complicated. The video profile is composed of a
+ container (systems portion), audio and video restrictions. In addition to
+ this, each portion might occur in multiple profiles (for example, all the
+ AVC_MP4_* profiles use the "MP4" systems portion.
+
+ We tackle this by defining a global restrictions section first, where all
+ the common bits are defined. Reusing these is helpful for the MPEG-2 and
+ MPEG-4 profiles which have several commonalities.
+ -->
+
+ <!--
+ Since several profiles share common restrictions, we define these
+ separately for reuse.
+ -->
+ <restrictions>
+ <!-- First, some common restrictions for reuse -->
+ <restriction type="video" id="CIF">
+ <field name="height" type="int">
+ <value>352</value>
+ </field>
+ <field name="width" type="int">
+ <value>288</value>
+ </field>
+ </restriction>
+
+ <restriction type="video" id="QVGA_4:3">
+ <field name="height" type="int">
+ <value>320</value>
+ </field>
+ <field name="width" type="int">
+ <value>240</value>
+ </field>
+ </restriction>
+
+ <!-- and others, like QCIF, VGA, ... -->
+
+ <restriction type="video" id="AVC_L1.2">
+ <!-- This one doesn't exist yet -->
+ <field name="avclevel" type="string">
+ <value>1</value>
+ <value>1b</value>
+ <value>1.1</value>
+ <value>1.2</value>
+ </field>
+ </restriction>
+
+ <!-- and define other profiles and levels too -->
+
+ <!-- Now, some "base classes" for the profiles -->
+
+ <restriction type="container" id="MP4">
+ <!-- MP4 container ("systems" profile) -->
+ <field name="name" type="string">
+ <value>audio/x-m4a</value>
+ </field>
+ </restriction>
+
+ <restriction type="video" id="BL">
+ <!-- AVC baseline profile -->
+ <field name="name" type="string">
+ <value>video/x-h264</value>
+ </field>
+
+ <!-- This one doesn't exist yet -->
+ <field name="avcprofile" type="string">
+ <value>baseline</value>
+ </field>
+
+ <field name="format" type="fourcc">
+ <!-- Possible values with 4:2:0 format -->
+ <value>I420</value>
+ <value>YV12</value>
+ </field>
+ </restriction>
+
+ <!--
+ The "parent" tag allows you to reuse all the caps of a given restriction
+ and then specify fields you want to append (if they are not present in
+ the parent) or override (if they are).
+
+ If there are multiple parents, the resultant restriction is the union of
+ all parents.
+
+ Note: The multiple-inheritance works differently for <dlna-profile>s
+ -->
+ <restriction type="video" id="BL_L1.2">
+ <parent name="BL" />
+ <parent name="AVC_L1.2" />
+ </restriction>
+
+ <restriction type="video" id="BL_L1.2_CIF">
+ <parent name="BL_L1.2" />
+ <parent name="CIF" />
+
+ <field name="framerate" type="fraction">
+ <value>15/1</value>
+ </field>
+ </restriction>
+
+ <restriction type="video" id="BL_L1.2_QVGA_4:3">
+ <parent name="BL_L1.2" />
+ <parent name="QVGA_4:3" />
+
+ <field name="framerate" type="fraction">
+ <value>20/1</value>
+ </field>
+ </restriction>
+
+ <!--
+ Define BL_L1.2_525SIF, BL_L1.2_QVGA_16:9, ... in a similar fashion
+ -->
+
+ <!-- For audio, assume that AAC, AAC_LTP, AAC_520 are defined -->
+ </restrictions>
+
+ <!-- Finally, AVC_MP4_BL_CIF15_AAC_520 is defined here -->
+ <dlna-profile name="AVC_MP4_BL_CIF15_AAC_520" mime="video/mp4" id="AVC">
+ <!-- container -->
+ <parent name="MP4" />
+ <!-- audio -->
+ <parent name="AAC_520" />
+ <!-- video -->
+ <!--
+ If a profile has multiple parents of the same stream type (video in this
+ case), it is sufficient for the media to match *any one* of these.
+
+ Note: This is different from how multiple parents behave for
+ <restriction>s
+ -->
+ <parent name="BL_L1.2_CIF" />
+ <parent name="BL_L1.2_QVGA_4:3" />
+ <!-- and so on -->
+ </dlna-profile>
+
+ <!--
+ Same restrictions, differnt container - use the base-profile attribute. You
+ can also append more restrictions, but you cannot override anything other
+ than the container.
+ -->
+ <dlna-profile name="AVC_MP4_BL_CIF15_AAC_520" mime="video/mp4" base-profile="AVC">
+ <!-- override container -->
+ <restriction type="container">
+ <field name="name" type="string">
+ <value>video/quicktime</value>
+ </field>
+ </restriction>
+ </dlna-profile>
+
+</dlna-profiles>
diff --git a/gst-convenience/Makefile.am b/gst-convenience/Makefile.am
new file mode 100644
index 0000000..51967c2
--- /dev/null
+++ b/gst-convenience/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS = gst-libs
+
+vapidir = $(datadir)/vala/vapi
+vapifiles = gstreamer-discoverer-gupnp-dlna-0.10.vapi gstreamer-profile-gupnp-dlna-0.10.vapi
+vapideps = gstreamer-discoverer-gupnp-dlna-0.10.deps gstreamer-profile-gupnp-dlna-0.10.deps
+
+EXTRA_DIST = $(vapifiles) $(vapideps)
+
+install-data-local:
+ $(INSTALL_DATA) -d ${DESTDIR}$(vapidir) -m 755
+ @for i in $(vapifiles) $(vapideps); do \
+ $(INSTALL_DATA) $(top_srcdir)/gst-convenience/$${i} ${DESTDIR}$(vapidir)/; \
+ done
+
+uninstall-local:
+ cd ${DESTDIR}${vapidir} && rm -f ${vapifiles} ${vapideps}
diff --git a/gst-convenience/gst-libs/Makefile.am b/gst-convenience/gst-libs/Makefile.am
new file mode 100644
index 0000000..062cb55
--- /dev/null
+++ b/gst-convenience/gst-libs/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = gst
diff --git a/gst-convenience/gst-libs/gst/Makefile.am b/gst-convenience/gst-libs/gst/Makefile.am
new file mode 100644
index 0000000..73e993c
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = \
+ discoverer \
+ profile
+
+noinst_HEADERS = gettext.h gst-i18n-plugin.h
diff --git a/gst-convenience/gst-libs/gst/discoverer/Makefile.am b/gst-convenience/gst-libs/gst/discoverer/Makefile.am
new file mode 100644
index 0000000..1131564
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/Makefile.am
@@ -0,0 +1,87 @@
+lib_LTLIBRARIES = libgstdiscoverer-gupnp-dlna-@GST_MAJORMINOR@.la
+
+glib_enum_define = GST_DISCOVERER
+glib_gen_prefix = __gst_discoverer
+glib_enum_headers = gstdiscoverer.h
+glib_gen_basename = gstdiscoverer
+
+#include $(top_srcdir)/common/gst-glib-gen.mak
+
+built_sources = gstdiscoverer-marshal.c gstdiscoverer-enumtypes.c
+built_headers = gstdiscoverer-marshal.h gstdiscoverer-enumtypes.h
+
+BUILT_SOURCES = $(built_sources) $(built_headers)
+
+nodist_libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@_la_SOURCES = $(BUILT_SOURCES)
+
+libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@_la_SOURCES = gstdiscoverer.c \
+ gstdiscoverer-types.c
+libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \
+ $(GST_VIDEO_CFLAGS) -I$(top_srcdir)/gst-convenience/gst-libs
+libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@_la_LIBADD = $(GST_BASE_LIBS) $(GST_VIDEO_LIBS)
+libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
+libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@_la_LIBTOOLFLAGS = --tag=disable-static
+
+libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@includedir = $(includedir)/gupnp-dlna-1.0/gst/discoverer
+libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS = gstdiscoverer.h
+nodist_libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS = gstdiscoverer-enumtypes.h
+
+CLEANFILES = #$(BUILT_SOURCES)
+
+EXTRA_DIST = gstdiscoverer-marshal.list $(BUILT_SOURCES)
+
+#if HAVE_INTROSPECTION
+#
+#BUILT_GIRSOURCES = GstDiscoverer-@GST_MAJORMINOR@.gir
+#
+#gir_headers=$(patsubst %,$(srcdir)/%, $(libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS))
+#gir_headers+=$(patsubst %,$(builddir)/%, $(nodist_libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS))
+#gir_sources=$(patsubst %,$(srcdir)/%, $(libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@_la_SOURCES))
+#gir_sources+=$(patsubst %,$(builddir)/%, $(nodist_libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@_la_SOURCES))
+#gir_cincludes=$(patsubst %,--c-include='gst/discoverer/%',$(libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS))
+#gir_cincludes+=$(patsubst %,--c-include='gst/discoverer/%',$(nodist_libgstdiscoverer_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS))
+#
+#GstDiscoverer-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgstdiscoverer-gupnp-dlna-@GST_MAJORMINOR@.la
+# $(AM_V_GEN)PKG_CONFIG_PATH="$(PKG_CONFIG_PATH):$(top_builddir)/pkgconfig" \
+# $(INTROSPECTION_SCANNER) -v --namespace GstDiscoverer \
+# --nsversion=@GST_MAJORMINOR@ \
+# --strip-prefix=Gst \
+# $(gir_cincludes) \
+# -I$(top_srcdir)/gst-libs \
+# -I$(top_builddir)/gst-libs \
+# --add-include-path=$$($(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@) \
+# --add-include-path=$$($(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_MAJORMINOR@) \
+# --add-include-path=$$($(PKG_CONFIG) --variable=girdir gstreamer-video-@GST_MAJORMINOR@) \
+# --library=libgstdiscoverer-gupnp-dlna-@GST_MAJORMINOR@.la \
+# --include=Gst-@GST_MAJORMINOR@ \
+# --include=GstBase-@GST_MAJORMINOR@ \
+# --include=GstVideo-@GST_MAJORMINOR@ \
+# --libtool="$(top_builddir)/libtool" \
+# --pkg gstreamer-@GST_MAJORMINOR@ \
+# --pkg gstreamer-base-@GST_MAJORMINOR@ \
+# --pkg gstreamer-video-@GST_MAJORMINOR@ \
+# --output $@ \
+# $(gir_headers) \
+# $(gir_sources)
+#
+## INTROSPECTION_GIRDIR/INTROSPECTION_TYPELIBDIR aren't the right place to
+## install anything - we need to install inside our prefix.
+#girdir = $(datadir)/gir-1.0
+#gir_DATA = $(BUILT_GIRSOURCES)
+#
+#typelibsdir = $(libdir)/girepository-1.0/
+#
+#typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib)
+#
+#%.typelib: %.gir $(INTROSPECTION_COMPILER)
+# $(AM_V_GEN)$(INTROSPECTION_COMPILER) \
+# --includedir=$(srcdir) \
+# --includedir=$(builddir) \
+# --includedir=$$($(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@) \
+# --includedir=$$($(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_MAJORMINOR@) \
+# --includedir=$$($(PKG_CONFIG) --variable=girdir gstreamer-video-@GST_MAJORMINOR@) \
+# $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F)
+#
+#CLEANFILES += $(BUILT_GIRSOURCES) $(typelibs_DATA)
+#
+#endif
diff --git a/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-enumtypes.c b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-enumtypes.c
new file mode 100644
index 0000000..e2d19f6
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-enumtypes.c
@@ -0,0 +1,48 @@
+
+
+
+#include "gstdiscoverer-enumtypes.h"
+
+#include "gstdiscoverer.h"
+
+/* enumerations from "gstdiscoverer.h" */
+GType
+gst_stream_type_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile)) {
+ static const GEnumValue values[] = {
+ { GST_STREAM_CONTAINER, "GST_STREAM_CONTAINER", "container" },
+ { GST_STREAM_AUDIO, "GST_STREAM_AUDIO", "audio" },
+ { GST_STREAM_VIDEO, "GST_STREAM_VIDEO", "video" },
+ { GST_STREAM_IMAGE, "GST_STREAM_IMAGE", "image" },
+ { GST_STREAM_UNKNOWN, "GST_STREAM_UNKNOWN", "unknown" },
+ { 0, NULL, NULL }
+ };
+ GType g_define_type_id = g_enum_register_static ("GstStreamType", values);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+GType
+gst_discoverer_result_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile)) {
+ static const GFlagsValue values[] = {
+ { GST_DISCOVERER_OK, "GST_DISCOVERER_OK", "ok" },
+ { GST_DISCOVERER_URI_INVALID, "GST_DISCOVERER_URI_INVALID", "uri-invalid" },
+ { GST_DISCOVERER_ERROR, "GST_DISCOVERER_ERROR", "error" },
+ { GST_DISCOVERER_TIMEOUT, "GST_DISCOVERER_TIMEOUT", "timeout" },
+ { GST_DISCOVERER_BUSY, "GST_DISCOVERER_BUSY", "busy" },
+ { GST_DISCOVERER_MISSING_PLUGINS, "GST_DISCOVERER_MISSING_PLUGINS", "missing-plugins" },
+ { 0, NULL, NULL }
+ };
+ GType g_define_type_id = g_flags_register_static ("GstDiscovererResult", values);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+
+
diff --git a/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-enumtypes.h b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-enumtypes.h
new file mode 100644
index 0000000..6bf98dd
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-enumtypes.h
@@ -0,0 +1,21 @@
+
+
+
+#ifndef __GST_DISCOVERER_ENUM_TYPES_H__
+#define __GST_DISCOVERER_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* enumerations from "gstdiscoverer.h" */
+GType gst_stream_type_get_type (void);
+#define GST_TYPE_STREAM_TYPE (gst_stream_type_get_type())
+GType gst_discoverer_result_get_type (void);
+#define GST_TYPE_DISCOVERER_RESULT (gst_discoverer_result_get_type())
+G_END_DECLS
+
+#endif /* __GST_DISCOVERER_ENUM_TYPES_H__ */
+
+
+
diff --git a/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.c b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.c
new file mode 100644
index 0000000..daabd2f
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.c
@@ -0,0 +1,87 @@
+#include "gstdiscoverer-marshal.h"
+
+#include <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:BOXED,BOXED (gstdiscoverer-marshal.list:1) */
+void
+__gst_discoverer_marshal_VOID__BOXED_BOXED (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__BOXED_BOXED) (gpointer data1,
+ gpointer arg_1,
+ gpointer arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__BOXED_BOXED callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__BOXED_BOXED) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_boxed (param_values + 1),
+ g_marshal_value_peek_boxed (param_values + 2),
+ data2);
+}
+
diff --git a/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.h b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.h
new file mode 100644
index 0000000..12f7f49
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.h
@@ -0,0 +1,20 @@
+
+#ifndef ____gst_discoverer_marshal_MARSHAL_H__
+#define ____gst_discoverer_marshal_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:BOXED,BOXED (gstdiscoverer-marshal.list:1) */
+extern void __gst_discoverer_marshal_VOID__BOXED_BOXED (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+G_END_DECLS
+
+#endif /* ____gst_discoverer_marshal_MARSHAL_H__ */
+
diff --git a/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.list b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.list
new file mode 100644
index 0000000..73ccad0
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-marshal.list
@@ -0,0 +1 @@
+VOID:BOXED,BOXED
diff --git a/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-types.c b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-types.c
new file mode 100644
index 0000000..5ba1d21
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer-types.c
@@ -0,0 +1,446 @@
+/* GStreamer
+ * Copyright (C) 2010 Collabora Multimedia
+ * 2010 Nokia Corporation
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gstdiscoverer.h"
+#include "gstdiscoverer-marshal.h"
+
+static GstStreamContainerInformation
+ * gst_stream_container_info_copy_int (GstStreamContainerInformation * ptr,
+ GHashTable * stream_map);
+
+/* Per-stream information */
+
+/**
+ * gst_stream_information_new:
+ *
+ * Returns a newly allocated #GstStreamInformation object. All fields are
+ * initialised to zero/NULL, so callers have to allocate member structures.
+ * However, these structures will automatically be freed if allocated in
+ * #gst_stream_information_free().
+ *
+ * Returns: a newly allocated #GstStreamInformation.
+ */
+GstStreamInformation *
+gst_stream_information_new (void)
+{
+ GstStreamInformation *info = g_new0 (GstStreamInformation, 1);
+ info->streamtype = GST_STREAM_UNKNOWN;
+ return info;
+}
+
+/* Does everything except freeing info */
+static void
+gst_stream_information_deinit (GstStreamInformation * info)
+{
+ if (info->next)
+ gst_stream_information_free (info->next);
+
+ if (info->caps)
+ gst_caps_unref (info->caps);
+
+ if (info->tags)
+ gst_tag_list_free (info->tags);
+
+ if (info->misc)
+ gst_structure_free (info->misc);
+}
+
+static GstStreamInformation *
+gst_stream_information_copy_int (GstStreamInformation * info,
+ GHashTable * stream_map)
+{
+ GstStreamInformation *ret = NULL;
+ g_return_val_if_fail (info != NULL, NULL);
+
+ switch (info->streamtype) {
+ case GST_STREAM_CONTAINER:
+ ret = (GstStreamInformation *)
+ gst_stream_container_info_copy_int (
+ (GstStreamContainerInformation *) info, stream_map);
+ break;
+
+ case GST_STREAM_AUDIO:
+ ret = (GstStreamInformation *)
+ gst_stream_audio_information_copy (
+ (GstStreamAudioInformation *) info);
+ break;
+
+ case GST_STREAM_VIDEO:
+ case GST_STREAM_IMAGE:
+ ret = (GstStreamInformation *)
+ gst_stream_video_information_copy (
+ (GstStreamVideoInformation *) info);
+ break;
+
+ default:
+ ret = gst_stream_information_new ();
+ break;
+ }
+
+ if (info->next) {
+ ret->next = gst_stream_information_copy_int (info->next, stream_map);
+ ret->next->previous = ret;
+ }
+
+ if (info->caps)
+ ret->caps = gst_caps_copy (info->caps);
+
+ if (info->tags)
+ ret->tags = gst_tag_list_copy (info->tags);
+
+ if (info->misc)
+ ret->misc = gst_structure_copy (info->misc);
+
+ if (stream_map)
+ g_hash_table_insert (stream_map, info, ret);
+
+ return ret;
+}
+
+/**
+ * gst_stream_information_copy:
+ * @info: the #GstStreamInformation to be copied
+ *
+ * Returns a deep copy of @info.
+ *
+ * Returns: a newly allocated #GstStreamInformation.
+ */
+GstStreamInformation *
+gst_stream_information_copy (GstStreamInformation * info)
+{
+ return gst_stream_information_copy_int (info, NULL);
+}
+
+/**
+ * gst_stream_information_free:
+ * @info: a #GstStreamInformation
+ *
+ * Frees @info, along with any associated data structures. For derived types,
+ * this also calls the appropriate free function.
+ */
+void
+gst_stream_information_free (GstStreamInformation * info)
+{
+ g_return_if_fail (info != NULL);
+
+ switch (info->streamtype) {
+ case GST_STREAM_CONTAINER:
+ gst_stream_container_information_free (
+ (GstStreamContainerInformation *) info);
+ break;
+
+ case GST_STREAM_AUDIO:
+ gst_stream_audio_information_free ((GstStreamAudioInformation *) info);
+ break;
+
+ case GST_STREAM_VIDEO:
+ case GST_STREAM_IMAGE:
+ gst_stream_video_information_free ((GstStreamVideoInformation *) info);
+ break;
+
+ default:
+ gst_stream_information_deinit (info);
+ g_free (info);
+ break;
+ }
+}
+
+GType
+gst_stream_information_get_type (void)
+{
+ static GType gst_stream_information_type = 0;
+
+ if (G_UNLIKELY (gst_stream_information_type == 0)) {
+ gst_stream_information_type =
+ g_boxed_type_register_static ("GstStreamInformation",
+ (GBoxedCopyFunc) gst_stream_information_copy,
+ (GBoxedFreeFunc) gst_stream_information_free);
+ }
+
+ return gst_stream_information_type;
+}
+
+
+/* Container information */
+
+GstStreamContainerInformation *
+gst_stream_container_information_new (void)
+{
+ GstStreamContainerInformation *info =
+ g_new0 (GstStreamContainerInformation, 1);
+ info->parent.streamtype = GST_STREAM_CONTAINER;
+ return info;
+}
+
+static GstStreamContainerInformation *
+gst_stream_container_info_copy_int (GstStreamContainerInformation * ptr,
+ GHashTable * stream_map)
+{
+ GstStreamContainerInformation *ret;
+ GList *tmp;
+
+ g_return_val_if_fail (ptr != NULL, NULL);
+
+ ret = gst_stream_container_information_new ();
+
+ for (tmp = ((GstStreamContainerInformation *) ptr)->streams; tmp;
+ tmp = tmp->next) {
+ GstStreamInformation *subtop =
+ gst_stream_information_copy ((GstStreamInformation *) tmp->data);
+ ret->streams = g_list_append (ret->streams, subtop);
+ if (stream_map)
+ g_hash_table_insert (stream_map, tmp->data, subtop);
+ }
+
+ return ret;
+}
+
+GstStreamContainerInformation *
+gst_stream_container_information_copy (GstStreamContainerInformation * ptr)
+{
+ return gst_stream_container_info_copy_int (ptr, NULL);
+}
+
+void
+gst_stream_container_information_free (GstStreamContainerInformation * ptr)
+{
+ GList *tmp;
+
+ g_return_if_fail (ptr != NULL);
+
+ gst_stream_information_deinit (&ptr->parent);
+
+ for (tmp = ((GstStreamContainerInformation *) ptr)->streams; tmp;
+ tmp = tmp->next) {
+ GstStreamInformation *subtop = (GstStreamInformation *) tmp->data;
+ gst_stream_information_free (subtop);
+ }
+
+ g_list_free (ptr->streams);
+ g_free (ptr);
+}
+
+GType
+gst_stream_container_information_get_type (void)
+{
+ static GType gst_stream_container_information_type = 0;
+
+ if (G_UNLIKELY (gst_stream_container_information_type == 0)) {
+ gst_stream_container_information_type =
+ g_boxed_type_register_static ("GstStreamContainerInformation",
+ (GBoxedCopyFunc) gst_stream_container_information_copy,
+ (GBoxedFreeFunc) gst_stream_container_information_free);
+ }
+
+ return gst_stream_container_information_type;
+}
+
+
+/* Audio information */
+
+GstStreamAudioInformation *
+gst_stream_audio_information_new (void)
+{
+ GstStreamAudioInformation *info = g_new0 (GstStreamAudioInformation, 1);
+ info->parent.streamtype = GST_STREAM_AUDIO;
+ return info;
+}
+
+GstStreamAudioInformation *
+gst_stream_audio_information_copy (GstStreamAudioInformation * ptr)
+{
+ GstStreamAudioInformation *ret;
+
+ g_return_val_if_fail (ptr != NULL, NULL);
+
+ ret = gst_stream_audio_information_new ();
+
+ ret->channels = ptr->channels;
+ ret->sample_rate = ptr->sample_rate;
+ ret->depth = ptr->depth;
+ ret->bitrate = ptr->bitrate;
+ ret->max_bitrate = ptr->max_bitrate;
+ ret->is_vbr = ptr->is_vbr;
+
+ return ret;
+}
+
+void
+gst_stream_audio_information_free (GstStreamAudioInformation * ptr)
+{
+ g_return_if_fail (ptr != NULL);
+ gst_stream_information_deinit (&ptr->parent);
+ g_free (ptr);
+}
+
+GType
+gst_stream_audio_information_get_type (void)
+{
+ static GType gst_stream_audio_information_type = 0;
+
+ if (G_UNLIKELY (gst_stream_audio_information_type == 0)) {
+ gst_stream_audio_information_type =
+ g_boxed_type_register_static ("GstStreamAudioInformation",
+ (GBoxedCopyFunc) gst_stream_audio_information_copy,
+ (GBoxedFreeFunc) gst_stream_audio_information_free);
+ }
+
+ return gst_stream_audio_information_type;
+}
+
+
+/* Video information */
+
+GstStreamVideoInformation *
+gst_stream_video_information_new (void)
+{
+ GstStreamVideoInformation *info = g_new0 (GstStreamVideoInformation, 1);
+
+ info->parent.streamtype = GST_STREAM_VIDEO;
+ g_value_init (&info->frame_rate, GST_TYPE_FRACTION);
+ g_value_init (&info->pixel_aspect_ratio, GST_TYPE_FRACTION);
+
+ return info;
+}
+
+GstStreamVideoInformation *
+gst_stream_video_information_copy (GstStreamVideoInformation * ptr)
+{
+ GstStreamVideoInformation *ret;
+
+ g_return_val_if_fail (ptr != NULL, NULL);
+
+ ret = gst_stream_video_information_new ();
+
+ /* Because the streamtype can also be GST_STREAM_IMAGE */
+ GST_STREAM_INFORMATION (ret)->streamtype =
+ GST_STREAM_INFORMATION (ptr)->streamtype;
+ ret->width = ptr->width;
+ ret->height = ptr->height;
+ ret->depth = ptr->depth;
+ g_value_copy (&ptr->frame_rate, &ret->frame_rate);
+ g_value_copy (&ptr->pixel_aspect_ratio, &ret->pixel_aspect_ratio);
+ ret->format = ptr->format;
+ ret->interlaced = ptr->interlaced;
+
+ return ret;
+}
+
+void
+gst_stream_video_information_free (GstStreamVideoInformation * ptr)
+{
+ g_return_if_fail (ptr != NULL);
+ gst_stream_information_deinit (&ptr->parent);
+ g_free (ptr);
+}
+
+GType
+gst_stream_video_information_get_type (void)
+{
+ static GType gst_stream_video_information_type = 0;
+
+ if (G_UNLIKELY (gst_stream_video_information_type == 0)) {
+ gst_stream_video_information_type =
+ g_boxed_type_register_static ("GstStreamVideoInformation",
+ (GBoxedCopyFunc) gst_stream_video_information_copy,
+ (GBoxedFreeFunc) gst_stream_video_information_free);
+ }
+
+ return gst_stream_video_information_type;
+}
+
+
+/* Global stream information */
+
+GstDiscovererInformation *
+gst_discoverer_information_new (void)
+{
+ GstDiscovererInformation *di = g_new0 (GstDiscovererInformation, 1);
+ return di;
+}
+
+GstDiscovererInformation *
+gst_discoverer_information_copy (GstDiscovererInformation * ptr)
+{
+ GstDiscovererInformation *ret;
+ GHashTable *stream_map = g_hash_table_new (g_direct_hash, NULL);
+ GList *tmp;
+
+ g_return_val_if_fail (ptr != NULL, NULL);
+
+ ret = gst_discoverer_information_new ();
+
+ ret->uri = g_strdup (ptr->uri);
+ if (ptr->stream_info) {
+ ret->stream_info = gst_stream_information_copy_int (ptr->stream_info,
+ stream_map);
+ }
+ ret->duration = ptr->duration;
+ if (ptr->misc)
+ ret->misc = gst_structure_copy (ptr->misc);
+
+ /* We need to set up the new list of streams to correspond to old one. The
+ * list just contains a set of pointers to streams in the stream_info tree,
+ * so we keep a map of old stream info objects to the corresponding new
+ * ones and use that to figure out correspondence in stream_list. */
+ for (tmp = ptr->stream_list; tmp; tmp = tmp->next) {
+ GstStreamInformation *old_stream = (GstStreamInformation *) tmp->data;
+ GstStreamInformation *new_stream = g_hash_table_lookup (stream_map,
+ old_stream);
+ g_assert (new_stream != NULL);
+ ret->stream_list = g_list_append (ret->stream_list, new_stream);
+ }
+
+ g_hash_table_destroy (stream_map);
+ return ret;
+}
+
+void
+gst_discoverer_information_free (GstDiscovererInformation * ptr)
+{
+ g_return_if_fail (ptr != NULL);
+
+ g_free (ptr->uri);
+
+ if (ptr->stream_info)
+ gst_stream_information_free (ptr->stream_info);
+
+ if (ptr->misc)
+ gst_structure_free (ptr->misc);
+
+ g_list_free (ptr->stream_list);
+
+ g_free (ptr);
+}
+
+GType
+gst_discoverer_information_get_type (void)
+{
+ static GType gst_discoverer_information_type = 0;
+
+ if (G_UNLIKELY (gst_discoverer_information_type == 0)) {
+ gst_discoverer_information_type =
+ g_boxed_type_register_static ("GstDiscovererInformation",
+ (GBoxedCopyFunc) gst_discoverer_information_copy,
+ (GBoxedFreeFunc) gst_discoverer_information_free);
+ }
+
+ return gst_discoverer_information_type;
+}
diff --git a/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer.c b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer.c
new file mode 100644
index 0000000..9010828
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer.c
@@ -0,0 +1,1251 @@
+/* GStreamer
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gstdiscoverer
+ * @short_description: Utility for discovering information on URIs.
+ *
+ * The #GstDiscoverer is a utility object which allows to get as much
+ * information as possible from one or many URIs.
+ *
+ * It provides two APIs, allowing usage in blocking or non-blocking mode.
+ *
+ * The blocking mode just requires calling @gst_discoverer_discover_uri
+ * with the URI one wishes to discover.
+ *
+ * The non-blocking mode requires a running #GMainLoop in the default
+ * #GMainContext, where one connects to the various signals, appends the
+ * URIs to be processed (through @gst_discoverer_append_uri) and then
+ * asks for the discovery to begin (through @gst_discoverer_start).
+ *
+ * The information #GstStructure contains the fllowing information:
+ * <variablelist>
+ * <varlistentry>
+ * <term>'duration'</term>
+ * <listitem>The duration in nanoseconds. May not be present in case of
+ * errors.</listitem>
+ * </varlistentry>
+ * </variablelist>
+ *
+ * Since 0.10.26
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "gstdiscoverer.h"
+#include "gstdiscoverer-marshal.h"
+
+GST_DEBUG_CATEGORY_STATIC (discoverer_debug);
+#define GST_CAT_DEFAULT discoverer_debug
+
+static GQuark _INFORMATION_QUARK;
+static GQuark _CAPS_QUARK;
+static GQuark _TAGS_QUARK;
+static GQuark _MISSING_PLUGIN_QUARK;
+static GQuark _STREAM_TOPOLOGY_QUARK;
+static GQuark _TOPOLOGY_PAD_QUARK;
+
+typedef struct
+{
+ GstDiscoverer *dc;
+ GstPad *pad;
+ GstElement *queue;
+ GstElement *sink;
+ GstTagList *tags;
+} PrivateStream;
+
+#define DISCO_LOCK(dc) g_mutex_lock (dc->lock);
+#define DISCO_UNLOCK(dc) g_mutex_unlock (dc->lock);
+
+static void
+_do_init (void)
+{
+ GST_DEBUG_CATEGORY_INIT (discoverer_debug, "discoverer", 0, "Discoverer");
+
+ _INFORMATION_QUARK = g_quark_from_string ("information");
+ _CAPS_QUARK = g_quark_from_string ("caps");
+ _TAGS_QUARK = g_quark_from_string ("tags");
+ _MISSING_PLUGIN_QUARK = g_quark_from_string ("missing-plugin");
+ _STREAM_TOPOLOGY_QUARK = g_quark_from_string ("stream-topology");
+ _TOPOLOGY_PAD_QUARK = g_quark_from_string ("pad");
+};
+
+G_DEFINE_TYPE_EXTENDED (GstDiscoverer, gst_discoverer, G_TYPE_OBJECT, 0,
+ _do_init ());
+
+enum
+{
+ SIGNAL_READY,
+ SIGNAL_STARTING,
+ SIGNAL_DISCOVERED,
+ LAST_SIGNAL
+};
+
+#define DEFAULT_PROP_TIMEOUT 15 * GST_SECOND
+
+enum
+{
+ PROP_0,
+ PROP_TIMEOUT
+};
+
+static guint gst_discoverer_signals[LAST_SIGNAL] = { 0 };
+
+static void gst_discoverer_set_timeout (GstDiscoverer * dc,
+ GstClockTime timeout);
+
+static void discoverer_bus_cb (GstBus * bus, GstMessage * msg,
+ GstDiscoverer * dc);
+static void uridecodebin_pad_added_cb (GstElement * uridecodebin, GstPad * pad,
+ GstDiscoverer * dc);
+static void uridecodebin_pad_removed_cb (GstElement * uridecodebin,
+ GstPad * pad, GstDiscoverer * dc);
+
+static void gst_discoverer_dispose (GObject * dc);
+static void gst_discoverer_finalize (GObject * dc);
+static void gst_discoverer_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_discoverer_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static void
+gst_discoverer_class_init (GstDiscovererClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->dispose = gst_discoverer_dispose;
+ gobject_class->finalize = gst_discoverer_finalize;
+
+ gobject_class->set_property = gst_discoverer_set_property;
+ gobject_class->get_property = gst_discoverer_get_property;
+
+ /* properties */
+ /**
+ * GstDiscoverer:timeout
+ *
+ * The duration (in nanoseconds) after which the discovery of an individual
+ * URI will timeout.
+ *
+ * If the discovery of a URI times out, the @GST_DISCOVERER_TIMEOUT will be
+ * set on the result flags.
+ */
+ g_object_class_install_property (gobject_class, PROP_TIMEOUT,
+ g_param_spec_uint64 ("timeout", "timeout", "Timeout",
+ GST_SECOND, 3600 * GST_SECOND, DEFAULT_PROP_TIMEOUT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /* signals */
+ /**
+ * GstDiscoverer::ready:
+ * @discoverer: the #GstDiscoverer
+ *
+ * Will be emitted when all pending URIs have been processed.
+ */
+ gst_discoverer_signals[SIGNAL_READY] =
+ g_signal_new ("ready", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstDiscovererClass, ready),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+
+ /**
+ * GstDiscoverer::starting:
+ * @discoverer: the #GstDiscoverer
+ *
+ * Will be emitted when the discover starts analyzing the pending URIs
+ */
+ gst_discoverer_signals[SIGNAL_STARTING] =
+ g_signal_new ("starting", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstDiscovererClass, starting),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+
+ /**
+ * GstDiscoverer::discovered:
+ * @discoverer: the #GstDiscoverer
+ * @info: the results #GstDiscovererInformation
+ * @error: (type GLib.Error): #GError, which will be non-NULL if an error
+ * occured during discovery
+ *
+ * Will be emitted when all information on a URI could be discovered.
+ */
+ gst_discoverer_signals[SIGNAL_DISCOVERED] =
+ g_signal_new ("discovered", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstDiscovererClass, discovered),
+ NULL, NULL, __gst_discoverer_marshal_VOID__BOXED_BOXED,
+ G_TYPE_NONE, 2, GST_TYPE_DISCOVERER_INFORMATION, GST_TYPE_G_ERROR);
+}
+
+#if (GST_CHECK_VERSION(0,10,26) || (GST_VERSION_MICRO == 25 && GST_VERSION_NANO >= 1))
+static void
+uridecodebin_element_added_cb (GstElement * uridecodebin,
+ GstElement * child, GstDiscoverer * dc)
+{
+ GST_DEBUG ("New element added to uridecodebin : %s",
+ GST_ELEMENT_NAME (child));
+
+ if (G_OBJECT_TYPE (child) == dc->decodebin2_type) {
+ g_object_set (child, "post-stream-topology", TRUE, NULL);
+ }
+}
+#endif
+
+static void
+gst_discoverer_init (GstDiscoverer * dc)
+{
+#if (GST_CHECK_VERSION(0,10,26) || (GST_VERSION_MICRO == 25 && GST_VERSION_NANO >= 1))
+ GstElement *tmp;
+#endif
+
+ dc->timeout = DEFAULT_PROP_TIMEOUT;
+ dc->async = FALSE;
+
+ dc->lock = g_mutex_new ();
+
+ GST_LOG ("Creating pipeline");
+ dc->pipeline = (GstBin *) gst_pipeline_new ("Discoverer");
+ GST_LOG_OBJECT (dc, "Creating uridecodebin");
+ dc->uridecodebin =
+ gst_element_factory_make ("uridecodebin", "discoverer-uri");
+ GST_LOG_OBJECT (dc, "Adding uridecodebin to pipeline");
+ gst_bin_add (dc->pipeline, dc->uridecodebin);
+
+ g_signal_connect (dc->uridecodebin, "pad-added",
+ G_CALLBACK (uridecodebin_pad_added_cb), dc);
+ g_signal_connect (dc->uridecodebin, "pad-removed",
+ G_CALLBACK (uridecodebin_pad_removed_cb), dc);
+
+ GST_LOG_OBJECT (dc, "Getting pipeline bus");
+ dc->bus = gst_pipeline_get_bus ((GstPipeline *) dc->pipeline);
+
+ g_signal_connect (dc->bus, "message", G_CALLBACK (discoverer_bus_cb), dc);
+
+ GST_DEBUG_OBJECT (dc, "Done initializing Discoverer");
+
+#if (GST_CHECK_VERSION(0,10,26) || (GST_VERSION_MICRO == 25 && GST_VERSION_NANO >= 1))
+ /* This is ugly. We get the GType of decodebin2 so we can quickly detect
+ * when a decodebin2 is added to uridecodebin so we can set the
+ * post-stream-topology setting to TRUE */
+ g_signal_connect (dc->uridecodebin, "element-added",
+ G_CALLBACK (uridecodebin_element_added_cb), dc);
+ tmp = gst_element_factory_make ("decodebin2", NULL);
+ dc->decodebin2_type = G_OBJECT_TYPE (tmp);
+ g_object_unref (tmp);
+#endif
+}
+
+static void
+discoverer_reset (GstDiscoverer * dc)
+{
+ GST_DEBUG_OBJECT (dc, "Resetting");
+
+ if (dc->pending_uris) {
+ g_list_foreach (dc->pending_uris, (GFunc) g_free, NULL);
+ g_list_free (dc->pending_uris);
+ dc->pending_uris = NULL;
+ }
+
+ gst_element_set_state ((GstElement *) dc->pipeline, GST_STATE_NULL);
+}
+
+static void
+gst_discoverer_dispose (GObject * obj)
+{
+ GstDiscoverer *dc = (GstDiscoverer *) obj;
+
+ GST_DEBUG_OBJECT (dc, "Disposing");
+
+ discoverer_reset (dc);
+
+ if (G_LIKELY (dc->pipeline)) {
+ /* pipeline was set to NULL in _reset */
+ g_object_unref (dc->pipeline);
+ g_object_unref (dc->bus);
+ dc->pipeline = NULL;
+ dc->uridecodebin = NULL;
+ dc->bus = NULL;
+ }
+
+ if (dc->lock) {
+ g_mutex_free (dc->lock);
+ dc->lock = NULL;
+ }
+}
+
+static void
+gst_discoverer_finalize (GObject * obj)
+{
+
+}
+
+static void
+gst_discoverer_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstDiscoverer *dc = (GstDiscoverer *) object;
+
+ switch (prop_id) {
+ case PROP_TIMEOUT:
+ gst_discoverer_set_timeout (dc, g_value_get_uint64 (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_discoverer_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstDiscoverer *dc = (GstDiscoverer *) object;
+
+ switch (prop_id) {
+ case PROP_TIMEOUT:
+ g_value_set_uint64 (value, dc->timeout);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_discoverer_set_timeout (GstDiscoverer * dc, GstClockTime timeout)
+{
+ GST_DEBUG_OBJECT (dc, "timeout : %" GST_TIME_FORMAT, GST_TIME_ARGS (timeout));
+
+ /* FIXME : update current pending timeout if we're running */
+ DISCO_LOCK (dc);
+ dc->timeout = timeout;
+ DISCO_UNLOCK (dc);
+}
+
+static gboolean
+_event_probe (GstPad * pad, GstEvent * event, PrivateStream * ps)
+{
+ if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
+ GstTagList *tl = NULL;
+
+ gst_event_parse_tag (event, &tl);
+ GST_DEBUG_OBJECT (pad, "tags %" GST_PTR_FORMAT, tl);
+ DISCO_LOCK (ps->dc);
+ ps->tags = gst_tag_list_merge (ps->tags, tl, GST_TAG_MERGE_APPEND);
+ DISCO_UNLOCK (ps->dc);
+ }
+
+ return TRUE;
+}
+
+static void
+uridecodebin_pad_added_cb (GstElement * uridecodebin, GstPad * pad,
+ GstDiscoverer * dc)
+{
+ PrivateStream *ps;
+ GstPad *sinkpad = NULL;
+
+ GST_DEBUG_OBJECT (dc, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ ps = g_slice_new (PrivateStream);
+
+ ps->dc = dc;
+ ps->pad = pad;
+ ps->queue = gst_element_factory_make ("queue", NULL);
+ ps->sink = gst_element_factory_make ("fakesink", NULL);
+ g_object_set (ps->sink, "silent", TRUE, NULL);
+ ps->tags = NULL;
+
+ if (G_UNLIKELY (ps->queue == NULL || ps->sink == NULL))
+ goto error;
+
+ g_object_set (ps->queue, "max-size-buffers", 1, NULL);
+
+ gst_bin_add_many (dc->pipeline, ps->queue, ps->sink, NULL);
+
+ if (!gst_element_link (ps->queue, ps->sink))
+ goto error;
+ if (!gst_element_sync_state_with_parent (ps->sink))
+ goto error;
+ if (!gst_element_sync_state_with_parent (ps->queue))
+ goto error;
+
+ sinkpad = gst_element_get_static_pad (ps->queue, "sink");
+ if (sinkpad == NULL)
+ goto error;
+ if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
+ goto error;
+ g_object_unref (sinkpad);
+
+ /* Add an event probe */
+ gst_pad_add_event_probe (pad, G_CALLBACK (_event_probe), ps);
+
+ DISCO_LOCK (dc);
+ dc->streams = g_list_append (dc->streams, ps);
+ DISCO_UNLOCK (dc);
+
+ GST_DEBUG_OBJECT (dc, "Done handling pad");
+
+ return;
+
+error:
+ GST_ERROR_OBJECT (dc, "Error while handling pad");
+ if (sinkpad)
+ g_object_unref (sinkpad);
+ if (ps->queue)
+ g_object_unref (ps->queue);
+ if (ps->sink)
+ g_object_unref (ps->sink);
+ g_free (ps);
+ return;
+}
+
+static void
+uridecodebin_pad_removed_cb (GstElement * uridecodebin, GstPad * pad,
+ GstDiscoverer * dc)
+{
+ GList *tmp;
+ PrivateStream *ps;
+ GstPad *sinkpad;
+
+ GST_DEBUG_OBJECT (dc, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ /* Find the PrivateStream */
+ DISCO_LOCK (dc);
+ for (tmp = dc->streams; tmp; tmp = tmp->next) {
+ ps = (PrivateStream *) tmp->data;
+ if (ps->pad == pad)
+ break;
+ }
+
+ if (tmp == NULL) {
+ DISCO_UNLOCK (dc);
+ GST_DEBUG ("The removed pad wasn't controlled by us !");
+ return;
+ }
+
+ dc->streams = g_list_delete_link (dc->streams, tmp);
+ DISCO_UNLOCK (dc);
+
+ gst_element_set_state (ps->sink, GST_STATE_NULL);
+ gst_element_set_state (ps->queue, GST_STATE_NULL);
+ gst_element_unlink (ps->queue, ps->sink);
+
+ sinkpad = gst_element_get_static_pad (ps->queue, "sink");
+ gst_pad_unlink (pad, sinkpad);
+ g_object_unref (sinkpad);
+
+ /* references removed here */
+ gst_bin_remove_many (dc->pipeline, ps->sink, ps->queue, NULL);
+
+ if (ps->tags) {
+ gst_tag_list_free (ps->tags);
+ }
+
+ g_slice_free1 (sizeof (PrivateStream), ps);
+
+ GST_DEBUG ("Done handling pad");
+}
+
+static GstStructure *
+collect_stream_information (GstDiscoverer * dc, PrivateStream * ps, guint idx)
+{
+ GstCaps *caps;
+ GstStructure *st;
+ gchar *stname;
+
+ stname = g_strdup_printf ("stream-%02d", idx);
+ st = gst_structure_empty_new (stname);
+ g_free (stname);
+
+ /* Get caps */
+ caps = gst_pad_get_negotiated_caps (ps->pad);
+ if (caps) {
+ GST_DEBUG ("Got caps %" GST_PTR_FORMAT, caps);
+ gst_structure_id_set (st, _CAPS_QUARK, GST_TYPE_CAPS, caps, NULL);
+
+ gst_caps_unref (caps);
+ } else
+ GST_WARNING ("Couldn't get negotiated caps from %s:%s",
+ GST_DEBUG_PAD_NAME (ps->pad));
+ if (ps->tags)
+ gst_structure_id_set (st, _TAGS_QUARK, GST_TYPE_STRUCTURE, ps->tags, NULL);
+
+ return st;
+}
+
+/* Parses a set of caps and tags in st and populates a GstStreamInformation
+ * structure (parent, if !NULL, otherwise it allocates one)
+ */
+static GstStreamInformation *
+collect_information (GstDiscoverer * dc, const GstStructure * st,
+ GstStreamInformation * parent)
+{
+ GstCaps *caps;
+ GstStructure *caps_st, *tags_st;
+ const gchar *name;
+ int tmp, tmp2;
+ guint utmp;
+ gboolean btmp;
+
+ if (!st || !gst_structure_id_has_field (st, _CAPS_QUARK)) {
+ GST_WARNING ("Couldn't find caps !");
+ if (parent)
+ return parent;
+ else
+ return gst_stream_information_new ();
+ }
+
+ gst_structure_id_get ((GstStructure *) st, _CAPS_QUARK, GST_TYPE_CAPS, &caps,
+ NULL);
+ caps_st = gst_caps_get_structure (caps, 0);
+ name = gst_structure_get_name (caps_st);
+
+ if (g_str_has_prefix (name, "audio/")) {
+ GstStreamAudioInformation *info;
+
+ if (parent)
+ info = (GstStreamAudioInformation *) parent;
+ else {
+ info = gst_stream_audio_information_new ();
+ info->parent.caps = caps;
+ }
+
+ if (gst_structure_get_int (caps_st, "rate", &tmp))
+ info->sample_rate = (guint) tmp;
+
+ if (gst_structure_get_int (caps_st, "channels", &tmp))
+ info->channels = (guint) tmp;
+
+ if (gst_structure_get_int (caps_st, "depth", &tmp))
+ info->depth = (guint) tmp;
+
+ if (gst_structure_id_has_field (st, _TAGS_QUARK)) {
+ gst_structure_id_get ((GstStructure *) st, _TAGS_QUARK,
+ GST_TYPE_STRUCTURE, &tags_st, NULL);
+ if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp))
+ info->bitrate = utmp;
+
+ if (gst_structure_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp))
+ info->max_bitrate = utmp;
+
+#ifdef GST_TAG_HAS_VBR
+ if (gst_structure_get_boolean (tags_st, GST_TAG_HAS_VBR, &btmp))
+ info->is_vbr = btmp;
+#endif
+
+ /* FIXME: Is it worth it to remove the tags we've parsed? */
+ info->parent.tags = gst_tag_list_merge (info->parent.tags,
+ (GstTagList *) tags_st, GST_TAG_MERGE_REPLACE);
+
+ gst_structure_free (tags_st);
+ }
+
+ return (GstStreamInformation *) info;
+
+ } else if (g_str_has_prefix (name, "video/") ||
+ g_str_has_prefix (name, "image/")) {
+ GstStreamVideoInformation *info;
+ GstVideoFormat format;
+
+ if (parent)
+ info = (GstStreamVideoInformation *) parent;
+ else {
+ info = gst_stream_video_information_new ();
+ info->parent.caps = caps;
+ }
+
+ if (gst_video_format_parse_caps (caps, &format, &tmp, &tmp2)) {
+ info->width = (guint) tmp;
+ info->height = (guint) tmp2;
+ info->format = format;
+ }
+
+ if (gst_structure_get_int (caps_st, "depth", &tmp))
+ info->depth = (guint) tmp;
+
+ if (gst_video_parse_caps_pixel_aspect_ratio (caps, &tmp, &tmp2))
+ gst_value_set_fraction (&info->pixel_aspect_ratio, tmp, tmp2);
+
+ if (gst_video_parse_caps_framerate (caps, &tmp, &tmp2))
+ gst_value_set_fraction (&info->frame_rate, tmp, tmp2);
+
+ if (gst_video_format_parse_caps_interlaced (caps, &btmp))
+ info->interlaced = btmp;
+
+ if (gst_structure_id_has_field (st, _TAGS_QUARK)) {
+ gst_structure_id_get ((GstStructure *) st, _TAGS_QUARK,
+ GST_TYPE_STRUCTURE, &tags_st, NULL);
+ /* FIXME: Is it worth it to remove the tags we've parsed? */
+ info->parent.tags = gst_tag_list_merge (info->parent.tags,
+ (GstTagList *) tags_st, GST_TAG_MERGE_REPLACE);
+ gst_structure_free (tags_st);
+ }
+
+ return (GstStreamInformation *) info;
+
+ } else {
+ /* None of the above - populate what information we can */
+ GstStreamInformation *info;
+
+ if (parent)
+ info = parent;
+ else {
+ info = gst_stream_information_new ();
+ info->caps = caps;
+ }
+
+ if (gst_structure_id_get ((GstStructure *) st, _TAGS_QUARK,
+ GST_TYPE_STRUCTURE, &tags_st, NULL)) {
+ info->tags = gst_tag_list_merge (info->tags, (GstTagList *) tags_st,
+ GST_TAG_MERGE_REPLACE);
+ gst_structure_free (tags_st);
+ }
+
+ return info;
+ }
+
+}
+
+static GstStructure *
+find_stream_for_node (GstDiscoverer * dc, const GstStructure * topology)
+{
+ GstPad *pad;
+ GstPad *target_pad = NULL;
+ GstStructure *st = NULL;
+ PrivateStream *ps;
+ guint i;
+ GList *tmp;
+
+ if (!gst_structure_id_has_field (topology, _TOPOLOGY_PAD_QUARK)) {
+ GST_DEBUG ("Could not find pad for node %" GST_PTR_FORMAT "\n", topology);
+ return NULL;
+ }
+
+ gst_structure_id_get ((GstStructure *) topology, _TOPOLOGY_PAD_QUARK,
+ GST_TYPE_PAD, &pad, NULL);
+
+ if (!dc->streams)
+ return NULL;
+
+ for (i = 0, tmp = dc->streams; tmp; tmp = tmp->next, i++) {
+ ps = (PrivateStream *) tmp->data;
+
+ target_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (ps->pad));
+ gst_object_unref (target_pad);
+
+ if (target_pad == pad)
+ break;
+ }
+
+ if (tmp)
+ st = collect_stream_information (dc, ps, i);
+
+ gst_object_unref (pad);
+
+ return st;
+}
+
+static gboolean
+child_is_raw_stream (GstCaps * parent, GstCaps * child)
+{
+ GstStructure *st1, *st2;
+ const gchar *name1, *name2;
+
+ st1 = gst_caps_get_structure (parent, 0);
+ name1 = gst_structure_get_name (st1);
+ st2 = gst_caps_get_structure (child, 0);
+ name2 = gst_structure_get_name (st2);
+
+ if ((g_str_has_prefix (name1, "audio/") &&
+ g_str_has_prefix (name2, "audio/x-raw")) ||
+ ((g_str_has_prefix (name1, "video/") ||
+ g_str_has_prefix (name1, "image/")) &&
+ g_str_has_prefix (name2, "video/x-raw"))) {
+ /* child is the "raw" sub-stream corresponding to parent */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* If a parent is non-NULL, collected stream information will be appended to it
+ * (and where the information exists, it will be overriden)
+ */
+static GstStreamInformation *
+parse_stream_topology (GstDiscoverer * dc, const GstStructure * topology,
+ GstStreamInformation * parent)
+{
+ GstStreamInformation *res = NULL;
+ GstCaps *caps = NULL;
+ const GValue *nval = NULL;
+
+ GST_DEBUG ("parsing: %" GST_PTR_FORMAT, topology);
+
+ nval = gst_structure_get_value (topology, "next");
+
+ if (nval == NULL || GST_VALUE_HOLDS_STRUCTURE (nval)) {
+ GstStructure *st = find_stream_for_node (dc, topology);
+ gboolean add_to_list = TRUE;
+
+ if (st) {
+ res = collect_information (dc, st, parent);
+ gst_structure_free (st);
+ } else {
+ /* Didn't find a stream structure, so let's just use the caps we have */
+ res = collect_information (dc, topology, parent);
+ }
+
+ if (nval == NULL) {
+ /* FIXME : aggregate with information from main streams */
+ GST_DEBUG ("Coudn't find 'next' ! might be the last entry");
+ } else {
+ GstCaps *caps;
+ const GstStructure *st;
+
+ GST_DEBUG ("next is a structure %" GST_PTR_FORMAT);
+
+ st = gst_value_get_structure (nval);
+
+ if (!parent)
+ parent = res;
+
+ if (gst_structure_id_get ((GstStructure *) st, _CAPS_QUARK, GST_TYPE_CAPS,
+ &caps, NULL)) {
+ if (gst_caps_can_intersect (parent->caps, caps)) {
+ /* We sometimes get an extra sub-stream from the parser. If this is
+ * the case, we just replace the parent caps with this stream's caps
+ * since they might contain more information */
+ gst_caps_unref (parent->caps);
+ parent->caps = caps;
+
+ parse_stream_topology (dc, st, parent);
+ add_to_list = FALSE;
+
+ } else if (child_is_raw_stream (parent->caps, caps)) {
+ /* This is the "raw" stream corresponding to the parent. This
+ * contains more information than the parent, tags etc. */
+ parse_stream_topology (dc, st, parent);
+ add_to_list = FALSE;
+ gst_caps_unref (caps);
+
+ } else {
+ GstStreamInformation *next = parse_stream_topology (dc, st, NULL);
+ res->next = next;
+ next->previous = res;
+ }
+ }
+ }
+
+ if (add_to_list) {
+ dc->current_info->stream_list =
+ g_list_append (dc->current_info->stream_list, res);
+ }
+
+ } else if (GST_VALUE_HOLDS_LIST (nval)) {
+ guint i, len;
+ GstStreamContainerInformation *cont;
+ GstTagList *tags;
+
+ if (!gst_structure_id_get ((GstStructure *) topology, _CAPS_QUARK,
+ GST_TYPE_CAPS, &caps, NULL))
+ GST_WARNING ("Couldn't find caps !");
+
+ len = gst_value_list_get_size (nval);
+ GST_DEBUG ("next is a list of %d entries", len);
+
+ cont = gst_stream_container_information_new ();
+ cont->parent.caps = caps;
+ res = (GstStreamInformation *) cont;
+
+ if (gst_structure_id_has_field (topology, _TAGS_QUARK)) {
+ gst_structure_id_get ((GstStructure *) topology, _TAGS_QUARK,
+ GST_TYPE_STRUCTURE, &tags, NULL);
+ cont->parent.tags =
+ gst_tag_list_merge (cont->parent.tags, (GstTagList *) tags,
+ GST_TAG_MERGE_APPEND);
+ gst_tag_list_free (tags);
+ }
+
+ for (i = 0; i < len; i++) {
+ const GValue *subv = gst_value_list_get_value (nval, i);
+ const GstStructure *subst = gst_value_get_structure (subv);
+ GstStreamInformation *substream;
+
+ GST_DEBUG ("%d %" GST_PTR_FORMAT, i, subst);
+
+ substream = parse_stream_topology (dc, subst, NULL);
+
+ substream->previous = res;
+ cont->streams = g_list_append (cont->streams, substream);
+ }
+ }
+
+ return res;
+}
+
+/* Called when pipeline is pre-rolled */
+static void
+discoverer_collect (GstDiscoverer * dc)
+{
+ GST_DEBUG ("Collecting information");
+
+ if (dc->streams) {
+ /* FIXME : Make this querying optional */
+ if (TRUE) {
+ GstFormat format = GST_FORMAT_TIME;
+ gint64 dur;
+
+ GST_DEBUG ("Attempting to query duration");
+
+ if (gst_element_query_duration ((GstElement *) dc->pipeline, &format,
+ &dur)) {
+ if (format == GST_FORMAT_TIME) {
+ GST_DEBUG ("Got duration %" GST_TIME_FORMAT, GST_TIME_ARGS (dur));
+ dc->current_info->duration = (guint64) dur;
+ }
+ }
+ }
+
+ if (dc->current_topology)
+ dc->current_info->stream_info = parse_stream_topology (dc,
+ dc->current_topology, NULL);
+
+ /*
+ * Images need some special handling. They do not have a duration, have
+ * caps named image/<foo> (th exception being MJPEG video which is also
+ * type image/jpeg), and should consist of precisely one stream (actually
+ * initially there are 2, the image and raw stream, but we squash these
+ * while parsing the stream topology). At some ponit, if we find that these
+ * conditions are not sufficient, we can count the number of decoders and
+ * parsers in the chain, and if there's more than one decoder, or any
+ * parser at all, we should not mark this as an image.
+ */
+ if (dc->current_info->duration == 0 &&
+ dc->current_info->stream_info != NULL &&
+ dc->current_info->stream_info->next == NULL) {
+ GstStructure *st =
+ gst_caps_get_structure (dc->current_info->stream_info->caps, 0);
+
+ if (g_str_has_prefix (gst_structure_get_name (st), "image/"))
+ dc->current_info->stream_info->streamtype = GST_STREAM_IMAGE;
+ }
+ }
+
+ if (dc->async) {
+ GST_DEBUG ("Emitting 'discoverered'");
+ g_signal_emit (dc, gst_discoverer_signals[SIGNAL_DISCOVERED], 0,
+ dc->current_info, dc->current_error);
+ /* Clients get a copy of current_info since it is a boxed type */
+ gst_discoverer_information_free (dc->current_info);
+ }
+}
+
+static void
+handle_current_async (GstDiscoverer * dc)
+{
+ /* FIXME : TIMEOUT ! */
+}
+
+
+/* Returns TRUE if processing should stop */
+static gboolean
+handle_message (GstDiscoverer * dc, GstMessage * msg)
+{
+ gboolean done = FALSE;
+
+ GST_DEBUG ("got a %s message", GST_MESSAGE_TYPE_NAME (msg));
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_ERROR:{
+ GError *gerr;
+ gchar *debug;
+
+ gst_message_parse_error (msg, &gerr, &debug);
+ GST_WARNING ("Got an error [debug:%s]", debug);
+ dc->current_error = gerr;
+ g_free (debug);
+
+ /* We need to stop */
+ done = TRUE;
+
+ dc->current_info->result |= GST_DISCOVERER_ERROR;
+ }
+ break;
+
+ case GST_MESSAGE_EOS:
+ GST_DEBUG ("Got EOS !");
+ done = TRUE;
+ break;
+
+ case GST_MESSAGE_ASYNC_DONE:
+ if (GST_MESSAGE_SRC (msg) == (GstObject *) dc->pipeline) {
+ GST_DEBUG ("Finished changing state asynchronously");
+ done = TRUE;
+
+ }
+ break;
+
+ case GST_MESSAGE_ELEMENT:
+ {
+ GQuark sttype = gst_structure_get_name_id (msg->structure);
+ GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg),
+ "structure %" GST_PTR_FORMAT, msg->structure);
+ if (sttype == _MISSING_PLUGIN_QUARK) {
+ dc->current_info->result |= GST_DISCOVERER_MISSING_PLUGINS;
+ dc->current_info->misc = gst_structure_copy (msg->structure);
+ } else if (sttype == _STREAM_TOPOLOGY_QUARK) {
+ dc->current_topology = gst_structure_copy (msg->structure);
+ }
+ }
+ break;
+
+ case GST_MESSAGE_TAG:
+ {
+ GstTagList *tl;
+
+ gst_message_parse_tag (msg, &tl);
+ GST_DEBUG ("Got tags %" GST_PTR_FORMAT, tl);
+ /* Merge with current tags */
+ dc->current_tags =
+ gst_tag_list_merge (dc->current_tags, tl, GST_TAG_MERGE_APPEND);
+ gst_tag_list_free (tl);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return done;
+}
+
+
+static void
+handle_current_sync (GstDiscoverer * dc)
+{
+ GTimer *timer;
+ gdouble deadline = ((gdouble) dc->timeout) / GST_SECOND;
+ GstMessage *msg;
+ gboolean done = FALSE;
+
+ timer = g_timer_new ();
+ g_timer_start (timer);
+
+ do {
+ /* poll bus with timeout */
+ /* FIXME : make the timeout more fine-tuned */
+ if ((msg = gst_bus_timed_pop (dc->bus, GST_SECOND / 2))) {
+ done = handle_message (dc, msg);
+ gst_message_unref (msg);
+ }
+
+ } while (!done && (g_timer_elapsed (timer, NULL) < deadline));
+
+ /* return result */
+ if (!done) {
+ GST_DEBUG ("we timed out!");
+ dc->current_info->result |= GST_DISCOVERER_TIMEOUT;
+ }
+
+ GST_DEBUG ("Done");
+
+ g_timer_stop (timer);
+ g_timer_destroy (timer);
+}
+
+static void
+_setup_locked (GstDiscoverer * dc)
+{
+ GstStateChangeReturn ret;
+
+ GST_DEBUG ("Setting up");
+
+ /* Pop URI off the pending URI list */
+ dc->current_info = gst_discoverer_information_new ();
+ dc->current_info->uri = (gchar *) dc->pending_uris->data;
+ dc->pending_uris = g_list_delete_link (dc->pending_uris, dc->pending_uris);
+
+ /* set uri on uridecodebin */
+ g_object_set (dc->uridecodebin, "uri", dc->current_info->uri, NULL);
+
+ GST_DEBUG ("Current is now %s", dc->current_info->uri);
+
+ /* set pipeline to PAUSED */
+ dc->running = TRUE;
+
+ DISCO_UNLOCK (dc);
+ GST_DEBUG ("Setting pipeline to PAUSED");
+ ret = gst_element_set_state ((GstElement *) dc->pipeline, GST_STATE_PAUSED);
+ DISCO_LOCK (dc);
+
+ GST_DEBUG_OBJECT (dc, "Pipeline going to PAUSED : %s",
+ gst_element_state_change_return_get_name (ret));
+}
+
+static void
+discoverer_cleanup (GstDiscoverer * dc)
+{
+ GST_DEBUG ("Cleaning up");
+
+ gst_bus_set_flushing (dc->bus, TRUE);
+ gst_element_set_state ((GstElement *) dc->pipeline, GST_STATE_READY);
+ gst_bus_set_flushing (dc->bus, FALSE);
+
+ DISCO_LOCK (dc);
+ if (dc->current_error)
+ g_error_free (dc->current_error);
+ dc->current_error = NULL;
+ if (dc->current_tags) {
+ gst_tag_list_free (dc->current_tags);
+ dc->current_tags = NULL;
+ }
+ if (dc->current_topology) {
+ gst_structure_free (dc->current_topology);
+ dc->current_topology = NULL;
+ }
+
+ dc->current_info = NULL;
+
+ /* Try popping the next uri */
+ if (dc->async) {
+ if (dc->pending_uris != NULL) {
+ _setup_locked (dc);
+ DISCO_UNLOCK (dc);
+ /* Start timeout */
+ handle_current_async (dc);
+ } else {
+ /* We're done ! */
+ DISCO_UNLOCK (dc);
+ g_signal_emit (dc, gst_discoverer_signals[SIGNAL_READY], 0);
+ }
+ } else
+ DISCO_UNLOCK (dc);
+
+ GST_DEBUG ("out");
+}
+
+static void
+discoverer_bus_cb (GstBus * bus, GstMessage * msg, GstDiscoverer * dc)
+{
+ GST_DEBUG ("dc->running:%d", dc->running);
+ if (dc->running) {
+ if (handle_message (dc, msg)) {
+ GST_DEBUG ("Stopping asynchronously");
+ dc->running = FALSE;
+ discoverer_collect (dc);
+ discoverer_cleanup (dc);
+ }
+ }
+}
+
+
+
+/* If there is a pending URI, it will pop it from the list of pending
+ * URIs and start the discovery on it.
+ *
+ * Returns GST_DISCOVERER_OK if the next URI was popped and is processing,
+ * else a error flag.
+ */
+static GstDiscovererResult
+start_discovering (GstDiscoverer * dc)
+{
+ GstDiscovererResult res = GST_DISCOVERER_OK;
+
+ GST_DEBUG ("Starting");
+
+ DISCO_LOCK (dc);
+ if (dc->pending_uris == NULL) {
+ GST_WARNING ("No URI to process");
+ res |= GST_DISCOVERER_URI_INVALID;
+ DISCO_UNLOCK (dc);
+ goto beach;
+ }
+
+ if (dc->current_info != NULL) {
+ GST_WARNING ("Already processing a file");
+ res |= GST_DISCOVERER_BUSY;
+ DISCO_UNLOCK (dc);
+ goto beach;
+ }
+
+ _setup_locked (dc);
+
+ DISCO_UNLOCK (dc);
+
+ if (dc->async)
+ handle_current_async (dc);
+ else
+ handle_current_sync (dc);
+
+beach:
+ return res;
+}
+
+
+/**
+ * gst_discoverer_start:
+ * @discoverer: A #GstDiscoverer
+ *
+ * Allow asynchronous discovering of URIs to take place.
+ */
+void
+gst_discoverer_start (GstDiscoverer * discoverer)
+{
+ GST_DEBUG_OBJECT (discoverer, "Starting...");
+
+ if (discoverer->async) {
+ GST_DEBUG_OBJECT (discoverer, "We were already started");
+ return;
+ }
+
+ discoverer->async = TRUE;
+ /* Connect to bus signals */
+ gst_bus_add_signal_watch (discoverer->bus);
+
+ start_discovering (discoverer);
+ GST_DEBUG_OBJECT (discoverer, "Started");
+}
+
+/**
+ * gst_discoverer_stop:
+ * @discoverer: A #GstDiscoverer
+ *
+ * Stop the discovery of any pending URIs and clears the list of
+ * pending URIS (if any).
+ */
+void
+gst_discoverer_stop (GstDiscoverer * discoverer)
+{
+ GST_DEBUG_OBJECT (discoverer, "Stopping...");
+
+ if (!discoverer->async) {
+ GST_DEBUG_OBJECT (discoverer,
+ "We were already stopped, or running synchronously");
+ return;
+ }
+
+ DISCO_LOCK (discoverer);
+ if (discoverer->running) {
+ /* FIXME : Stop any ongoing discovery */
+ }
+ DISCO_UNLOCK (discoverer);
+
+ /* Remove signal watch */
+ gst_bus_remove_signal_watch (discoverer->bus);
+ discoverer_reset (discoverer);
+
+ discoverer->async = FALSE;
+
+ GST_DEBUG_OBJECT (discoverer, "Stopped");
+}
+
+/**
+ * gst_discoverer_append_uri:
+ * @discoverer: A #GstDiscoverer
+ * @uri: the URI to add.
+ *
+ * Appends the given @uri to the list of URIs to discoverer. The actual
+ * discovery of the @uri will only take place if @gst_discoverer_start has
+ * been called.
+ *
+ * Returns: TRUE if the @uri was succesfully appended to the list of pending
+ * uris, else FALSE
+ */
+gboolean
+gst_discoverer_append_uri (GstDiscoverer * discoverer, gchar * uri)
+{
+ gboolean can_run;
+
+ GST_DEBUG_OBJECT (discoverer, "uri : %s", uri);
+
+ DISCO_LOCK (discoverer);
+ can_run = (discoverer->pending_uris == NULL);
+ discoverer->pending_uris =
+ g_list_append (discoverer->pending_uris, g_strdup (uri));
+ DISCO_UNLOCK (discoverer);
+
+ if (can_run)
+ start_discovering (discoverer);
+
+ return TRUE;
+}
+
+
+/* Synchronous mode */
+/**
+ * gst_discoverer_discover_uri:
+ *
+ * @discoverer: A #GstDiscoverer
+ * @uri: The URI to run on.
+ * @err: If an error occured, this field will be filled in.
+ *
+ * Synchronously discovers the given @uri.
+ *
+ * Returns: (transfer none): see #GstDiscovererInformation. The caller must free this structure
+ * after use.
+ */
+GstDiscovererInformation *
+gst_discoverer_discover_uri (GstDiscoverer * discoverer, gchar * uri,
+ GError ** err)
+{
+ GstDiscovererResult res = 0;
+ GstDiscovererInformation *info;
+
+ GST_DEBUG_OBJECT (discoverer, "uri:%s", uri);
+
+ DISCO_LOCK (discoverer);
+ if (G_UNLIKELY (discoverer->current_info)) {
+ DISCO_UNLOCK (discoverer);
+ GST_WARNING_OBJECT (discoverer, "Already handling a uri");
+ return NULL;
+ }
+
+ discoverer->pending_uris =
+ g_list_append (discoverer->pending_uris, g_strdup (uri));
+ DISCO_UNLOCK (discoverer);
+
+ res = start_discovering (discoverer);
+ discoverer_collect (discoverer);
+
+ /* Get results */
+ if (discoverer->current_error)
+ *err = g_error_copy (discoverer->current_error);
+ else
+ *err = NULL;
+ discoverer->current_info->result |= res;
+ info = discoverer->current_info;
+
+ discoverer_cleanup (discoverer);
+
+ return info;
+}
+
+/**
+ * gst_discoverer_new:
+ * @timeout: The timeout to set on the discoverer
+ *
+ * Creates a new #GstDiscoverer with the provided timeout.
+ *
+ * Returns: The new #GstDiscoverer. Free with g_object_unref() when done.
+ */
+GstDiscoverer *
+gst_discoverer_new (GstClockTime timeout)
+{
+ return g_object_new (GST_TYPE_DISCOVERER, "timeout", timeout, NULL);
+}
diff --git a/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer.h b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer.h
new file mode 100644
index 0000000..2597eb0
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/discoverer/gstdiscoverer.h
@@ -0,0 +1,331 @@
+/* GStreamer
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * 2009 Nokia Corporation
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GST_DISCOVERER_H_
+#define _GST_DISCOVERER_H_
+
+#ifndef GST_USE_UNSTABLE_API
+#warning "GstDiscoverer is unstable API and may change in future."
+#warning "You can define GST_USE_UNSTABLE_API to avoid this warning."
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/discoverer/gstdiscoverer-enumtypes.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstStreamType:
+ * @GST_STREAM_CONTAINER: Container media stream
+ * @GST_STREAM_AUDIO: Audio media stream
+ * @GST_STREAM_VIDEO: Video media stream
+ * @GST_STREAM_IMAGE: Single-picture media stream
+ * @GST_STREAM_UNKNOWN: Unknown media stream
+ *
+ * The various types of #GstStreamInformation.
+ */
+typedef enum {
+ GST_STREAM_CONTAINER,
+ GST_STREAM_AUDIO,
+ GST_STREAM_VIDEO,
+ GST_STREAM_IMAGE,
+ GST_STREAM_UNKNOWN,
+} GstStreamType;
+
+typedef struct _GstStreamInformation GstStreamInformation;
+
+/**
+ * GstStreamInformation:
+ * @streamtype: the type of stream, can be a container or a media type (audio/video/text)
+ * @previous: previous #GstStreamInformation in a chain, NULL for starting points
+ * @next: next #GstStreamInformation in a chain, NULL for containers
+ * @caps: #GstCaps for the stream
+ * @tags: #GstTagList containing tags for the stream
+ * @misc: #GstStructure with additional information (for example, codec version, profile, etc.)
+ *
+ * Base structure for informations concerning a media stream. Depending on the @streamtype,
+ * One can find more media-specific information in #GstStreamAudioInformation,
+ * #GstStreamVideoInformation, #GstStreamContainerInformation.
+ */
+struct _GstStreamInformation {
+ GstStreamType streamtype;
+
+ GstStreamInformation *previous; /* NULL for starting points */
+ GstStreamInformation *next; /* NULL for containers */
+
+ GstCaps *caps;
+ GstTagList *tags;
+ GstStructure *misc;
+};
+
+GstStreamInformation * gst_stream_information_new (void);
+GstStreamInformation *
+gst_stream_information_copy (GstStreamInformation * info);
+void gst_stream_information_free (GstStreamInformation *topology);
+GType gst_stream_information_get_type (void);
+
+#define GST_TYPE_STREAM_INFORMATION (gst_stream_information_get_type ())
+#define GST_STREAM_INFORMATION(object) ((GstStreamInformation *)(object))
+
+
+/**
+ * GstStreamContainerInformation:
+ * @parent: See #GstStreamInformation for fields
+ * @streams: List of #GstStreamInformation objects in this container
+ *
+ * #GstStreamInformation specific to streams of type #GST_STREAM_CONTAINER.
+ */
+typedef struct _GstStreamContainerInformation GstStreamContainerInformation;
+
+struct _GstStreamContainerInformation {
+ GstStreamInformation parent;
+
+ GList *streams;
+};
+
+GstStreamContainerInformation * gst_stream_container_information_new (void);
+GstStreamContainerInformation *
+gst_stream_container_information_copy (GstStreamContainerInformation * ptr);
+void gst_stream_container_information_free (GstStreamContainerInformation *ptr);
+GType gst_stream_container_information_get_type (void);
+
+#define GST_TYPE_STREAM_CONTAINER_INFORMATION \
+ (gst_stream_container_information_get_type ())
+#define GST_STREAM_CONTAINER_INFORMATION(object) \
+ ((GstStreamContainerInformation *)(object))
+
+
+/**
+ * GstStreamAudioInformation:
+ * @parent: See #GstStreamInformation for fields
+ * @channels: Number of channels in the stream
+ * @sample_rate: Sampling rate of the stream in Hz
+ * @depth: Number of bits used per sample
+ * @bitrate: Bitrate of the stream in bits/second
+ * @is_vbr: True if the stream has a variable bitrate
+ *
+ * #GstStreamInformation specific to streams of type #GST_STREAM_AUDIO.
+ */
+typedef struct _GstStreamAudioInformation GstStreamAudioInformation;
+
+struct _GstStreamAudioInformation {
+ GstStreamInformation parent;
+
+ guint channels;
+ guint sample_rate;
+ guint depth;
+
+ guint bitrate;
+ guint max_bitrate;
+ gboolean is_vbr;
+};
+
+GstStreamAudioInformation * gst_stream_audio_information_new (void);
+GstStreamAudioInformation *
+gst_stream_audio_information_copy (GstStreamAudioInformation * ptr);
+void gst_stream_audio_information_free (GstStreamAudioInformation *ptr);
+GType gst_stream_audio_information_get_type (void);
+
+#define GST_TYPE_STREAM_AUDIO_INFORMATION \
+ (gst_stream_audio_information_get_type ())
+#define GST_STREAM_AUDIO_INFORMATION(object) \
+ ((GstStreamAudioInformation *)(object))
+
+
+/**
+ * GstStreamVideoInformation:
+ * @parent: See #GstStreamInformation for fields
+ * @width: Width of the video stream
+ * @height: Height of the video stream
+ * @depth: Depth in bits of the video stream (only relevant for RGB streams)
+ * @frame_rate: Frame rte of the video stream as a fraction
+ * @pixel_aspect_ratio: PAR of the video stream as a fraction
+ * @format: Colorspace and depth of the stream as a #GstVideoFormat
+ * @interlaced: True if the stream is interlaced, false otherwise
+ *
+ * #GstStreamInformation specific to streams of type #GST_STREAM_VIDEO.
+ */
+typedef struct _GstStreamVideoInformation GstStreamVideoInformation;
+
+struct _GstStreamVideoInformation {
+ GstStreamInformation parent;
+
+ guint width;
+ guint height;
+ guint depth;
+ GValue frame_rate;
+ GValue pixel_aspect_ratio;
+ GstVideoFormat format;
+ gboolean interlaced;
+};
+
+GstStreamVideoInformation * gst_stream_video_information_new (void);
+GstStreamVideoInformation *
+gst_stream_video_information_copy (GstStreamVideoInformation * ptr);
+void gst_stream_video_information_free (GstStreamVideoInformation *ptr);
+GType gst_stream_video_information_get_type (void);
+
+#define GST_TYPE_STREAM_VIDEO_INFORMATION \
+ (gst_stream_video_information_get_type ())
+#define GST_STREAM_VIDEO_INFORMATION(object) \
+ ((GstStreamVideoInformation *)(object))
+
+
+/**
+ * GstDiscovererResult:
+ * @GST_DISCOVERER_OK: The discovery was successfull
+ * @GST_DISCOVERER_URI_INVALID: the URI is invalid
+ * @GST_DISCOVERER_ERROR: an error happend and the GError is set
+ * @GST_DISCOVERER_TIMEOUT: the discovery timed-out
+ * @GST_DISCOVERER_BUSY: the discoverer was already discovering a file
+ * @GST_DISCOVERER_MISSING_PLUGINS: Some plugins are missing for full discovery
+ *
+ * Result values for the discovery process.
+ */
+typedef enum
+ {
+ GST_DISCOVERER_OK = 0,
+ GST_DISCOVERER_URI_INVALID = (1<<0),
+ GST_DISCOVERER_ERROR = (1<<1),
+ GST_DISCOVERER_TIMEOUT = (1<<2),
+ GST_DISCOVERER_BUSY = (1<<3),
+ GST_DISCOVERER_MISSING_PLUGINS = (1<<4)
+ } GstDiscovererResult;
+
+
+/**
+ * GstDiscovererInformation:
+ * @uri: The URI for which the information was discovered
+ * @result: Result of discovery as a #GstDiscovererResult
+ * @stream_info: #GstStreamInformation struct with information about the stream and its substreams, preserving the original hierarchy
+ * @stream_list: #GList of streams for easy iteration
+ * @duration: Duration of the stream in nanoseconds
+ * @misc: Miscellaneous information stored as a #GstStructure (for example, information about missing plugins)
+ *
+ * Structure containing the information of a URI analyzed by #GstDiscoverer.
+ */
+typedef struct _GstDiscovererInformation GstDiscovererInformation;
+
+struct _GstDiscovererInformation {
+ gchar *uri;
+ GstDiscovererResult result;
+
+ /* Sub-streams */
+ GstStreamInformation *stream_info;
+ GList *stream_list;
+
+ /* Stream global information */
+ GstClockTime duration;
+ GstStructure *misc;
+};
+
+GstDiscovererInformation * gst_discoverer_information_new (void);
+GstDiscovererInformation *
+gst_discoverer_information_copy (GstDiscovererInformation * ptr);
+void gst_discoverer_information_free (GstDiscovererInformation *ptr);
+GType gst_discoverer_information_get_type (void);
+
+#define GST_TYPE_DISCOVERER_INFORMATION (gst_discoverer_information_get_type ())
+#define GST_DISCOVERER_INFORMATION(object) \
+ ((GstDiscovererInformation *)(object))
+
+
+#define GST_TYPE_DISCOVERER \
+ (gst_discoverer_get_type())
+#define GST_DISCOVERER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DISCOVERER,GstDiscoverer))
+#define GST_DISCOVERER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DISCOVERER,GstDiscovererClass))
+#define GST_IS_DISCOVERER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DISCOVERER))
+#define GST_IS_DISCOVERER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DISCOVERER))
+
+typedef struct _GstDiscoverer GstDiscoverer;
+typedef struct _GstDiscovererClass GstDiscovererClass;
+
+/**
+ * GstDiscoverer:
+ *
+ * The #GstDiscoverer structure.
+ **/
+struct _GstDiscoverer {
+ GObject parent;
+
+ /*< private >*/
+ gboolean async;
+
+ /* allowed time to discover each uri in nanoseconds */
+ GstClockTime timeout;
+
+ /* list of pending URI to process (current excluded) */
+ GList *pending_uris;
+
+ GMutex *lock;
+
+ /* TRUE if processing a URI */
+ gboolean running;
+
+ /* current items */
+ /* FIXME : Protect all this with a lock */
+ GstDiscovererInformation *current_info;
+ GError *current_error;
+ GstStructure *current_topology;
+ GstTagList *current_tags;
+
+ /* List of private streams */
+ GList *streams;
+
+ /* Global elements */
+ GstBin *pipeline;
+ GstElement *uridecodebin;
+ GstBus *bus;
+
+ GType decodebin2_type;
+};
+
+struct _GstDiscovererClass {
+ GObjectClass parentclass;
+
+ /*< signals >*/
+ void (*ready) (GstDiscoverer *discoverer);
+ void (*starting) (GstDiscoverer *discoverer);
+ void (*discovered) (GstDiscoverer *discoverer,
+ GstDiscovererInformation *info,
+ GError *err);
+};
+
+GType gst_discoverer_get_type (void);
+GstDiscoverer *gst_discoverer_new (GstClockTime timeout);
+
+/* Asynchronous API */
+void gst_discoverer_start (GstDiscoverer *discoverer);
+void gst_discoverer_stop (GstDiscoverer *discoverer);
+gboolean gst_discoverer_append_uri (GstDiscoverer *discoverer, gchar *uri);
+
+
+/* Synchronous API */
+GstDiscovererInformation *
+gst_discoverer_discover_uri (GstDiscoverer * discoverer, gchar * uri, GError ** err);
+
+G_END_DECLS
+
+#endif /* _GST_DISCOVERER_H */
diff --git a/gst-convenience/gst-libs/gst/gettext.h b/gst-convenience/gst-libs/gst/gettext.h
new file mode 100644
index 0000000..8b262f4
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/gettext.h
@@ -0,0 +1,69 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+ Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
+
+ This program 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, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that 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 program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option. */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions. */
+# include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+ chokes if dcgettext is defined as a macro. So include it now, to make
+ later inclusions of <locale.h> a NOP. We don't include <libintl.h>
+ as well because people using "gettext.h" will not include <libintl.h>,
+ and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+ is OK. */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Disabled NLS.
+ The casts to 'const char *' serve the purpose of producing warnings
+ for invalid uses of the value returned from these functions.
+ On pre-ANSI systems without 'const', the config.h file is supposed to
+ contain "#define const". */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+ extraction of messages, but does not call gettext(). The run-time
+ translation is done at a different place in the code.
+ The argument, String, should be a literal string. Concatenated strings
+ and other string expressions won't work.
+ The macro's expansion is not parenthesized, so that it is suitable as
+ initializer for static 'char[]' or 'const char[]' variables. */
+#define gettext_noop(String) String
+
+#endif /* _LIBGETTEXT_H */
diff --git a/gst-convenience/gst-libs/gst/gst-i18n-plugin.h b/gst-convenience/gst-libs/gst/gst-i18n-plugin.h
new file mode 100644
index 0000000..2c37a61
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/gst-i18n-plugin.h
@@ -0,0 +1,37 @@
+/* GStreamer
+ * Copyright (C) 2004 Thomas Vander Stichele <thomas@apestaart.org>
+ *
+ * gst-i18n-plugins.h: internationalization macros for the GStreamer plugins
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_I18N_PLUGIN_H__
+#define __GST_I18N_PLUGIN_H__
+
+#include <locale.h> /* some people need it and some people don't */
+#include "gettext.h" /* included with gettext distribution and copied */
+
+#ifndef GETTEXT_PACKAGE
+#error You must define GETTEXT_PACKAGE before including this header.
+#endif
+
+/* we want to use shorthand _() for translating and N_() for marking */
+#define _(String) dgettext (GETTEXT_PACKAGE, String)
+#define N_(String) gettext_noop (String)
+/* FIXME: if we need it, we can add Q_ as well, like in glib */
+
+#endif /* __GST_I18N_PLUGIN_H__ */
diff --git a/gst-convenience/gst-libs/gst/profile/Makefile.am b/gst-convenience/gst-libs/gst/profile/Makefile.am
new file mode 100644
index 0000000..adc21a8
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/profile/Makefile.am
@@ -0,0 +1,82 @@
+# variables used for enum/marshal generation
+glib_enum_headers = gstprofile.h
+glib_enum_define = GST_PROFILE
+glib_gen_prefix = gst_profile
+glib_gen_basename = profile
+
+built_sources = profile-enumtypes.c
+built_headers = profile-enumtypes.h
+BUILT_SOURCES = $(built_sources) $(built_headers)
+
+lib_LTLIBRARIES = libgstprofile-gupnp-dlna-@GST_MAJORMINOR@.la
+
+CLEANFILES = #$(BUILT_SOURCES)
+EXTRA_DIST = $(BUILT_SOURCES)
+
+libgstprofile_gupnp_dlna_@GST_MAJORMINOR@_la_SOURCES = \
+ gstprofile.c
+nodist_libgstprofile_gupnp_dlna_@GST_MAJORMINOR@_la_SOURCES = $(BUILT_SOURCES)
+
+libgstprofile_gupnp_dlna_@GST_MAJORMINOR@includedir = $(includedir)/gupnp-dlna-1.0/gst/profile
+libgstprofile_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS = \
+ gstprofile.h
+nodist_libgstprofile_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS = $(built_headers)
+
+libgstprofile_gupnp_dlna_@GST_MAJORMINOR@_la_CFLAGS = -I$(top_srcdir)/gst-convenience/gst-libs $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
+libgstprofile_gupnp_dlna_@GST_MAJORMINOR@_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
+libgstprofile_gupnp_dlna_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
+
+#include $(top_srcdir)/common/gst-glib-gen.mak
+
+#if HAVE_INTROSPECTION
+#
+#BUILT_GIRSOURCES = GstProfile-@GST_MAJORMINOR@.gir
+#
+#gir_headers=$(patsubst %,$(srcdir)/%, $(libgstprofile_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS))
+#gir_headers+=$(patsubst %,$(builddir)/%, $(nodist_libgstprofile_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS))
+#gir_sources=$(patsubst %,$(srcdir)/%, $(libgstprofile_gupnp_dlna_@GST_MAJORMINOR@_la_SOURCES))
+#gir_sources+=$(patsubst %,$(builddir)/%, $(nodist_libgstprofile_gupnp_dlna_@GST_MAJORMINOR@_la_SOURCES))
+#gir_cincludes=$(patsubst %,--c-include='gst/profile/%',$(libgstprofile_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS))
+#gir_cincludes+=$(patsubst %,--c-include='gst/profile/%',$(nodist_libgstprofile_gupnp_dlna_@GST_MAJORMINOR@include_HEADERS))
+#
+#GstProfile-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgstprofile-gupnp-dlna-@GST_MAJORMINOR@.la
+# $(AM_V_GEN)PKG_CONFIG_PATH="$(PKG_CONFIG_PATH):$(top_builddir)/pkgconfig" \
+# $(INTROSPECTION_SCANNER) -v --namespace GstProfile \
+# --nsversion=@GST_MAJORMINOR@ \
+# --strip-prefix=Gst \
+# $(gir_cincludes) \
+# -I$(top_srcdir)/gst-libs \
+# -I$(top_builddir)/gst-libs \
+# --add-include-path=$$($(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@) \
+# --add-include-path=$$($(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_MAJORMINOR@) \
+# --library=libgstprofile-gupnp-dlna-@GST_MAJORMINOR@.la \
+# --include=Gst-@GST_MAJORMINOR@ \
+# --include=GstBase-@GST_MAJORMINOR@ \
+# --include=GstVideo-@GST_MAJORMINOR@ \
+# --libtool="$(top_builddir)/libtool" \
+# --pkg gstreamer-@GST_MAJORMINOR@ \
+# --pkg gstreamer-base-@GST_MAJORMINOR@ \
+# --output $@ \
+# $(gir_headers) \
+# $(gir_sources)
+#
+## INTROSPECTION_GIRDIR/INTROSPECTION_TYPELIBDIR aren't the right place to
+## install anything - we need to install inside our prefix.
+#girdir = $(datadir)/gir-1.0
+#gir_DATA = $(BUILT_GIRSOURCES)
+#
+#typelibsdir = $(libdir)/girepository-1.0/
+#
+#typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib)
+#
+#%.typelib: %.gir $(INTROSPECTION_COMPILER)
+# $(AM_V_GEN)$(INTROSPECTION_COMPILER) \
+# --includedir=$(srcdir) \
+# --includedir=$(builddir) \
+# --includedir=$$($(PKG_CONFIG) --variable=girdir gstreamer-@GST_MAJORMINOR@) \
+# --includedir=$$($(PKG_CONFIG) --variable=girdir gstreamer-base-@GST_MAJORMINOR@) \
+# $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F)
+#
+#CLEANFILES += $(BUILT_GIRSOURCES) $(typelibs_DATA)
+#
+#endif
diff --git a/gst-convenience/gst-libs/gst/profile/gstprofile.c b/gst-convenience/gst-libs/gst/profile/gstprofile.c
new file mode 100644
index 0000000..d686459
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/profile/gstprofile.c
@@ -0,0 +1,553 @@
+/* GStreamer encoding profiles library
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * (C) 2009 Nokia Corporation
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gstprofile
+ * @short_description: Encoding profile library
+ *
+ * Since 0.10.XXXX
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstprofile.h"
+
+/* GstEncodingProfile API */
+
+GType
+gst_encoding_profile_get_type (void)
+{
+ static GType gst_encoding_profile_type = 0;
+
+ if (G_UNLIKELY (gst_encoding_profile_type == 0)) {
+ gst_encoding_profile_type =
+ g_boxed_type_register_static ("GstEncodingProfile",
+ (GBoxedCopyFunc) gst_encoding_profile_copy,
+ (GBoxedFreeFunc) gst_encoding_profile_free);
+ }
+
+ return gst_encoding_profile_type;
+}
+
+GType
+gst_stream_encoding_profile_get_type (void)
+{
+ static GType gst_stream_encoding_profile_type = 0;
+
+ if (G_UNLIKELY (gst_stream_encoding_profile_type == 0)) {
+ gst_stream_encoding_profile_type =
+ g_boxed_type_register_static ("GstStreamEncodingProfile",
+ (GBoxedCopyFunc) gst_stream_encoding_profile_copy,
+ (GBoxedFreeFunc) gst_stream_encoding_profile_free);
+ }
+
+ return gst_stream_encoding_profile_type;
+}
+
+GType
+gst_video_encoding_profile_get_type (void)
+{
+ static GType gst_video_encoding_profile_type = 0;
+
+ if (G_UNLIKELY (gst_video_encoding_profile_type == 0)) {
+ gst_video_encoding_profile_type =
+ g_boxed_type_register_static ("GstVideoEncodingProfile",
+ (GBoxedCopyFunc) gst_stream_encoding_profile_copy,
+ (GBoxedFreeFunc) gst_stream_encoding_profile_free);
+ }
+
+ return gst_video_encoding_profile_type;
+}
+
+/**
+ * gst_encoding_profile_copy:
+ * @profile: a #GstEncodingProfile
+ *
+ * Creates a copy of the given profile.
+ *
+ * Returns: a new #GstEncodingProfile
+ */
+GstEncodingProfile *
+gst_encoding_profile_copy (GstEncodingProfile * prof)
+{
+ GstEncodingProfile *copy;
+ GList *tmp;
+ GstStreamEncodingProfile *sprof;
+
+ copy = gst_encoding_profile_new (prof->name, prof->format, prof->preset,
+ prof->multipass);
+
+ for (tmp = prof->encodingprofiles; tmp; tmp = tmp->next) {
+ sprof = (GstStreamEncodingProfile *) tmp->data;
+ copy->encodingprofiles = g_list_append (copy->encodingprofiles,
+ gst_stream_encoding_profile_copy (sprof));
+ }
+
+ return copy;
+}
+
+/**
+ * gst_encoding_profile_free:
+ * @profile: a #GstEncodingProfile
+ *
+ * Frees the profile and associated streams
+ */
+void
+gst_encoding_profile_free (GstEncodingProfile * prof)
+{
+ if (prof->name)
+ g_free (prof->name);
+ if (prof->format)
+ gst_caps_unref (prof->format);
+ if (prof->preset)
+ g_free (prof->preset);
+
+ /* FIXME : Free stream profiles */
+ g_list_foreach (prof->encodingprofiles,
+ (GFunc) gst_stream_encoding_profile_free, NULL);
+ g_list_free (prof->encodingprofiles);
+
+ g_free (prof);
+}
+
+/**
+ * gst_encoding_profile_new:
+ * @name: the name to use for the profile
+ * @format: the #GstCaps corresponding to the output container format. Can
+ * be #NULL for container-less encoding profiles, in which case only a single
+ * #GstStreamEncodingProfile can be added to it.
+ * @preset: the #GstPreset to use on the muxer, can be #NULL
+ * @multipass: Set to %TRUE if this profile uses multi-pass video encoding.
+ *
+ * Creates a new #GstEncodingProfile.
+ *
+ * All provided allocatable arguments will be internally copied, so can be
+ * safely freed/unreferenced after calling this method.
+ *
+ * Returns: the newly created #GstEncodingProfile.
+ */
+GstEncodingProfile *
+gst_encoding_profile_new (gchar * name, GstCaps * format, gchar * preset,
+ gboolean multipass)
+{
+ GstEncodingProfile *prof;
+
+ prof = g_new0 (GstEncodingProfile, 1);
+
+ prof->name = g_strdup (name);
+ if (format)
+ prof->format = gst_caps_copy (format);
+ if (preset)
+ prof->preset = g_strdup (preset);
+ prof->multipass = multipass;
+
+ return prof;
+}
+
+/**
+ * gst_encoding_profile_add_stream:
+ * @profile: the #GstEncodingProfile to use
+ * @stream: the #GstStreamEncodingProfile to add.
+ *
+ * Add a #GstStreamEncodingProfile to the list of profiles handled by @profile.
+ *
+ * No copy of @stream will be made, if you wish to use it elsewhere after this
+ * method you should copy it.
+ *
+ * Returns: %TRUE if the @stream was properly added, else %FALSE.
+ */
+gboolean
+gst_encoding_profile_add_stream (GstEncodingProfile * profile,
+ GstStreamEncodingProfile * stream)
+{
+ if ((profile->format == NULL) && profile->encodingprofiles) {
+ GST_ERROR ("Container-less profiles can also have one stream profile");
+ return FALSE;
+ }
+ /* FIXME : Maybe we should have a better way to detect if an exactly
+ * similar profile is already present */
+ profile->encodingprofiles = g_list_append (profile->encodingprofiles, stream);
+
+ return TRUE;
+}
+
+/**
+ * gst_encoding_profile_get_codec_caps:
+ * @profile: a #GstEncodingProfile
+ *
+ * Returns all the caps that can bypass encoding process.
+ *
+ * This is useful if you wish to connect (uri)decodebin to encodebin and avoid
+ * as much decoding/encoding as possible. You can pass those caps to the 'caps'
+ * properties of those elements.
+ *
+ * Returns: The caps that will bypass the encoding process.
+ */
+GstCaps *
+gst_encoding_profile_get_codec_caps (GstEncodingProfile * profile)
+{
+ GstCaps *res;
+ GList *tmp;
+
+ res = gst_caps_new_empty ();
+
+ for (tmp = profile->encodingprofiles; tmp; tmp = g_list_next (tmp)) {
+ GstStreamEncodingProfile *sprof = (GstStreamEncodingProfile *) tmp->data;
+ gst_caps_append (res, gst_stream_encoding_profile_get_output_caps (sprof));
+ }
+
+ return res;
+}
+
+
+/* GstStreamEncodingProfile API */
+/**
+ * gst_stream_encoding_profile_new:
+ * @type: a #GstEncodingProfileType
+ * @format: the #GstCaps of the encoding media.
+ * @preset: the preset(s) to use on the encoder, can be #NULL
+ * @restriction: the #GstCaps used to restrict the input to the encoder, can be
+ * NULL.
+ * @presence: the number of time this stream must be used. 0 means any number of
+ * times (including never)
+ *
+ * Creates a new #GstStreamEncodingProfile to be used in a #GstEncodingProfile.
+ *
+ * All provided allocatable arguments will be internally copied, so can be
+ * safely freed/unreferenced after calling this method.
+ *
+ * Returns: the newly created #GstStreamEncodingProfile.
+ */
+GstStreamEncodingProfile *
+gst_stream_encoding_profile_new (GstEncodingProfileType type,
+ GstCaps * format, gchar * preset, GstCaps * restriction, guint presence)
+{
+ GstStreamEncodingProfile *prof;
+
+ if (type == GST_ENCODING_PROFILE_VIDEO)
+ prof = (GstStreamEncodingProfile *) g_new0 (GstVideoEncodingProfile, 1);
+ else
+ prof = g_new0 (GstStreamEncodingProfile, 1);
+ prof->type = type;
+ prof->format = gst_caps_copy (format);
+ prof->preset = g_strdup (preset);
+ if (restriction)
+ prof->restriction = gst_caps_copy (restriction);
+ else
+ prof->restriction = gst_caps_new_any ();
+ prof->presence = presence;
+
+ return prof;
+}
+
+/**
+ * gst_stream_encoding_profile_free:
+ * @profile: a #GstStreamEncodingProfile
+ *
+ * Frees the profile and associated entries.
+ */
+void
+gst_stream_encoding_profile_free (GstStreamEncodingProfile * prof)
+{
+ gst_caps_unref (prof->format);
+ if (prof->preset)
+ g_free (prof->preset);
+ if (prof->restriction)
+ gst_caps_unref (prof->restriction);
+ g_free (prof);
+}
+
+/**
+ * gst_stream_encoding_profile_copy:
+ * @profile: a #GstStreamEncodingProfile
+ *
+ * Copies the stream profile and associated entries.
+ *
+ * Returns: A copy of the provided profile
+ */
+GstStreamEncodingProfile *
+gst_stream_encoding_profile_copy (GstStreamEncodingProfile * prof)
+{
+ GstStreamEncodingProfile *copy;
+
+ if (prof->type == GST_ENCODING_PROFILE_VIDEO)
+ copy = gst_video_encoding_profile_new (prof->format, prof->preset,
+ prof->restriction, prof->presence,
+ ((GstVideoEncodingProfile *) prof)->pass);
+ else
+ copy =
+ gst_stream_encoding_profile_new (prof->type, prof->format, prof->preset,
+ prof->restriction, prof->presence);
+
+ return copy;
+}
+
+/**
+ * gst_video_encoding_profile_new:
+ * @format: the #GstCaps
+ * @preset: the preset(s) to use on the encoder, can be #NULL
+ * @restriction: the #GstCaps used to restrict the input to the encoder, can be
+ * NULL.
+ * @presence: the number of time this stream must be used. 0 means any number of
+ * times (including never)
+ * @pass: For which pass this profile should be used. Set to 0 if no multipass
+ * encoding is needed.
+ *
+ * Creates a new #GstStreamEncodingProfile to be used in a #GstEncodingProfile.
+ *
+ * All provided allocatable arguments will be internally copied, so can be
+ * safely freed/unreferenced after calling this method.
+ *
+ * Returns: the newly created #GstStreamEncodingProfile.
+ */
+GstStreamEncodingProfile *
+gst_video_encoding_profile_new (GstCaps * format, gchar * preset,
+ GstCaps * restriction, guint presence, guint pass)
+{
+ GstStreamEncodingProfile *prof;
+
+ prof =
+ gst_stream_encoding_profile_new (GST_ENCODING_PROFILE_VIDEO, format,
+ preset, restriction, presence);
+ ((GstVideoEncodingProfile *) prof)->pass = pass;
+
+ return prof;
+}
+
+/**
+ * gst_stream_encoding_profile_get_output_caps:
+ * @profile: a #GstStreamEncodingProfile
+ *
+ * Computes the full output caps that this @profile will generate.
+ *
+ * Returns: The full output caps for the given @profile. Call gst_caps_unref
+ * when you are done with the caps.
+ */
+GstCaps *
+gst_stream_encoding_profile_get_output_caps (GstStreamEncodingProfile * profile)
+{
+ GstCaps *out, *tmp;
+ GstStructure *st, *outst;
+ GQuark out_name;
+ guint i, len;
+
+ /* fast-path */
+ if ((profile->restriction == NULL) || gst_caps_is_any (profile->restriction))
+ return gst_caps_copy (profile->format);
+
+ /* Combine the format with the restriction caps */
+ outst = gst_caps_get_structure (profile->format, 0);
+ out_name = gst_structure_get_name_id (outst);
+ tmp = gst_caps_new_empty ();
+ len = gst_caps_get_size (profile->restriction);
+
+ for (i = 0; i < len; i++) {
+ st = gst_structure_copy (gst_caps_get_structure (profile->restriction, i));
+ st->name = out_name;
+ gst_caps_append_structure (tmp, st);
+ }
+
+ out = gst_caps_intersect (tmp, profile->format);
+ gst_caps_unref (tmp);
+
+ return out;
+}
+
+/**
+ * gst_encoding_category_list_target:
+ * @category: a profile target category name. Can be NULL.
+ *
+ * NOT IMPLEMENTED
+ *
+ * Returns: the list of all available #GstEncodingTarget for the given @category.
+ * If @category is #NULL, then all available #GstEncodingTarget are returned.
+ */
+
+GList *
+gst_encoding_category_list_target (gchar * category)
+{
+ /* FIXME : IMPLEMENT */
+ return NULL;
+}
+
+/**
+ * gst_profile_list_target_categories:
+ *
+ * list available profile target categories
+ *
+ * NOT IMPLEMENTED
+ */
+GList *
+gst_profile_list_target_categories (void)
+{
+ /* FIXME : IMPLEMENT */
+ return NULL;
+}
+
+/**
+ * gst_profile_target_save:
+ *
+ * NOT IMPLEMENTED
+ */
+gboolean
+gst_profile_target_save (GstEncodingTarget * target)
+{
+ /* FIXME : IMPLEMENT */
+ return FALSE;
+}
+
+/**
+ * gst_pb_utils_create_encoder:
+ * @caps: The #GstCaps corresponding to a codec format
+ * @preset: The name of a preset, can be #NULL
+ * @name: The name to give to the returned instance, can be #NULL.
+ *
+ * Creates an encoder which can output the given @caps. If several encoders can
+ * output the given @caps, then the one with the highest rank will be picked.
+ * If a @preset is specified, it will be applied to the created encoder before
+ * returning it.
+ * If a @preset is specified, then the highest-ranked encoder that can accept
+ * the givein preset will be returned.
+ *
+ * Returns: The encoder instance with the preset applied if it is available.
+ * #NULL if no encoder is available.
+ */
+GstElement *
+gst_pb_utils_create_encoder (GstCaps * caps, gchar * preset, gchar * name)
+{
+ /* FIXME : IMPLEMENT */
+ return NULL;
+}
+
+
+/**
+ * gst_pb_utils_create_encoder_format:
+ *
+ * Convenience version of @gst_pb_utils_create_encoder except one does not need
+ * to create a #GstCaps.
+ */
+
+GstElement *
+gst_pb_utils_create_encoder_format (gchar * format, gchar * preset,
+ gchar * name)
+{
+ /* FIXME : IMPLEMENT */
+ return NULL;
+}
+
+/**
+ * gst_pb_utils_create_muxer:
+ * @caps: The #GstCaps corresponding to a codec format
+ * @preset: The name of a preset, can be NULL
+ *
+ * Creates an muxer which can output the given @caps. If several muxers can
+ * output the given @caps, then the one with the highest rank will be picked.
+ * If a @preset is specified, it will be applied to the created muxer before
+ * returning it.
+ * If a @preset is specified, then the highest-ranked muxer that can accept
+ * the givein preset will be returned.
+ *
+ * Returns: The muxer instance with the preset applied if it is available.
+ * #NULL if no muxer is available.
+ */
+GstElement *
+gst_pb_utils_create_muxer (GstCaps * caps, gchar * preset)
+{
+ /* FIXME : IMPLEMENT */
+ return NULL;
+}
+
+
+/**
+ * gst_pb_utils_create_muxer_format:
+ *
+ * Convenience version of @gst_pb_utils_create_muxer except one does not need
+ * to create a #GstCaps.
+ */
+GstElement *
+gst_pb_utils_create_muxer_format (gchar * format, gchar * preset, gchar * name)
+{
+ /* FIXME : IMPLEMENT */
+ return NULL;
+}
+
+
+/**
+ * gst_pb_utils_encoders_compatible_with_muxer:
+ * @muxer: a muxer instance
+ *
+ * Finds a list of available encoders whose output can be fed to the given
+ * @muxer.
+ *
+ * Returns: A list of compatible encoders, or #NULL if none can be found.
+ */
+GList *
+gst_pb_utils_encoders_compatible_with_muxer (GstElement * muxer)
+{
+ /* FIXME : IMPLEMENT */
+ return NULL;
+}
+
+GList *
+gst_pb_utils_muxers_compatible_with_encoder (GstElement * encoder)
+{
+ /* FIXME : IMPLEMENT */
+ return NULL;
+}
+
+
+/*
+ * GstPreset modifications
+ */
+
+/**
+ * gst_preset_create:
+ * @preset: The #GstPreset on which to create the preset
+ * @name: A name for the preset
+ * @properties: The properties
+ *
+ * Creates a new preset with the given properties. This preset will only
+ * exist during the lifetime of the process.
+ * If you wish to use it after the lifetime of the process, you must call
+ * @gst_preset_save_preset.
+ *
+ * Returns: #TRUE if the preset could be created, else #FALSE.
+ */
+gboolean
+gst_preset_create (GstPreset * preset, gchar * name, GstStructure * properties)
+{
+ /* FIXME : IMPLEMENT */
+ return FALSE;
+}
+
+/**
+ * gst_preset_reset:
+ * @preset: a #GstPreset
+ *
+ * Sets all the properties of the element back to their default values.
+ */
+/* FIXME : This could actually be put at the GstObject level, or maybe even
+ * at the GObject level. */
+void
+gst_preset_reset (GstPreset * preset)
+{
+
+}
diff --git a/gst-convenience/gst-libs/gst/profile/gstprofile.h b/gst-convenience/gst-libs/gst/profile/gstprofile.h
new file mode 100644
index 0000000..2364011
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/profile/gstprofile.h
@@ -0,0 +1,234 @@
+/* GStreamer encoding profiles library
+ * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
+ * (C) 2009 Nokia Corporation
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_PROFILE_H__
+#define __GST_PROFILE_H__
+
+#ifndef GST_USE_UNSTABLE_API
+#warning "GstProfile is unstable API and may change in future."
+#warning "You can define GST_USE_UNSTABLE_API to avoid this warning."
+#endif
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#include <gst/profile/profile-enumtypes.h>
+
+#define GST_TYPE_ENCODING_PROFILE (gst_encoding_profile_get_type())
+#define GST_ENCODING_PROFILE(object) ((GstEncodingProfile*)object)
+
+#define GST_TYPE_STREAM_ENCODING_PROFILE (gst_stream_encoding_profile_get_type())
+#define GST_STREAM_ENCODING_PROFILE(object) ((GstStreamEncodingProfile*)object)
+
+#define GST_TYPE_VIDEO_ENCODING_PROFILE (gst_video_encoding_profile_get_type())
+#define GST_VIDEO_ENCODING_PROFILE(object) ((GstVideoEncodingProfile*)object)
+
+/**
+ * GstEncodingProfileType:
+ * @GST_ENCODING_PROFILE_UNKNOWN: unknown stream
+ * @GST_ENCODING_PROFILE_VIDEO: video stream
+ * @GST_ENCODING_PROFILE_AUDIO: audio stream
+ * @GST_ENCODING_PROFILE_TEXT: text/subtitle stream
+ * @GST_ENCODING_PROFILE_IMAGE: image
+ *
+ * Type of stream for a #GstStreamEncodingProfile
+ **/
+typedef enum {
+ GST_ENCODING_PROFILE_UNKNOWN,
+ GST_ENCODING_PROFILE_VIDEO,
+ GST_ENCODING_PROFILE_AUDIO,
+ GST_ENCODING_PROFILE_TEXT,
+ GST_ENCODING_PROFILE_IMAGE,
+ /* Room for extenstion */
+} GstEncodingProfileType;
+
+typedef struct _GstEncodingTarget GstEncodingTarget;
+typedef struct _GstEncodingProfile GstEncodingProfile;
+typedef struct _GstStreamEncodingProfile GstStreamEncodingProfile;
+typedef struct _GstVideoEncodingProfile GstVideoEncodingProfile;
+
+/* FIXME/UNKNOWNS
+ *
+ * Should encoding categories be well-known strings/quarks ?
+ *
+ */
+
+/**
+ * GstEncodingTarget:
+ * @name: The name of the target profile.
+ * @category: The target category (device, service, use-case).
+ * @profiles: A list of #GstProfile this device supports.
+ *
+ */
+struct _GstEncodingTarget {
+ gchar *name;
+ gchar *category;
+ GList *profiles;
+};
+
+/**
+ * GstEncodingProfile:
+ * @name: The name of the profile
+ * @format: The #GstCaps corresponding to the muxing format.
+ * @preset: The name of the #GstPreset(s) to be used on the muxer. This is optional.
+ * @multipass: Whether this profile is a multi-pass profile or not.
+ * @encodingprofiles: A list of #GstStreamEncodingProfile for the various streams.
+ *
+ * Profile for a specific combination of format/stream encoding.
+ */
+
+struct _GstEncodingProfile {
+ gchar *name;
+ GstCaps *format;
+ gchar *preset;
+ gboolean multipass;
+ GList *encodingprofiles;
+};
+
+/**
+ * GstStreamEncodingProfile:
+ * @type: Type of profile
+ * @format: The GStreamer mime type corresponding to the encoding format.
+ * @preset: The name of the #GstPreset to be used on the encoder. This is optional.
+ * @restriction: The #GstCaps restricting the input. This is optional.
+ * @presence: The number of streams that can be created. 0 => any.
+ *
+ * Invididual stream profile, to be used in #GstEncodingProfile
+ */
+struct _GstStreamEncodingProfile {
+ GstEncodingProfileType type;
+ GstCaps *format;
+ gchar *preset;
+ GstCaps *restriction;
+ guint presence;
+};
+
+/**
+ * GstVideoEncodingProfile:
+ * @profile: common #GstEncodingProfile part.
+ * @pass: The pass number if this is part of a multi-pass profile. Starts at 1
+ * for multi-pass. Set to 0 if this is not part of a multi-pass profile.
+ * @vfr: Variable Frame Rate allowed.
+ * If %FALSE (default), the incoming video frames will be duplicated/dropped in
+ * order to produce an exact constant framerate.
+ * If %TRUE, then the incoming video stream will not be modified.
+ *
+ * Variant of #GstStreamEncodingProfile for video streams, allows specifying the @pass.
+ */
+struct _GstVideoEncodingProfile {
+ GstStreamEncodingProfile profile;
+ guint pass;
+ gboolean vfr;
+};
+
+/* GstEncodingProfile API */
+GType gst_encoding_profile_get_type (void);
+GstEncodingProfile * gst_encoding_profile_new (gchar *name, GstCaps *format,
+ gchar *preset,
+ gboolean multipass);
+GstEncodingProfile * gst_encoding_profile_copy (GstEncodingProfile *profile);
+
+void gst_encoding_profile_free (GstEncodingProfile *profile);
+
+gboolean gst_encoding_profile_add_stream (GstEncodingProfile *profile,
+ GstStreamEncodingProfile *stream);
+GstCaps * gst_encoding_profile_get_codec_caps (GstEncodingProfile *profile);
+
+
+/* GstStreamEncodingProfile API */
+GType gst_stream_encoding_profile_get_type (void);
+GstStreamEncodingProfile * gst_stream_encoding_profile_new (GstEncodingProfileType type,
+ GstCaps *format,
+ gchar *preset,
+ GstCaps *restriction,
+ guint presence);
+
+GType gst_video_encoding_profile_get_type (void);
+GstStreamEncodingProfile * gst_video_encoding_profile_new (GstCaps *format,
+ gchar *preset,
+ GstCaps *restriction,
+ guint presence,
+ guint pass);
+
+GstStreamEncodingProfile * gst_stream_encoding_profile_copy (GstStreamEncodingProfile *profile);
+
+void gst_stream_encoding_profile_free (GstStreamEncodingProfile *profile);
+
+GstCaps * gst_stream_encoding_profile_get_output_caps (GstStreamEncodingProfile *profile);
+
+/* Generic helper API */
+GList *gst_encoding_category_list_target (gchar *category);
+
+GList *gst_profile_list_target_categories (void);
+
+gboolean gst_profile_target_save (GstEncodingTarget *target);
+
+
+/*
+ * Application convenience methods (possibly to be added in gst-pb-utils)
+ */
+
+GstElement *gst_pb_utils_create_encoder(GstCaps *caps, gchar *preset, gchar *name);
+GstElement *gst_pb_utils_create_encoder_format(gchar *format, gchar *preset,
+ gchar *name);
+
+GstElement *gst_pb_utils_create_muxer(GstCaps *caps, gchar *preset);
+GstElement *gst_pb_utils_create_muxer_format(gchar *format, gchar *preset,
+ gchar *name);
+
+GList *gst_pb_utils_encoders_compatible_with_muxer(GstElement *muxer);
+
+GList *gst_pb_utils_muxers_compatible_with_encoder(GstElement *encoder);
+
+
+/*
+ * GstPreset modifications
+ */
+
+/**
+ * gst_preset_create:
+ * @preset: The #GstPreset on which to create the preset
+ * @name: A name for the preset
+ * @properties: The properties
+ *
+ * Creates a new preset with the given properties. This preset will only
+ * exist during the lifetime of the process.
+ * If you wish to use it after the lifetime of the process, you must call
+ * @gst_preset_save_preset.
+ *
+ * Returns: #TRUE if the preset could be created, else #FALSE.
+ */
+gboolean gst_preset_create (GstPreset *preset, gchar *name,
+ GstStructure *properties);
+
+/**
+ * gst_preset_reset:
+ * @preset: a #GstPreset
+ *
+ * Sets all the properties of the element back to their default values.
+ */
+/* FIXME : This could actually be put at the GstObject level, or maybe even
+ * at the GObject level. */
+void gst_preset_reset (GstPreset *preset);
+
+G_END_DECLS
+
+#endif /* __GST_PROFILE_H__ */
diff --git a/gst-convenience/gst-libs/gst/profile/profile-enumtypes.c b/gst-convenience/gst-libs/gst/profile/profile-enumtypes.c
new file mode 100644
index 0000000..0136351
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/profile/profile-enumtypes.c
@@ -0,0 +1,29 @@
+
+
+
+#include "profile-enumtypes.h"
+
+#include "gstprofile.h"
+
+/* enumerations from "gstprofile.h" */
+GType
+gst_encoding_profile_type_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile)) {
+ static const GEnumValue values[] = {
+ { GST_ENCODING_PROFILE_UNKNOWN, "GST_ENCODING_PROFILE_UNKNOWN", "unknown" },
+ { GST_ENCODING_PROFILE_VIDEO, "GST_ENCODING_PROFILE_VIDEO", "video" },
+ { GST_ENCODING_PROFILE_AUDIO, "GST_ENCODING_PROFILE_AUDIO", "audio" },
+ { GST_ENCODING_PROFILE_TEXT, "GST_ENCODING_PROFILE_TEXT", "text" },
+ { GST_ENCODING_PROFILE_IMAGE, "GST_ENCODING_PROFILE_IMAGE", "image" },
+ { 0, NULL, NULL }
+ };
+ GType g_define_type_id = g_enum_register_static ("GstEncodingProfileType", values);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+
+
diff --git a/gst-convenience/gst-libs/gst/profile/profile-enumtypes.h b/gst-convenience/gst-libs/gst/profile/profile-enumtypes.h
new file mode 100644
index 0000000..25dbe46
--- /dev/null
+++ b/gst-convenience/gst-libs/gst/profile/profile-enumtypes.h
@@ -0,0 +1,19 @@
+
+
+
+#ifndef __GST_PROFILE_ENUM_TYPES_H__
+#define __GST_PROFILE_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* enumerations from "gstprofile.h" */
+GType gst_encoding_profile_type_get_type (void);
+#define GST_TYPE_ENCODING_PROFILE_TYPE (gst_encoding_profile_type_get_type())
+G_END_DECLS
+
+#endif /* __GST_PROFILE_ENUM_TYPES_H__ */
+
+
+
diff --git a/gst-convenience/gstreamer-discoverer-gupnp-dlna-0.10.deps b/gst-convenience/gstreamer-discoverer-gupnp-dlna-0.10.deps
new file mode 100644
index 0000000..366fb41
--- /dev/null
+++ b/gst-convenience/gstreamer-discoverer-gupnp-dlna-0.10.deps
@@ -0,0 +1,2 @@
+gstreamer-0.10
+gstreamer-video-0.10
diff --git a/gst-convenience/gstreamer-discoverer-gupnp-dlna-0.10.vapi b/gst-convenience/gstreamer-discoverer-gupnp-dlna-0.10.vapi
new file mode 100644
index 0000000..e5b510d
--- /dev/null
+++ b/gst-convenience/gstreamer-discoverer-gupnp-dlna-0.10.vapi
@@ -0,0 +1,106 @@
+/* gstreamer-discoverer-0.10.vapi generated by vapigen, do not modify. */
+
+[CCode (cprefix = "Gst", lower_case_cprefix = "gst_")]
+namespace Gst {
+ [CCode (cheader_filename = "gst/discoverer/gstdiscoverer.h")]
+ public class Discoverer : GLib.Object {
+ public bool @async;
+ public weak Gst.Bus bus;
+ public weak GLib.Error current_error;
+ public weak Gst.DiscovererInformation current_info;
+ public weak Gst.TagList current_tags;
+ public weak Gst.Structure current_topology;
+ public GLib.Type decodebin2_type;
+ public weak GLib.Mutex @lock;
+ public weak GLib.List pending_uris;
+ public weak Gst.Bin pipeline;
+ public bool running;
+ public weak GLib.List streams;
+ public weak Gst.Element uridecodebin;
+ [CCode (has_construct_function = false)]
+ public Discoverer (Gst.ClockTime timeout);
+ public bool append_uri (string uri);
+ public Gst.DiscovererInformation discover_uri (string uri) throws GLib.Error;
+ public void start ();
+ public void stop ();
+ [NoAccessorMethod]
+ public uint64 timeout { get; set construct; }
+ public virtual signal void discovered (Gst.DiscovererInformation info, GLib.Error err);
+ public virtual signal void ready ();
+ public virtual signal void starting ();
+ }
+ [Compact]
+ [CCode (copy_function = "gst_discoverer_information_copy", type_id = "GST_TYPE_DISCOVERER_INFORMATION", cheader_filename = "gst/discoverer/gstdiscoverer.h")]
+ public class DiscovererInformation {
+ public Gst.ClockTime duration;
+ public weak Gst.Structure misc;
+ public Gst.DiscovererResult result;
+ public weak Gst.StreamInformation stream_info;
+ public weak GLib.List<Gst.StreamInformation> stream_list;
+ public weak string uri;
+ [CCode (has_construct_function = false)]
+ public DiscovererInformation ();
+ }
+ [Compact]
+ [CCode (copy_function = "gst_stream_audio_information_copy", type_id = "GST_TYPE_STREAM_AUDIO_INFORMATION", cheader_filename = "gst/discoverer/gstdiscoverer.h")]
+ public class StreamAudioInformation : Gst.StreamInformation {
+ public uint bitrate;
+ public uint channels;
+ public uint depth;
+ public bool is_vbr;
+ public uint max_bitrate;
+ public uint sample_rate;
+ [CCode (has_construct_function = false)]
+ public StreamAudioInformation ();
+ }
+ [Compact]
+ [CCode (copy_function = "gst_stream_container_information_copy", type_id = "GST_TYPE_STREAM_CONTAINER_INFORMATION", cheader_filename = "gst/discoverer/gstdiscoverer.h")]
+ public class StreamContainerInformation : Gst.StreamInformation {
+ public weak GLib.List streams;
+ [CCode (has_construct_function = false)]
+ public StreamContainerInformation ();
+ }
+ [Compact]
+ [CCode (copy_function = "gst_stream_information_copy", type_id = "GST_TYPE_STREAM_INFORMATION", cheader_filename = "gst/discoverer/gstdiscoverer.h")]
+ public class StreamInformation {
+ public weak Gst.Caps caps;
+ public weak Gst.Structure misc;
+ public weak Gst.StreamInformation next;
+ public weak Gst.StreamInformation previous;
+ public Gst.StreamType streamtype;
+ public weak Gst.TagList tags;
+ [CCode (has_construct_function = false)]
+ public StreamInformation ();
+ }
+ [Compact]
+ [CCode (copy_function = "gst_stream_video_information_copy", type_id = "GST_TYPE_STREAM_VIDEO_INFORMATION", cheader_filename = "gst/discoverer/gstdiscoverer.h")]
+ public class StreamVideoInformation : Gst.StreamInformation {
+ public uint depth;
+ public Gst.VideoFormat format;
+ public Gst.Value frame_rate;
+ public uint height;
+ public bool interlaced;
+ public Gst.Value pixel_aspect_ratio;
+ public uint width;
+ [CCode (has_construct_function = false)]
+ public StreamVideoInformation ();
+ }
+ [CCode (cprefix = "GST_DISCOVERER_", cheader_filename = "gst/discoverer/gstdiscoverer.h")]
+ [Flags]
+ public enum DiscovererResult {
+ OK,
+ URI_INVALID,
+ ERROR,
+ TIMEOUT,
+ BUSY,
+ MISSING_PLUGINS
+ }
+ [CCode (cprefix = "GST_STREAM_", cheader_filename = "gst/discoverer/gstdiscoverer.h")]
+ public enum StreamType {
+ CONTAINER,
+ AUDIO,
+ VIDEO,
+ IMAGE,
+ UNKNOWN
+ }
+}
diff --git a/gst-convenience/gstreamer-profile-gupnp-dlna-0.10.deps b/gst-convenience/gstreamer-profile-gupnp-dlna-0.10.deps
new file mode 100644
index 0000000..6ef6190
--- /dev/null
+++ b/gst-convenience/gstreamer-profile-gupnp-dlna-0.10.deps
@@ -0,0 +1 @@
+gstreamer-0.10
diff --git a/gst-convenience/gstreamer-profile-gupnp-dlna-0.10.vapi b/gst-convenience/gstreamer-profile-gupnp-dlna-0.10.vapi
new file mode 100644
index 0000000..698c207
--- /dev/null
+++ b/gst-convenience/gstreamer-profile-gupnp-dlna-0.10.vapi
@@ -0,0 +1,78 @@
+/* gstreamer-profile-0.10.vapi generated by vapigen, do not modify. */
+
+[CCode (cprefix = "Gst", lower_case_cprefix = "gst_")]
+namespace Gst {
+ [Compact]
+ [CCode (copy_function = "gst_encoding_profile_copy", type_id = "GST_TYPE_ENCODING_PROFILE", cheader_filename = "gst/profile/gstprofile.h")]
+ public class EncodingProfile {
+ public weak GLib.List encodingprofiles;
+ public weak Gst.Caps format;
+ public bool multipass;
+ public weak string name;
+ public weak string preset;
+ [CCode (has_construct_function = false)]
+ public EncodingProfile (string name, Gst.Caps format, string? preset, bool multipass);
+ public bool add_stream (owned Gst.StreamEncodingProfile stream);
+ public unowned Gst.EncodingProfile copy ();
+ public unowned Gst.Caps get_codec_caps ();
+ }
+ [Compact]
+ [CCode (cheader_filename = "gst/profile/gstprofile.h")]
+ public class EncodingTarget {
+ public weak string category;
+ public weak string name;
+ public weak GLib.List profiles;
+ }
+ [Compact]
+ [CCode (copy_function = "gst_stream_encoding_profile_copy", type_id = "GST_TYPE_STREAM_ENCODING_PROFILE", cheader_filename = "gst/profile/gstprofile.h")]
+ public class StreamEncodingProfile {
+ public weak Gst.Caps format;
+ public uint presence;
+ public weak string preset;
+ public weak Gst.Caps restriction;
+ public Gst.EncodingProfileType type;
+ [CCode (has_construct_function = false)]
+ public StreamEncodingProfile (Gst.EncodingProfileType type, Gst.Caps format, string? preset, Gst.Caps? restriction, uint presence);
+ public unowned Gst.StreamEncodingProfile copy ();
+ public unowned Gst.Caps get_output_caps ();
+ }
+ [Compact]
+ [CCode (free_function = "gst_stream_encoding_profile_free", type_id = "GST_TYPE_VIDEO_ENCODING_PROFILE", cheader_filename = "gst/profile/gstprofile.h")]
+ public class VideoEncodingProfile : Gst.StreamEncodingProfile {
+ public uint pass;
+ public weak Gst.StreamEncodingProfile profile;
+ public bool vfr;
+ [CCode (type = "GstStreamEncodingProfile*", has_construct_function = false)]
+ public VideoEncodingProfile (Gst.Caps format, string? preset, Gst.Caps? restriction, uint presence, uint pass);
+ }
+ [CCode (cprefix = "GST_ENCODING_PROFILE_", cheader_filename = "gst/profile/gstprofile.h")]
+ public enum EncodingProfileType {
+ UNKNOWN,
+ VIDEO,
+ AUDIO,
+ TEXT,
+ IMAGE
+ }
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static unowned GLib.List encoding_category_list_target (string category);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static unowned Gst.Element pb_utils_create_encoder (Gst.Caps caps, string? preset, string name);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static unowned Gst.Element pb_utils_create_encoder_format (string format, string preset, string name);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static unowned Gst.Element pb_utils_create_muxer (Gst.Caps caps, string? preset);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static unowned Gst.Element pb_utils_create_muxer_format (string format, string preset, string name);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static unowned GLib.List pb_utils_encoders_compatible_with_muxer (Gst.Element muxer);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static unowned GLib.List pb_utils_muxers_compatible_with_encoder (Gst.Element encoder);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static bool preset_create (Gst.Preset preset, string name, Gst.Structure properties);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static void preset_reset (Gst.Preset preset);
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static unowned GLib.List profile_list_target_categories ();
+ [CCode (cheader_filename = "gstreamer-profile-0.10.h")]
+ public static bool profile_target_save (Gst.EncodingTarget target);
+}
diff --git a/gupnp-dlna-1.0-uninstalled.pc.in b/gupnp-dlna-1.0-uninstalled.pc.in
new file mode 100644
index 0000000..b926eac
--- /dev/null
+++ b/gupnp-dlna-1.0-uninstalled.pc.in
@@ -0,0 +1,11 @@
+prefix=
+exec_prefix=
+libdir=${pcfiledir}/libgupnp-dlna
+includedir=${pcfiledir}/
+
+Name: gupnp-dlna-1.0
+Description: GObject-based AV specific UPnP library
+Version: @VERSION@
+Libs: ${libdir}/libgupnp-dlna-1.0.la
+Cflags: -I${includedir}/gupnp-dlna-1.0 -I$(top_srcdir)/gst-convenience/gst-libs
+Requires: gstreamer-0.10 gstreamer-base-0.10 gstreamer-video-0.10
diff --git a/gupnp-dlna-1.0.pc.in b/gupnp-dlna-1.0.pc.in
new file mode 100644
index 0000000..9c0e2d3
--- /dev/null
+++ b/gupnp-dlna-1.0.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: gupnp-dlna-1.0
+Description: GObject-based AV specific UPnP library
+Version: @VERSION@
+Libs: -L${libdir} -lgupnp-dlna-1.0
+Cflags: -I${includedir}/gupnp-dlna-1.0 -I$(top_srcdir)/gst-convenience/gst-libs
+Requires: gstreamer-0.10 gstreamer-base-0.10 gstreamer-video-0.10
diff --git a/libgupnp-dlna/Makefile.am b/libgupnp-dlna/Makefile.am
new file mode 100644
index 0000000..8098ddc
--- /dev/null
+++ b/libgupnp-dlna/Makefile.am
@@ -0,0 +1,56 @@
+# Version format current:revision:age
+# If the library source code has changed at all since the last update, then
+# increment revision (‘c:r:a’ becomes ‘c:r+1:a’).
+# If any interfaces have been added, removed, or changed since the last update,
+# increment current, and set revision to 0.
+# If any interfaces have been added since the last public release, then
+# increment age.
+# If any interfaces have been removed since the last public release, then set
+# age to 0.
+LTVERSION = 0:0:0
+
+shareddir = $(datadir)/gupnp-dlna
+
+AM_CFLAGS = -I$(top_srcdir) \
+ -I$(top_srcdir)/gst-convenience/gst-libs \
+ $(LIBXML_CFLAGS) \
+ $(GST_CFLAGS) \
+ $(GST_BASE_CFLAGS) \
+ $(GST_VIDEO_CFLAGS) \
+ -DDATA_DIR='"$(shareddir)"'
+
+lib_LTLIBRARIES = libgupnp-dlna-1.0.la
+
+gupnp-dlna-marshal.c: gupnp-dlna-marshal.list
+ $(AM_V_GEN) \
+ $(GLIB_GENMARSHAL) --prefix=gupnp_dlna_marshal $(srcdir)/gupnp-dlna-marshal.list --header --body > gupnp-dlna-marshal.c
+
+gupnp-dlna-marshal.h: gupnp-dlna-marshal.list
+ $(AM_V_GEN) \
+ $(GLIB_GENMARSHAL) --prefix=gupnp_dlna_marshal $(srcdir)/gupnp-dlna-marshal.list --header > gupnp-dlna-marshal.h
+
+BUILT_SOURCES = gupnp-dlna-marshal.c gupnp-dlna-marshal.h
+
+libgupnp_dlna_incdir = $(includedir)/gupnp-dlna-1.0/libgupnp-dlna
+
+libgupnp_dlna_1_0_la_LDFLAGS = -version-info $(LTVERSION) -no-undefined
+
+libgupnp_dlna_inc_HEADERS = gupnp-dlna-profile.h \
+ gupnp-dlna-information.h \
+ gupnp-dlna-discoverer.h \
+ gupnp-dlna-load.h
+
+libgupnp_dlna_1_0_la_SOURCES = gupnp-dlna-information.c \
+ gupnp-dlna-discoverer.c \
+ gupnp-dlna-profile.c \
+ gupnp-dlna-profiles.c \
+ gupnp-dlna-load.c \
+ $(BUILT_SOURCES)
+
+libgupnp_dlna_1_0_la_LIBADD = $(LIBXML_LIBS) \
+ $(top_builddir)/gst-convenience/gst-libs/gst/discoverer/.libs/libgstdiscoverer-gupnp-dlna-0.10.la \
+ $(top_builddir)/gst-convenience/gst-libs/gst/profile/.libs/libgstprofile-gupnp-dlna-0.10.la
+
+EXTRA_DIST = gupnp-dlna-marshal.list
+
+CLEANFILES = $(BUILT_SOURCES)
diff --git a/libgupnp-dlna/gupnp-dlna-discoverer.c b/libgupnp-dlna/gupnp-dlna-discoverer.c
new file mode 100644
index 0000000..a8c8481
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-discoverer.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gupnp-dlna-discoverer.h"
+#include "gupnp-dlna-marshal.h"
+
+enum {
+ DONE,
+ SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST];
+
+
+G_DEFINE_TYPE (GUPnPDLNADiscoverer, gupnp_dlna_discoverer, GST_TYPE_DISCOVERER)
+
+static void
+gupnp_dlna_discoverer_dispose (GObject *object)
+{
+ G_OBJECT_CLASS (gupnp_dlna_discoverer_parent_class)->dispose (object);
+}
+
+static void
+gupnp_dlna_discoverer_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gupnp_dlna_discoverer_parent_class)->finalize (object);
+}
+
+static void gupnp_dlna_discovered_cb (GstDiscoverer *discoverer,
+ GstDiscovererInformation *info,
+ GError *err)
+{
+ GUPnPDLNAInformation *dlna = NULL;
+
+ if (info)
+ dlna = gupnp_dlna_information_new_from_discoverer_info (info);
+
+ g_signal_emit (GUPNP_DLNA_DISCOVERER (discoverer),
+ signals[DONE], 0, dlna, err);
+
+ if (dlna)
+ g_object_unref (dlna);
+}
+
+static void
+gupnp_dlna_discoverer_class_init (GUPnPDLNADiscovererClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gupnp_dlna_discoverer_dispose;
+ object_class->finalize = gupnp_dlna_discoverer_finalize;
+
+ /**
+ * GUPnPDLNADiscoverer::done:
+ * @discoverer: the #GUPnPDLNADiscoverer
+ * @dlna: the results as #GUPnPDLNAInformation
+ *
+ * Will be emitted when all information on a URI could be discovered.
+ *
+ * The reciever must free @dlna with #gupnp_dlna_information_free() when
+ * done using it.
+ */
+ signals[DONE] =
+ g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GUPnPDLNADiscovererClass, done),
+ NULL, NULL,
+ gupnp_dlna_marshal_VOID__OBJECT_BOXED,
+ G_TYPE_NONE, 2, GUPNP_TYPE_DLNA_INFORMATION,
+ GST_TYPE_G_ERROR);
+}
+
+static void
+gupnp_dlna_discoverer_init (GUPnPDLNADiscoverer *self)
+{
+ g_signal_connect (&self->parent,
+ "discovered",
+ G_CALLBACK (gupnp_dlna_discovered_cb),
+ NULL);
+}
+
+GUPnPDLNADiscoverer*
+gupnp_dlna_discoverer_new (GstClockTime timeout)
+{
+ return g_object_new (GUPNP_TYPE_DLNA_DISCOVERER,
+ "timeout", timeout,
+ NULL);
+}
+
+/* Asynchronous API */
+void gupnp_dlna_discoverer_start (GUPnPDLNADiscoverer *discoverer)
+{
+ gst_discoverer_start (GST_DISCOVERER (discoverer));
+}
+void gupnp_dlna_discoverer_stop (GUPnPDLNADiscoverer *discoverer)
+{
+ gst_discoverer_stop (GST_DISCOVERER (discoverer));
+}
+
+gboolean
+gupnp_dlna_discoverer_discover_uri (GUPnPDLNADiscoverer *discoverer, gchar *uri)
+{
+ return gst_discoverer_append_uri (GST_DISCOVERER (discoverer), uri);
+}
+
+/* Synchronous API */
+GUPnPDLNAInformation *
+gupnp_dlna_discoverer_discover_uri_sync (GUPnPDLNADiscoverer *discoverer,
+ gchar *uri,
+ GError **err)
+{
+ GstDiscovererInformation *info;
+
+ info = gst_discoverer_discover_uri (GST_DISCOVERER (discoverer),
+ uri,
+ err);
+
+ if (info)
+ return gupnp_dlna_information_new_from_discoverer_info (info);
+
+ return NULL;
+}
diff --git a/libgupnp-dlna/gupnp-dlna-discoverer.h b/libgupnp-dlna/gupnp-dlna-discoverer.h
new file mode 100644
index 0000000..6b9feb9
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-discoverer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GUPNP_DLNA_DISCOVERER
+#define _GUPNP_DLNA_DISCOVERER
+
+#include <glib-object.h>
+#include <gst/discoverer/gstdiscoverer.h>
+#include "gupnp-dlna-information.h"
+
+G_BEGIN_DECLS
+
+#define GUPNP_TYPE_DLNA_DISCOVERER gupnp_dlna_discoverer_get_type()
+
+#define GUPNP_DLNA_DISCOVERER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ GUPNP_TYPE_DLNA_DISCOVERER, \
+ GUPnPDLNADiscoverer))
+
+#define GUPNP_DLNA_DISCOVERER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ GUPNP_TYPE_DLNA_DISCOVERER, \
+ GUPnPDLNADiscovererClass))
+
+#define GUPNP_IS_DLNA_DISCOVERER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ GUPNP_TYPE_DLNA_DISCOVERER))
+
+#define GUPNP_IS_DLNA_DISCOVERER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ GUPNP_TYPE_DLNA_DISCOVERER))
+
+#define GUPNP_DLNA_DISCOVERER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ GUPNP_TYPE_DLNA_DISCOVERER, \
+ GUPnPDLNADiscovererClass))
+
+/** GUPnPDLNADiscoverer
+ * @parent: The parent #GstDiscoverer object
+ *
+ * The GUPnPDLNADiscoverer API provides a light-weight wrapper over the
+ * #GstDiscoverer API. The latter provides a simple interface to discover
+ * media metadata given a URI. #GUPnPDLNADiscoverer extends this API to also
+ * provide a DLNA profile name and mime type for the media.
+ *
+ * The API provided is identical to the API provided by #GstDiscoverer.
+ */
+typedef struct {
+ GstDiscoverer parent;
+} GUPnPDLNADiscoverer;
+
+typedef struct {
+ GstDiscovererClass parent_class;
+
+ void (*done) (GUPnPDLNADiscoverer *discoverer,
+ GUPnPDLNAInformation *dlna,
+ GError *err);
+} GUPnPDLNADiscovererClass;
+
+GType gupnp_dlna_discoverer_get_type (void);
+
+GUPnPDLNADiscoverer* gupnp_dlna_discoverer_new (GstClockTime timeout);
+
+/* Asynchronous API */
+void gupnp_dlna_discoverer_start (GUPnPDLNADiscoverer *discoverer);
+void gupnp_dlna_discoverer_stop (GUPnPDLNADiscoverer *discoverer);
+gboolean
+gupnp_dlna_discoverer_discover_uri (GUPnPDLNADiscoverer *discoverer,
+ gchar *uri);
+
+/* Synchronous API */
+GUPnPDLNAInformation *
+gupnp_dlna_discoverer_discover_uri_sync (GUPnPDLNADiscoverer *discoverer,
+ gchar *uri,
+ GError **err);
+
+G_END_DECLS
+
+#endif /* _GUPNP_DLNA_DISCOVERER */
diff --git a/libgupnp-dlna/gupnp-dlna-information.c b/libgupnp-dlna/gupnp-dlna-information.c
new file mode 100644
index 0000000..c0933aa
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-information.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gupnp-dlna-information.h"
+
+G_DEFINE_TYPE (GUPnPDLNAInformation, gupnp_dlna_information, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ GUPNP_TYPE_DLNA_INFORMATION, \
+ GUPnPDLNAInformationPrivate))
+
+typedef struct _GUPnPDLNAInformationPrivate GUPnPDLNAInformationPrivate;
+
+struct _GUPnPDLNAInformationPrivate {
+ GstDiscovererInformation *info;
+ gchar *name;
+ gchar *mime;
+};
+
+enum {
+ PROP_0,
+ PROP_DLNA_NAME,
+ PROP_DLNA_MIME,
+ PROP_DISCOVERER_INFO,
+};
+
+static void
+gupnp_dlna_information_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GUPnPDLNAInformation *self = GUPNP_DLNA_INFORMATION (object);
+ GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self);
+
+ switch (property_id) {
+ case PROP_DLNA_NAME:
+ g_value_set_string (value, priv->name);
+ break;
+
+ case PROP_DLNA_MIME:
+ g_value_set_string (value, priv->mime);
+ break;
+
+ case PROP_DISCOVERER_INFO:
+ g_value_set_boxed (value, priv->info);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
+ property_id,
+ pspec);
+ break;
+ }
+}
+
+static void
+gupnp_dlna_information_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GUPnPDLNAInformation *self = GUPNP_DLNA_INFORMATION (object);
+ GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self);
+
+ switch (property_id) {
+ case PROP_DLNA_NAME:
+ g_free (priv->name);
+ priv->name = g_value_dup_string (value);
+ break;
+
+ case PROP_DLNA_MIME:
+ g_free (priv->mime);
+ priv->mime = g_value_dup_string (value);
+ break;
+
+ case PROP_DISCOVERER_INFO:
+ if (priv->info)
+ gst_discoverer_information_free (priv->info);
+ priv->info = g_value_dup_boxed (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
+ property_id,
+ pspec);
+ break;
+ }
+}
+
+
+static void
+gupnp_dlna_information_finalize (GObject *object)
+{
+ GUPnPDLNAInformation *self = GUPNP_DLNA_INFORMATION (object);
+ GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self);
+
+ g_free (priv->name);
+ g_free (priv->mime);
+ if (priv->info)
+ gst_discoverer_information_free (priv->info);
+
+ G_OBJECT_CLASS (gupnp_dlna_information_parent_class)->finalize (object);
+}
+
+static void
+gupnp_dlna_information_class_init (GUPnPDLNAInformationClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GParamSpec *pspec;
+
+ g_type_class_add_private (klass, sizeof (GUPnPDLNAInformation));
+
+ object_class->get_property = gupnp_dlna_information_get_property;
+ object_class->set_property = gupnp_dlna_information_set_property;
+ object_class->finalize = gupnp_dlna_information_finalize;
+
+ pspec = g_param_spec_string ("name",
+ "DLNA profile name",
+ "The name of the DLNA profile"
+ "corresponding to the strream",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_DLNA_NAME, pspec);
+
+ pspec = g_param_spec_string ("mime",
+ "DLNA profile MIME type corresponding"
+ "to the stream",
+ "The MIME type of the DLNA profile",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_DLNA_NAME, pspec);
+
+ pspec = g_param_spec_boxed ("info",
+ "Stream metadata",
+ "Metadata of the stream in an"
+ "GstDiscovererInformation structure",
+ GST_TYPE_DISCOVERER_INFORMATION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class,
+ PROP_DISCOVERER_INFO,
+ pspec);
+}
+
+static void
+gupnp_dlna_information_init (GUPnPDLNAInformation *self)
+{
+ GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self);
+
+ priv->name = NULL;
+ priv->mime = NULL;
+ priv->info = NULL;
+}
+
+GUPnPDLNAInformation*
+gupnp_dlna_information_new (gchar *name,
+ gchar *mime,
+ GstDiscovererInformation *info)
+{
+ return g_object_new (GUPNP_TYPE_DLNA_INFORMATION,
+ "name", name,
+ "mime", mime,
+ "info", info,
+ NULL);
+}
+
+gchar *
+gupnp_dlna_information_get_name (GUPnPDLNAInformation *self)
+{
+ GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self);
+ return priv->name;
+}
+
+gchar *
+gupnp_dlna_information_get_mime (GUPnPDLNAInformation *self)
+{
+ GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self);
+ return priv->mime;
+}
+
+GstDiscovererInformation *
+gupnp_dlna_information_get_info (GUPnPDLNAInformation *self)
+{
+ GUPnPDLNAInformationPrivate *priv = GET_PRIVATE (self);
+ return priv->info;
+}
diff --git a/libgupnp-dlna/gupnp-dlna-information.h b/libgupnp-dlna/gupnp-dlna-information.h
new file mode 100644
index 0000000..4b626d1
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-information.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GUPNP_DLNA_INFORMATION_H__
+#define __GUPNP_DLNA_INFORMATION_H__
+
+#include <gst/discoverer/gstdiscoverer.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GUPNP_TYPE_DLNA_INFORMATION gupnp_dlna_information_get_type()
+
+#define GUPNP_DLNA_INFORMATION(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GUPNP_TYPE_DLNA_INFORMATION, GUPnPDLNAInformation))
+
+#define GUPNP_DLNA_INFORMATION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GUPNP_TYPE_DLNA_INFORMATION, GUPnPDLNAInformationClass))
+
+#define GUPNP_IS_DLNA_INFORMATION(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GUPNP_TYPE_DLNA_INFORMATION))
+
+#define GUPNP_IS_DLNA_INFORMATION_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GUPNP_TYPE_DLNA_INFORMATION))
+
+#define GUPNP_DLNA_INFORMATION_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GUPNP_TYPE_DLNA_INFORMATION, GUPnPDLNAInformationClass))
+
+typedef struct {
+ GObject parent;
+} GUPnPDLNAInformation;
+
+typedef struct {
+ GObjectClass parent_class;
+} GUPnPDLNAInformationClass;
+
+GType gupnp_dlna_information_get_type (void);
+
+GUPnPDLNAInformation*
+gupnp_dlna_information_new (gchar *name,
+ gchar *mime,
+ GstDiscovererInformation *info);
+
+gchar * gupnp_dlna_information_get_name (GUPnPDLNAInformation *self);
+gchar * gupnp_dlna_information_get_mime (GUPnPDLNAInformation *self);
+GstDiscovererInformation *
+gupnp_dlna_information_get_info (GUPnPDLNAInformation *self);
+
+G_GNUC_INTERNAL GUPnPDLNAInformation *
+gupnp_dlna_information_new_from_discoverer_info (GstDiscovererInformation * info);
+
+
+G_END_DECLS
+
+#endif /* __GUPNP_DLNA_INFORMATION_H__ */
diff --git a/libgupnp-dlna/gupnp-dlna-load.c b/libgupnp-dlna/gupnp-dlna-load.c
new file mode 100644
index 0000000..08e53e0
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-load.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libxml/xmlreader.h>
+#include <libxml/relaxng.h>
+#include <gst/profile/gstprofile.h>
+#include "gupnp-dlna-load.h"
+#include "gupnp-dlna-profile.h"
+
+#define GST_CAPS_NULL_NAME "NULL"
+#define DLNA_DATA_DIR DATA_DIR \
+ G_DIR_SEPARATOR_S "dlna-profiles" G_DIR_SEPARATOR_S
+
+static gboolean
+copy_func (GQuark field_id, const GValue *value, gpointer data)
+{
+ GstStructure *st2 = (GstStructure *)data;
+
+ if (!gst_structure_has_field (st2, g_quark_to_string (field_id)))
+ gst_structure_id_set_value (st2, field_id, value);
+
+ return TRUE;
+}
+
+/* Note: It is assumed that caps1 and caps2 have only 1 structure each */
+static GstCaps *
+merge_caps (GstCaps *caps1, GstCaps *caps2)
+{
+ GstStructure *st1, *st2;
+ GstCaps *ret;
+ gboolean any = FALSE;
+
+ /* If one of the caps GST_CAPS_ANY, gst_caps_merge will result in a
+ * GST_CAPS_ANY, which might not be correct for us */
+ if (!gst_caps_is_any (caps1) && !gst_caps_is_any (caps2)) {
+ any = TRUE;
+ gst_caps_merge (caps1, gst_caps_copy (caps2));
+ gst_caps_do_simplify (caps1);
+ }
+
+ ret = gst_caps_make_writable (caps1);
+ st1 = gst_caps_get_structure (ret, 0);
+ if (gst_caps_get_size (caps1) == 2)
+ /* Non-merged fields were copied to a second structure in caps1 at
+ * gst_merge_caps() time */
+ st2 = gst_caps_get_structure (ret, 1);
+ else
+ /* Either one of the caps was GST_CAPS_ANY, or there were no
+ * unmerged fields */
+ st2 = gst_caps_get_structure (caps2, 0);
+
+ /* If caps1 has a name, we retain it. If not, and caps2 does, caps1
+ * gets caps2's name. */
+ if ((g_strcmp0 (GST_CAPS_NULL_NAME,
+ gst_structure_get_name (st1)) == 0) &&
+ (g_strcmp0 (GST_CAPS_NULL_NAME,
+ gst_structure_get_name (st2)) != 0)) {
+ gst_structure_set_name (st1, gst_structure_get_name (st2));
+ }
+
+ /* We now walk over the structures and append any fields that are in
+ * caps2 but not in caps1. */
+ if (any || gst_caps_get_size (caps1) == 2)
+ gst_structure_foreach (st2, copy_func, st1);
+
+ if (gst_caps_get_size (caps1) == 2)
+ gst_caps_remove_structure (ret, 1);
+
+ return ret;
+}
+
+static xmlChar *
+get_value (xmlTextReaderPtr reader)
+{
+ xmlChar *value = NULL, *curr;
+ int ret = 1;
+
+ curr = xmlTextReaderName (reader);
+
+ /* This function may be called with reader pointing to a <field> or
+ * the element just below a <field>. In the former case, we move the
+ * cursor forward and then continue processing. */
+ if (xmlStrEqual (curr, BAD_CAST ("field")))
+ ret = xmlTextReaderRead (reader);
+ xmlFree (curr);
+
+ while (ret == 1) {
+ xmlChar *tag;
+
+ tag = xmlTextReaderName (reader);
+
+ if (xmlTextReaderNodeType (reader) == 1 &&
+ xmlStrEqual (tag, BAD_CAST ("value"))) {
+ /* <value> */
+
+ /* Note: This assumes you won't have a comment in the
+ * middle of your text */
+ do {
+ ret = xmlTextReaderRead (reader);
+ } while (ret == 1 &&
+ xmlTextReaderNodeType (reader) != 3 &&
+ xmlTextReaderNodeType (reader) != 15);
+
+ /* We're now at the real text between a <value> and a
+ * </value> */
+
+ if (xmlTextReaderNodeType (reader) == 3)
+ value = xmlTextReaderValue (reader);
+ }
+
+ if (xmlTextReaderNodeType (reader) == 15 &&
+ xmlStrEqual (tag, BAD_CAST ("value"))) {
+ /* </value> */
+ xmlFree (tag);
+ break;
+ }
+
+ xmlFree (tag);
+ ret = xmlTextReaderRead (reader);
+ }
+
+ if (!value)
+ g_warning ("Empty <value>s are illegal");
+ return value;
+}
+
+static void
+xml_str_free (xmlChar *str, gpointer unused)
+{
+ xmlFree (str);
+}
+
+static void
+dlna_encoding_profile_add_stream (GstEncodingProfile *profile,
+ GstStreamEncodingProfile *stream_profile)
+{
+ GList *i;
+
+ /* Try to merge with an existing stream profile of the same type */
+ for (i = profile->encodingprofiles; i; i = i->next) {
+ GstStreamEncodingProfile *cur =
+ (GstStreamEncodingProfile *) i->data;
+
+ if (cur->type != stream_profile->type)
+ continue;
+
+ /* Since we maintain only one stream profile for each type,
+ * this will get executed exactly once */
+ gst_caps_merge (cur->format,
+ gst_caps_copy (stream_profile->format));
+
+ gst_stream_encoding_profile_free (stream_profile);
+ goto done;
+ }
+
+ /* If we get here, there's no existing stream of this type */
+ gst_encoding_profile_add_stream (profile, stream_profile);
+
+done:
+ return;
+}
+
+static void process_range (xmlTextReaderPtr reader, GString *caps_str)
+{
+ xmlChar *min, *max;
+
+ min = xmlTextReaderGetAttribute (reader, BAD_CAST ("min"));
+ max = xmlTextReaderGetAttribute (reader, BAD_CAST ("max"));
+
+ g_string_append_printf (caps_str, "[ %s, %s ]", min, max);
+
+ xmlFree (min);
+ xmlFree (max);
+}
+
+static int
+process_field (xmlTextReaderPtr reader, GString *caps_str)
+{
+ int ret;
+ xmlChar *name;
+ xmlChar *type;
+ GList *values = NULL;
+ gboolean done = FALSE;
+
+ name = xmlTextReaderGetAttribute (reader, BAD_CAST ("name"));
+ type = xmlTextReaderGetAttribute (reader, BAD_CAST ("type"));
+
+ /*
+ * This function reads a <field> and appends it to caps_str in the
+ * GstCaps-as-a-string format:
+ *
+ * Single value: field = (type) value
+ * Multiple values: field = (type) { value1, value2, value3 }
+ * Range: field = (type) [ min, max ]
+ */
+
+ /* Fields are comma-separeted. The leading comma is okay for the first
+ * field - we will be prepending the restriction name to this string */
+ g_string_append_printf (caps_str, ", %s = (%s) ", name, type);
+ xmlFree (name);
+ xmlFree (type);
+
+ ret = xmlTextReaderRead (reader);
+ while (ret == 1 && !done) {
+ xmlChar *tag;
+
+ tag = xmlTextReaderName (reader);
+
+ switch (xmlTextReaderNodeType (reader)) {
+ case 1:
+ if (xmlStrEqual (tag, BAD_CAST ("range"))) {
+ /* <range> */
+ process_range (reader, caps_str);
+ } else if (xmlStrEqual (tag, BAD_CAST ("value"))) {
+ /* <value> */
+ xmlChar *value;
+
+ value = get_value (reader);
+
+ if (value)
+ values = g_list_append (values, value);
+ }
+
+ break;
+
+ case 15:
+ if (xmlStrEqual (tag, BAD_CAST ("field")))
+ /* </field> */
+ done = TRUE;
+
+ break;
+
+ default:
+ break;
+ }
+
+ xmlFree (tag);
+ ret = xmlTextReaderRead (reader);
+ }
+
+ if (g_list_length (values) == 1)
+ /* Single value */
+ g_string_append_printf (caps_str, "%s",
+ (xmlChar *) values->data);
+ else if (g_list_length (values) > 1) {
+ /* Multiple values */
+ GList *tmp = values->next;
+ g_string_append_printf (caps_str, "{ %s",
+ (xmlChar *) values->data);
+
+ do {
+ g_string_append_printf (caps_str, ", %s",
+ (xmlChar *) tmp->data);
+ } while ((tmp = tmp->next) != NULL);
+
+ g_string_append_printf (caps_str, " }");
+ }
+
+ if (values) {
+ g_list_foreach (values, (GFunc) xml_str_free, NULL);
+ g_list_free (values);
+ }
+
+ return ret;
+}
+
+static GstStreamEncodingProfile *
+process_parent (xmlTextReaderPtr reader, GHashTable *restrictions)
+{
+ xmlChar *parent;
+ GstStreamEncodingProfile *profile;
+
+ parent = xmlTextReaderGetAttribute (reader, BAD_CAST ("name"));
+ profile = g_hash_table_lookup (restrictions, parent);
+
+ if (!profile)
+ g_warning ("Could not find parent restriction: %s", parent);
+
+ xmlFree (parent);
+ return gst_stream_encoding_profile_copy (profile);
+}
+
+static GstStreamEncodingProfile *
+process_restriction (xmlTextReaderPtr reader, GHashTable *restrictions)
+{
+ GstStreamEncodingProfile *stream_profile = NULL;
+ GstEncodingProfileType type;
+ GstCaps *caps = NULL;
+ GString *caps_str = g_string_sized_new (100);
+ GList *parents = NULL, *tmp;
+ xmlChar *id, *name = NULL, *restr_type;
+ int ret;
+ gboolean done = FALSE;
+
+ /* First, we walk through the fields in this restriction, and make a
+ * string that can be parsed by gst_caps_from_string (). We then make
+ * a GstCaps from this string, and use the other metadata to make a
+ * GstStreamEncodingProfile */
+
+ id = xmlTextReaderGetAttribute (reader, BAD_CAST ("id"));
+ restr_type = xmlTextReaderGetAttribute (reader, BAD_CAST ("type"));
+
+ ret = xmlTextReaderRead (reader);
+ while (ret == 1 && !done) {
+ xmlChar *tag;
+
+ tag = xmlTextReaderName (reader);
+
+ switch (xmlTextReaderNodeType (reader)) {
+ case 1:
+ if (xmlStrEqual (tag, BAD_CAST ("field"))) {
+ /* <field> */
+ xmlChar *field;
+
+ field = xmlTextReaderGetAttribute (reader,
+ BAD_CAST ("name"));
+
+ /* We handle the "name" field specially - if
+ * present, it is the caps name */
+ if (xmlStrEqual (field, BAD_CAST ("name")))
+ name = get_value (reader);
+ else
+ process_field (reader, caps_str);
+
+ xmlFree (field);
+ } else if (xmlStrEqual (tag, BAD_CAST ("parent"))) {
+ /* <parent> */
+ GstStreamEncodingProfile *profile =
+ process_parent (reader, restrictions);
+
+ if (profile)
+ /* Collect parents in a list - we'll
+ * coalesce them later */
+ parents = g_list_append (parents,
+ profile);
+ }
+
+ break;
+
+ case 15:
+ if (xmlStrEqual (tag, BAD_CAST ("restriction")))
+ /* </restriction> */
+ done = TRUE;
+
+ break;
+
+ default:
+ break;
+ }
+
+ xmlFree (tag);
+ ret = xmlTextReaderRead (reader);
+ }
+
+ /* If the restriction doesn't have a name, we make it up */
+ if (!name)
+ name = BAD_CAST (g_strdup (GST_CAPS_NULL_NAME));
+ g_string_prepend (caps_str, (gchar *) name);
+ xmlFree (name);
+
+ if (xmlStrEqual (restr_type, BAD_CAST ("container")))
+ type = GST_ENCODING_PROFILE_UNKNOWN;
+ else if (xmlStrEqual (restr_type, BAD_CAST ("audio")))
+ type = GST_ENCODING_PROFILE_AUDIO;
+ else if (xmlStrEqual (restr_type, BAD_CAST ("video")))
+ type = GST_ENCODING_PROFILE_VIDEO;
+ else if (xmlStrEqual (restr_type, BAD_CAST ("image")))
+ type = GST_ENCODING_PROFILE_IMAGE;
+ else {
+ g_warning ("Support for '%s' restrictions not yet implemented",
+ restr_type);
+ goto out;
+ }
+
+ caps = gst_caps_from_string (caps_str->str);
+ g_string_free (caps_str, TRUE);
+ g_return_val_if_fail (caps != NULL, NULL);
+
+ tmp = parents;
+ while (tmp) {
+ /* Merge all the parent caps. The child overrides parent
+ * attributes */
+ GstStreamEncodingProfile *profile = tmp->data;
+ caps = merge_caps (caps, profile->format);
+ gst_stream_encoding_profile_free (profile);
+ tmp = tmp->next;
+ }
+
+ stream_profile = gst_stream_encoding_profile_new (type,
+ caps,
+ NULL,
+ GST_CAPS_ANY,
+ 0);
+
+ if (id) {
+ /* Make a copy so we can free it at the end of processing
+ * without worrying about it being reffed by an encoding
+ * profile */
+ GstStreamEncodingProfile *tmp =
+ gst_stream_encoding_profile_copy (stream_profile);
+ g_hash_table_insert (restrictions, id, tmp);
+ }
+
+out:
+ xmlFree (restr_type);
+ if (caps)
+ gst_caps_unref (caps);
+ if (parents)
+ g_list_free (parents);
+ return stream_profile;
+}
+
+static void
+process_restrictions (xmlTextReaderPtr reader, GHashTable *restrictions)
+{
+ /* While we use a GstStreamEncodingProfile to store restrictions here,
+ * this is not how they are finally used. This is just a convenient
+ * container for the format caps and stream type. Once the restriction
+ * is used in a profile, all the restrictions of the same type
+ * (audio/video) are merged into a single GstStreamEncodingProfile,
+ * which is added to the GstEncodingProfile for the DLNA profile.
+ */
+
+ int ret = xmlTextReaderRead (reader);
+
+ while (ret == 1) {
+ xmlChar *tag;
+
+ tag = xmlTextReaderName (reader);
+
+ switch (xmlTextReaderNodeType (reader)) {
+ case 1:
+ if (xmlStrEqual (tag, BAD_CAST ("restriction"))) {
+ /* <restriction> */
+ GstStreamEncodingProfile *stream =
+ process_restriction (reader,
+ restrictions);
+ gst_stream_encoding_profile_free (stream);
+ }
+
+ break;
+
+ case 15:
+ if (xmlStrEqual (tag, BAD_CAST ("restrictions"))) {
+ /* </restrictions> */
+ xmlFree (tag);
+ return;
+ }
+
+ default:
+ break;
+ }
+
+ xmlFree (tag);
+ ret = xmlTextReaderRead (reader);
+ }
+}
+
+static int
+process_dlna_profile (xmlTextReaderPtr reader,
+ GList **profiles,
+ GHashTable *restrictions,
+ GHashTable *profile_ids)
+{
+ int ret;
+ GUPnPDLNAProfile *profile;
+ GstStreamEncodingProfile *stream_profile;
+ GstEncodingProfile *enc_profile, *base = NULL;
+ GstCaps *format = NULL;
+ GList *stream_profiles = NULL, *streams;
+ xmlChar *name, *mime, *id, *base_profile;
+ gboolean done = FALSE;
+
+ name = xmlTextReaderGetAttribute (reader, BAD_CAST ("name"));
+ mime = xmlTextReaderGetAttribute (reader, BAD_CAST ("mime"));
+ id = xmlTextReaderGetAttribute (reader, BAD_CAST ("id"));
+ base_profile = xmlTextReaderGetAttribute (reader,
+ BAD_CAST ("base-profile"));
+
+ ret = xmlTextReaderRead (reader);
+ while (ret == 1 && !done) {
+ xmlChar *tag;
+
+ tag = xmlTextReaderName (reader);
+
+ switch (xmlTextReaderNodeType (reader)) {
+ case 1:
+ if (xmlStrEqual (tag, BAD_CAST ("restriction"))) {
+ stream_profile =
+ process_restriction (reader,
+ restrictions);
+ } else if (xmlStrEqual (tag, BAD_CAST ("parent"))) {
+ stream_profile =
+ process_parent (reader, restrictions);
+ }
+
+ if (!stream_profile)
+ break;
+
+ if (stream_profile->type ==
+ GST_ENCODING_PROFILE_UNKNOWN) {
+ format = gst_caps_copy (
+ stream_profile->format);
+ gst_stream_encoding_profile_free (stream_profile);
+ } else {
+ stream_profiles =
+ g_list_append (stream_profiles,
+ stream_profile);
+ }
+
+ break;
+
+ case 15:
+ if (xmlStrEqual (tag, BAD_CAST ("dlna-profile")))
+ done = TRUE;
+
+ default:
+ break;
+ }
+
+ xmlFree (tag);
+ ret = xmlTextReaderRead (reader);
+ }
+
+ if (base_profile) {
+ base = g_hash_table_lookup (profile_ids, base_profile);
+ if (!base)
+ g_warning ("Invalid base-profile reference");
+ }
+
+ if (!base) {
+ /* Create a new GstEncodingProfile */
+ if (!format)
+ format = GST_CAPS_NONE;
+ enc_profile = gst_encoding_profile_new ((gchar *) name,
+ format,
+ NULL,
+ 0);
+ } else {
+ /* We're inherting from a parent profile */
+ enc_profile = gst_encoding_profile_copy (base);
+
+ g_free (enc_profile->name);
+ enc_profile->name = g_strdup ((gchar *) name);
+
+ if (format) {
+ gst_caps_unref (enc_profile->format);
+ enc_profile->format = gst_caps_copy (format);
+ }
+ }
+
+ for (streams = stream_profiles; streams; streams = streams->next) {
+ GstStreamEncodingProfile *stream_profile =
+ (GstStreamEncodingProfile *)
+ streams->data;
+ /* The stream profile *must* not be referenced after this */
+ dlna_encoding_profile_add_stream (enc_profile, stream_profile);
+ }
+
+ profile = gupnp_dlna_profile_new ((gchar *) name,
+ (gchar *) mime,
+ enc_profile);
+ *profiles = g_list_append (*profiles, profile);
+
+ if (id)
+ /* id is freed when the hash table is destroyed */
+ g_hash_table_insert (profile_ids, id, enc_profile);
+ else
+ /* we've got a copy in profile, so we're done with this */
+ gst_encoding_profile_free (enc_profile);
+
+ g_list_free (stream_profiles);
+ gst_caps_unref (format);
+ xmlFree (mime);
+ xmlFree (name);
+ if (base_profile)
+ xmlFree (base_profile);
+
+ return ret;
+}
+
+static GList *
+process_include (xmlTextReaderPtr reader,
+ GHashTable *restrictions,
+ GHashTable *profile_ids)
+{
+ xmlChar *path;
+ GList *ret;
+
+ path = xmlTextReaderGetAttribute (reader, BAD_CAST ("ref"));
+
+ if (!g_path_is_absolute ((gchar *) path)) {
+ gchar *tmp = g_strconcat (DLNA_DATA_DIR,
+ G_DIR_SEPARATOR_S,
+ path,
+ NULL);
+ xmlFree (path);
+ path = BAD_CAST (tmp);
+ }
+
+ ret = gupnp_dlna_load_profiles_from_file ((gchar *) path,
+ restrictions,
+ profile_ids);
+ xmlFree (path);
+
+ return ret;
+}
+
+GList *
+gupnp_dlna_load_profiles_from_file (const char *file_name,
+ GHashTable *restrictions,
+ GHashTable *profile_ids)
+{
+ GList *profiles = NULL;
+ xmlTextReaderPtr reader;
+ xmlRelaxNGParserCtxtPtr rngp;
+ xmlRelaxNGPtr rngs;
+ int ret;
+
+ reader = xmlNewTextReaderFilename (file_name);
+ if (!reader)
+ return NULL;
+
+ /* Load the schema for validation */
+ rngp = xmlRelaxNGNewParserCtxt (DLNA_DATA_DIR "dlna-profiles.rng");
+ rngs = xmlRelaxNGParse (rngp);
+ xmlTextReaderRelaxNGSetSchema (reader, rngs);
+
+ ret = xmlTextReaderRead (reader);
+ while (ret == 1) {
+ xmlChar *tag;
+
+ tag = xmlTextReaderName (reader);
+
+ switch (xmlTextReaderNodeType (reader)) {
+ /* Start tag */
+ case 1:
+ if (xmlStrEqual (tag, BAD_CAST ("include"))) {
+ /* <include> */
+ GList *include =
+ process_include (reader,
+ restrictions,
+ profile_ids);
+ profiles = g_list_concat (profiles,
+ include);
+ } else if (xmlStrEqual (tag,
+ BAD_CAST ("restrictions"))) {
+ /* <restrictions> */
+ process_restrictions (reader,
+ restrictions);
+ } else if (xmlStrEqual (tag,
+ BAD_CAST ("dlna-profile"))) {
+ /* <dlna-profile> */
+ process_dlna_profile (reader,
+ &profiles,
+ restrictions,
+ profile_ids);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ xmlFree (tag);
+ ret = xmlTextReaderRead (reader);
+ }
+
+ xmlFreeTextReader (reader);
+ xmlRelaxNGFree (rngs);
+ xmlRelaxNGFreeParserCtxt (rngp);
+
+ return profiles;
+}
+
+GList *
+gupnp_dlna_load_profiles_from_dir (gchar *profile_dir)
+{
+ GDir *dir;
+ GHashTable *restrictions =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) xmlFree,
+ (GDestroyNotify)
+ gst_stream_encoding_profile_free);
+ GHashTable *profile_ids =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) xmlFree,
+ (GDestroyNotify)
+ gst_encoding_profile_free);
+ GList *profiles = NULL;
+
+ if ((dir = g_dir_open (profile_dir, 0, NULL))) {
+ const gchar *entry;
+
+ while ((entry = g_dir_read_name (dir))) {
+ gchar *path = g_strconcat (profile_dir,
+ G_DIR_SEPARATOR_S,
+ entry,
+ NULL);
+
+ if (g_str_has_suffix (entry, ".xml") &&
+ g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
+ profiles = g_list_concat (profiles,
+ gupnp_dlna_load_profiles_from_file (
+ path,
+ restrictions,
+ profile_ids));
+ }
+
+ g_free (path);
+ }
+
+ g_dir_close (dir);
+ }
+
+ g_hash_table_unref (restrictions);
+ g_hash_table_unref (profile_ids);
+ return profiles;
+}
+
+GList *
+gupnp_dlna_load_profiles_from_disk (void)
+{
+ return gupnp_dlna_load_profiles_from_dir (DLNA_DATA_DIR);
+}
diff --git a/libgupnp-dlna/gupnp-dlna-load.h b/libgupnp-dlna/gupnp-dlna-load.h
new file mode 100644
index 0000000..1a622a9
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-load.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Authors: Zeeshan Ali <zeeshanak@gnome.org>
+ * <zeeshan.ali@nokia.com>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GUPNP_DLNA_LOAD_H__
+#define __GUPNP_DLNA_LOAD_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+GList *
+gupnp_dlna_load_profiles_from_file (const gchar *file_name,
+ GHashTable *restrictions,
+ GHashTable *profile_ids);
+
+GList *
+gupnp_dlna_load_profiles_from_dir (gchar *profile_dir);
+
+GList *
+gupnp_dlna_load_profiles_from_disk (void);
+
+G_END_DECLS
+
+#endif /* __GUPNP_DLNA_LOAD_H__ */
diff --git a/libgupnp-dlna/gupnp-dlna-marshal.list b/libgupnp-dlna/gupnp-dlna-marshal.list
new file mode 100644
index 0000000..c03532c
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-marshal.list
@@ -0,0 +1,2 @@
+BOOLEAN:STRING,UINT,STRING,POINTER
+VOID:OBJECT,BOXED
diff --git a/libgupnp-dlna/gupnp-dlna-profile.c b/libgupnp-dlna/gupnp-dlna-profile.c
new file mode 100644
index 0000000..321c0ef
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-profile.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gupnp-dlna-profile.h"
+
+G_DEFINE_TYPE (GUPnPDLNAProfile, gupnp_dlna_profile, G_TYPE_OBJECT)
+
+static void
+gupnp_dlna_profile_finalize (GObject *object)
+{
+ GUPnPDLNAProfile *self = GUPNP_DLNA_PROFILE (object);
+
+ if (self->name)
+ g_free (self->name);
+ if (self->mime)
+ g_free (self->mime);
+ if (self->enc_profile)
+ gst_encoding_profile_free (self->enc_profile);
+
+ G_OBJECT_CLASS (gupnp_dlna_profile_parent_class)->finalize (object);
+}
+
+static void
+gupnp_dlna_profile_class_init (GUPnPDLNAProfileClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gupnp_dlna_profile_finalize;
+}
+
+static void
+gupnp_dlna_profile_init (GUPnPDLNAProfile *self)
+{
+ self->name = NULL;
+ self->mime = NULL;
+ self->enc_profile = NULL;
+}
+
+GUPnPDLNAProfile*
+gupnp_dlna_profile_new (gchar *name,
+ gchar *mime,
+ GstEncodingProfile *enc_profile)
+{
+ GUPnPDLNAProfile *profile = g_object_new (GUPNP_TYPE_DLNA_PROFILE,
+ NULL);
+
+ profile->name = g_strdup (name);
+ profile->mime = g_strdup (mime);
+ profile->enc_profile = gst_encoding_profile_copy (enc_profile);
+
+ return profile;
+}
diff --git a/libgupnp-dlna/gupnp-dlna-profile.h b/libgupnp-dlna/gupnp-dlna-profile.h
new file mode 100644
index 0000000..fd17f5a
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-profile.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GUPNP_DLNA_PROFILE_H__
+#define __GUPNP_DLNA_PROFILE_H__
+
+#include <gst/profile/gstprofile.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GUPNP_TYPE_DLNA_PROFILE gupnp_dlna_profile_get_type()
+
+#define GUPNP_DLNA_PROFILE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ GUPNP_TYPE_DLNA_PROFILE, \
+ GUPnPDLNAProfile))
+
+#define GUPNP_DLNA_PROFILE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ GUPNP_TYPE_DLNA_PROFILE, \
+ GUPnPDLNAProfileClass))
+
+#define GUPNP_IS_DLNA_PROFILE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GUPNP_TYPE_DLNA_PROFILE))
+
+#define GUPNP_IS_DLNA_PROFILE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GUPNP_TYPE_DLNA_PROFILE))
+
+#define GUPNP_DLNA_PROFILE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ GUPNP_TYPE_DLNA_PROFILE, \
+ GUPnPDLNAProfileClass))
+
+typedef struct {
+ GObject parent;
+
+ gchar *name;
+ gchar *mime;
+ GstEncodingProfile *enc_profile;
+} GUPnPDLNAProfile;
+
+typedef struct {
+ GObjectClass parent_class;
+} GUPnPDLNAProfileClass;
+
+GType gupnp_dlna_profile_get_type (void);
+
+GUPnPDLNAProfile* gupnp_dlna_profile_new (gchar *name,
+ gchar *mime,
+ GstEncodingProfile *enc_profile);
+
+G_END_DECLS
+
+#endif /* __GUPNP_DLNA_PROFILE_H__ */
diff --git a/libgupnp-dlna/gupnp-dlna-profiles.c b/libgupnp-dlna/gupnp-dlna-profiles.c
new file mode 100644
index 0000000..390ebff
--- /dev/null
+++ b/libgupnp-dlna/gupnp-dlna-profiles.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <gst/profile/gstprofile.h>
+#include "gupnp-dlna-discoverer.h"
+#include "gupnp-dlna-load.h"
+#include "gupnp-dlna-profile.h"
+
+/*
+ * This file provides the infrastructure to load DLNA profiles and the
+ * corresponding restrictions from an on-disk representation, and use them to
+ * map a given stream to its DLNA profile, if possible.
+ *
+ * Each DLNA profile is represented as a GstEncodingProfile (there might be
+ * exceptions where a single DLNA profile is represented by multiple
+ * GstEncodingProfiles - right now that's only LPCM).
+ *
+ * For a GstEncodingProfile "profile", the following fields will be populated:
+ *
+ * profile.name = "<DLNA Profile Name>"
+ * profile.format = Muxing format caps (with restrictions) if specified,
+ * else GST_CAPS_NONE
+ * profile.encodingprofiles = GList of GstStreamEncodingProfiles
+ *
+ * For each stream of the given profile, "profile.encodingprofiles" will have
+ * a GstEncodingProfile representing the restrictions for that stream (for a
+ * video format there will be one audio and one video stream, for example).
+ *
+ * enc_profile.type = GST_ENCODING_PROFILE_{AUDIO,VIDEO,...} (UNKNOWN for
+ * container restrictions)
+ * enc_profile.format = GstCaps with all the restrictions for this format
+ * enc_profile.restriction = GST_CAPS_ANY
+ *
+ * We assume that all DLNA profiles have exactly one audio stream, or one audio
+ * stream and one video stream.
+ *
+ * Things yet to account for:
+ *
+ * 1. Multiple audio/video streams (we need to pick the "main" one - how?
+ * Possibly get information from the demuxer.)
+ *
+ * 2. How do we handle discovered metadata which is in tags, but not in caps?
+ * Could potentially move it to caps in a post-discovery, pre-guessing
+ * phase
+ */
+
+/* New profile guessing API */
+
+static GList *profiles = NULL;
+
+static gboolean is_video_profile (GstEncodingProfile *profile)
+{
+ GList *i;
+
+ for (i = profile->encodingprofiles; i; i = i->next)
+ if (((GstStreamEncodingProfile *) i->data)->type ==
+ GST_ENCODING_PROFILE_VIDEO)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean structure_can_intersect (const GstStructure *st1,
+ const GstStructure *st2)
+{
+ /* Since there is no API to intersect GstStructures, we cheat (thanks
+ * for the idea, tpm!) and make caps from the structuresa */
+
+ GstCaps *caps1, *caps2;
+ gboolean ret;
+
+ caps1 = gst_caps_new_full (gst_structure_copy (st1), NULL);
+ caps2 = gst_caps_new_full (gst_structure_copy (st2), NULL);
+
+ ret = gst_caps_can_intersect (caps1, caps2);
+
+ gst_caps_unref (caps1);
+ gst_caps_unref (caps2);
+ return ret;
+}
+
+static gboolean structure_is_subset (const GstStructure *st1,
+ const GstStructure *st2)
+{
+ int i;
+
+ for (i = 0; i < gst_structure_n_fields (st2); i++) {
+ const gchar *name = gst_structure_nth_field_name (st2, i);
+
+ if (!gst_structure_has_field(st1, name))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Returns TRUE if stream_caps and profile_caps can intersect, and the
+ * intersecting structure from profile_caps is a subset of stream_caps. Put
+ * simply, the condition being met is that stream_caps intersects with
+ * profile_caps, and that intersection includes *all* fields specified by
+ * profile_caps (viz. all the fields specified by the DLNA profile's
+ * restrictions)
+ */
+static gboolean caps_can_intersect_and_is_subset (GstCaps *stream_caps,
+ GstCaps *profile_caps)
+{
+ int i;
+ GstStructure *stream_st, *profile_st;
+
+ stream_st = gst_caps_get_structure (stream_caps, 0);
+
+ for (i = 0; i < gst_caps_get_size (profile_caps); i++) {
+ profile_st = gst_caps_get_structure (profile_caps, i);
+
+ if (structure_can_intersect (stream_st, profile_st) &&
+ structure_is_subset (stream_st, profile_st))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+match_profile (GstEncodingProfile *profile,
+ GstCaps *caps,
+ GstEncodingProfileType type)
+{
+ GList *i;
+
+ /* Profiles with an empty name are used only for inheritance and should
+ * not be matched against. */
+ if (profile->name[0] == '\0')
+ return FALSE;
+
+ for (i = profile->encodingprofiles; i; i = i->next) {
+ GstStreamEncodingProfile *enc_profile = i->data;
+
+ if (enc_profile->type == type &&
+ caps_can_intersect_and_is_subset (caps,
+ enc_profile->format))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+check_container (GstDiscovererInformation *info, GstEncodingProfile *profile)
+{
+ /* Top-level GstStreamInformation in the topology will be
+ * the container */
+ if (info->stream_info->streamtype == GST_STREAM_CONTAINER &&
+ gst_caps_can_intersect (info->stream_info->caps, profile->format))
+ return TRUE;
+ else if (info->stream_info->streamtype != GST_STREAM_CONTAINER &&
+ gst_caps_is_empty (profile->format))
+ return TRUE;
+
+ return FALSE;
+}
+
+static GstCaps *
+caps_from_audio_stream_info (GstStreamAudioInformation *audio)
+{
+ GstCaps *caps = gst_caps_copy ((GST_STREAM_INFORMATION (audio))->caps);
+
+ if (audio->sample_rate)
+ gst_caps_set_simple (caps, "rate", G_TYPE_INT,
+ audio->sample_rate, NULL);
+ if (audio->channels)
+ gst_caps_set_simple (caps, "channels", G_TYPE_INT,
+ audio->channels, NULL);
+ if (audio->bitrate)
+ gst_caps_set_simple (caps, "bitrate", G_TYPE_INT,
+ audio->bitrate, NULL);
+ if (audio->max_bitrate)
+ gst_caps_set_simple (caps, "maximum-bitrate", G_TYPE_INT,
+ audio->max_bitrate, NULL);
+ if (audio->depth)
+ gst_caps_set_simple (caps, "depth", G_TYPE_INT,
+ audio->depth, NULL);
+
+ return caps;
+}
+
+static gboolean
+check_audio_profile (GstEncodingProfile *profile,
+ GstDiscovererInformation *info)
+{
+ GstCaps *caps;
+ GList *i;
+ gboolean found = FALSE;
+
+ /* Optimisation TODO: this can be pre-computed */
+ if (is_video_profile (profile))
+ return FALSE;
+
+ for (i = info->stream_list; !found && i; i = i->next) {
+ GstStreamInformation *stream = (GstStreamInformation *) i->data;
+ GstStreamAudioInformation *audio;
+
+ if (stream->streamtype != GST_STREAM_AUDIO)
+ continue;
+
+ audio = GST_STREAM_AUDIO_INFORMATION (stream);
+ caps = caps_from_audio_stream_info (audio);
+
+ if (match_profile (profile, caps, GST_ENCODING_PROFILE_AUDIO)) {
+ found = TRUE;
+ break;
+ }
+
+ gst_caps_unref (caps);
+ }
+
+ return found;
+}
+
+static void
+guess_audio_profile (GstDiscovererInformation *info, gchar **name, gchar **mime)
+{
+ GList *i;
+
+ for (i = profiles; i; i = i->next) {
+ GUPnPDLNAProfile *profile = (GUPnPDLNAProfile *)(i->data);
+
+ if (check_audio_profile (profile->enc_profile, info) &&
+ check_container (info, profile->enc_profile)) {
+ *name = g_strdup (profile->name);
+ *mime = g_strdup (profile->mime);
+ break;
+ }
+ }
+}
+
+static GstCaps *
+caps_from_video_stream_info (GstStreamVideoInformation *video)
+{
+ GstStreamInformation *stream = GST_STREAM_INFORMATION (video);
+ GstCaps *caps = gst_caps_copy (stream->caps);
+ int n, d;
+
+ if (video->height)
+ gst_caps_set_simple (caps, "height", G_TYPE_INT,
+ video->height, NULL);
+ if (video->width)
+ gst_caps_set_simple (caps, "width", G_TYPE_INT,
+ video->width, NULL);
+ if (GST_VALUE_HOLDS_FRACTION (&video->frame_rate)) {
+ n = gst_value_get_fraction_numerator (&video->frame_rate);
+ d = gst_value_get_fraction_denominator (&video->frame_rate);
+ gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION,
+ n, d, NULL);
+ }
+ if (GST_VALUE_HOLDS_FRACTION (&video->pixel_aspect_ratio)) {
+ n = gst_value_get_fraction_numerator (
+ &video->pixel_aspect_ratio);
+ d = gst_value_get_fraction_denominator (
+ &video->pixel_aspect_ratio);
+ gst_caps_set_simple (caps, "pixel-aspect-ratio",
+ GST_TYPE_FRACTION, n, d, NULL);
+ }
+ if (video->format != GST_VIDEO_FORMAT_UNKNOWN)
+ gst_caps_set_simple (caps, "format", GST_TYPE_FOURCC,
+ gst_video_format_to_fourcc (video->format),
+ NULL);
+ if (stream->tags) {
+ guint bitrate;
+ if (gst_tag_list_get_uint (stream->tags, "bitrate", &bitrate))
+ gst_caps_set_simple (caps, "bitrate", G_TYPE_INT,
+ (int) bitrate, NULL);
+ if (gst_tag_list_get_uint (stream->tags, "maximum-bitrate",
+ &bitrate))
+ gst_caps_set_simple (caps, "maximum-bitrate",
+ G_TYPE_INT, (int) bitrate, NULL);
+ }
+
+ return caps;
+}
+
+static gboolean
+check_video_profile (GstEncodingProfile *profile,
+ GstDiscovererInformation *info)
+{
+ GList *i;
+ gboolean found_video = FALSE, found_audio = FALSE;;
+
+ /* Check video and audio restrictions */
+ for (i = info->stream_list;
+ i && !(found_video && found_audio);
+ i = i->next) {
+ GstStreamInformation *stream;
+ GstCaps *caps = NULL;
+
+ stream = (GstStreamInformation *) i->data;
+
+ if (!found_video &&
+ stream->streamtype == GST_STREAM_VIDEO) {
+ caps = caps_from_video_stream_info (
+ GST_STREAM_VIDEO_INFORMATION (stream));
+ if (match_profile (profile,
+ caps,
+ GST_ENCODING_PROFILE_VIDEO))
+ found_video = TRUE;
+ } else if (!found_audio &&
+ stream->streamtype == GST_STREAM_AUDIO) {
+ caps = caps_from_audio_stream_info (
+ GST_STREAM_AUDIO_INFORMATION (stream));
+ if (match_profile (profile,
+ caps,
+ GST_ENCODING_PROFILE_AUDIO))
+ found_audio = TRUE;
+ }
+
+ if (caps)
+ gst_caps_unref (caps);
+ }
+
+ if (!found_video || !found_audio)
+ return FALSE;
+
+ /* Check container restrictions */
+ if (!check_container (info, profile))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+guess_video_profile (GstDiscovererInformation *info, gchar **name, gchar **mime)
+{
+ GUPnPDLNAProfile *profile = NULL;
+ GList *i;
+
+ for (i = profiles; i; i = i->next) {
+ profile = (GUPnPDLNAProfile *)(i->data);
+
+ if (check_video_profile (profile->enc_profile, info)) {
+ *name = g_strdup (profile->name);
+ *mime = g_strdup (profile->mime);
+ break;
+ }
+ }
+}
+
+static void
+guess_image_profile (GstStreamInformation *info, gchar **name, gchar **mime)
+{
+ GstStreamVideoInformation *video = GST_STREAM_VIDEO_INFORMATION (info);
+ GstCaps *caps;
+ GList *i;
+ gboolean found = FALSE;
+
+ if (!info || info->streamtype != GST_STREAM_IMAGE)
+ return;
+
+ caps = caps_from_video_stream_info (video);
+
+ for (i = profiles; !found && i; i = i->next) {
+ GUPnPDLNAProfile *profile = (GUPnPDLNAProfile *)(i->data);
+
+ /* Optimisation TODO: this can be pre-computed */
+ if (is_video_profile (profile->enc_profile))
+ continue;
+
+ if (match_profile (profile->enc_profile,
+ caps,
+ GST_ENCODING_PROFILE_IMAGE)) {
+ /* Found a match */
+ *name = g_strdup (profile->name);
+ *mime = g_strdup (profile->mime);
+ break;
+ }
+ }
+
+ gst_caps_unref (caps);
+}
+
+GUPnPDLNAInformation *
+gupnp_dlna_information_new_from_discoverer_info (GstDiscovererInformation * info)
+{
+ GUPnPDLNAInformation *dlna;
+ GstStreamType type = GST_STREAM_UNKNOWN;
+ GList *tmp = info->stream_list;
+ gchar *name = NULL, *mime = NULL;
+
+ if (!profiles) {
+ profiles = g_list_concat (profiles,
+ gupnp_dlna_load_profiles_from_disk ());
+ }
+
+ while (tmp) {
+ GstStreamInformation *stream_info =
+ (GstStreamInformation *) tmp->data;
+
+ if (stream_info->streamtype == GST_STREAM_VIDEO)
+ type = GST_STREAM_VIDEO;
+ else if (stream_info->streamtype == GST_STREAM_IMAGE)
+ type = GST_STREAM_IMAGE;
+ else if (stream_info->streamtype == GST_STREAM_AUDIO &&
+ type != GST_STREAM_VIDEO)
+ type = GST_STREAM_AUDIO;
+
+ tmp = tmp->next;
+ }
+
+ if (type == GST_STREAM_AUDIO)
+ guess_audio_profile (info, &name, &mime);
+ else if (type == GST_STREAM_VIDEO)
+ guess_video_profile (info, &name, &mime);
+ else if (type == GST_STREAM_IMAGE)
+ /* There will be only one GstStreamInformation for images */
+ guess_image_profile (info->stream_info, &name, &mime);
+
+ dlna = gupnp_dlna_information_new (name, mime, info);
+
+ g_debug ("DLNA profile: %s -> %s, %s", info->uri, name, mime);
+
+ g_free (name);
+ g_free (mime);
+ return dlna;
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..f740e01
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,5 @@
+noinst_PROGRAMS = dlna-profile-parser
+AM_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/gst-convenience/gst-libs $(GST_CFLAGS)
+AM_LDFLAGS = $(top_builddir)/libgupnp-dlna/libgupnp-dlna-1.0.la $(top_builddir)/gst-convenience/gst-libs/gst/profile/.libs/libgstprofile-gupnp-dlna-@GST_MAJORMINOR@.la
+
+dlna_profile_parser_SOURCES = dlna-profile-parser.c
diff --git a/tests/dlna-profile-parser.c b/tests/dlna-profile-parser.c
new file mode 100644
index 0000000..ed35a4e
--- /dev/null
+++ b/tests/dlna-profile-parser.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Authors: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <libgupnp-dlna/gupnp-dlna-load.h>
+#include <libgupnp-dlna/gupnp-dlna-profile.h>
+#include <gst/profile/gstprofile.h>
+#include <stdlib.h>
+
+static void usage (void)
+{
+ g_print ("Usage: dlna-profile-parser file1 file2 ... dir1 dir2 ...\n");
+}
+
+static void print_caps (GstCaps *caps)
+{
+ int i;
+
+ for (i = 0; i < gst_caps_get_size (caps); i++) {
+ GstStructure *structure = gst_caps_get_structure (caps, i);
+ gchar *tmp = gst_structure_to_string (structure);
+
+ g_print ("%s`- %s\n", i ? " " : "", tmp);
+
+ g_free (tmp);
+ }
+}
+
+static void print_profile (GUPnPDLNAProfile *profile, gpointer unused)
+{
+ GList *tmp = profile->enc_profile->encodingprofiles;
+ gchar *caps_str = gst_caps_to_string (profile->enc_profile->format);
+
+ g_print ("Loaded DLNA Profile: %s, %s - format %s\n",
+ profile->name,
+ profile->mime,
+ caps_str);
+
+ while (tmp) {
+ print_caps (((GstStreamEncodingProfile *) tmp->data)->format);
+ tmp = tmp->next;
+ }
+
+ g_print ("\n");
+ g_free (caps_str);
+}
+
+int
+main (int argc, char **argv)
+{
+ GList *profiles = NULL;
+ GHashTable *restrictions, *profile_ids;
+ gint i;
+
+ g_thread_init (NULL);
+ gst_init (&argc, &argv);
+
+ if (argc < 2) {
+ usage ();
+ return EXIT_FAILURE;
+ }
+
+ restrictions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) xmlFree,
+ (GDestroyNotify)
+ gst_stream_encoding_profile_free);
+ profile_ids = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) xmlFree,
+ (GDestroyNotify)
+ gst_encoding_profile_free);
+
+
+ for (i = 1; i < argc; i++) {
+ GList *tmp;
+
+ if (g_file_test (argv[i], G_FILE_TEST_IS_DIR))
+ tmp = gupnp_dlna_load_profiles_from_dir (argv[i]);
+ else
+ tmp = gupnp_dlna_load_profiles_from_file (argv[i],
+ restrictions,
+ profile_ids);
+
+ profiles = g_list_concat (profiles, tmp);
+ }
+
+ g_list_foreach (profiles, (GFunc)print_profile, NULL);
+ g_list_foreach (profiles, (GFunc)g_object_unref, NULL);
+
+ g_hash_table_unref (restrictions);
+ g_hash_table_unref (profile_ids);
+ return EXIT_SUCCESS;
+}
diff --git a/tests/dlna/xml/dlna-profile-illegal-base.xml b/tests/dlna/xml/dlna-profile-illegal-base.xml
new file mode 100644
index 0000000..914be81
--- /dev/null
+++ b/tests/dlna/xml/dlna-profile-illegal-base.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+
+<!-- illegal reference - should not crash -->
+
+<dlna-profiles>
+ <dlna-profile name="LPCM" mime="audio/mpeg" base-profile="foo">
+ <restriction type="audio">
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/tests/dlna/xml/dlna-profile-illegal-parent.xml b/tests/dlna/xml/dlna-profile-illegal-parent.xml
new file mode 100644
index 0000000..32c77fa
--- /dev/null
+++ b/tests/dlna/xml/dlna-profile-illegal-parent.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+
+<!-- illegal reference - should not crash -->
+
+<dlna-profiles>
+ <dlna-profile name="LPCM" mime="audio/mpeg">
+ <parent name="foo" />
+ <restriction type="audio">
+ </restriction>
+ </dlna-profile>
+</dlna-profiles>
diff --git a/tests/dlna/xml/dlna-profile-missing-attribute.xml b/tests/dlna/xml/dlna-profile-missing-attribute.xml
new file mode 100644
index 0000000..783db94
--- /dev/null
+++ b/tests/dlna/xml/dlna-profile-missing-attribute.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<!-- missing attribute -->
+
+<dlna-profiles>
+ <dlna-profile name="LPCM">
+ </dlna-profile>
+</dlna-profiles>
diff --git a/tests/dlna/xml/dlna-profile-parent-order.xml b/tests/dlna/xml/dlna-profile-parent-order.xml
new file mode 100644
index 0000000..df61ad4
--- /dev/null
+++ b/tests/dlna/xml/dlna-profile-parent-order.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<!-- order of parent/restriction in dlna-profile -->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="foo" type="container">
+ <field name="name" type="string">
+ <value>foo</value>
+ </field>
+ </restriction>
+ <restriction id="bar" type="audio">
+ <field name="name" type="string">
+ <value>bar</value>
+ </field>
+ </restriction>
+ </restrictions>
+
+ <dlna-profile name="MP3X" mime="audio/mpeg">
+ <parent name="bar" />
+
+ <restriction id="MP3X" type="audio">
+ <field name="name" type="string">
+ <value>audio/mpeg</value>
+ </field>
+ </restriction>
+
+ <parent name="foo" />
+ </dlna-profile>
+</dlna-profiles>
diff --git a/tests/dlna/xml/dlna-profiles-empty.xml b/tests/dlna/xml/dlna-profiles-empty.xml
new file mode 100644
index 0000000..47a75df
--- /dev/null
+++ b/tests/dlna/xml/dlna-profiles-empty.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+
+<!-- empty file -->
+
+<dlna-profiles>
+</dlna-profiles>
diff --git a/tests/dlna/xml/dlna-profiles-restrictions-order.xml b/tests/dlna/xml/dlna-profiles-restrictions-order.xml
new file mode 100644
index 0000000..41f195b
--- /dev/null
+++ b/tests/dlna/xml/dlna-profiles-restrictions-order.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+
+<!-- order of dlna-profile/restriction in dlna-profiles -->
+
+<dlna-profiles>
+ <dlna-profile name="foo" mime="bar">
+ </dlna-profile>
+
+ <restrictions>
+ </restrictions>
+
+ <dlna-profile name="zoo" mime="baz">
+ </dlna-profile>
+
+ <restrictions>
+ </restrictions>
+
+ <dlna-profile name="orly" mime="yarly">
+ </dlna-profile>
+</dlna-profiles>
diff --git a/tests/dlna/xml/field-no-value.xml b/tests/dlna/xml/field-no-value.xml
new file mode 100644
index 0000000..73a7147
--- /dev/null
+++ b/tests/dlna/xml/field-no-value.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+
+<!-- field with no value -->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="MP3X" type="audio">
+ <field name="name" type="string">
+ </field>
+ </restriction>
+ </restrictions>
+</dlna-profiles>
diff --git a/tests/dlna/xml/range-invalid.xml b/tests/dlna/xml/range-invalid.xml
new file mode 100644
index 0000000..1472d5f
--- /dev/null
+++ b/tests/dlna/xml/range-invalid.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+
+<!-- bad range -->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="MP3X" type="audio">
+ <field name="name" type="int">
+ <range min="1" />
+ </field>
+ </restriction>
+ </restrictions>
+</dlna-profiles>
diff --git a/tests/dlna/xml/restriction-duplicate.xml b/tests/dlna/xml/restriction-duplicate.xml
new file mode 100644
index 0000000..7ad4fce
--- /dev/null
+++ b/tests/dlna/xml/restriction-duplicate.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+
+<!-- two restrictions with the same name -->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="LPCM" type="audio">
+ </restriction>
+ <restriction id="LPCM" type="audio">
+ </restriction>
+ </restrictions>
+</dlna-profiles>
diff --git a/tests/dlna/xml/restriction-forward-reference.xml b/tests/dlna/xml/restriction-forward-reference.xml
new file mode 100644
index 0000000..b584425
--- /dev/null
+++ b/tests/dlna/xml/restriction-forward-reference.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+
+<!-- forward references do not work -->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="MP3X" type="audio">
+ <parent name="MP3" />
+ </restriction>
+ <restriction id="MP3" type="audio">
+ </restriction>
+ </restrictions>
+</dlna-profiles>
diff --git a/tests/dlna/xml/restrictions-illegal-parent.xml b/tests/dlna/xml/restrictions-illegal-parent.xml
new file mode 100644
index 0000000..a7fc5ac
--- /dev/null
+++ b/tests/dlna/xml/restrictions-illegal-parent.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+
+<!-- bad parent tag -->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction type="audio">
+ <parent name="foo" />
+ </restriction>
+ </restrictions>
+</dlna-profiles>
diff --git a/tests/dlna/xml/value-empty.xml b/tests/dlna/xml/value-empty.xml
new file mode 100644
index 0000000..c78fedc
--- /dev/null
+++ b/tests/dlna/xml/value-empty.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+
+<!-- empty value - should generate a warning -->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="MP3X" type="audio">
+ <field name="name" type="string">
+ <value></value>
+ </field>
+ </restriction>
+ </restrictions>
+</dlna-profiles>
diff --git a/tests/dlna/xml/value-invalid.xml b/tests/dlna/xml/value-invalid.xml
new file mode 100644
index 0000000..90a4552
--- /dev/null
+++ b/tests/dlna/xml/value-invalid.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+
+<!-- invalid value type -->
+
+<dlna-profiles>
+ <restrictions>
+ <restriction id="MP3X" type="audio">
+ <field name="name" type="complex">
+ <value>2+3i</value>
+ </field>
+ </restriction>
+ </restrictions>
+</dlna-profiles>