Imported mafw-gst-renderer_0.1.2009.47-1+0m5
authorRoman Moravcik <roman.moravcik@gmail.com>
Mon, 18 Jan 2010 08:37:12 +0000 (09:37 +0100)
committerRoman Moravcik <roman.moravcik@gmail.com>
Mon, 18 Jan 2010 08:37:12 +0000 (09:37 +0100)
55 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/mafw-gst-renderer.install.in [new file with mode: 0644]
debian/mafw-gst-renderer.postinst [new file with mode: 0644]
debian/mafw-gst-renderer.prerm [new file with mode: 0644]
debian/rules [new file with mode: 0755]
libmafw-gst-renderer/Makefile.am [new file with mode: 0644]
libmafw-gst-renderer/blanking.c [new file with mode: 0644]
libmafw-gst-renderer/blanking.h [new file with mode: 0644]
libmafw-gst-renderer/gstscreenshot.c [new file with mode: 0644]
libmafw-gst-renderer/gstscreenshot.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-marshal.list [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state-paused.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state-paused.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state-playing.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state-playing.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-state.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-utils.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-utils.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-worker.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer-worker.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-gst-renderer.h [new file with mode: 0644]
libmafw-gst-renderer/mafw-playlist-iterator.c [new file with mode: 0644]
libmafw-gst-renderer/mafw-playlist-iterator.h [new file with mode: 0644]
mafw-gst-renderer-uninstalled.pc.in [new file with mode: 0644]
tests/Makefile.am [new file with mode: 0644]
tests/check-mafw-gst-renderer.c [new file with mode: 0644]
tests/check-main.c [new file with mode: 0644]
tests/mafw-mock-playlist.c [new file with mode: 0644]
tests/mafw-mock-playlist.h [new file with mode: 0644]
tests/mafw-mock-pulseaudio.c [new file with mode: 0644]
tests/mafw-mock-pulseaudio.h [new file with mode: 0644]
tests/mafw-test-player.c [new file with mode: 0644]
tests/media/test.avi [new file with mode: 0644]
tests/media/test.wav [new file with mode: 0644]
tests/media/testframe.png [new file with mode: 0644]
tests/test.suppressions [new file with mode: 0644]
welcome [deleted file]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..a2c188d
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,10 @@
+Visa Smolander <visa.smolander@nokia.com>
+Zeeshan Ali <zeeshan.ali@nokia.com>
+Mikael Saarenpää <mikael.saarenpaa@nokia.com>
+Antía Puentes Felpeto <apuentes@igalia.com>
+Iago Toral Quiroga <itoral@igalia.com>
+Juan Suárez Romero <jasuarez@igalia.com>
+Sandor Pinter <sandor.pinter@blumsoft.eu>
+Xabier Rodríguez Calvar <xrcalvar@igalia.com>
+Juha Kellokoski <veitikka6@gmail.com>
+Mika Tapojarvi <mika.tapojarvi@sse.fi>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..5ab7695
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,504 @@
+                 GNU LESSER GENERAL PUBLIC LICENSE
+                      Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                 GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  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.
+\f
+  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.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  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
+\f
+           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 Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <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 (file)
index 0000000..e69de29
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..6ab6aea
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# Makefile.am for MAFW gst renderer library.
+#
+# Author: Visa Smolander <visa.smolander@nokia.com>
+#
+# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved.
+
+
+SUBDIRS                                = libmafw-gst-renderer
+
+if ENABLE_TESTS
+SUBDIRS                                += tests
+endif
+
+noinst_DATA                    = mafw-gst-renderer-uninstalled.pc
+EXTRA_DIST                     = mafw-gst-renderer-uninstalled.pc.in
+
+# 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 *-stamp omf.make
+
+if ENABLE_COVERAGE
+LCOV_DATA_DIR                  = lcov-data
+LCOV_DATA_FILE                 = lcov.info
+
+distclean-local:
+       -rm -rf $(LCOV_DATA_DIR)
+
+lcov-zero-counters:
+       $(LCOV) -z -d .
+
+lcov:
+       -mkdir -p $(LCOV_DATA_DIR)
+       $(LCOV) -c -d . -o $(LCOV_DATA_DIR)/$(LCOV_DATA_FILE)
+       genhtml -s $(LCOV_DATA_DIR)/$(LCOV_DATA_FILE) -o $(LCOV_DATA_DIR)
+       @echo
+       @echo "Please, have a look on ./$(LCOV_DATA_DIR)/index.html for coverage statistics"
+       @echo
+endif
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..1457d2a
--- /dev/null
@@ -0,0 +1,179 @@
+dnl AM_PATH_CHECK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for check, and define CHECK_CFLAGS and CHECK_LIBS
+dnl
+
+AC_DEFUN([AM_PATH_CHECK],
+[
+  AC_ARG_WITH(check,
+  [  --with-check=PATH       prefix where check is installed [default=auto]])
+
+  min_check_version=ifelse([$1], ,0.8.2,$1)
+
+  AC_MSG_CHECKING(for check - version >= $min_check_version)
+
+  if test x$with_check = xno; then
+    AC_MSG_RESULT(disabled)
+    ifelse([$3], , AC_MSG_ERROR([disabling check is not supported]), [$3])
+  else
+    if test "x$with_check" != x; then
+      CHECK_CFLAGS="-I$with_check/include"
+      CHECK_LIBS="-L$with_check/lib -lcheck"
+    else
+      CHECK_CFLAGS=""
+      CHECK_LIBS="-lcheck"
+    fi
+
+    ac_save_CFLAGS="$CFLAGS"
+    ac_save_LIBS="$LIBS"
+
+    CFLAGS="$CFLAGS $CHECK_CFLAGS"
+    LIBS="$CHECK_LIBS $LIBS"
+
+    rm -f conf.check-test
+    AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <check.h>
+
+int main ()
+{
+  int major, minor, micro;
+  char *tmp_version;
+
+  system ("touch conf.check-test");
+
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = strdup("$min_check_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string\n", "$min_check_version");
+     return 1;
+   }
+
+  if ((CHECK_MAJOR_VERSION != check_major_version) ||
+      (CHECK_MINOR_VERSION != check_minor_version) ||
+      (CHECK_MICRO_VERSION != check_micro_version))
+    {
+      printf("\n*** The check header file (version %d.%d.%d) does not match\n",
+             CHECK_MAJOR_VERSION, CHECK_MINOR_VERSION, CHECK_MICRO_VERSION);
+      printf("*** the check library (version %d.%d.%d).\n",
+             check_major_version, check_minor_version, check_micro_version);
+      return 1;
+    }
+
+  if ((check_major_version > major) ||
+      ((check_major_version == major) && (check_minor_version > minor)) ||
+      ((check_major_version == major) && (check_minor_version == minor) && (check_micro_version >= micro)))
+    {
+      return 0;
+    }
+  else
+    {
+      printf("\n*** An old version of check (%d.%d.%d) was found.\n",
+             check_major_version, check_minor_version, check_micro_version);
+      printf("*** You need a version of check being at least %d.%d.%d.\n", major, minor, micro);
+      printf("***\n");
+      printf("*** If you have already installed a sufficiently new version, this error\n");
+      printf("*** probably means that the wrong copy of the check library and header\n");
+      printf("*** file is being found. Rerun configure with the --with-check=PATH option\n");
+      printf("*** to specify the prefix where the correct version was installed.\n");
+    }
+
+  return 1;
+}
+],, no_check=yes, [echo $ac_n "cross compiling; assumed OK... $ac_c"])
+
+    CFLAGS="$ac_save_CFLAGS"
+    LIBS="$ac_save_LIBS"
+
+    if test "x$no_check" = x ; then
+      AC_MSG_RESULT(yes)
+      ifelse([$2], , :, [$2])
+    else
+      AC_MSG_RESULT(no)
+      if test -f conf.check-test ; then
+        :
+      else
+        echo "*** Could not run check test program, checking why..."
+        CFLAGS="$CFLAGS $CHECK_CFLAGS"
+        LIBS="$CHECK_LIBS $LIBS"
+        AC_TRY_LINK([
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <check.h>
+], ,  [ echo "*** The test program compiled, but did not run. This usually means"
+        echo "*** that the run-time linker is not finding check. You'll need to set your"
+        echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+        echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+        echo "*** is required on your system"
+        echo "***"
+        echo "*** If you have an old version installed, it is best to remove it, although"
+        echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+      [ echo "*** The test program failed to compile or link. See the file config.log for"
+        echo "*** the exact error that occured." ])
+
+        CFLAGS="$ac_save_CFLAGS"
+        LIBS="$ac_save_LIBS"
+      fi
+
+      CHECK_CFLAGS=""
+      CHECK_LIBS=""
+
+      rm -f conf.check-test
+      ifelse([$3], , AC_MSG_ERROR([check not found]), [$3])
+    fi
+
+    AC_SUBST(CHECK_CFLAGS)
+    AC_SUBST(CHECK_LIBS)
+
+    rm -f conf.check-test
+
+  fi
+])
+
+dnl AM_MAFW_PLUGIN_CHECK(PLUGIN, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Test to check whether a plugin exists
+dnl
+
+AC_DEFUN([AM_MAFW_PLUGIN_CHECK],
+[
+   AC_MSG_CHECKING(for MAFW plugin)
+
+   ac_save_CFLAGS="$CFLAGS"
+   ac_save_LIBS="$LIBS"
+   ac_save_CC="$CC"
+
+   CFLAGS="$CFLAGS $(pkg-config mafw --cflags)"
+   LIBS="$LIBS $(pkg-config mafw --libs)"
+   CC="libtool --mode=link gcc"
+
+   AC_TRY_RUN([
+#include <libmafw/mafw.h>
+
+int main ()
+{
+  MafwRegistry *registry = NULL;
+  gboolean result;
+
+  g_type_init();
+
+  registry =  MAFW_REGISTRY(mafw_get_local_registry());
+  if (!registry) {
+     printf ("Cannot get registry\n");
+     return 1;
+  }
+  result = mafw_local_registry_load_plugin(MAFW_LOCAL_REGISTRY(registry), $1, NULL);
+  if (!result) {
+     printf ("Unable to load %s\n", $1);
+     return 1;
+  } else {
+     return 0;
+  }
+}
+   ], [$2], [$3])
+
+  CFLAGS="$ac_save_CFLAGS"
+  LIBS="$ac_save_LIBS"
+  CC="$ac_save_CC"
+])
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..d88de29
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+export AUTOMAKE="automake-1.9"
+export ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/`
+
+autoreconf -v -f -i || exit 1
+test -n "$NOCONFIGURE" || ./configure \
+       --enable-debug --enable-maintainer-mode "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..9b146ec
--- /dev/null
@@ -0,0 +1,222 @@
+#
+# configure.ac for MAFW gstreamer renderer library
+# 
+# Author: Visa Smolander <visa.smolander@nokia.com>
+#
+# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved.
+
+AC_PREREQ([2.53])
+AC_INIT([mafw-gst-renderer], [0.1.2009.47-1])
+
+AC_CONFIG_SRCDIR([libmafw-gst-renderer/mafw-gst-renderer.h])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_AUX_DIR([build-aux])
+
+AM_INIT_AUTOMAKE([foreign tar-ustar])
+AM_MAINTAINER_MODE
+
+AC_DISABLE_STATIC
+
+dnl Prevent AC_PROG_CC adding '-g -O2' to CFLAGS.
+SAVEDCFLAGS="$CFLAGS"
+AC_PROG_CC
+if test "x$GCC" = xyes; then
+       CFLAGS="$SAVEDCFLAGS"
+fi
+
+AC_PROG_LIBTOOL
+AC_PROG_INSTALL
+
+# DISABLED_BY_DEFAULT(NAME, DESCRIPTION)
+# ---------------------------------
+# Creates a new --enable-* option, with default value `no'.
+AC_DEFUN([DISABLED_BY_DEFAULT], [dnl
+         AC_ARG_ENABLE([$1], AS_HELP_STRING([--enable-$1], [$2]), [],[dnl
+         m4_bpatsubst([enable_$1], [[^0-9a-z]], [_])=no])dnl
+])# DISABLED_BY_DEFAULT
+
+# ENABLED_BY_DEFAULT(NAME, DESCRIPTION)
+# ---------------------------------
+# Creates a new --disable-* option, with default value `yes'.
+AC_DEFUN([ENABLED_BY_DEFAULT], [dnl
+         AC_ARG_ENABLE([$1], AS_HELP_STRING([--disable-$1], [$2]), [],[dnl
+         m4_bpatsubst([enable_$1], [[^0-9a-z]], [_])=yes])dnl
+])# ENABLED_BY_DEFAULT
+
+dnl Prerequisites.
+
+GSTREAMER_VERSION=0.10.20
+
+AM_PATH_GLIB_2_0(2.15.0, [], [], [glib])
+PKG_CHECK_MODULES(DEPS,
+                 gobject-2.0 >= 2.0
+                 gstreamer-0.10 >= $GSTREAMER_VERSION
+                 gstreamer-plugins-base-0.10 >= $GSTREAMER_VERSION
+                 mafw >= 0.1
+                 libosso >= 2.0
+                 x11
+                 hal
+                 totem-plparser
+                  gconf-2.0 >= 2.0
+                 gnome-vfs-2.0
+)
+
+dnl Check for GdkPixbuf, needed for dumping current frame
+GDKPIXBUF_REQUIRED=2.12.0
+AC_ARG_ENABLE(gdkpixbuf,
+             AS_HELP_STRING([--disable-gdkpixbuf],
+                            [Disable GdkPixbuf support, required for current frame dumping]),,
+             [enable_gdkpixbuf=auto])
+
+if test "x$enable_gdkpixbuf" != "xno"; then
+   PKG_CHECK_MODULES(GDKPIXBUF,
+                    [gdk-pixbuf-2.0 >= $GDKPIXBUF_REQUIRED],
+                    [have_gdkpixbuf=yes],
+                    [have_gdkpixbuf=no])
+   AC_SUBST(GDKPIXBUF_LIBS)
+   AC_SUBST(GDKPIXBUF_CFLAGS)
+
+   if test "x$have_gdkpixbuf" = "xyes"; then
+      AC_DEFINE(HAVE_GDKPIXBUF, [], [Define if we have GdkPixbuf])
+   fi
+else
+   have_gdkpixbuf="no (disabled)"
+fi
+
+if test "x$enable_gdkpixbuf" = "xyes"; then
+   if test "x$have_gdkpixbuf" != "xyes"; then
+      AC_MSG_ERROR([Couldn't find GdkPixbuf >= $GDKPIXBUF_REQUIRED.])
+   fi
+fi
+
+AM_CONDITIONAL(HAVE_GDKPIXBUF, test "x$have_gdkpixbuf" = "xyes")
+
+
+dnl Check for Conic, needed connection error handling
+CONIC_REQUIRED=0.16
+AC_ARG_ENABLE(conic,
+             AS_HELP_STRING([--disable-conic],
+                            [Disable Conic support, required to handle network errors]),,
+             [enable_conic=auto])
+
+if test "x$enable_conic" != "xno"; then
+   PKG_CHECK_MODULES(CONIC,
+                    [conic >= $CONIC_REQUIRED],
+                    [have_conic=yes],
+                    [have_conic=no])
+   AC_SUBST(CONIC_LIBS)
+   AC_SUBST(CONIC_CFLAGS)
+
+   if test "x$have_conic" = "xyes"; then
+      AC_DEFINE(HAVE_CONIC, [], [Define if we have Conic])
+   fi
+else
+   have_conic="no (disabled)"
+fi
+
+if test "x$enable_conic" = "xyes"; then
+   if test "x$have_conic" != "xyes"; then
+      AC_MSG_ERROR([Couldn't find Conic >= $CONIC_REQUIRED.])
+   fi
+fi
+
+AM_CONDITIONAL(HAVE_CONIC, test "x$have_conic" = "xyes")
+
+
+
+plugindir=`$PKG_CONFIG --variable=plugindir mafw`
+AC_SUBST(plugindir)
+
+dnl Default compile flags.  (NOTE: CFLAGS is reserved for the user!)
+
+AC_SUBST([_CFLAGS])
+AC_SUBST([_LDFLAGS])
+_CFLAGS="-Wall -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations"
+_CFLAGS="$_CFLAGS -ggdb3"
+
+dnl Configure-time options.
+
+dnl Debugging.
+DISABLED_BY_DEFAULT([debug], [compile with debug flags and extra output])
+if test "x$enable_debug" = xyes; then
+       AC_DEFINE([MAFW_DEBUG], [1], [Enable debugging related parts.])
+       _CFLAGS="$_CFLAGS -O0 -Werror -DGTK_DISABLE_DEPRECATED"
+else
+       AC_DEFINE([G_DEBUG_DISABLE], [1], [Disable g_debug() calls.])
+       _CFLAGS="$_CFLAGS -O2"
+fi
+AS_IF([test "x$enable_debug" = xyes],
+       [AC_DEFINE([MAFW_DEBUG], [1], [Enable extra debug messages])
+        CFLAGS="$CFLAGS -Werror -O0 -ggdb3 -DGTK_DISABLE_DEPRECATED"],
+       [AC_DEFINE([G_DEBUG_DISABLE], [1], [Disable g_debug calls])
+        CFLAGS="$CFLAGS -O2"])
+
+
+dnl Tests.
+DISABLED_BY_DEFAULT([tests], [disable unit tests])
+if test "x${SBOX_DPKG_INST_ARCH}" = "xarmel"; then
+   AC_MSG_WARN([Tests are disabled for compilation in armel])
+   enable_tests="no"
+fi
+if test "x$enable_tests" = xyes; then
+       PKG_CHECK_MODULES(CHECKMORE, [checkmore, check >= 0.9.4])
+       if test -z "$CHECKMORE_LIBS"; then
+               AC_MSG_WARN([checkmore is needed for unit tests!])
+       fi
+fi
+AM_CONDITIONAL(ENABLE_TESTS,
+               [test "x$enable_tests" = xyes && test -n "$CHECKMORE_LIBS"])
+
+dnl Volume handling
+if test "x${SBOX_DPKG_INST_ARCH}" = "xi386"; then
+   DISABLED_BY_DEFAULT([pulse-volume], [enable volume handling with pulse])
+else
+   ENABLED_BY_DEFAULT([pulse-volume], [enable volume handling with pulse])
+fi
+if test "x$enable_pulse_volume" = xno; then
+       AC_DEFINE([MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME], [1], [Disables volume handling with pulse.])
+else
+        PKG_CHECK_MODULES(VOLUME, libpulse-mainloop-glib >= 0.9.15)
+fi
+
+
+dnl Mute
+DISABLED_BY_DEFAULT([mute], [enable mute handling])
+if test "x$enable_mute" = xyes; then
+       AC_DEFINE([MAFW_GST_RENDERER_ENABLE_MUTE], [1], [Enable mute.])
+fi
+
+dnl Tracing.
+DISABLED_BY_DEFAULT([tracing], [enable function instrumentation (tracing)])
+if test "x$enable_tracing" = xyes; then
+       _CFLAGS="$_CFLAGS -finstrument-functions -rdynamic"
+fi
+
+dnl Coverage.
+DISABLED_BY_DEFAULT([coverage], [enable coverage data generation (gcov)])
+if test "x$enable_coverage" = xyes; then
+       AC_PATH_PROG(LCOV, [lcov], [lcov])
+       if test "x$LCOV" = x; then
+               echo You need to install lcov to get actual reports!
+               echo See http://ltp.sf.net/coverage/lcov.php
+       fi
+       if test "x$SBOX_USE_CCACHE" == xyes; then
+               AC_MSG_ERROR([Please set SBOX_USE_CCACHE=no to use coverage.])
+       fi
+       _CFLAGS="$_CFLAGS -fprofile-arcs -ftest-coverage"
+       _LDFLAGS="$_LDFLAGS -g -lgcov"
+fi
+AM_CONDITIONAL(ENABLE_COVERAGE,
+               [test "x$enable_coverage" != xno && test -n "$LCOV"])
+
+dnl Output files.
+
+AC_CONFIG_FILES([
+  Makefile
+  mafw-gst-renderer-uninstalled.pc
+  libmafw-gst-renderer/Makefile
+  tests/Makefile
+  debian/mafw-gst-renderer.install
+])
+
+AC_OUTPUT
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..9517254
--- /dev/null
@@ -0,0 +1,1114 @@
+mafw-gst-renderer (0.1.2009.47-1+0m5) unstable; urgency=low
+
+  * This entry has been added by BIFH queue processor
+    Suffix +0m5 added to package revision
+
+ -- mika tapojarvi <ext-mika.tapojarvi@nokia.com>  Thu, 26 Nov 2009 11:40:45 +0200
+
+mafw-gst-renderer (0.1.2009.47-1) unstable; urgency=low
+
+  * Fixes: NB#141508 - Specific video file (mjpeg) makes Mediaplayer unusable
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Fri, 20 Nov 2009 04:18:15 +0200
+
+mafw-gst-renderer (0.1.2009.44-1) unstable; urgency=low
+
+  * Fixes: NB#143299 - mafw_renderer_get_current_metadata don't give correct duration.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Wed, 28 Oct 2009 23:48:22 +0200
+
+mafw-gst-renderer (0.1.2009.42-2) unstable; urgency=low
+
+  * Version increased.
+
+ -- Mika Tapojärvi <ext-mika.tapojarvi@nokia.com>  Tue, 20 Oct 2009 13:26:36 +0300
+
+mafw-gst-renderer (0.1.2009.42-1) unstable; urgency=low
+
+  * Version and changelog updated for pre-release 0.2009.42-1
+  * Rebuild needed.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Mon, 12 Oct 2009 17:21:48 +0300
+
+mafw-gst-renderer (0.1.2009.40-1) unstable; urgency=low
+
+  * Version and changelog updated for pre-release 0.2009.40-1
+  * PR_1_1_baseline copied from trunk.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Sun, 04 Oct 2009 15:47:32 +0300
+
+mafw-gst-renderer (0.1.2009.39-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.39-1
+  * Check for changes only in device having the video-out property.
+  * Disabled test of pulse reconnection as it is not needed with fake volume
+    manager to fix volume test
+  * Changed default fake volume initialization to 0.485 instead of 1 to fix 
+    volume tests
+  * Added return of initialized fake volume manager in an idle call to fix
+    volume tests
+  * Added function to reset volume to pulse pipeline in case pulse volume
+    management is disabled
+  * Added fake volume manager and disabled normal handling with pulse when
+    pulse volume is disabled in compilation.
+  * We avoid setting audiosink to the pipeline and native flags when pulse
+    volume is disabled.
+  * Moved checking for pulse dependency to its conditional compilation
+  * Added support for conditional compilation regarding to volume in
+    renderer
+  * Adds a macro to round values when converting from nanoseconds to seconds.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 18 Sep 2009 14:21:32 +0300
+
+mafw-gst-renderer (0.1.2009.37-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.37-1
+  * Fixes: NB#121136 - [Power Management]Device never goto power saving mode when video playback is connected to TV out
+  * Fixes: NB#134730 - [PR1.1 proposal] <MemLeak> mafw-gst-renderer.c
+  * Fixes: NB#134728 - [PR1.1 proposal] <MemLeak> mafw-gst-renderer-worker.c
+  * Fixes: NB#134495 - [PR1.1 proposal] State changed signal does not come sometimes when stream is played
+  * if for some reason client starts a resume operation
+    after a seek on a stream and by the time we get the resume command we
+    have not started buffering yet, make sure we signal the state change
+    to playing to the client.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 04 Sep 2009 12:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.35-3) unstable; urgency=low
+
+  * MAFW Sales RC4.
+  * Fixes: NB#129912 - Audio playback jarring while receiving a SMS while multiple browser/applications are open.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Tue, 25 Aug 2009 14:15:34 +0300
+
+mafw-gst-renderer (0.1.2009.35-2) unstable; urgency=low
+
+  * First MAFW Sales RC.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Fri, 21 Aug 2009 16:38:27 +0300
+
+mafw-gst-renderer (0.1.2009.35-1) unstable; urgency=low
+
+  * Fixes: NB#129912 - Audio playback jarring while receiving a SMS while multiple browser/applications are open.
+  * Increased the priority of the mafw-gst-renderer.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Thu, 20 Aug 2009 17:34:03 +0300
+
+mafw-gst-renderer (0.1.2009.34-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.34-3
+  * Fixes: NB#132950 - Video seeking is slow most of the time
+  * Check if we get a different duration that the source one and update it
+    if needed in renderer
+  * Added function mafw_gst_renderer_update_source_duration
+  * Reworked mafw_gst_renderer_increase_playcount to use _get_source function
+  * Reworked mafw_gst_renderer_get_metadata to use _get_source function
+  * Created _get_source function in renderer
+  * Reworked _check_duration condition into two different ifs and extracted
+    duration in seconds calculation
+  * Added duration to the source metadata request and kept it in the
+    renderer metadata structure.
+  * Duration in renderer converted in gint instead of guint to hold
+    negative values
+  * flag GST_SEEK_FLAG_KEY_UNIT.
+  * Patch provided by Rene Stadler.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Mon, 17 Aug 2009 22:06:17 +0300
+
+mafw-gst-renderer (0.1.2009.33-3) unstable; urgency=low
+
+  * Fixes: NB#131655 - UPnP: Playback starts from the first instead of playing from where we paused if left the device idle
+  * Fixes: NB#131609 - Mafw-dbus-wrapper crashes. Audio cannot be heard from device's loudspeaker.
+  * Replaced assertion with critical in volume manager for unsuccessful
+    setting volume in pulse operations.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Thu, 13 Aug 2009 17:03:10 +0300
+
+
+mafw-gst-renderer (0.1.2009.33-2) unstable; urgency=low
+
+  * Fixes: NB#128110 - Playback neither stopped nor internal mmc is mounted onto pc when connected in mass storage mode
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Mon, 10 Aug 2009 12:27:43 +0300
+
+mafw-gst-renderer (0.1.2009.33-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.33-1
+  * Fixes: NB#128110 - Playback neither stopped nor internal mmc is mounted onto pc when connected in mass storage mode
+  * New Build-Dependency libosso-gnomevfs2-dev added.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Wed, 05 Aug 2009 12:37:05 +0300
+
+mafw-gst-renderer (0.1.2009.32-1) unstable; urgency=low
+
+  * MAFW, pre-release 0.1.2009.32-1
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Sun, 02 Aug 2009 22:32:27 +0300
+
+mafw-gst-renderer (0.1.2009.30-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.30-3
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Wed, 22 Jul 2009 14:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.30-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.30-2
+  * Fixes: NB#128479 - Seeking in UPnP signals pause and keeps on playing 
+  * Solved problem when pausing while buffering in renderer worker
+  * We activate state changes if we resume while buffering to report the
+    state change to playing in renderer worker
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Wed, 22 Jul 2009 14:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.30-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.30-1
+  * Added more debug to the _handle_buffering function in renderer worker
+  * Just set pipeline to playing when finished buffering of a stream when
+    seeking to avoid signalling PAUSE.
+  * Added documentation in _handle_state_changed function in renderer worker
+  * Added comments in _do_play function in renderer worker
+  * Added comments in _finalize_startup funcion in renderer worker
+  * Reworked _handle_state_changed to use GST_STATE_TRANSITION in renderer 
+    worker.
+  * Reworked _handle_state_changed to use _do_pause in renderer worker
+  * Created _do_pause function to notify the state change and get the
+    current frame on pause if needed in renderer worker.
+  * Removed buffering info from renderer as it was being handled by worker
+  * Removed seek in pause comments as we are not using them in renderer worker
+  * Simplified if condition in state managegement in renderer worker.
+  * Removed state assignment because it could be done at one point in
+    _handle_state_changed in renderer worker.
+  * Added comments in field names in renderer worker to clarify their use
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 17 Jul 2009 14:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.28-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.28-2
+  * Ref the assigned playlist
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Tue, 07 Jul 2009 14:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.28-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.28-1
+  * Fixes: NB#123689 - mafw-dbus-wrapper-76CE-6-4559.rcore.lzo crashed
+  * Ref the assigned playlist
+  * Removing an unnecessary comparison.
+  * Added g_debug in volume manager to indicate that it is initialized and
+    return the instance
+  * Changed _connect function to get an InitCbCallback in volume manager
+  * Modified _reconnect function in volume manager to receive an
+    InitCbCallback and propertly return the volume manager if reconnection
+    happens before having connected a first time
+  * Written function to create the InitCbClosure in volume manager
+  * When [re-]connection to pulse fails, we log a critical and reconnect
+    after 1 second
+  * Modified log in renderer tests to log only errors
+  * Added tests for pulseaudio reconnection in renderer
+  * Removed volume tests in playing since it is not needed anymore as it
+    does not depend on the playing state.
+  * Added check for receiving mute when property is changed in renderer
+    tests
+  * In renderer tests, wait for the volume manager to be initialized when
+    testing properties
+  * Added pulseaudio mock to renderer tests
+  * Added small code comment in renderer tests
+  * Solved problem with mute not enable in renderer tests
+  * Fixed problem when adding elements to a mock playlist in renderer
+    tests
+  * Fixed problem with playlist size initialization in playlist iterator in
+    renderer that was causing problems in the tests
+  * Fixed a bug in the tests when testing the stats updating
+  * Fixed problem with playlist reference count in renderer tests.
+  * Replaced pulsesink and xvimagesink by fakesink in renderer tests
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 03 Jul 2009 11:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.27-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.27-2
+  * Fixes: NB#123701 - mafw-dbus-wrapper-76CE-6-1218.rcore.lzo crashed
+  * Fixes: NB#116836 - Streaming stops after taping on Next button during paused state
+  * Fixes: NB#123545 - mafw-gst-renderer get-position returns uninitialized value
+  * Fixes: NB#124469 - Device going to power saving mode after seek for video streams
+  * Fixes: NB#124116 - Position not progressing after seeking
+  * Fixes: NB#117860 - Inability to handle multiple resource of UPnP items
+  * Make sure we stay paused while buffering.
+  * Always keep worker->state up-to-date.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Tue, 30 Jun 2009 14:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.27-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.27-1
+  * Added function to remove the _set_timeout and used not to call it twice
+    innecessarily.
+  * Changed the order of adding the timeout to set the volume to pulse and
+    calling the timeout immediately as that runs on the mainloop and that
+    execution is delayed to that
+  * Checked for NULL operation when writing volume to pulse to avoid
+    crashes. Critical used instead.
+  * In _ext_stream_restore2_read_cb changed assertion for critical when
+    getting eol < 0 in volume manager
+  * Discarded ext_stream_restore2 volume event when eol < 0. Crash avoided
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Thu, 25 Jun 2009 13:40:00 +0300
+
+mafw-gst-renderer (0.1.2009.26-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.26-1
+  * Check for NULL in the current_metadata.
+  * Removed useles warning message
+  * Updating states and blanking even if we do not need to report them.
+  * Move pause notification from _do_play to handle_buffering.
+  * Build fix.
+  * make _get_position be a state dependant function.
+    On Transitioning it sets position to 0, on Stopped raises an error.
+  * Improved state management in mafw-gst-renderer
+    when seeking (compare new state with worker state to decide
+    if we have to ignore the state transition and in any case,
+    always update the worker state).
+  * Activate playbin2 flags. Speeds up video start time to half.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Tue, 23 Jun 2009 14:33:24 +0300
+
+mafw-gst-renderer (0.1.2009.25-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.25-2
+  * Fixes: NB#117207 - Random MAFW-DBUS-WRAPPER crashes observed
+  * Fixes: NB#104213 - 'Unable to Find Media file' information note not displayed when MMC removed.
+  * Fixes: NB#116426 - Renderer fails to go to pause state when media clips are seeked while streaming
+  * Fixes: NB#121545 - pa_context_connect fails though flag PA_CONTEXT_NOFAIL set
+  * Fixes: NB#120942 - State changed signal is not coming when play issued right after seeking with video streams
+  * Extended current metadata function.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Mon, 15 Jun 2009 14:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.25-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.25-1
+  * Reconnection to pulse is done using an idle call in volume manager.
+  * If PLAY is issued rigtht after seek,
+    signal the state change when we are done buffering. Still,
+    there is a problem because we should not execute a PLAY command
+    while buffering...
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 12 Jun 2009 11:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.24-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.24-3
+  * Fixes: NB#118019 - Playing a clip of Unsupported format will stop the playback. 
+  * Fixes: NB#120378 - Video doesn't start to play when clicked
+  * Stop setting volume if pulse is down in volume manager
+  * Written small workaround for problem when getting an empty error while
+    saving a pixbuf in renderer worker.
+  * Log a warning message when processing an error.
+  * A mafw-gst-renderer dependency version shortened to 0.9.15-1.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Wed, 10 Jun 2009 11:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.24-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.24-2
+  * Added "mafw_renderer_get_current_metadata" function to the API.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Mon, 08 Jun 2009 11:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.24-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.24-1
+  * Fixes: NB#120287 - Video recorded using other camera (e.g. Canon, Pentax) not playing smooth
+  * Fixed debug for seekability in renderer.
+  * We check always GStreamer seekability unless it is reported FALSE from
+    source.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Thu, 04 Jun 2009 10:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.23-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.23-2
+  * Fixes: NB#119440 - Dbus wrapper crashes when high bit rate, high resolution clips are played.
+  * Fixes: NB#119613 - Random mafw-dbus-wrapper coredump generated after booting.
+  * Fixes: NB#119467 - Deleted playlists are not freed.
+  * Fixes: NB#118459 - Cannot play Nokia 5800 video clip 
+  * Fixes: NB#115776 - Renderer state changed signal are not coming when playing and seeking video
+  * No need to ref the playlist in the renderer, 
+    the playlist manager does that already.
+  * Changed to avoid setting the timeout if neither volume nor mute have
+    changed in volume manager
+  * Changed debug messages in volume manager
+  * We ignore mute if it not enabled (default = not enabled).
+  * Added option to configure.ac to disable or enable mute handling
+  * Use g_error_new_literal if string is constant.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Tue, 02 Jun 2009 12:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.23-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.23-1
+  * Signalling volume when reconnecting to pulse
+  * Added code to reconnect to pulse when it gets disconnected.
+  * Closure callback for initialization is not called if it is NULL in
+    volume manager.
+  * Created _connect function to connect with pulse and reworker the init
+    function in volume manager.
+  * Created _destroy_context function and changed _destroy_idle function to
+    do it in volume manager.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 29 May 2009 10:30:00 +0300
+
+mafw-gst-renderer (0.1.2009.22-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.22-3
+  * Use appropriate error codes when the renderer cannot create
+    the playback pipeline and when there are problems with the
+    video window.
+  * Abort the renderer if cannot create pipeline ot sinks.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Wed, 27 May 2009 10:30:00 +0300
+
+mafw-gst-renderer (0.1.2009.22-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.22-2
+  * Fixes: NB#114972 - NP: AUdio- Taping on Next or Previous song volume goes to 0 level
+  * Fixes: NB#109166 - NP view - Volume bar does not popup for Hardware volume +/- key presses
+  * Fixes: NB#116070 - video plays before displaying unsupported resolution error message
+  * Fixes: NB#112697 - gst-renderer not listening volume change events from pulseaudio
+  * Fixes: NB#118001 - seekbar is disabled after playing a unsupported clip
+  * We switch volume manager volume to the just set so we signal it directly
+    when the change is requested. We don't signal any change in the volume
+    subscription if we have and operation in progress.
+  * We keep the pa_operation when writing the volume and cancel it and unref
+    it when a new one comes.
+  * Changed requested_* structure members to current_* in volume manager
+  * Renamed volume and mute structure members to pulse_* in volume manager.
+  * Fixed a small problem with mute. We were forgetting to set it when
+    sending it to pulse.
+  * Changed timeout interval for setting volume to 200ms.
+  * We just change the volume as soon as we get the first call and then we
+    add the timeout to filter following changes.
+  * Improved rounding volumes as we signalled different ones from the one we
+    were setting.
+  * Added protections to public methods so that they are not called when
+    volume manager is not alive yet.
+  * Minor changes when falling back to playbin usage.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Mon, 25 May 2009 12:21:24 +0300
+
+mafw-gst-renderer (0.1.2009.22-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.22-1
+  * If we fallback to use playbin instead of playbin2, check that
+    we can actually create an instance before attempting to
+    set properties.
+  * Changed to filter volume and mute changes to join several in a single
+    call.
+  * Setting property to ensure pulse role when initializing volume manager
+    in renderer.
+  * Separated role and its prefix for checking in volume manager in renderer
+  * Added more debug to volume manager in renderer
+  * Added dependencies to debian/control and configure.ac
+  * Migrated volume management in worker to use pulse implementation.
+  * Added code to manage volume with pulse. This code was developed in
+    collaboration with Marc-André Lureau in gitorious. Link:
+    http://gitorious.org/~calvaris/mafw-pulse/calvaris
+  * Added compilation of volume files to Makefile.am in renderer.
+  * Added empty files with licenses for volume management in renderer.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 22 May 2009 12:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.21-4) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.21-4
+  * Reuse video sink component, do not create a new one every time
+    we play something.
+  * Ref audio and video sink before setting them, since we want them
+    to persist after the pipeline has been destroyed.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Wed, 20 May 2009 11:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.21-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.21-3
+  * Fixes: NB#104494 - Missing renderer error when video clib has high framerate 
+  * Fixes: NB#115514 - UPnP:After seeking the seekbar on Pause state audio/video clip started playing
+  * Fixes: NB#115304 - Video playback will stop if we plug in usb cable
+  * Fixes: NB#115299 - Media player seek bar comes to starting position if usb cable is pluged in
+  * Properly initialize variable.
+  * Use specific error codes for unsupported 
+    resolution and unsupported fps conditions.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Tue, 19 May 2009 10:30:00 +0300
+
+mafw-gst-renderer (0.1.2009.21-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.21-2
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Mon, 18 May 2009 11:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.21-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.21-1
+  * Fixes: NB#117015 - video plays in windowed mode
+  * Fixes: NB#100842 - seeking in aac over http fails
+  * Fixes: NB#115126 - Media Player abuses Tracker API while playing songs
+  * Do not reuse video sink for now, somehow it gets
+    broken after some time and as a result we cannot set XID of target
+    video window to use.
+  * Create and configure audio and video sinks once, then provide these
+    to playbin2 when a new pipeline is created.
+  * Changed _handle_duration to use _check_duration and added for
+    seekability too.
+  * Reworked _query_duration_and_seekability into _check_duration and _check
+    seekability.
+  * Added timeout to query duration and seekability and changed when it was
+    called.
+  * Set timeout id to 0 when timeout is removed
+  * Used function to compare duration in seconds instead of comparing pure
+    nano seconds.
+  * Added function to compare durations in seconds.
+  * Query duration and seek after buffering is complete.
+  * Reworked adding the timeout to use a function.
+  * Modified seekability emission to be done only with changes and adapted
+    to type changes.
+  * Modified duration emission to be emitted only with changes.
+  * Media seekability initialized to unknown when playback begins.
+  * SeekabilityType renamed and moved to renderer worker.
+  * Renderer worker seekability uses its type now instead of gboolean.
+  * Stored duration and seek timeout id to be able to remove it later in
+    renderer worker.
+  * Added query for duration and seekability some seconds after going to
+    PLAYING to have more accurate information from GStreamer in renreder
+    worker.
+  * Reworked _finalize_startup in renderer worker into _finalize_startup and
+    _query_duration_and_seekability. The first uses now the second.
+  * Set pulse sink's latency-time property to half of buffer-time,
+    this provides double buffering capabilities and should also help
+    with avoiding audio glitches. Also, removed buffering for volume
+    pipeline, since that is not needed.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Thu, 14 May 2009 10:09:18 +0300
+
+mafw-gst-renderer (0.1.2009.20-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.20-1
+  * Fixes: NB#107221 - Connecting and disconnecting with an AP while playing an MP3 cause audio breakage
+  * Fixes: NB#114181 - Video aspect ratio is not preserved
+  * Set appropriate buffering settings for audio sink
+    to avoid audio glitches on certain scenarios.
+  * Maintainer changed in control file.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Thu, 07 May 2009 11:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.19-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.19-1
+  * Fixes: NB#108833 - [Onsite] Audio volume is turned full when song is changed
+  * Fixes: NB#113464 - Dbus connection lost error message displayed in while trying to up/down volume level from MTG 
+  * Fixes: NB#96483 - Gstreamer renderer does not change from GST_STATE_PAUSED to GST_STATE_READY after timeout
+  * Fixes: NB#104494 - Missing renderer error when video clib has high framerate
+  * Fixes: NB#110654 - mafw-dbus-wrapper prints about a critical error
+  * Fixes: NB#103987 - Audio playback is breaking for few seconds while playing mms live streams.
+  * Fixes power management issues caused by the volume pipeline
+    being always in PAUSED state. While a proper implementation
+    for volume management is not in place, this temporal fix
+    workarounds the problem by setting it to PAUSED on demand
+    when global volume has to be read, and then setting it back
+    to READY.
+  * Do not check video caps on prepare-xwindow-id, instead wait
+    for prerolling to finish, otherwise caps are not set yet.
+  * Use 'g_timeout_add_seconds' where possible.
+  * Added monitor volume hack again, but when going to playing instead of
+    stopping.
+  * Revert "Added monitor_hack again but called only when stopping the worker."
+  * Added monitor_hack again but called only when stopping the worker.
+  * Revert "Added dirty hack to be aware of the volume changes that don't 
+    happen"
+  * Revert "Hack to monitor volume does not return TRUE, but FALSE and 
+    reinstalls"
+  * Improved playlist-change handling
+  * Hack to monitor volume does not return TRUE, but FALSE and reinstalls
+    the timeout not to do it at regular intervals, but a timeout after last
+    callback is run.
+  * Do not go to PLAYING state until we are done buffering.
+  * Disable native flags in playbin2 temporarily since this is causing
+    trouble with some videos. Also, allow videos with resolution 
+    up to 848x576.
+  * Added dirty hack to be aware of the volume changes that don't happen
+    inside mafw code. As soon as GStreamer adds notify::volume signals, we
+    have to remove this immediately.
+  * Setting volume when playing is done only for playback volume.
+  * Moved volume management from playback pipeline to volume pipeline. We
+    don't update the volume pipeline because it should be updated but we do it
+    with the playback one because it has always to be set up.
+  * Moved listening to volume signals from playback pipeline to volume
+    pipeline in renderer
+  * Added customized pipeline to always listen to volume changes in renderer
+  * Added support to distinguish between audio and video codec errors.
+  * Fixed critical warning message.
+  * When we get the ckey coming from the sync bus to the async bus, we check
+    the caps and raise an error if they are not suitable.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Thu, 30 Apr 2009 10:00:00 +0300
+
+mafw-gst-renderer (0.1.2009.18-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.18-1
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Thu, 23 Apr 2009 15:30:00 +0300
+
+mafw-gst-renderer (0.1.2009.17-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.17-2
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 17 Apr 2009 09:18:07 +0300
+
+mafw-gst-renderer (0.1.2009.17-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.17-1
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Wed, 15 Apr 2009 15:59:15 +0300
+
+mafw-gst-renderer (0.1.2009.16-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.16-2
+  * Fixes: NB#110043 - Mafw-dbus-wrapper crash is observed while switching between proxy playlist in a particular case
+  * Fixes: NB#108725 - DLNA CTT tool gives a failed verdict on "MT HTTP Header: Range - use in HEAD/GET requests"
+  * Added pre-unmount signal handling in the renderer. 
+  * Added debug for seekability in renderer.
+  * Renderer uses now as first choice seekability coming from source and if not 
+    defined, we query GStreamer as it happened so far.
+  * Added requesting seekability to source in renderer.
+  * Added support for seekability coming from source in renderer.
+  * Removed assumption of positive seekability for local files with known 
+    duration.
+  * If GStreamer cannot answer to a request for seekability, we assume it is 
+    not.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Tue, 14 Apr 2009 15:06:34 +0300
+
+mafw-gst-renderer (0.1.2009.16-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.16-1
+  * All tags reported by Gst are referenced and freed later on, 
+    when they have emitted to clients. However, _emit_renderer_art
+    was obtaining a reference to a tag value and freeing that
+    reference when done, leading to a double unref later on.
+
+ -- Mika Tapojärvi <mika.tapojarvi@sse.fi>  Wed, 08 Apr 2009 12:41:14 +0300
+
+mafw-gst-renderer (0.1.2009.15-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.15-2
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 03 Apr 2009 09:17:14 +0300
+
+mafw-gst-renderer (0.1.2009.15-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.15-1
+  * Fixes: NB#106136 - Metadata not shown properly for radio stations.
+  * Added transport-actions property. For the moment contains information
+    about Seek operation.
+  * Unit test disabled by default for system integration purposes.
+  * Some tags are detected when Gstreamer is already
+    in GST_STATE_PLAYING, so in this case, emit them right away, otherwise
+    they are never emitted to the UI.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 03 Apr 2009 09:17:14 +0300
+
+mafw-gst-renderer (0.1.2009.14-4) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.14-4
+  * Update playcount id should be 0 while _notify_play is run in renderer
+    so it doesn have sense checking about that
+  * Removed update_playcount_needed as behavior can be accoplished only with 
+    timeout id in renderer.
+  * Moved the code to remove the update_playcount to the state class to fix
+    state pattern.
+  * _update_playcount_cb made public inside the renderer to be called from other 
+    parts of it.
+  * Moved the code to add the timeout to state-transitioning.
+  * Moved the code from the state notify_eos in the base renderer to the 
+    state-playing.
+  * Added initialization of the update_playcount structures in renderer.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Wed, 01 Apr 2009 09:34:16 +0300
+
+mafw-gst-renderer (0.1.2009.14-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.14-3
+  * Fixes: NB#107595 - Xid not set error comes when video is played to the end and then played again with media player
+  * Removed guessing the seekability from renderer in favor of only GStreamer
+    query.
+  * Setting pipeline to NULL without checking for async changes as it
+    cannot happen according to Stefan comments.
+  * Must always stop() on EOS when there are 
+    no more items to play. This frees X resources if playing video,
+    otherwise setting a new Xid afterward leads to a BadWindow X 
+    error.
+  * Enabling gstreamer optimization flags
+  * Creating pipeline at startup and, soon after the playback has ended, to 
+    speed up the starting of the playback
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Tue, 31 Mar 2009 09:20:00 +0200
+
+mafw-gst-renderer (0.1.2009.14-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.14-2
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Mon, 30 Mar 2009 09:23:13 +0200
+
+mafw-gst-renderer (0.1.2009.14-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.14-1
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Fri, 27 Mar 2009 09:30:00 +0200
+
+mafw-gst-renderer (0.1.2009.13-5) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.13-5
+  * Fixes: NB#102972 - Pause AAC clip from UPnP server timer shows as 00:00
+  * Changed _notify_buffer_status in state-transitioning in renderer
+    to use the do_notify_buffer status as code was the same after removing timer
+  * Removed timer support from renderer utils
+  * Removed timer handling in renderer.
+  * Removed timer use from renderer get_position
+  * Changed API to return gint instead if guint in the get_position
+    callback
+  * Set Visa as integrator.
+  * Upgrade copyright year.
+  * Add headers for Makefile.am and configure.ac files.
+  * Set Visa Smolander as the contact person in headers.
+
+ -- Juha Kellokoski <veitikka6@gmail.com>  Thu, 26 Mar 2009 09:53:00 +0200
+
+mafw-gst-renderer (0.1.2009.13-4) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.13-4
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Wed, 25 Mar 2009 09:16:48 +0200
+
+mafw-gst-renderer (0.1.2009.13-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.13-3
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Tue, 24 Mar 2009 09:23:08 +0200
+
+mafw-gst-renderer (0.1.2009.12-4) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.12-4
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Wed, 18 Mar 2009 09:17:01 +0200
+
+mafw-gst-renderer (0.1.2009.12-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.12-3
+  * Fixed CID 610
+  * Fixed CID 2592
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Wed, 18 Mar 2009 09:17:01 +0200
+
+mafw-gst-renderer (0.1.2009.12-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.12-2
+  * Fixes: NB#102172 - Total clip duration shown wrongly for vbr clips.
+  * Fixes: NB#105468 - Mafw-dbus-wrapper freezes when commands are given consecutively
+  * Corrected the double tag emission when pausing in transitioning
+    and going to GST_STATE_READY in renderer worker
+  * Moved _free_taglist functions above in renderer worker
+  * Removed tag_list as global variable to be inside the renderer
+    worker
+  * Modified other functions according this point
+  * Moved _add_ready_timeout from _construct_pipeline to _do_play
+    in renderer to allow us going to ready just after building the pipeline
+    because in the other case we hadn't received the seekability yet.
+  * Solved a memory leak when freeing the tag list in renderer worker
+  * Changed going to GST_STATE_READY only for seekable streams in renderer
+    worker
+  * Code to go to GST_STATE_READY after sometime in PAUSED in renderer
+    worker reactivated
+  * Added checks for NULL buffers when emitting the current frame on
+    paused.
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Tue, 17 Mar 2009 09:21:54 +0200
+
+mafw-gst-renderer (0.1.2009.11-6) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.11-6
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Thu, 12 Mar 2009 09:13:36 +0200
+
+mafw-gst-renderer (0.1.2009.11-5) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.11-5
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Thu, 12 Mar 2009 09:13:36 +0200
+
+mafw-gst-renderer (0.1.2009.11-4) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.11-4
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Wed, 11 Mar 2009 09:09:57 +0200
+
+mafw-gst-renderer (0.1.2009.11-3) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.11-3
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Tue, 10 Mar 2009 09:12:41 +0200
+
+mafw-gst-renderer (0.1.2009.11-2) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.11-2
+  * Fixes: NB#104680 - System UI freezed while playing high resolution albumart clips
+  * Added buffering info test in renderer
+  * Added support to get buffering information in renderer tests
+  * Added tests for properties management in renderer tests
+  * Solved a problem that could cause some race conditions in volume handling
+    in renderer worker
+  * Moved _set_volume and _set_mute functions in the worker file in
+    renderer.
+  * Added support to manage properties values in renderer tests
+  * Added test for duration emission in renderer tests
+  * Added test for get_position in renderer
+  * Added tests for media art emission in renderer
+  * Added GStreamer tag management in renderer tests
+  * Added testframe.png to renderer tests
+  * Activated and fixed video tests compilation in renderer
+  * Added functions to metadata checks in renderer tests.
+  * Added error policy tests in renderer
+  * Added support in renderer tests to receive expected error callbacks
+  * Fixed problem with update lastplayed in renderer tests.
+  * Solved problem in playcount renderer tests.
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Mon, 09 Mar 2009 09:24:38 +0200
+
+mafw-gst-renderer (0.1.2009.11-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.11-1
+  * Notify when third-party applications modify gstreamer volume.
+  * Enabling unit tests to mafw-gst-renderer.
+  * Update playcount and last-played when reaching EOS, or after 10 seconds.
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Thu, 05 Mar 2009 14:43:54 +0200
+
+mafw-gst-renderer (0.1.2009.10-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.10-1
+  * Fixed segfault
+  * Removed gstreamer0.10-selector from the mafw-gst-renderer dependencies.
+  * Allow blanking when TV Out cable is plugged.
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Thu, 26 Feb 2009 14:06:24 +0200
+
+mafw-gst-renderer (0.1.2009.9-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.09-1
+  * Disabling optimization flags
+  * Removing helixbin usage
+  * Delaying metadatas received from gstreamer
+  * Preload some gst plugins at startup
+  * Adding some optimization flags to the pipeline
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Thu, 19 Feb 2009 17:26:04 +0200
+
+mafw-gst-renderer (0.1.2009.8-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.08-1
+  * Fixes: NB#98725
+  * Fixes: NB#100158
+  * Added Nokia copyright to gstcreenshot.[ch] files in renderer.
+  * Made bvw_frame_conv_convert asynchronous
+  * Adapted renderer worker to that conversion.
+  * Reusing pipeline for color space conversion in renderer.
+  * Changed raw video art saving to use bacon video widget conversion in
+    mafw-gst-renderer
+  * Added gstsnapshot.[ch] files to renderer to convert raw frames into
+    understandable format for gdk_pixbuf
+  * Changed tmp file name for gstreamer renderer emitted art
+  * Changed function _save_graphic_file_from_gst_buffer to
+    save an unconverted video/x-raw-rgb.
+  * Revert "Fake function that copies a fake frame to test"
+
+ -- Juha Kellokoski <ext-juha.kellokoski@nokia.com>  Thu, 12 Feb 2009 14:22:39 +0200
+
+mafw-gst-renderer (0.1.2009.07-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.07-1
+  * Changed highest resolution for fremantle
+
+ -- Mika Tapojärvi <ext-mika.tapojarvi@nokia.com>  Fri, 06 Feb 2009 08:38:37 +0200
+
+mafw-gst-renderer (0.1.2009.06-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.06-1
+  * Changed highest resolution for fremantle
+  * Added GTK_DISABLE_DEPRECATED parameters for mafw-tracker-source 
+    configure.ac. Added extended descriptions for 
+  * mafw-gst-renderer.
+  * Build fix
+
+ -- Mika Tapojärvi <ext-mika.tapojarvi@nokia.com>  Fri, 30 Jan 2009 14:03:15 +0200
+
+mafw-gst-renderer (0.1.2009.05-1) unstable; urgency=low
+
+  * MAFW gst renderer, pre-release 0.2009.05-1
+  * Changing the base class of the extension objects to GInitiallyUnowned
+  * Changed testframe.jpeg
+
+ -- Mika Tapojärvi <ext-mika.tapojarvi@nokia.com>  Thu, 22 Jan 2009 14:33:27 +0200
+
+mafw-gst-renderer (0.1.2009.04-1) unstable; urgency=low
+
+  * MAFW, pre-release 0.2009.04-1
+  * Changed testframe.jpeg
+  * Reducing lintian warnings.
+  * Fixes: NB#97304
+  * Fixes: NB#94990
+  * Fixes: NB#85894
+
+ -- Mika Tapojärvi <ext-mika.tapojarvi@nokia.com>  Fri, 16 Jan 2009 14:59:38 +0200
+
+mafw-gst-renderer (0.1.2009.03-1) unstable; urgency=low
+
+  * MAFW, pre-release 0.1.2009.03-1
+  * Testing the error reporting when resuming in transitioning state
+    without having paused.
+  * Sent a GError if trying to resume in transitioning state without
+    having paused before.
+  * Reset timer when stopping.
+  * Remated stream_timer_* functions into media_timer_* because
+    name was confusing.
+  * Renamed _is_playlist into uri_is_playlist and moved to -utils in
+    renderer.
+  * Changed its calls to use the new name.
+  * Changed condition about reporting the error when performing _do_seek.
+  * Added comment about the playlist mode error handling in renderer
+    worker.
+  * Changed tagmap array for a GHashTable in renderer worker.
+  * Removed playback mode and pl struct from global variables and added
+    to worker structure.
+  * Added worker as parameter to _reset_pl_info and _on_pl_entry_parsed
+    in renderer because it is needed to use the parameters inside worker structure.
+  * Removed old_error handler because it was not being used.
+  * In renderer worker renamed:
+     * NORMAL_MODE -> WORKER_MODE_SINGLE_PLAY
+     * PLAYLIST_MODE -> WORKER_MODE_PLAYLIST
+  * Changed _metadata_set_cb to g_debug if operation was correct or if
+    it failed.
+  * Reindented _update_playcount_metadata_cb parameters in renderer.
+  * Logged error in _update_playcount_metadata_cb in renderer because
+    it was just being ignored. This causes the tests to log the warning.
+  * Added a meaningful error to a test in renderer.
+  * Fixed _update_playcount_metadata_cb documentation in renderer.
+  * Renamed _playcount_metadata into _update_playcount_metadata_cb in
+    renderer.
+  * mafw_gstreamer_renderer_get_position changed not to test for
+    callback != NULL because it is already being checked in preconditions.
+  * Changed mafw_gstreamer_renderer_get_status not to test callback != NULL
+    because it is already checked in preconditions.
+  * In mafw_gstreamer_renderer_get_status added check for callback != NULL
+    in preconditions.
+  * Renamed unable_play_count into play_failed_count in renderer.
+  * Added G_LOG_DOMAIN for check-mafw-gstreamer-renderer.
+  * Removed the CHECK-MLS traces from renderer tests.
+  * Changed the G_LOG_DOMAIN for given files to have more suitable ones
+    in renderer.
+
+ -- Mika Tapojärvi <ext-mika.tapojarvi@nokia.com>  Thu, 08 Jan 2009 16:13:10 +0200
+
+mafw-gst-renderer (0.1.2008.52-1) unstable; urgency=low
+
+  * Renamed midas to mafw
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Mon, 22 Dec 2008 12:55:59 +0200
+
+midas-gstreamer-renderer (0.1.2008.52) unstable; urgency=low
+
+  * Removing libtotem-pl-parser
+  * Deactivated code of the timeout to go to ready until bug with gstreamer is clarified.
+  * Added functions to add, remove and timeout function itself to switch from PAUSED to READY in renderer worker.
+    Used this functions to implement the behavior when pausing.
+  * Fixes NB#85894     incorrect duration reported
+  * Fixes NB#93484      Playlist format other than wpl and ram shows its icon incorrectly in Playlists container
+  * Fixes NB#94990      Gstreamer renderer gives seekable metadata TRUE for radio stream
+
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 19 Dec 2008 15:28:58 +0200
+
+midas-gstreamer-renderer (0.1.2008.51-1) unstable; urgency=low
+
+  * (ha)xmas: hardwire ximagesink is removed
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Mon, 15 Dec 2008 12:55:59 +0200
+
+midas-gstreamer-renderer (0.1.2008.51) unstable; urgency=low
+
+  * (ha)xmas: hardwire ximagesink temporarily
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 12 Dec 2008 14:57:37 +0200
+
+midas-gstreamer-renderer (0.1.2008.50) unstable; urgency=low
+
+  * Added get_last_index method the the mock playlist in renderer
+  tests.
+  * Added new test cases for various use cases.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 05 Dec 2008 13:29:06 +0200
+
+midas-gstreamer-renderer (0.1.2008.49) unstable; urgency=low
+
+  * In development.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 28 Nov 2008 14:35:35 +0200
+
+midas-gstreamer-renderer (0.1.2008.48) unstable; urgency=low
+
+  * In renderer:
+   --Renamed _get_graphic_file_path into _get_tmp_file_from_pool and
+    changed to use the tmp files pool.
+   --Replaced the calls to use _get_tmp_file_from_pool with the
+     proper changes to parameters and return values.
+  * Added resume operation to transitioning state in renderer that
+    allows to resume in transitioning.
+  * Added stop after pausing while transitioning to finish process in a better
+    way in renderer tests.
+  * Implemented HAL listener which stops renderer when usb cable is plugged
+    in, and we are playing something from memory card
+  * Changed do_next and do_prev to begin playback if pressing next
+    or prev when going beyond the playlist limits in renderer.
+  * Added mechanism to start/stop wrappers on package install/removal
+    (ignoring scratchbox support for now).  Uses DSME.
+    Added an Xsession.post script.
+    Updated affected components
+  * Fixes: NB#92843    MidasRenderer ""playlist-changed"" and ""media-changed"" signals occur in random order when changing playlist
+  * Fixes: NB#92238    Play state not preserved when next pressed in the end of the playlist.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 21 Nov 2008 16:59:53 +0200
+
+midas-gstreamer-renderer (0.1.2008.47) unstable; urgency=low
+
+  * Send error signal when handling playback errors.
+  * Added conic network detection in renderer
+  * Added error handing in renderer for CODEC_NOT_FOUND , seek error, network errors, DRM errors.
+  * Fixes: NB#87297    Property of shuffle operation not reflected to the last item of playlist
+  * Fixes: NB#87354    shuffle not applied to dynamically added clips
+  * Fixes: NB#87667    Midas-playlist-daemon crashes with repeat mode 'on' & playing unsupported clip
+  * Fixes: NB#91530    Media-changed signal comes everytime item is added to playlist
+  * Fixes: NB#91566    glib criticals observed when audio playback is stopped
+  * Fixes: NB#87841    Next and previous gives ""Index out of bounds error"" on last and first items
+  * Fixes: NB#91893    local sink crashes when invalid playlist items are played
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 14 Nov 2008 12:30:41 +0200
+
+midas-gstreamer-renderer (0.1.2008.46) unstable; urgency=low
+
+  * Fixes: NB#91061     gstreamer-gnomevfs pkgs missing.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 07 Nov 2008 11:58:32 +0200
+
+midas-gstreamer-renderer (0.1.2008.45) unstable; urgency=low
+
+  *  Implemented move_to_last based on playlist get_last_index function
+  *  Implemented tag emission of renderer art in MidasGstreamerRenderer.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 31 Oct 2008 14:14:16 +0200
+
+midas-gstreamer-renderer (0.1.2008.44) unstable; urgency=low
+
+  * Added support to request the current frame
+  * Added support for the property in the renderer and its storage on the worker.
+  * added gdkpixbuf dependency.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 24 Oct 2008 10:11:52 +0300
+
+midas-gstreamer-renderer (0.1.2008.43) unstable; urgency=low
+
+  * Renaming local-sink->midas-gstreamer-renderer
+  * support of playback on diablo
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 17 Oct 2008 15:03:14 +0300
+
+midas-gstreamer-renderer (0.1.2008.42) unstable; urgency=low
+
+  * Fixes: NB#89265    unable to parse wpl playlist format files in local sink
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 10 Oct 2008 16:10:02 +0300
+
+midas-gstreamer-renderer (0.1.2008.40) unstable; urgency=low
+
+  * Using playbin2.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Mon, 29 Sep 2008 07:02:28 +0000
+
+midas-gstreamer-renderer (0.1.2008.39) unstable; urgency=low
+
+  * Fixes: NB#87723    Play item pointer switches back to first item of playlist while repeat mode of playlist is not enabled
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Sun, 21 Sep 2008 18:35:06 +0300
+
+midas-gstreamer-renderer (0.1.2008.38) unstable; urgency=low
+
+  * Unit tests fixed after the use_count API addition.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Mon, 15 Sep 2008 08:11:12 +0300
+
+midas-gstreamer-renderer (0.1.2008.37) unstable; urgency=low
+
+  * small face-lift (configure.ac, Makefile.am:s and build fixes)
+  * Fixes: NB#86160    Media continues to play after deleting the playlist
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Mon, 08 Sep 2008 08:37:04 +0300
+
+midas-gstreamer-renderer (0.1.2008.36) unstable; urgency=low
+
+  * Fixes: NB#87757    Seeking gives unknown seek mode as error in callback function
+  * Fixes: NB#87414    Seek option is not enabled for mp3 format files
+  * Fixes: NB#87463    playback is switched back to 20 seconds time stamp when the forward button is pressed
+  * Fixes: NB#87524    Video full screen turns blank during pause playback state
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Mon, 01 Sep 2008 08:21:43 +0300
+
+midas-gstreamer-renderer (0.1.2008.35) unstable; urgency=low
+
+  * In development.
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Sun, 24 Aug 2008 19:42:40 +0300
+
+midas-gstreamer-renderer (0.1.2008.34) unstable; urgency=low
+
+  * More strict parameter checking by set_position().
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Fri, 15 Aug 2008 09:48:16 +0300
+
+midas-gstreamer-renderer (0.1.2008.33) unstable; urgency=low
+
+  * Initial release.
+  * Fixes: NB#85491    stop() in transitioning state
+  * Fixes: NB#85894    incorrect duration reported
+  * Fixes: NB#85481    Unable to seek attached mp3 file using MAFW api
+  * Fixes: NB#85675    sink::metadata-changed signal reports the is-seekable key in integer
+  * Fixes: NB#86692    local-sink doesn't emit buffering-info signals
+  * Fixes: NB#85161    attempts to play media having unsupported format results in error message
+  * Fixes: NB#85160    GLIB CRITICAL message trying to get the iradio-name metadata from gstreamer
+  * Fixes: NB#85892    Pausing resets playback
+  * Fixes: NB#85897    media always reported to be unseekable
+  * Fixes: NB#86160    Media continues to play after deleting the playlist
+  * Fixes: NB#86654    crash while playing from a playlist
+  * Fixes: NB#85149    play(callback) is not invoked
+  * Fixes: NB#85150    only the first item of the playlist is played
+  * Fixes: NB#85498    sink should advance to next item if current is unplayable
+  * Fixes: NB#86893    UPnP media content not playing
+  * Fixes: NB#85472    play() starts last play_object()ed item again
+  * Fixes: NB#85475    assign_playlist() starts playing
+  * Fixes: NB#85479    misleading index in media-changed for play_object()
+  * Fixes: NB#85628    crash if invoked without callback
+  * Fixes: NB#87082    gstreamer criticals during playback
+  * Fixes: NB#85489    critical warnings via assign_playlist(NULL)
+  * Fixes: NB#87084    assign_playlist() critical warnings
+  * Fixes: NB#86956    midas-dbus-wrapper dies when playback is attempted in mentioned scenario
+
+ -- Zeeshan Ali <zeeshan.ali@nokia.com>  Sun, 10 Aug 2008 19:52:39 +0300
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..b8626c4
--- /dev/null
@@ -0,0 +1 @@
+4
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..f475874
--- /dev/null
@@ -0,0 +1,28 @@
+Source: mafw-gst-renderer
+Section: misc
+Priority: optional
+Maintainer: Juha Kellokoski <veitikka6@gmail.com>
+Build-Depends: debhelper (>= 4.0.0), libglib2.0-dev,
+               libgstreamer0.10-dev (>= 0.10.20-0maemo3),
+               libgstreamer-plugins-base0.10-dev (>= 0.10.20-0maemo5),
+               libx11-dev, libosso-dev (>= 2.0), libmafw0-dev (>= 0.1),
+               checkmore, gstreamer0.10-plugins-base,
+               gstreamer0.10-plugins-good,
+               libhal-dev, libtotem-plparser-dev, libpulse-dev (>= 0.9.15-1),
+               libgconf2-dev, libosso-gnomevfs2-dev
+Standards-Version: 3.7.2
+
+Package: mafw-gst-renderer
+Section: libs
+Architecture: any
+Depends: gconf2, ${shlibs:Depends}, ${misc:Depends}
+Description: MAFW gst renderer plugin
+ Renderer plugin for MAFW-gst
+
+Package: mafw-gst-renderer-dbg
+Section: devel
+Architecture: any
+Priority: extra
+Depends: mafw-gst-renderer (= ${binary:Version})
+Description: debug symbols for mafw-gst-renderer
+ MAFW-gst renderer debug symbols
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..79772e7
--- /dev/null
@@ -0,0 +1,6 @@
+Copyright (C) 2007, 2008, 2009 Nokia Corporation. All rights reserved.
+
+Contact: Visa Smolander <visa.smolander@nokia.com>
+
+
+
diff --git a/debian/mafw-gst-renderer.install.in b/debian/mafw-gst-renderer.install.in
new file mode 100644 (file)
index 0000000..f350be5
--- /dev/null
@@ -0,0 +1 @@
+@plugindir@/*.so
diff --git a/debian/mafw-gst-renderer.postinst b/debian/mafw-gst-renderer.postinst
new file mode 100644 (file)
index 0000000..924970d
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+#DEBHELPER#
+
+test -x /usr/bin/mafw.sh && /usr/bin/mafw.sh start mafw-gst-renderer -7 \
+|| true;
diff --git a/debian/mafw-gst-renderer.prerm b/debian/mafw-gst-renderer.prerm
new file mode 100644 (file)
index 0000000..084ef87
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+#DEBHELPER#
+
+test -x /usr/bin/mafw.sh && /usr/bin/mafw.sh stop mafw-gst-renderer \
+|| true;
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..1e89b65
--- /dev/null
@@ -0,0 +1,96 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -O0
+else
+       CFLAGS += -O2
+endif
+
+ifneq (,$(findstring thumb,$(DEB_BUILD_OPTIONS)))
+        CFLAGS += -mthumb
+endif
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -O0 -ggdb3 -finstrument-functions
+endif
+
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+       INSTALL_PROGRAM += -s
+endif
+maybe_coverage := $(if $(filter lcov,$(DEB_BUILD_OPTIONS)),--enable-coverage,)
+
+configure-stamp:
+       dh_testdir
+       CFLAGS="$(CFLAGS)" ./autogen.sh \
+               --host=$(DEB_HOST_GNU_TYPE) \
+               --build=$(DEB_BUILD_GNU_TYPE) \
+               --disable-dependency-tracking \
+               --prefix=/usr \
+               $(maybe_coverage)
+       touch configure-stamp
+
+build: build-stamp
+build-stamp:  configure-stamp
+       dh_testdir
+       $(MAKE)
+ifeq ($(findstring nocheck,$(DEB_BUILD_OPTIONS)),)
+       $(MAKE) check
+endif
+       touch build-stamp
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp configure-stamp
+       [ ! -f Makefile ] || $(MAKE) distclean
+       dh_clean 
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k 
+       dh_installdirs
+       $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp
+
+binary-indep: build install
+
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+       dh_installchangelogs ChangeLog
+       dh_installdocs
+       dh_install --sourcedir=debian/tmp -v
+       dh_link
+       dh_strip --dbg-package=mafw-gst-renderer
+       dh_compress
+       dh_fixperms
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+
+distcheck: build
+       dh_testdir 
+       $(MAKE) distcheck
+
+vg:
+       dh_testdir
+       $(MAKE) -C "$(CURDIR)/tests" vg
+               
+
+.PHONY: build clean binary-indep binary-arch binary install distcheck vg
+
diff --git a/libmafw-gst-renderer/Makefile.am b/libmafw-gst-renderer/Makefile.am
new file mode 100644 (file)
index 0000000..a564ab9
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Makefile.am for MAFW gst renderer library.
+#
+# Author: Zeeshan Ali <zeeshan.ali@nokia.com>
+#
+# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved.
+
+plugin_LTLIBRARIES             = mafw-gst-renderer.la
+
+BUILT_SOURCES                  = mafw-gst-renderer-marshal.c \
+                                 mafw-gst-renderer-marshal.h
+
+mafw_gst_renderer_la_SOURCES   = $(BUILT_SOURCES) \
+                                 blanking.c blanking.h \
+                                 mafw-gst-renderer.c mafw-gst-renderer.h \
+                                 mafw-gst-renderer-utils.c mafw-gst-renderer-utils.h \
+                                 mafw-gst-renderer-worker.c mafw-gst-renderer-worker.h \
+                                 mafw-gst-renderer-worker-volume.c mafw-gst-renderer-worker-volume.h \
+                                 mafw-gst-renderer-state.c mafw-gst-renderer-state.h \
+                                 mafw-gst-renderer-state-playing.c mafw-gst-renderer-state-playing.h \
+                                 mafw-gst-renderer-state-paused.c mafw-gst-renderer-state-paused.h \
+                                 mafw-gst-renderer-state-stopped.c mafw-gst-renderer-state-stopped.h \
+                                 mafw-gst-renderer-state-transitioning.c mafw-gst-renderer-state-transitioning.h \
+                                 mafw-playlist-iterator.c mafw-playlist-iterator.h
+
+mafw_gst_renderer_la_CPPFLAGS  = $(DEPS_CFLAGS) $(VOLUME_CFLAGS) \
+                                 -DPREFIX=\"$(prefix)\" $(_CFLAGS)
+mafw_gst_renderer_la_LDFLAGS   = -avoid-version -module $(_LDFLAGS)
+mafw_gst_renderer_la_LIBADD    = $(DEPS_LIBS) $(VOLUME_LIBS) \
+                                 -lgstinterfaces-0.10 -lgstpbutils-0.10
+
+if HAVE_GDKPIXBUF
+mafw_gst_renderer_la_SOURCES += gstscreenshot.c gstscreenshot.h
+mafw_gst_renderer_la_CPPFLAGS += $(GDKPIXBUF_CFLAGS)
+mafw_gst_renderer_la_LIBADD += $(GDKPIXBUF_LIBS)
+endif
+
+if HAVE_CONIC
+mafw_gst_renderer_la_CPPFLAGS += $(CONIC_CFLAGS)
+mafw_gst_renderer_la_LIBADD += $(CONIC_LIBS)
+endif
+
+mafw-gst-renderer-marshal.c: mafw-gst-renderer-marshal.list
+       ( \
+               echo '#include "mafw-gst-renderer-marshal.h"'; \
+               $(GLIB_GENMARSHAL) --prefix=mafw_gst_renderer_marshal --body $^ \
+       ) > $@
+
+mafw-gst-renderer-marshal.h: mafw-gst-renderer-marshal.list
+       $(GLIB_GENMARSHAL) --prefix=mafw_gst_renderer_marshal --header \
+               $^ > $@
+
+EXTRA_DIST                     = mafw-gst-renderer-marshal.list
+CLEANFILES                     = *.gcno *.gcda
+MAINTAINERCLEANFILES           = Makefile.in *.loT
diff --git a/libmafw-gst-renderer/blanking.c b/libmafw-gst-renderer/blanking.c
new file mode 100644 (file)
index 0000000..4a12836
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+#include <libosso.h>
+#include "blanking.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-blanking"
+
+/* In seconds */
+#define VIDEO_BLANKING_TIMER_INTERVAL  45
+
+static guint blanking_timeout_id = 0;
+static osso_context_t *osso_ctx = NULL;
+static gboolean can_control_blanking = TRUE;
+static gboolean is_blanking_prohibited = FALSE;
+
+static void remove_blanking_timeout(void)
+{
+       if (blanking_timeout_id) {
+               g_source_remove(blanking_timeout_id);
+               blanking_timeout_id = 0;
+       }
+}
+
+/*
+ * Re-enables screen blanking.
+ */
+void blanking_allow(void)
+{
+        is_blanking_prohibited = FALSE;
+        remove_blanking_timeout();
+}
+
+static gboolean no_blanking_timeout(void)
+{
+       /* Stop trying if it fails. */
+       return osso_display_blanking_pause(osso_ctx) == OSSO_OK;
+}
+
+/*
+ * Adds a timeout to periodically disable screen blanking.
+ */
+void blanking_prohibit(void)
+{
+        is_blanking_prohibited = TRUE;
+       if ((!osso_ctx) || (!can_control_blanking))
+               return;
+       osso_display_state_on(osso_ctx);
+       osso_display_blanking_pause(osso_ctx);
+       if (blanking_timeout_id == 0) {
+               blanking_timeout_id =
+                       g_timeout_add_seconds(VIDEO_BLANKING_TIMER_INTERVAL,
+                                              (gpointer)no_blanking_timeout,
+                                              NULL);
+       }
+}
+
+void blanking_init(void)
+{
+       /* It's enough to initialize it once for a process. */
+       if (osso_ctx)
+               return;
+       osso_ctx = osso_initialize(PACKAGE, VERSION, 0, NULL);
+       if (!osso_ctx)
+               g_warning("osso_initialize failed, screen may go black");
+        is_blanking_prohibited = FALSE;
+        /* Default policy is to allow user to control blanking */
+        blanking_control(TRUE);
+}
+
+void blanking_deinit(void)
+{
+       if (!osso_ctx)
+               return;
+       blanking_control(FALSE);
+       osso_deinitialize(osso_ctx);
+       osso_ctx = NULL;
+}
+
+void blanking_control(gboolean activate)
+{
+        can_control_blanking = activate;
+        if (!can_control_blanking) {
+                remove_blanking_timeout();
+        } else {
+                /* Restore the last state */
+                if (is_blanking_prohibited) {
+                        blanking_prohibit();
+                } else {
+                        blanking_allow();
+                }
+        }
+}
diff --git a/libmafw-gst-renderer/blanking.h b/libmafw-gst-renderer/blanking.h
new file mode 100644 (file)
index 0000000..6a69e61
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef BLANKING_H
+#define BLANKING_H
+
+G_BEGIN_DECLS
+
+void blanking_init(void);
+void blanking_deinit(void);
+void blanking_allow(void);
+void blanking_prohibit(void);
+void blanking_control(gboolean activate);
+
+G_END_DECLS
+
+#endif
diff --git a/libmafw-gst-renderer/gstscreenshot.c b/libmafw-gst-renderer/gstscreenshot.c
new file mode 100644 (file)
index 0000000..f7f177e
--- /dev/null
@@ -0,0 +1,259 @@
+/* Small helper element for format conversion
+ * (c) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Portion Copyright © 2009 Nokia Corporation and/or its
+ * subsidiary(-ies).* All rights reserved. *
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <string.h>
+
+#include "gstscreenshot.h"
+
+typedef struct {
+       GstBuffer *result;
+       GstElement *src;
+       GstElement *sink;
+       GstElement *pipeline;
+       BvwFrameConvCb cb;
+       gpointer cb_data;
+} GstScreenshotData;
+
+/* GST_DEBUG_CATEGORY_EXTERN (_totem_gst_debug_cat); */
+/* #define GST_CAT_DEFAULT _totem_gst_debug_cat */
+
+static void feed_fakesrc(GstElement *src, GstBuffer *buf, GstPad *pad,
+                        gpointer data)
+{
+       GstBuffer *in_buf = GST_BUFFER(data);
+
+       g_assert(GST_BUFFER_SIZE(buf) >= GST_BUFFER_SIZE(in_buf));
+       g_assert(!GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_READONLY));
+
+       gst_buffer_set_caps(buf, GST_BUFFER_CAPS(in_buf));
+
+       memcpy(GST_BUFFER_DATA(buf), GST_BUFFER_DATA(in_buf),
+              GST_BUFFER_SIZE(in_buf));
+
+       GST_BUFFER_SIZE(buf) = GST_BUFFER_SIZE(in_buf);
+
+       GST_DEBUG("feeding buffer %p, size %u, caps %" GST_PTR_FORMAT,
+                 buf, GST_BUFFER_SIZE(buf), GST_BUFFER_CAPS(buf));
+
+       gst_buffer_unref(in_buf);
+}
+
+static void save_result(GstElement *sink, GstBuffer *buf, GstPad *pad,
+                       gpointer data)
+{
+       GstScreenshotData *gsd = data;
+
+       gsd->result = gst_buffer_ref(buf);
+
+       GST_DEBUG("received converted buffer %p with caps %" GST_PTR_FORMAT,
+                 gsd->result, GST_BUFFER_CAPS(gsd->result));
+}
+
+static gboolean create_element(const gchar *factory_name, GstElement **element,
+                              GError **err)
+{
+       *element = gst_element_factory_make(factory_name, NULL);
+       if (*element)
+               return TRUE;
+
+       if (err && *err == NULL) {
+               *err = g_error_new(
+                       GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN,
+                       "cannot create element '%s' - please check your "
+                       "GStreamer installation", factory_name);
+       }
+
+       return FALSE;
+}
+
+static gboolean finalize_process(GstScreenshotData *gsd)
+{
+       g_signal_handlers_disconnect_matched(gsd->sink, (GSignalMatchType)
+                                            G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+                                            save_result, NULL);
+       g_signal_handlers_disconnect_matched(gsd->src, (GSignalMatchType)
+                                            G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+                                            feed_fakesrc, NULL);
+       gst_element_set_state(gsd->pipeline, GST_STATE_NULL);
+
+       g_free(gsd);
+
+       return FALSE;
+}
+
+static gboolean async_bus_handler(GstBus *bus, GstMessage *msg,
+                                 gpointer data)
+{
+       GstScreenshotData *gsd = data;
+       gboolean keep_watch = TRUE;
+
+       switch (GST_MESSAGE_TYPE(msg)) {
+       case GST_MESSAGE_EOS: {
+               if (gsd->result != NULL) {
+                       GST_DEBUG("conversion successful: result = %p",
+                                 gsd->result);
+               } else {
+                       GST_WARNING("EOS but no result frame?!");
+               }
+               gsd->cb(gsd->result, gsd->cb_data);
+               keep_watch = finalize_process(gsd);
+               break;
+       }
+       case GST_MESSAGE_ERROR: {
+               gchar *dbg = NULL;
+               GError *error = NULL;
+
+               gst_message_parse_error(msg, &error, &dbg);
+               if (error != NULL) {
+                       g_warning("Could not take screenshot: %s",
+                                 error->message);
+                       GST_DEBUG("%s [debug: %s]", error->message,
+                                 GST_STR_NULL(dbg));
+                       g_error_free(error);
+               } else {
+                       g_warning("Could not take screenshot(and "
+                                 "NULL error!)");
+               }
+               g_free(dbg);
+               gsd->result = NULL;
+               gsd->cb(gsd->result, gsd->cb_data);
+               keep_watch = finalize_process(gsd);
+               break;
+       }
+       default:
+               break;
+       }
+
+       return keep_watch;
+}
+
+/* takes ownership of the input buffer */
+gboolean bvw_frame_conv_convert(GstBuffer *buf, GstCaps *to_caps,
+                               BvwFrameConvCb cb, gpointer cb_data)
+{
+       static GstElement *src = NULL, *sink = NULL, *pipeline = NULL,
+               *filter1 = NULL, *filter2 = NULL;
+       static GstBus *bus;
+       GError *error = NULL;
+       GstCaps *to_caps_no_par;
+       GstScreenshotData *gsd;
+
+       g_return_val_if_fail(GST_BUFFER_CAPS(buf) != NULL, FALSE);
+       g_return_val_if_fail(cb != NULL, FALSE);
+
+       if (pipeline == NULL) {
+               GstElement *csp, *vscale;
+
+               pipeline = gst_pipeline_new("screenshot-pipeline");
+               if(pipeline == NULL) {
+                       g_warning("Could not take screenshot: "
+                                 "no pipeline (unknown error)");
+                       return FALSE;
+               }
+
+               /* videoscale is here to correct for the
+                * pixel-aspect-ratio for us */
+               GST_DEBUG("creating elements");
+               if(!create_element("fakesrc", &src, &error) ||
+                  !create_element("ffmpegcolorspace", &csp, &error) ||
+                  !create_element("videoscale", &vscale, &error) ||
+                  !create_element("capsfilter", &filter1, &error) ||
+                  !create_element("capsfilter", &filter2, &error) ||
+                  !create_element("fakesink", &sink, &error)) {
+                       g_warning("Could not take screenshot: %s",
+                                 error->message);
+                       g_error_free(error);
+                       return FALSE;
+               }
+
+               GST_DEBUG("adding elements");
+               gst_bin_add_many(GST_BIN(pipeline), src, csp, filter1, vscale,
+                                filter2, sink, NULL);
+
+               g_object_set(sink, "preroll-queue-len", 1,
+                            "signal-handoffs", TRUE, NULL);
+
+               /* set to 'fixed' sizetype */
+               g_object_set(src, "sizetype", 2, "num-buffers", 1,
+                            "signal-handoffs", TRUE, NULL);
+
+               /* FIXME: linking is still way too expensive, profile
+                * this properly */
+               GST_DEBUG("linking src->csp");
+               if(!gst_element_link_pads(src, "src", csp, "sink"))
+                       return FALSE;
+
+               GST_DEBUG("linking csp->filter1");
+               if(!gst_element_link_pads(csp, "src", filter1, "sink"))
+                       return FALSE;
+
+               GST_DEBUG("linking filter1->vscale");
+               if(!gst_element_link_pads(filter1, "src", vscale, "sink"))
+                       return FALSE;
+
+               GST_DEBUG("linking vscale->capsfilter");
+               if(!gst_element_link_pads(vscale, "src", filter2, "sink"))
+                       return FALSE;
+
+               GST_DEBUG("linking capsfilter->sink");
+               if(!gst_element_link_pads(filter2, "src", sink, "sink"))
+                       return FALSE;
+
+               bus = gst_element_get_bus(pipeline);
+       }
+
+       /* adding this superfluous capsfilter makes linking cheaper */
+       to_caps_no_par = gst_caps_copy(to_caps);
+       gst_structure_remove_field(gst_caps_get_structure(to_caps_no_par, 0),
+                                  "pixel-aspect-ratio");
+       g_object_set(filter1, "caps", to_caps_no_par, NULL);
+       gst_caps_unref(to_caps_no_par);
+
+       g_object_set(filter2, "caps", to_caps, NULL);
+       gst_caps_unref(to_caps);
+
+       gsd = g_new0(GstScreenshotData, 1);
+
+       gsd->src = src;
+       gsd->sink = sink;
+       gsd->pipeline = pipeline;
+       gsd->cb = cb;
+       gsd->cb_data = cb_data;
+
+       g_signal_connect(sink, "handoff", G_CALLBACK(save_result), gsd);
+
+       g_signal_connect(src, "handoff", G_CALLBACK(feed_fakesrc), buf);
+
+       gst_bus_add_watch(bus, async_bus_handler, gsd);
+
+       /* set to 'fixed' sizetype */
+       g_object_set(src, "sizemax", GST_BUFFER_SIZE(buf), NULL);
+
+       GST_DEBUG("running conversion pipeline");
+       gst_element_set_state(pipeline, GST_STATE_PLAYING);
+
+       return TRUE;
+}
diff --git a/libmafw-gst-renderer/gstscreenshot.h b/libmafw-gst-renderer/gstscreenshot.h
new file mode 100644 (file)
index 0000000..d3cf23c
--- /dev/null
@@ -0,0 +1,36 @@
+/* Small helper element for format conversion
+ * (c) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Portion Copyright © 2009 Nokia Corporation and/or its
+ * subsidiary(-ies).* All rights reserved. *
+ *
+ * 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 __BVW_FRAME_CONV_H__
+#define __BVW_FRAME_CONV_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef void (*BvwFrameConvCb)(GstBuffer *result, gpointer user_data);
+
+gboolean bvw_frame_conv_convert (GstBuffer *buf, GstCaps *to,
+                                BvwFrameConvCb cb, gpointer cb_data);
+
+G_END_DECLS
+
+#endif /* __BVW_FRAME_CONV_H__ */
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-marshal.list b/libmafw-gst-renderer/mafw-gst-renderer-marshal.list
new file mode 100644 (file)
index 0000000..113502f
--- /dev/null
@@ -0,0 +1,2 @@
+# MafwPlaylistIterator::playlist-changed(clip_changed, domain, code, message)
+VOID: BOOLEAN, UINT, INT, STRING
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c b/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c
new file mode 100644 (file)
index 0000000..742d474
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "mafw-gst-renderer-state-paused.h"
+#include "mafw-gst-renderer-utils.h"
+#include <libmafw/mafw.h>
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-state-paused"
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _do_play(MafwGstRendererState *self, GError **error);
+static void _do_play_object(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error);
+static void _do_stop(MafwGstRendererState *self, GError **error);
+static void _do_resume(MafwGstRendererState *self, GError **error);
+static void _do_set_position(MafwGstRendererState *self,
+                            MafwRendererSeekMode mode, gint seconds,
+                            GError **error);
+static void _do_get_position(MafwGstRendererState *self,
+                            gint *seconds,
+                            GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void _do_next(MafwGstRendererState *self, GError **error);
+static void _do_previous(MafwGstRendererState *self, GError **error);
+static void _do_goto_index(MafwGstRendererState *self, guint index,
+                          GError **error);
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwGstRendererState *self,
+                            const gchar *object_id,
+                            GHashTable *metadata,
+                            GError **error);
+
+/*----------------------------------------------------------------------------
+  Notification worker
+  ----------------------------------------------------------------------------*/
+
+static void _notify_play(MafwGstRendererState *self, GError **error);
+static void _notify_seek(MafwGstRendererState *self, GError **error);
+static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent,
+                                 GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist editing signals
+  ----------------------------------------------------------------------------*/
+
+static void _playlist_contents_changed(MafwGstRendererState *self,
+                                      gboolean clip_changed,
+                                      GError **error);
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+static GValue* _get_property_value(MafwGstRendererState *self,
+                                  const gchar *name);
+
+/*----------------------------------------------------------------------------
+  Memory card event handlers
+  ----------------------------------------------------------------------------*/
+
+static void _handle_pre_unmount(MafwGstRendererState *self,
+                               const gchar *mount_point);
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+G_DEFINE_TYPE(MafwGstRendererStatePaused, mafw_gst_renderer_state_paused,
+             MAFW_TYPE_GST_RENDERER_STATE);
+
+static void mafw_gst_renderer_state_paused_init(MafwGstRendererStatePaused *self)
+{
+}
+
+static void mafw_gst_renderer_state_paused_class_init(
+       MafwGstRendererStatePausedClass *klass)
+{
+        MafwGstRendererStateClass *state_class;
+
+       state_class = MAFW_GST_RENDERER_STATE_CLASS(klass);
+        g_return_if_fail(state_class != NULL);
+
+       state_class->name = g_strdup("Paused");
+
+       /* Playback */
+
+       state_class->play         = _do_play;
+       state_class->play_object  = _do_play_object;
+       state_class->stop         = _do_stop;
+        state_class->resume       = _do_resume;
+       state_class->set_position = _do_set_position;
+       state_class->get_position = _do_get_position;
+
+       /* Playlist */
+
+       state_class->next       = _do_next;
+       state_class->previous   = _do_previous;
+       state_class->goto_index = _do_goto_index;
+
+        /* Notification metadata */
+
+        state_class->notify_metadata = _notify_metadata;
+
+        /* Notification worker */
+
+        state_class->notify_play = _notify_play;
+        /* state_class->notify_pause is not allowed */
+        state_class->notify_seek = _notify_seek;
+        state_class->notify_buffer_status = _notify_buffer_status;
+
+       /* Playlist editing signals */
+
+       state_class->playlist_contents_changed =
+               _playlist_contents_changed;
+
+       /* Property methods */
+
+       state_class->get_property_value = _get_property_value;
+
+        /* Memory card event handlers */
+
+       state_class->handle_pre_unmount = _handle_pre_unmount;
+}
+
+GObject *mafw_gst_renderer_state_paused_new(MafwGstRenderer *renderer)
+{
+       MafwGstRendererState *state;
+
+        state = MAFW_GST_RENDERER_STATE(
+               g_object_new(MAFW_TYPE_GST_RENDERER_STATE_PAUSED, NULL));
+       state->renderer = renderer;
+
+       return G_OBJECT(state);
+}
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _do_play(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+       mafw_gst_renderer_state_do_play(self, error);
+}
+
+static void _do_play_object(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error)
+{
+       MafwGstRendererPlaybackMode cur_mode, prev_mode;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+
+       self->renderer->worker->stay_paused = FALSE;
+       prev_mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+       mafw_gst_renderer_state_do_play_object(self, object_id, error);
+       cur_mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+
+       /* If this happens it means that we interrupted playlist playback
+          so let's resume it when play_object is finished */
+       if (cur_mode != prev_mode) {
+               self->renderer->resume_playlist = TRUE;
+       }
+}
+
+static void _do_stop(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+
+       self->renderer->worker->stay_paused = FALSE;
+       /* Stop playback */
+        mafw_gst_renderer_state_do_stop(self, error);
+}
+
+static void _do_resume(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+
+        MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+       self->renderer->worker->stay_paused = FALSE;
+        mafw_gst_renderer_worker_resume(renderer->worker);
+
+        /* Transition will be done after receiving notify_play */
+}
+
+static void _do_set_position(MafwGstRendererState *self,
+                            MafwRendererSeekMode mode, gint seconds,
+                            GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+       self->renderer->worker->stay_paused = TRUE;
+       mafw_gst_renderer_state_do_set_position(self, mode, seconds, error);
+}
+
+static void _do_get_position(MafwGstRendererState *self,
+                            gint *seconds,
+                            GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+       mafw_gst_renderer_state_do_get_position(self, seconds, error);
+}
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void _do_next(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+       self->renderer->worker->stay_paused = TRUE;
+       mafw_gst_renderer_state_do_next(self, error);
+}
+
+static void _do_previous(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+       self->renderer->worker->stay_paused = TRUE;
+       mafw_gst_renderer_state_do_prev(self, error);
+}
+
+static void _do_goto_index(MafwGstRendererState *self, guint index,
+                          GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+       self->renderer->worker->stay_paused = FALSE;
+       mafw_gst_renderer_state_do_goto_index(self, index, error);
+}
+
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwGstRendererState *self,
+                            const gchar *object_id,
+                            GHashTable *metadata,
+                            GError **error)
+{
+       g_debug("running _notify_metadata...");
+        /* Kindly Ignore this notification:
+          probably a delayed (now useless) metadata resolution */
+}
+
+
+/*----------------------------------------------------------------------------
+  Notification worker
+  ----------------------------------------------------------------------------*/
+
+static void _notify_play(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+
+        MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+        /* Change status to play */
+        mafw_gst_renderer_set_state(renderer, Playing);
+}
+
+static void _notify_seek(MafwGstRendererState *self, GError **error)
+{
+        mafw_gst_renderer_state_do_notify_seek(self, error);
+}
+
+static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent,
+                                 GError **error)
+{
+       mafw_gst_renderer_state_do_notify_buffer_status (self, percent, error);
+}
+
+/*----------------------------------------------------------------------------
+  Playlist editing signals
+  ----------------------------------------------------------------------------*/
+
+static void _playlist_contents_changed(MafwGstRendererState *self,
+                                      gboolean clip_changed,
+                                      GError **error)
+{
+       MafwGstRendererPlaybackMode mode;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self));
+
+       /* Play the new index only if we are not in standalone mode.
+          Otherwise, when play_object finishes the new item will be
+          played if that's been suggested with renderer->resume_playlist */
+       mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+       if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST) {
+               mafw_gst_renderer_state_do_play(self, error);
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+GValue* _get_property_value(MafwGstRendererState *self, const gchar *name)
+{
+       GValue *value = NULL;
+
+       g_return_val_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self), value);
+
+       if (!g_strcmp0(name, MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)) {
+               gboolean is_seekable =
+                       mafw_gst_renderer_worker_get_seekable(
+                               self->renderer->worker);
+
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_STRING);
+               if (is_seekable) {
+                       g_value_set_string(value, "seek");
+               } else {
+                       g_value_set_string(value, "");
+               }
+       }
+
+       return value;
+}
+
+/*----------------------------------------------------------------------------
+  Memory card event handlers
+  ----------------------------------------------------------------------------*/
+
+static void _handle_pre_unmount(MafwGstRendererState *self,
+                               const gchar *mount_point)
+{
+       gchar *mount_uri;
+
+       /* If not playing anything, bail out */
+       if (!self->renderer->media->uri) {
+               return;
+       }
+
+       /* Check if mount point is URI or path, we need URI */
+       if (!g_str_has_prefix(mount_point, "file://")) {
+               mount_uri = g_filename_to_uri(mount_point, NULL, NULL);
+       } else {
+               mount_uri = g_strdup(mount_point);
+       }
+
+       /* Stop if playing from unmounted location */
+       if (g_str_has_prefix(self->renderer->media->uri, mount_uri)) {
+                g_debug("PAUSED-STATE: stopping to mount card");
+               mafw_gst_renderer_stop(MAFW_RENDERER(self->renderer),
+                                      NULL,
+                                      NULL);
+       }
+
+       g_free(mount_uri);
+}
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h b/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h
new file mode 100644 (file)
index 0000000..f0098d8
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_GST_RENDERER_STATE_PAUSED_H
+#define MAFW_GST_RENDERER_STATE_PAUSED_H
+
+
+#include "mafw-gst-renderer.h"
+#include "mafw-gst-renderer-state.h"
+
+G_BEGIN_DECLS
+
+/*----------------------------------------------------------------------------
+  GObject type conversion macros
+  ----------------------------------------------------------------------------*/
+
+#define MAFW_TYPE_GST_RENDERER_STATE_PAUSED             \
+        (mafw_gst_renderer_state_paused_get_type())
+#define MAFW_GST_RENDERER_STATE_PAUSED(obj)                             \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER_STATE_PAUSED, \
+                                   MafwGstRendererStatePaused))
+#define MAFW_IS_GST_RENDERER_STATE_PAUSED(obj)                          \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER_STATE_PAUSED))
+#define MAFW_GST_RENDERER_STATE_PAUSED_CLASS(klass)                     \
+       (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER_STATE_PAUSED, \
+                                MafwGstRendererStatePaused))
+#define MAFW_GST_RENDERER_STATE_PAUSED_GET_CLASS(obj)                   \
+       (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER_STATE_PAUSED, \
+                                  MafwGstRendererStatePausedClass))
+#define MAFW_IS_GST_RENDERER_STATE_PAUSED_CLASS(klass)                  \
+       (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER_STATE_PAUSED))
+
+/*----------------------------------------------------------------------------
+  Type definitions
+  ----------------------------------------------------------------------------*/
+
+
+typedef struct _MafwGstRendererStatePaused MafwGstRendererStatePaused;
+typedef struct _MafwGstRendererStatePausedClass MafwGstRendererStatePausedClass;
+
+struct _MafwGstRendererStatePausedClass {
+       MafwGstRendererStateClass parent_class;
+};
+
+struct _MafwGstRendererStatePaused {
+        MafwGstRendererState parent;
+};
+
+GType mafw_gst_renderer_state_paused_get_type(void);
+
+GObject *mafw_gst_renderer_state_paused_new(MafwGstRenderer *renderer);
+
+G_END_DECLS
+
+#endif
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c b/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c
new file mode 100644 (file)
index 0000000..bc7bb8c
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "mafw-gst-renderer-state-playing.h"
+#include "mafw-gst-renderer-utils.h"
+#include <libmafw/mafw.h>
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-state-playing"
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _do_play(MafwGstRendererState *self, GError **error);
+static void _do_play_object(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error);
+static void _do_stop(MafwGstRendererState *self, GError **error);
+static void _do_pause(MafwGstRendererState *self, GError **error);
+static void _do_set_position(MafwGstRendererState *self,
+                            MafwRendererSeekMode mode, gint seconds,
+                            GError **error);
+static void _do_get_position(MafwGstRendererState *self,
+                            gint *seconds,
+                            GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void _do_next(MafwGstRendererState *self, GError **error);
+static void _do_previous(MafwGstRendererState *self, GError **error);
+static void _do_goto_index(MafwGstRendererState *self, guint index,
+                          GError **error);
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwGstRendererState *self,
+                            const gchar *object_id,
+                            GHashTable *metadata,
+                            GError **error);
+
+/*----------------------------------------------------------------------------
+  Notification worker
+  ----------------------------------------------------------------------------*/
+
+static void _notify_play(MafwGstRendererState *self, GError **error);
+static void _notify_pause(MafwGstRendererState *self, GError **error);
+static void _notify_seek(MafwGstRendererState *self, GError **error);
+static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent,
+                                 GError **error);
+static void _notify_eos(MafwGstRendererState *self, GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist editing signals
+  ----------------------------------------------------------------------------*/
+
+static void _playlist_contents_changed(MafwGstRendererState *self,
+                                      gboolean clip_changed,
+                                      GError **error);
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+static GValue* _get_property_value(MafwGstRendererState *self,
+                                  const gchar *name);
+
+/*----------------------------------------------------------------------------
+  Memory card event handlers
+  ----------------------------------------------------------------------------*/
+
+static void _handle_pre_unmount(MafwGstRendererState *self,
+                               const gchar *mount_point);
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+G_DEFINE_TYPE(MafwGstRendererStatePlaying, mafw_gst_renderer_state_playing,
+             MAFW_TYPE_GST_RENDERER_STATE);
+
+static void mafw_gst_renderer_state_playing_init(MafwGstRendererStatePlaying *self)
+{
+}
+
+static void mafw_gst_renderer_state_playing_class_init(
+       MafwGstRendererStatePlayingClass *klass)
+{
+        MafwGstRendererStateClass *state_class;
+
+       state_class = MAFW_GST_RENDERER_STATE_CLASS(klass);
+        g_return_if_fail(state_class != NULL);
+
+       state_class->name = g_strdup("Playing");
+
+       /* Playback */
+
+       state_class->play         = _do_play;
+       state_class->play_object  = _do_play_object;
+       state_class->stop         = _do_stop;
+       state_class->pause        = _do_pause;
+        /* state_class->resume is not allowed */
+       state_class->set_position = _do_set_position;
+       state_class->get_position = _do_get_position;
+
+       /* Playlist */
+
+       state_class->next       = _do_next;
+       state_class->previous   = _do_previous;
+       state_class->goto_index = _do_goto_index;
+
+        /* Notification metadata */
+
+        state_class->notify_metadata = _notify_metadata;
+
+        /* Notification worker */
+
+        state_class->notify_play          = _notify_play;
+        state_class->notify_pause         = _notify_pause;
+        state_class->notify_seek          = _notify_seek;
+        state_class->notify_buffer_status = _notify_buffer_status;
+        state_class->notify_eos           = _notify_eos;
+
+       /* Playlist editing signals */
+
+       state_class->playlist_contents_changed =
+               _playlist_contents_changed;
+
+       /* Property methods */
+
+       state_class->get_property_value = _get_property_value;
+
+       /* Memory card event handlers */
+
+       state_class->handle_pre_unmount = _handle_pre_unmount;
+}
+
+GObject *mafw_gst_renderer_state_playing_new(MafwGstRenderer *renderer)
+{
+       MafwGstRendererState *state;
+
+       state = MAFW_GST_RENDERER_STATE(
+               g_object_new(MAFW_TYPE_GST_RENDERER_STATE_PLAYING, NULL));
+       state->renderer = renderer;
+
+       return G_OBJECT(state);
+}
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _do_play(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+       mafw_gst_renderer_state_do_play(self, error);
+}
+
+static void _do_play_object(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error)
+{
+       MafwGstRendererPlaybackMode cur_mode, prev_mode;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+
+       prev_mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+       mafw_gst_renderer_state_do_play_object(self, object_id, error);
+       cur_mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+
+       /* If this happens it means that we interrupted playlist playback
+          so let's resume it when play_object is finished */
+       if (cur_mode != prev_mode) {
+               self->renderer->resume_playlist = TRUE;
+       }
+}
+
+static void _do_stop(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+
+       /* Stop playback */
+        mafw_gst_renderer_state_do_stop(self, error);
+}
+
+static void _do_pause(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+
+        MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+        mafw_gst_renderer_worker_pause(renderer->worker);
+
+        /* Transition will be done when receiving pause
+         * notification */
+}
+
+static void _do_set_position(MafwGstRendererState *self,
+                            MafwRendererSeekMode mode, gint seconds,
+                            GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+       mafw_gst_renderer_state_do_set_position(self, mode, seconds, error);
+}
+
+static void _do_get_position(MafwGstRendererState *self,
+                            gint *seconds,
+                            GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+       mafw_gst_renderer_state_do_get_position(self, seconds, error);
+}
+
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void _do_next(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+       mafw_gst_renderer_state_do_next(self, error);
+}
+
+static void _do_previous(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+       mafw_gst_renderer_state_do_prev(self, error);
+}
+
+static void _do_goto_index(MafwGstRendererState *self, guint index,
+                          GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+       mafw_gst_renderer_state_do_goto_index(self, index, error);
+}
+
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwGstRendererState *self,
+                            const gchar *object_id,
+                            GHashTable *metadata,
+                            GError **error)
+{
+       g_debug("running _notify_metadata...");
+        /* Kindly Ignore this notification:
+          probably a delayed (now useless) metadata resolution */
+}
+
+
+/*----------------------------------------------------------------------------
+  Notification worker
+  ----------------------------------------------------------------------------*/
+
+static void _notify_play(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+        /* Kindly ignore this notification: it's received when seeking
+         * in a stream */
+}
+
+static void _notify_pause(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+
+        MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+        /* Change status to pause */
+        mafw_gst_renderer_set_state(renderer, Paused);
+}
+
+static void _notify_seek(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+        mafw_gst_renderer_state_do_notify_seek(self, error);
+}
+
+static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent,
+                                 GError **error)
+{
+       mafw_gst_renderer_state_do_notify_buffer_status (self, percent, error);
+}
+
+static void _notify_eos(MafwGstRendererState *self, GError **error)
+{
+        MafwGstRenderer *renderer;
+       MafwGstRendererMovementResult move_type;
+       MafwGstRendererPlaybackMode mode;
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+        /* Update playcount */
+       if (renderer->update_playcount_id > 0) {
+               g_source_remove(renderer->update_playcount_id);
+                mafw_gst_renderer_update_stats(renderer);
+        }
+
+       /* Notice: playback has already stopped, so calling
+        * mafw_gst_renderer_stop or mafw_gst_renderer_state_stop
+        * here is an error.
+        * To set the renderer state to Stopped use this instead:
+        * mafw_gst_renderer_set_state(self->renderer, Stopped);
+        */
+
+       /* If we are not in playlist mode, switch to it,
+          otherwise move to the next in the playlist */
+       mode = mafw_gst_renderer_get_playback_mode(renderer);
+       if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) {
+               mafw_gst_renderer_worker_stop(self->renderer->worker);
+               mafw_gst_renderer_set_state(self->renderer, Stopped);
+               mafw_gst_renderer_set_playback_mode(
+                       renderer, MAFW_GST_RENDERER_MODE_PLAYLIST);
+               mafw_gst_renderer_set_media_playlist(renderer);
+
+               /* Do we have to resume playlist playback? */
+               if (renderer->resume_playlist) {
+                       mafw_gst_renderer_state_play(self, error);
+               }
+       } else {
+               /* Move to next in playlist */
+               move_type =
+                       mafw_gst_renderer_move(renderer,
+                                             MAFW_GST_RENDERER_MOVE_TYPE_NEXT,
+                                             0, error);
+
+               switch (move_type) {
+               case MAFW_GST_RENDERER_MOVE_RESULT_OK:
+                       mafw_gst_renderer_state_play(self, error);
+                       break;
+               case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT:
+               case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST:
+                       mafw_gst_renderer_worker_stop(self->renderer->worker);
+                       mafw_gst_renderer_set_state(self->renderer, Stopped);
+                       break;
+               case MAFW_GST_RENDERER_MOVE_RESULT_ERROR:
+                       break;
+               default:
+                       g_critical("Movement not controlled");
+               }
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Playlist editing signals
+  ----------------------------------------------------------------------------*/
+
+static void _playlist_contents_changed(MafwGstRendererState *self,
+                                      gboolean clip_changed,
+                                      GError **error)
+{
+       MafwGstRendererPlaybackMode mode;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self));
+
+       /* Play the new index only if we are not in standalone mode.
+          Otherwise, when play_object finishes the new item will be
+          played if that's been suggested with renderer->resume_playlist */
+       mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+       if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST) {
+               mafw_gst_renderer_state_do_play(self, error);
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+GValue* _get_property_value(MafwGstRendererState *self, const gchar *name)
+{
+       GValue *value = NULL;
+
+       g_return_val_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self), value);
+
+       if (!g_strcmp0(name, MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)) {
+               gboolean is_seekable =
+                       mafw_gst_renderer_worker_get_seekable(
+                               self->renderer->worker);
+
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_STRING);
+               if (is_seekable) {
+                       g_value_set_string(value, "seek");
+               } else {
+                       g_value_set_string(value, "");
+               }
+       }
+
+       return value;
+}
+
+/*----------------------------------------------------------------------------
+  Memory card event handlers
+  ----------------------------------------------------------------------------*/
+
+static void _handle_pre_unmount(MafwGstRendererState *self,
+                               const gchar *mount_point)
+{
+       gchar *mount_uri;
+
+       /* If not playing anything, bail out */
+       if (!self->renderer->media->uri) {
+               return;
+       }
+
+       /* Check if mount point is URI or path, we need URI */
+       if (!g_str_has_prefix(mount_point, "file://")) {
+               mount_uri = g_filename_to_uri(mount_point, NULL, NULL);
+       } else {
+               mount_uri = g_strdup(mount_point);
+       }
+
+       /* Stop if playing from unmounted location */
+       if (g_str_has_prefix(self->renderer->media->uri, mount_uri)) {
+               mafw_gst_renderer_stop(MAFW_RENDERER(self->renderer),
+                                      NULL,
+                                      NULL);
+       }
+
+       g_free(mount_uri);
+}
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h b/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h
new file mode 100644 (file)
index 0000000..0127edb
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_GST_RENDERER_STATE_PLAYING_H
+#define MAFW_GST_RENDERER_STATE_PLAYING_H
+
+
+#include "mafw-gst-renderer.h"
+#include "mafw-gst-renderer-state.h"
+
+G_BEGIN_DECLS
+
+/*----------------------------------------------------------------------------
+  GObject type conversion macros
+  ----------------------------------------------------------------------------*/
+
+#define MAFW_TYPE_GST_RENDERER_STATE_PLAYING            \
+        (mafw_gst_renderer_state_playing_get_type())
+#define MAFW_GST_RENDERER_STATE_PLAYING(obj)                            \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER_STATE_PLAYING, \
+                                   MafwGstRendererStatePlaying))
+#define MAFW_IS_GST_RENDERER_STATE_PLAYING(obj)                         \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER_STATE_PLAYING))
+#define MAFW_GST_RENDERER_STATE_PLAYING_CLASS(klass)                    \
+       (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER_STATE_PLAYING, \
+                                MafwGstRendererStatePlaying))
+#define MAFW_GST_RENDERER_STATE_PLAYING_GET_CLASS(obj)                  \
+       (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER_STATE_PLAYING, \
+                                  MafwGstRendererStatePlayingClass))
+#define MAFW_IS_GST_RENDERER_STATE_PLAYING_CLASS(klass)                 \
+       (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER_STATE_PLAYING))
+
+/*----------------------------------------------------------------------------
+  Type definitions
+  ----------------------------------------------------------------------------*/
+
+
+typedef struct _MafwGstRendererStatePlaying MafwGstRendererStatePlaying;
+typedef struct _MafwGstRendererStatePlayingClass MafwGstRendererStatePlayingClass;
+
+struct _MafwGstRendererStatePlayingClass {
+       MafwGstRendererStateClass parent_class;
+};
+
+struct _MafwGstRendererStatePlaying {
+        MafwGstRendererState parent;
+};
+
+GType mafw_gst_renderer_state_playing_get_type(void);
+
+GObject *mafw_gst_renderer_state_playing_new(MafwGstRenderer *renderer);
+
+G_END_DECLS
+
+#endif
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c b/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c
new file mode 100644 (file)
index 0000000..3b46057
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <libmafw/mafw-errors.h>
+#include "mafw-gst-renderer-state-stopped.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-state-stopped"
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _do_play(MafwGstRendererState *self, GError **error);
+static void _do_play_object(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error);
+static void _do_stop(MafwGstRendererState *self, GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void _do_next(MafwGstRendererState *self, GError **error);
+static void _do_previous(MafwGstRendererState *self, GError **error);
+static void _do_goto_index(MafwGstRendererState *self, guint index,
+                          GError **error);
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwGstRendererState *self,
+                            const gchar *object_id,
+                            GHashTable *metadata,
+                            GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist editing signals
+  ----------------------------------------------------------------------------*/
+
+static void _playlist_contents_changed(MafwGstRendererState *self,
+                                      gboolean clip_changed,
+                                      GError **error);
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+static GValue* _get_property_value(MafwGstRendererState *self,
+                                  const gchar *name);
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+G_DEFINE_TYPE(MafwGstRendererStateStopped, mafw_gst_renderer_state_stopped,
+             MAFW_TYPE_GST_RENDERER_STATE);
+
+static void mafw_gst_renderer_state_stopped_init(MafwGstRendererStateStopped *self)
+{
+}
+
+static void mafw_gst_renderer_state_stopped_class_init(
+       MafwGstRendererStateStoppedClass *klass)
+{
+       MafwGstRendererStateClass *state_klass;
+
+       state_klass = MAFW_GST_RENDERER_STATE_CLASS(klass);
+       g_return_if_fail(state_klass != NULL);
+
+       state_klass->name = g_strdup("Stopped");
+
+       /* Playback */
+
+       state_klass->play        = _do_play;
+       state_klass->play_object = _do_play_object;
+       state_klass->stop        = _do_stop;
+
+       /* Playlist */
+
+       state_klass->next       = _do_next;
+       state_klass->previous   = _do_previous;
+       state_klass->goto_index = _do_goto_index;
+
+       /* Metadata */
+
+       state_klass->notify_metadata = _notify_metadata;
+
+       /* Playlist editing signals */
+
+       state_klass->playlist_contents_changed =
+               _playlist_contents_changed;
+
+       /* Property methods */
+
+       state_klass->get_property_value = _get_property_value;
+}
+
+GObject *mafw_gst_renderer_state_stopped_new(MafwGstRenderer *renderer)
+{
+       MafwGstRendererState *state;
+
+        state = MAFW_GST_RENDERER_STATE(
+               g_object_new(MAFW_TYPE_GST_RENDERER_STATE_STOPPED, NULL));
+       state->renderer = renderer;
+
+       return G_OBJECT(state);
+}
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _do_play(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self));
+       mafw_gst_renderer_state_do_play(self, error);
+}
+
+static void _do_play_object(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error)
+{
+       MafwGstRendererPlaybackMode cur_mode, prev_mode;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self));
+
+       prev_mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+       mafw_gst_renderer_state_do_play_object(self, object_id, error);
+       cur_mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+
+       /* If this happens it means that we interrupted playlist mode
+          but we did so in Stopped state, so when play_object finishes
+          we want to stay Stopped */
+       if (cur_mode != prev_mode) {
+               self->renderer->resume_playlist = FALSE;
+       }
+}
+
+static void _do_stop(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self));
+       /* We are already in Stopped state, so do nothing */
+}
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void _do_next(MafwGstRendererState *self, GError **error)
+{
+        MafwGstRenderer *renderer = NULL;
+       MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self));
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       value = mafw_gst_renderer_move(renderer,
+                                     MAFW_GST_RENDERER_MOVE_TYPE_NEXT,
+                                     0, error);
+
+       switch (value) {
+       case MAFW_GST_RENDERER_MOVE_RESULT_ERROR:
+       case MAFW_GST_RENDERER_MOVE_RESULT_OK:
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST:
+               g_set_error (error,
+                            MAFW_RENDERER_ERROR,
+                            MAFW_RENDERER_ERROR_NO_MEDIA,
+                            "There is no playlist or media to play");
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT:
+               mafw_playlist_iterator_reset(renderer->iterator, NULL);
+               mafw_gst_renderer_set_media_playlist(renderer);
+               break;
+       default:
+               g_critical("Movement not controlled");
+       }
+}
+
+static void _do_previous(MafwGstRendererState *self, GError **error)
+{
+        MafwGstRenderer *renderer = NULL;
+       MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK;
+
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self));
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       value = mafw_gst_renderer_move(renderer,
+                                     MAFW_GST_RENDERER_MOVE_TYPE_PREV,
+                                     0, error);
+
+       switch (value) {
+       case MAFW_GST_RENDERER_MOVE_RESULT_ERROR:
+       case MAFW_GST_RENDERER_MOVE_RESULT_OK:
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST:
+               g_set_error(error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_NO_MEDIA,
+                           "There is no playlist or media to play");
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT:
+
+               mafw_playlist_iterator_move_to_last(renderer->iterator, NULL);
+               mafw_gst_renderer_set_media_playlist(renderer);
+               break;
+       default:
+               g_critical("Movement not controlled");
+       }
+}
+
+static void _do_goto_index(MafwGstRendererState *self, guint index,
+                          GError **error)
+{
+        MafwGstRenderer *renderer = NULL;
+       MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self));
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       value = mafw_gst_renderer_move(renderer,
+                                     MAFW_GST_RENDERER_MOVE_TYPE_INDEX,
+                                     index, error);
+
+       switch (value) {
+       case MAFW_GST_RENDERER_MOVE_RESULT_ERROR:
+       case MAFW_GST_RENDERER_MOVE_RESULT_OK:
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST:
+               g_set_error(error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_NO_MEDIA,
+                           "There is no playlist or media to play");
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT:
+               g_set_error(error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_INDEX_OUT_OF_BOUNDS,
+                           "Index is out of bounds");
+               break;
+       default:
+               g_critical("Movement not controlled");
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwGstRendererState *self,
+                            const gchar *object_id,
+                            GHashTable *metadata,
+                            GError **error)
+{
+       g_debug("running _notify_metadata...");
+       /* This happens because we issued a play() command, this moved us to
+          Transitioning state, waiting for the URL of the objectid to play,
+          but before we got the URL and moved to Playing state, a stop()
+          command was issued. Now we got the results of the stopped play()
+          command, so we just ignore the result and stay in Stopped state. */
+
+}
+
+/*----------------------------------------------------------------------------
+  Playlist editing signals
+  ----------------------------------------------------------------------------*/
+
+static void _playlist_contents_changed(MafwGstRendererState *self,
+                                      gboolean clip_changed,
+                                      GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self));
+
+       /* Do nothing, we just stay in Stopped state in any case */
+}
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+GValue* _get_property_value(MafwGstRendererState *self, const gchar *name)
+{
+       GValue *value = NULL;
+
+       g_return_val_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self), value);
+
+       if (!g_strcmp0(name, MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)) {
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_STRING);
+               g_value_set_string(value, "");
+       }
+
+       return value;
+}
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h b/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h
new file mode 100644 (file)
index 0000000..106ff33
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_GST_RENDERER_STATE_STOPPED_H
+#define MAFW_GST_RENDERER_STATE_STOPPED_H
+
+
+#include "mafw-gst-renderer.h"
+#include "mafw-gst-renderer-state.h"
+
+G_BEGIN_DECLS
+
+/*----------------------------------------------------------------------------
+  GObject type conversion macros
+  ----------------------------------------------------------------------------*/
+
+#define MAFW_TYPE_GST_RENDERER_STATE_STOPPED            \
+        (mafw_gst_renderer_state_stopped_get_type())
+#define MAFW_GST_RENDERER_STATE_STOPPED(obj)                            \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER_STATE_STOPPED, \
+                                   MafwGstRendererStateStopped))
+#define MAFW_IS_GST_RENDERER_STATE_STOPPED(obj)                         \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER_STATE_STOPPED))
+#define MAFW_GST_RENDERER_STATE_STOPPED_CLASS(klass)                    \
+       (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER_STATE_STOPPED, \
+                                MafwGstRendererStateStopped))
+#define MAFW_GST_RENDERER_STATE_STOPPED_GET_CLASS(obj)                  \
+       (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER_STATE_STOPPED, \
+                                  MafwGstRendererStateStoppedClass))
+#define MAFW_IS_GST_RENDERER_STATE_STOPPED_CLASS(klass)                 \
+       (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER_STATE_STOPPED))
+
+/*----------------------------------------------------------------------------
+  Type definitions
+  ----------------------------------------------------------------------------*/
+
+
+typedef struct _MafwGstRendererStateStopped MafwGstRendererStateStopped;
+typedef struct _MafwGstRendererStateStoppedClass MafwGstRendererStateStoppedClass;
+
+struct _MafwGstRendererStateStoppedClass {
+       MafwGstRendererStateClass parent_class;
+};
+
+struct _MafwGstRendererStateStopped {
+        MafwGstRendererState parent;
+};
+
+GType mafw_gst_renderer_state_stopped_get_type(void);
+
+GObject *mafw_gst_renderer_state_stopped_new(MafwGstRenderer *renderer);
+
+G_END_DECLS
+
+#endif
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c b/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c
new file mode 100644 (file)
index 0000000..fdf8a17
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <string.h>
+#include "mafw-gst-renderer-state-transitioning.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-state-transitioning"
+
+#define UPDATE_DELAY 10
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _do_play(MafwGstRendererState *self, GError **error);
+static void _do_play_object(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error);
+static void _do_pause(MafwGstRendererState *self, GError **error);
+static void _do_stop(MafwGstRendererState *self, GError **error);
+static void _do_resume(MafwGstRendererState *self, GError **error);
+static void _do_get_position(MafwGstRendererState *self, gint *seconds, 
+                            GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void _do_next(MafwGstRendererState *self, GError **error);
+static void _do_previous(MafwGstRendererState *self,GError **error);
+static void _do_goto_index(MafwGstRendererState *self, guint index,
+                          GError **error);
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwGstRendererState *self,
+                            const gchar *object_id,
+                            GHashTable *metadata,
+                            GError **error);
+
+/*----------------------------------------------------------------------------
+  Notification worker
+  ----------------------------------------------------------------------------*/
+
+static void _notify_play(MafwGstRendererState *self, GError **error);
+static void _notify_pause(MafwGstRendererState *self,GError **error);
+
+static void _notify_buffer_status(MafwGstRendererState *self,
+                                 gdouble percent,
+                                 GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist editing signals
+  ----------------------------------------------------------------------------*/
+
+static void _playlist_contents_changed(MafwGstRendererState *self,
+                                      gboolean clip_changed,
+                                      GError **error);
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+static GValue* _get_property_value(MafwGstRendererState *self,
+                                  const gchar *name);
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+G_DEFINE_TYPE(MafwGstRendererStateTransitioning,
+             mafw_gst_renderer_state_transitioning,
+             MAFW_TYPE_GST_RENDERER_STATE);
+
+static void mafw_gst_renderer_state_transitioning_init(
+       MafwGstRendererStateTransitioning *self)
+{
+}
+
+static void mafw_gst_renderer_state_transitioning_class_init(
+       MafwGstRendererStateTransitioningClass *klass)
+{
+       MafwGstRendererStateClass *state_klass ;
+
+       state_klass = MAFW_GST_RENDERER_STATE_CLASS(klass);
+       g_return_if_fail(state_klass != NULL);
+
+       state_klass->name = g_strdup("Transitioning");
+
+       /* Playback */
+
+       state_klass->play         = _do_play;
+       state_klass->play_object  = _do_play_object;
+       state_klass->stop         = _do_stop;
+       state_klass->pause        = _do_pause;
+       state_klass->resume       = _do_resume;
+       state_klass->get_position = _do_get_position;
+
+       /* Playlist */
+
+       state_klass->next       = _do_next;
+       state_klass->previous   = _do_previous;
+       state_klass->goto_index = _do_goto_index;
+
+       /* Metadata */
+
+       state_klass->notify_metadata = _notify_metadata;
+
+       /* Notification worker */
+
+       state_klass->notify_play          = _notify_play;
+       state_klass->notify_pause         = _notify_pause;
+       state_klass->notify_buffer_status = _notify_buffer_status;
+
+       /* Playlist editing signals */
+
+       state_klass->playlist_contents_changed =
+               _playlist_contents_changed;
+
+       /* Property methods */
+
+       state_klass->get_property_value = _get_property_value;
+}
+
+GObject *mafw_gst_renderer_state_transitioning_new(MafwGstRenderer *renderer)
+{
+       MafwGstRendererState *state;
+
+        state = MAFW_GST_RENDERER_STATE(
+               g_object_new(MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING, NULL));
+       state->renderer = renderer;
+
+       return G_OBJECT(state);
+}
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _do_play(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+       mafw_gst_renderer_state_do_play(self, error);
+}
+
+static void _do_play_object(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error)
+{
+       MafwGstRendererPlaybackMode cur_mode, prev_mode;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+
+       prev_mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+       mafw_gst_renderer_state_do_play_object(self, object_id, error);
+       cur_mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+
+       /* If this happens it means that we interrupted playlist playback
+        so let's resume it when play_object is finished */
+       if (cur_mode != prev_mode) {
+               self->renderer->resume_playlist = TRUE;
+       }
+}
+
+static void _do_stop(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+
+       /* Stop playback */
+       mafw_gst_renderer_state_do_stop(self, error);
+}
+
+static void _do_pause(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+       g_debug("Got pause while transitioning");
+       self->renderer->worker->stay_paused = TRUE;
+}
+
+static void _do_resume(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+       if (self->renderer->worker->stay_paused) {
+               g_debug("Got resume while transitioning/paused");
+               self->renderer->worker->stay_paused = FALSE;
+       } else {
+               g_set_error(error, MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_CANNOT_PLAY,
+                           "cannot resume in transitioning state without "
+                           "having paused before");
+       }
+}
+
+static void _do_get_position(MafwGstRendererState *self, gint *seconds, 
+                            GError **error)
+{
+       *seconds = 0;
+}
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void _do_next(MafwGstRendererState *self, GError **error)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+       mafw_gst_renderer_state_do_next(self, error);
+}
+
+static void _do_previous(MafwGstRendererState *self, GError **error)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+       mafw_gst_renderer_state_do_prev(self, error);
+}
+
+static void _do_goto_index(MafwGstRendererState *self, guint index,
+                          GError **error)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+       mafw_gst_renderer_state_do_goto_index(self, index, error);
+}
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwGstRendererState *self,
+                            const gchar *object_id,
+                            GHashTable *metadata,
+                            GError **error)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+
+       MafwGstRenderer *renderer;
+       GValue *mval;
+        gpointer value;
+        gint nuris, i;
+        gchar **uris;
+        gchar *uri;
+
+       g_debug("running _notify_metadata...");
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       /* If we have received metadata for the item that we are playing
+          then play it */
+       if (object_id && renderer->media->object_id &&
+           !strcmp(object_id, renderer->media->object_id)) {
+                /* Check how many uris provide the object_id */
+                value = g_hash_table_lookup(metadata, MAFW_METADATA_KEY_URI);
+                nuris = mafw_metadata_nvalues(value);
+                if (nuris == 1) {
+                        mval = mafw_metadata_first(metadata,
+                                                   MAFW_METADATA_KEY_URI);
+                        g_assert(mval);
+                       g_free(renderer->media->uri);
+                       renderer->media->uri =
+                                g_strdup(g_value_get_string(mval));
+                       uri = renderer->media->uri;
+                } else if (nuris > 1) {
+                        uris = g_new0(gchar *, nuris + 1);
+                        for (i = 0; i < nuris; i++) {
+                                mval = g_value_array_get_nth(value, i);
+                                uris[i] = (gchar *) g_value_get_string(mval);
+                        }
+
+                        /* Try the first URI, if that fails to play back another
+                         * one will be selected until we get a successful one or
+                         * all failed. On success, the selected URI will be
+                         * emitted as metadata */
+                        g_free(renderer->media->uri);
+                        renderer->media->uri = g_strdup(uris[0]);
+                } else {
+                        g_assert_not_reached();
+                }
+
+                /* Set seekability property; currently, if several uris are
+                 * provided it uses the value of the first uri. If later another
+                 * uri is actually played, then this value should be changed. */
+                mval = mafw_metadata_first(metadata,
+                                           MAFW_METADATA_KEY_IS_SEEKABLE);
+                if (mval != NULL) {
+                        renderer->media->seekability =
+                                g_value_get_boolean(mval) ?
+                                SEEKABILITY_SEEKABLE : SEEKABILITY_NO_SEEKABLE;
+                        g_debug("_notify_metadata: source seekability %d",
+                                renderer->media->seekability);
+                } else {
+                        renderer->media->seekability = SEEKABILITY_UNKNOWN;
+                        g_debug("_notify_metadata: source seekability unknown");
+                }
+
+               /* Check for source duration to keep it updated if needed */
+                mval = mafw_metadata_first(metadata,
+                                           MAFW_METADATA_KEY_DURATION);
+
+                if (mval != NULL) {
+                        renderer->media->duration = g_value_get_int(mval);
+                        g_debug("_notify_metadata: source duration %d",
+                               renderer->media->duration);
+               } else {
+                        renderer->media->duration = -1;
+                        g_debug("_notify_metadata: source duration unknown");
+                }
+
+                /* Play the available uri(s) */
+                if (nuris == 1) {
+                       mafw_gst_renderer_worker_play(renderer->worker, uri);
+               } else {
+                        mafw_gst_renderer_worker_play_alternatives(
+                                renderer->worker, uris);
+                        g_free(uris);
+                }
+        }
+}
+
+/*----------------------------------------------------------------------------
+  Notification worker
+  ----------------------------------------------------------------------------*/
+
+static void _notify_play(MafwGstRendererState *self, GError **error)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+
+       MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       if (renderer->media->object_id)
+       {
+                renderer->update_playcount_id = g_timeout_add_seconds(
+                       UPDATE_DELAY,
+                       mafw_gst_renderer_update_stats,
+                       renderer);
+       }
+
+       mafw_gst_renderer_set_state(renderer, Playing);
+}
+
+static void _notify_pause(MafwGstRendererState *self, GError **error)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+
+       MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+       self->renderer->worker->stay_paused = FALSE;
+        mafw_gst_renderer_set_state(renderer, Paused);
+}
+
+static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent,
+                                 GError **error)
+{
+       mafw_gst_renderer_state_do_notify_buffer_status (self, percent, error);
+}
+
+/*----------------------------------------------------------------------------
+  Playlist editing signals
+  ----------------------------------------------------------------------------*/
+
+static void _playlist_contents_changed(MafwGstRendererState *self,
+                                      gboolean clip_changed,
+                                      GError **error)
+{
+       MafwGstRendererPlaybackMode mode;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self));
+
+       /* Play the new index only if we are not in standalone mode.
+          Otherwise, when play_object finishes the new item will be
+          played if that's been suggested with renderer->resume_playlist */
+       mode = mafw_gst_renderer_get_playback_mode(self->renderer);
+       if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST) {
+               mafw_gst_renderer_state_do_play(self, error);
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+GValue* _get_property_value(MafwGstRendererState *self, const gchar *name)
+{
+       GValue *value = NULL;
+
+       g_return_val_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self),
+               value);
+
+       if (!g_strcmp0(name, MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)) {
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_STRING);
+               g_value_set_string(value, "");
+       }
+
+       return value;
+}
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h b/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h
new file mode 100644 (file)
index 0000000..4530a4f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_GST_RENDERER_STATE_TRANSITIONING_H
+#define MAFW_GST_RENDERER_STATE_TRANSITIONING_H
+
+
+#include "mafw-gst-renderer.h"
+#include "mafw-gst-renderer-state.h"
+#include "mafw-gst-renderer-utils.h"
+
+G_BEGIN_DECLS
+
+/*----------------------------------------------------------------------------
+  GObject type conversion macros
+  ----------------------------------------------------------------------------*/
+
+#define MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING              \
+        (mafw_gst_renderer_state_transitioning_get_type())
+#define MAFW_GST_RENDERER_STATE_(obj)                                   \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj),                              \
+                                   MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING, \
+                                   MafwGstRendererStateTransitioning))
+#define MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(obj)                   \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj),                              \
+                                   MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING))
+#define MAFW_GST_RENDERER_STATE_TRANSITIONING_CLASS(klass)              \
+       (G_TYPE_CHECK_CLASS_CAST((klass),                               \
+                                MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING, \
+                                MafwGstRendererStateTransitioning))
+#define MAFW_GST_RENDERER_STATE_TRANSITIONING_GET_CLASS(obj)            \
+       (G_TYPE_INSTANCE_GET_CLASS((obj),                               \
+                                  MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING, \
+                                  MafwGstRendererStateTransitioningClass))
+#define MAFW_IS_GST_RENDERER_STATE_TRANSITIONING_CLASS(klass)           \
+       (G_TYPE_CHECK_CLASS_TYPE((klass),                               \
+                                MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING))
+
+/*----------------------------------------------------------------------------
+  Type definitions
+  ----------------------------------------------------------------------------*/
+
+
+typedef struct _MafwGstRendererStateTransitioning MafwGstRendererStateTransitioning;
+typedef struct _MafwGstRendererStateTransitioningClass MafwGstRendererStateTransitioningClass;
+
+struct _MafwGstRendererStateTransitioningClass {
+       MafwGstRendererStateClass parent_class;
+};
+
+struct _MafwGstRendererStateTransitioning {
+        MafwGstRendererState parent;
+};
+
+GType mafw_gst_renderer_state_transitioning_get_type(void);
+
+GObject *mafw_gst_renderer_state_transitioning_new(MafwGstRenderer *renderer);
+
+G_END_DECLS
+
+#endif
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state.c b/libmafw-gst-renderer/mafw-gst-renderer-state.c
new file mode 100644 (file)
index 0000000..274fcec
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libmafw/mafw-renderer.h>
+#include <libmafw/mafw-errors.h>
+
+#include "mafw-gst-renderer.h"
+#include "mafw-gst-renderer-state.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-state"
+
+/*----------------------------------------------------------------------------
+  Default playback implementations
+  ----------------------------------------------------------------------------*/
+
+static void _default_play(MafwGstRendererState *self, GError **error)
+{
+       g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PLAY,
+                   "Play: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+
+static void _default_play_object(MafwGstRendererState *self,
+                                const gchar *objectid,
+                                GError **error)
+{
+       g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PLAY,
+                   "Play object: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_stop(MafwGstRendererState *self, GError **error)
+{
+       g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_STOP,
+                   "Stop: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_pause(MafwGstRendererState *self, GError **error)
+{
+       g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PAUSE,
+                   "Pause: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_resume(MafwGstRendererState *self, GError **error)
+{
+       g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PLAY,
+                   "Resume: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_set_position (MafwGstRendererState *self,
+                                  MafwRendererSeekMode mode, gint seconds,
+                                  GError **error)
+{
+       g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PLAY,
+                   "Set position: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_get_position (MafwGstRendererState *self,
+                                  gint *seconds,
+                                  GError **error)
+{
+       g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_GET_POSITION,
+                   "Get position: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+/*----------------------------------------------------------------------------
+  Default playlist implementations
+  ----------------------------------------------------------------------------*/
+
+static void _default_next(MafwGstRendererState *self, GError **error)
+{
+       g_set_error(error, MAFW_EXTENSION_ERROR, MAFW_EXTENSION_ERROR_FAILED,
+                   "Next: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_previous(MafwGstRendererState *self, GError **error)
+{
+       g_set_error(error, MAFW_EXTENSION_ERROR, MAFW_EXTENSION_ERROR_FAILED,
+                   "Previous: Operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_goto_index(MafwGstRendererState *self, guint index,
+                               GError **error)
+{
+       g_set_error(error, MAFW_EXTENSION_ERROR, MAFW_EXTENSION_ERROR_FAILED,
+                   "Goto index: operation not allowed in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+/*----------------------------------------------------------------------------
+  Default notify  metadata implementation
+  ----------------------------------------------------------------------------*/
+
+static void _default_notify_metadata(MafwGstRendererState *self,
+                                    const gchar *object_id,
+                                    GHashTable *metadata,
+                                    GError **error)
+{
+
+       g_critical("Notify metadata: got unexpected metadata in %s state",
+                   MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+/*----------------------------------------------------------------------------
+  Default notify worker implementations
+  ----------------------------------------------------------------------------*/
+
+static void _default_notify_play(MafwGstRendererState *self, GError **error)
+{
+       g_critical("Notify play: unexpected Play notification received in %s "
+                  "state", MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_notify_pause(MafwGstRendererState *self, GError **error)
+{
+
+       g_critical("Notify pause: unexpected Pause notification received %s "
+                  "state", MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_notify_seek(MafwGstRendererState *self, GError **error)
+{
+       g_critical("Notify seek: incorrect operation in %s state",
+                  MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+static void _default_notify_buffer_status(MafwGstRendererState *self,
+                                         gdouble percent,
+                                         GError **error)
+{
+       g_critical("Notify buffer status: incorrect operation in %s state",
+                  MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+
+static void _default_notify_eos(MafwGstRendererState *self, GError **error)
+{
+       g_critical("Notify eos: incorrect operation in %s state",
+                  MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+/*----------------------------------------------------------------------------
+  Default playlist editing signal handlers implementation
+  ----------------------------------------------------------------------------*/
+
+static void _default_playlist_contents_changed(MafwGstRendererState *self,
+                                              gboolean clip_changed,
+                                              GError **error)
+{
+       g_warning("playlist::contents-changed not implemented in %s state",
+                 MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+/*----------------------------------------------------------------------------
+  Default property methods implementation
+  ----------------------------------------------------------------------------*/
+
+static GValue* _default_get_property_value(MafwGstRendererState *self,
+                                       const gchar *name)
+{
+       g_warning("get_property_value function not implemented in %s state",
+                 MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+       return NULL;
+}
+
+/*----------------------------------------------------------------------------
+  Default memory card event handlers implementation
+  ----------------------------------------------------------------------------*/
+
+static void _default_handle_pre_unmount(MafwGstRendererState *self,
+                                       const gchar *mount_point)
+{
+       g_debug("pre-unmount signal received: %s in state %s", mount_point,
+               MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name);
+}
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+G_DEFINE_ABSTRACT_TYPE(MafwGstRendererState, mafw_gst_renderer_state,
+                      G_TYPE_OBJECT);
+
+static void mafw_gst_renderer_state_init(MafwGstRendererState *self)
+{
+}
+
+static void mafw_gst_renderer_state_class_init(MafwGstRendererStateClass *klass)
+{
+       /* Playback */
+
+       klass->play         = _default_play;
+       klass->play_object  = _default_play_object;
+       klass->stop         = _default_stop;
+       klass->pause        = _default_pause;
+       klass->resume       = _default_resume;
+       klass->set_position = _default_set_position;
+       klass->get_position = _default_get_position;
+
+       /* Playlist */
+
+       klass->next              = _default_next;
+       klass->previous          = _default_previous;
+       klass->goto_index        = _default_goto_index;
+
+       /* Notification metadata */
+
+       klass->notify_metadata = _default_notify_metadata;
+
+       /* Notification worker */
+
+       klass->notify_play          = _default_notify_play;
+       klass->notify_pause         = _default_notify_pause;
+       klass->notify_seek          = _default_notify_seek;
+       klass->notify_buffer_status = _default_notify_buffer_status;
+       klass->notify_eos           = _default_notify_eos;
+
+       klass->notify_eos           = _default_notify_eos;
+
+       /* Playlist editing signals */
+
+       klass->playlist_contents_changed =
+               _default_playlist_contents_changed;
+
+       /* Property methods */
+
+       klass->get_property_value = _default_get_property_value;
+
+       /* Memory card event handlers */
+
+       klass->handle_pre_unmount = _default_handle_pre_unmount;
+}
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_play(MafwGstRendererState *self, GError **error)
+
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->play(self, error);
+}
+
+void mafw_gst_renderer_state_play_object(MafwGstRendererState *self,
+                                      const gchar *object_id,
+                                      GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->play_object(self, object_id,
+                                                          error);
+}
+
+void mafw_gst_renderer_state_stop(MafwGstRendererState *self, GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->stop(self, error);
+}
+
+void mafw_gst_renderer_state_pause(MafwGstRendererState *self, GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->pause(self, error);
+}
+
+void mafw_gst_renderer_state_resume(MafwGstRendererState *self, GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->resume(self, error);
+}
+
+void mafw_gst_renderer_state_set_position(MafwGstRendererState *self,
+                                        MafwRendererSeekMode mode, gint seconds,
+                                        GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->set_position(self, mode, seconds,
+                                                            error);
+}
+
+void mafw_gst_renderer_state_get_position(MafwGstRendererState *self,
+                                         gint *seconds,
+                                         GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->get_position(self, seconds, 
+                                                             error);
+}
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_next(MafwGstRendererState *self, GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->next(self, error);
+}
+
+void mafw_gst_renderer_state_previous(MafwGstRendererState *self, GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->previous(self, error);
+}
+
+void mafw_gst_renderer_state_goto_index(MafwGstRendererState *self, guint index,
+                                     GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->goto_index(self, index, error);
+
+}
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+void  mafw_gst_renderer_state_notify_metadata(MafwGstRendererState *self,
+                                           const gchar *object_id,
+                                           GHashTable *metadata,
+                                           GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_metadata(self, object_id,
+                                                              metadata,
+                                                              error);
+}
+
+/*----------------------------------------------------------------------------
+  Notification worker
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_notify_play(MafwGstRendererState *self,
+                                       GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_play(self, error);
+}
+
+void mafw_gst_renderer_state_notify_pause(MafwGstRendererState *self,
+                                       GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_pause(self, error);
+}
+
+void mafw_gst_renderer_state_notify_seek(MafwGstRendererState *self,
+                                       GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_seek(self, error);
+}
+
+void mafw_gst_renderer_state_notify_buffer_status(MafwGstRendererState *self,
+                                               gdouble percent,
+                                               GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_buffer_status(self,
+                                                                   percent,
+                                                                   error);
+}
+
+void mafw_gst_renderer_state_notify_eos(MafwGstRendererState *self,
+                                      GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_eos(self, error);
+}
+
+/*----------------------------------------------------------------------------
+  Playlist editing handlers
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_playlist_contents_changed_handler(
+       MafwGstRendererState *self,
+       gboolean clip_changed,
+       GError **error)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->playlist_contents_changed(
+               self,
+               clip_changed,
+               error);
+}
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+GValue* mafw_gst_renderer_state_get_property_value(MafwGstRendererState *self,
+                                                  const gchar *name)
+{
+       return MAFW_GST_RENDERER_STATE_GET_CLASS(self)->get_property_value(
+               self,
+               name);
+}
+
+/*----------------------------------------------------------------------------
+  Memory card event handlers
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_handle_pre_unmount(MafwGstRendererState *self,
+                                               const gchar *mount_point)
+{
+       MAFW_GST_RENDERER_STATE_GET_CLASS(self)->
+               handle_pre_unmount(self, mount_point);
+}
+
+/*----------------------------------------------------------------------------
+  Helpers
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_do_play(MafwGstRendererState *self, GError **error)
+{
+        MafwGstRenderer *renderer;
+        GError *gm_error = NULL;
+       MafwGstRendererPlaybackMode mode;
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+        /* Stop any on going playback */
+        mafw_gst_renderer_worker_stop(renderer->worker);
+
+       /* Play command only affects playlists, so switch to playlist
+          mode first if necessary */
+       mode = mafw_gst_renderer_get_playback_mode(renderer);
+       if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) {
+               mafw_gst_renderer_set_playback_mode(
+                       renderer, MAFW_GST_RENDERER_MODE_PLAYLIST);
+               mafw_gst_renderer_set_media_playlist(renderer);
+       }
+
+       /* Do we have any objectid to play? Otherwise we cannot do it */
+        if (renderer->media->object_id) {
+               /* If so, resolve URI for this objectid */
+                mafw_gst_renderer_get_metadata(renderer,
+                                            renderer->media->object_id,
+                                            &gm_error);
+                if (gm_error) {
+                       MafwGstRendererErrorClosure *error_closure;
+                       if (error) {
+                                g_set_error(error,
+                                            MAFW_RENDERER_ERROR,
+                                            MAFW_RENDERER_ERROR_NO_MEDIA,
+                                            "Unable to find media");
+                       }
+
+                       /* This is a playback error: execute error policy */
+                       error_closure = g_new0(MafwGstRendererErrorClosure, 1);
+                       error_closure->renderer = renderer;
+                       error_closure->error = g_error_copy(gm_error);
+                       g_idle_add(mafw_gst_renderer_manage_error_idle,
+                                  error_closure);
+
+                       g_error_free(gm_error);
+                } else {
+                        mafw_gst_renderer_set_state(renderer, Transitioning);
+               }
+        } else if (error) {
+                g_set_error(error,
+                            MAFW_RENDERER_ERROR,
+                            MAFW_RENDERER_ERROR_NO_MEDIA,
+                            "There is no media to play");
+                mafw_gst_renderer_set_state(renderer, Stopped);
+        }
+}
+
+void  mafw_gst_renderer_state_do_play_object(MafwGstRendererState *self,
+                                          const gchar *object_id,
+                                          GError **error)
+{
+        MafwGstRenderer *renderer;
+        GError *gm_error = NULL;
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       /* Stop any ongoing playback */
+        mafw_gst_renderer_worker_stop(renderer->worker);
+
+        if (object_id) {
+               /* Switch to standalone mode */
+               mafw_gst_renderer_set_playback_mode(
+                       renderer, MAFW_GST_RENDERER_MODE_STANDALONE);
+
+                mafw_gst_renderer_set_object(renderer, object_id);
+                mafw_gst_renderer_get_metadata(renderer,
+                                            renderer->media->object_id,
+                                            &gm_error);
+                if (gm_error) {
+                       MafwGstRendererErrorClosure *error_closure;
+                        if (error) {
+                                g_set_error(error,
+                                            MAFW_RENDERER_ERROR,
+                                            MAFW_RENDERER_ERROR_NO_MEDIA,
+                                            "Unable to find media");
+                       }
+
+                       /* This is a playback error: execute error policy */
+                       error_closure = g_new0(MafwGstRendererErrorClosure, 1);
+                       error_closure->renderer = renderer;
+                       error_closure->error = g_error_copy(gm_error);
+                       g_idle_add(mafw_gst_renderer_manage_error_idle,
+                                  error_closure);
+                        g_error_free(gm_error);
+                } else {
+                       /* Play object has been successful */
+                        mafw_gst_renderer_set_state(renderer, Transitioning);
+               }
+        } else if (error) {
+                g_set_error(error,
+                            MAFW_RENDERER_ERROR,
+                            MAFW_RENDERER_ERROR_NO_MEDIA,
+                            "There is no media to play");
+                mafw_gst_renderer_set_state(renderer, Stopped);
+        }
+}
+
+void mafw_gst_renderer_state_do_stop(MafwGstRendererState *self, GError **error)
+{
+        MafwGstRenderer *renderer;
+       MafwGstRendererPlaybackMode mode;
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       /* Stop any ongoing playback */
+       mafw_gst_renderer_worker_stop(renderer->worker);
+
+       /* Cancel update */
+       if (renderer->update_playcount_id > 0) {
+               g_source_remove(renderer->update_playcount_id);
+               renderer->update_playcount_id = 0;
+       }
+
+       /* Set new state */
+       mafw_gst_renderer_set_state(renderer, Stopped);
+
+       /* If we were playing a standalone object, then go back
+          to playlist mode and stay stopped */
+       mode = mafw_gst_renderer_get_playback_mode(renderer);
+       if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) {
+               mafw_gst_renderer_set_playback_mode(
+                       renderer, MAFW_GST_RENDERER_MODE_PLAYLIST);
+               mafw_gst_renderer_set_media_playlist(renderer);
+       }
+}
+
+void mafw_gst_renderer_state_do_next (MafwGstRendererState *self, GError **error)
+{
+        MafwGstRenderer *renderer;
+       MafwGstRendererMovementResult move_type;
+       MafwGstRendererPlaybackMode mode;
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       /* If we are in standalone mode, we switch back to playlist
+        * mode. Then we resume playback only if renderer->resume_playlist
+        * was set.
+        * If we are in playlist mode we just move to the next and
+        * play.
+        */
+       mode = mafw_gst_renderer_get_playback_mode(renderer);
+       if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) {
+               mafw_gst_renderer_set_playback_mode(
+                       renderer, MAFW_GST_RENDERER_MODE_PLAYLIST);
+               mafw_gst_renderer_set_media_playlist(renderer);
+       }
+
+       move_type = mafw_gst_renderer_move(renderer,
+                                         MAFW_GST_RENDERER_MOVE_TYPE_NEXT,
+                                         0, error);
+       switch (move_type) {
+       case MAFW_GST_RENDERER_MOVE_RESULT_OK:
+               if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST ||
+                   renderer->resume_playlist) {
+                       /* We issued the comand in playlist mode, or
+                         in standalone mode but with resume_playlist
+                         set, so let's play the new item */
+                       mafw_gst_renderer_state_play(self, error);
+
+               } else {
+                       /* We issued the command in standalone mode and we
+                         do not want to resume playlist, so let's
+                         move to Stopped */
+                       mafw_gst_renderer_state_stop(self, NULL);
+               }
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST:
+               g_set_error(error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_NO_MEDIA,
+                           "There is no playlist or media to play");
+               mafw_gst_renderer_state_stop(self, NULL);
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT:
+               /* Normal mode */
+               mafw_playlist_iterator_reset(renderer->iterator, NULL);
+               mafw_gst_renderer_set_media_playlist(renderer);
+               mafw_gst_renderer_state_play(self, error);
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_ERROR:
+               break;
+       default:
+               g_critical("Movement not controlled");
+       }
+}
+
+void mafw_gst_renderer_state_do_prev(MafwGstRendererState *self, GError **error)
+{
+        MafwGstRenderer *renderer;
+       MafwGstRendererMovementResult move_type;
+       MafwGstRendererPlaybackMode mode;
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       mode = mafw_gst_renderer_get_playback_mode(renderer);
+       if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) {
+               mafw_gst_renderer_set_playback_mode(
+                       renderer, MAFW_GST_RENDERER_MODE_PLAYLIST);
+               mafw_gst_renderer_set_media_playlist(renderer);
+       }
+
+       move_type = mafw_gst_renderer_move(renderer,
+                                         MAFW_GST_RENDERER_MOVE_TYPE_PREV,
+                                         0, error);
+       switch (move_type) {
+       case MAFW_GST_RENDERER_MOVE_RESULT_OK:
+               if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST ||
+                   renderer->resume_playlist) {
+                       /* We issued the comand in playlist mode, or
+                         in standalone mode but with resume_playlist
+                         set, so let's play the new item */
+                       mafw_gst_renderer_state_play(self, error);
+
+               } else {
+                       /* We issued the command in standalone mode and we
+                         do not want to resume playlist, so let's
+                         move to Stopped */
+                       mafw_gst_renderer_state_stop(self, NULL);
+               }
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST:
+               g_set_error(error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_NO_MEDIA,
+                           "There is no playlist or media to play");
+               mafw_gst_renderer_state_stop(self, NULL);
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT:
+               /* Normal mode */
+               mafw_playlist_iterator_move_to_last(renderer->iterator, NULL);
+               mafw_gst_renderer_set_media_playlist(renderer);
+               mafw_gst_renderer_state_play(self, error);
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_ERROR:
+               break;
+       default:
+               g_critical("Movement not controlled");
+       }
+}
+
+
+void mafw_gst_renderer_state_do_goto_index(MafwGstRendererState *self,
+                                        guint index,
+                                        GError **error)
+{
+        MafwGstRenderer *renderer;
+       MafwGstRendererMovementResult move_type;
+       MafwGstRendererPlaybackMode mode;
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       /* If we are in standalone mode, we switch back to playlist
+        * mode. Then we resume playback only if renderer->resume_playlist
+        * was set.
+        * If we are in playlist mode we just move to the next and
+        * play.
+        */
+       mode = mafw_gst_renderer_get_playback_mode(renderer);
+       if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) {
+               mafw_gst_renderer_set_playback_mode(
+                       renderer, MAFW_GST_RENDERER_MODE_PLAYLIST);
+               mafw_gst_renderer_set_media_playlist(renderer);
+       }
+
+       move_type = mafw_gst_renderer_move(renderer, MAFW_GST_RENDERER_MOVE_TYPE_INDEX, index, error);
+
+       switch (move_type) {
+       case MAFW_GST_RENDERER_MOVE_RESULT_OK:
+               if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST ||
+                   renderer->resume_playlist) {
+                       /* We issued the comand in playlist mode, or
+                         in standalone mode but with resume_playlist
+                         set, so let's play the new item */
+                       mafw_gst_renderer_state_play(self, error);
+
+               } else {
+                       /* We issued the command in standalone mode and we
+                         do not want to resume playlist, so let's
+                         move to Stopped */
+                       mafw_gst_renderer_state_stop(self, NULL);
+               }
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST:
+               g_set_error(error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_NO_MEDIA,
+                           "There is no playlist or media to play");
+               mafw_gst_renderer_state_stop(self, NULL);
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT:
+               g_set_error(error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_INDEX_OUT_OF_BOUNDS,
+                           "Index is out of bounds");
+               mafw_gst_renderer_state_stop(self, NULL);
+               break;
+       case MAFW_GST_RENDERER_MOVE_RESULT_ERROR:
+               break;
+       default:
+               g_critical("Movement not controlled");
+       }
+}
+
+void mafw_gst_renderer_state_do_get_position(MafwGstRendererState *self,
+                                           gint *seconds,
+                                           GError **error)
+{
+       *seconds = mafw_gst_renderer_worker_get_position(self->renderer->worker);
+       if (*seconds < 0) {
+               *seconds = 0;
+               g_set_error(error, MAFW_EXTENSION_ERROR, 
+                           MAFW_RENDERER_ERROR_CANNOT_GET_POSITION,
+                           "Position query failed");
+       }
+}
+
+void mafw_gst_renderer_state_do_set_position(MafwGstRendererState *self,
+                                           MafwRendererSeekMode mode,
+                                           gint seconds,
+                                           GError **error)
+{
+       MafwGstRenderer *renderer;
+       GstSeekType seektype;
+
+        renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       /* TODO Gst stuff should be moved to worker, not handled here... */
+       if (mode == SeekAbsolute) {
+               if (seconds < 0) {
+                       seektype = GST_SEEK_TYPE_END;
+                       seconds *= -1;
+               } else {
+                       seektype = GST_SEEK_TYPE_SET;
+               }
+       } else if (mode == SeekRelative) {
+               seektype = GST_SEEK_TYPE_CUR;
+       } else {
+               g_critical("Unknown seek mode: %d", mode);
+               g_set_error(error, MAFW_EXTENSION_ERROR,
+                           MAFW_EXTENSION_ERROR_INVALID_PARAMS,
+                           "Unknown seek mode: %d", mode);
+               return;
+       }
+       if (renderer->seek_pending) {
+               g_debug("seek pending, storing position %d", seconds);
+               renderer->seek_type_pending = seektype;
+               renderer->seeking_to = seconds;
+       } else {
+               renderer->seek_pending = TRUE;
+                mafw_gst_renderer_worker_set_position(renderer->worker,
+                                                    seektype,
+                                                    seconds,
+                                                    error);
+       }
+}
+
+void mafw_gst_renderer_state_do_notify_seek(MafwGstRendererState *self,
+                                          GError **error)
+{
+        MafwGstRenderer *renderer;
+
+       renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+        if (renderer->seeking_to != -1) {
+                renderer->seek_pending = TRUE;
+                mafw_gst_renderer_worker_set_position(renderer->worker,
+                                                    renderer->seek_type_pending,
+                                                    renderer->seeking_to,
+                                                    NULL);
+        } else {
+                renderer->seek_pending = FALSE;
+        }
+        renderer->seeking_to = -1;
+}
+
+void mafw_gst_renderer_state_do_notify_buffer_status(MafwGstRendererState *self,
+                                                   gdouble percent,
+                                                   GError **error)
+{
+        MafwGstRenderer *renderer = NULL;
+
+        g_return_if_fail(MAFW_IS_GST_RENDERER_STATE(self));
+
+        renderer = MAFW_GST_RENDERER_STATE(self)->renderer;
+
+       mafw_renderer_emit_buffering_info(MAFW_RENDERER(renderer), percent / 100.0);
+}
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state.h b/libmafw-gst-renderer/mafw-gst-renderer-state.h
new file mode 100644 (file)
index 0000000..5fd3325
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_GST_RENDERER_STATE_H
+#define MAFW_GST_RENDERER_STATE_H
+
+
+#include <glib-object.h>
+#include "mafw-gst-renderer-worker.h"
+
+/* Solving the cyclic dependencies */
+typedef struct _MafwGstRendererState MafwGstRendererState;
+typedef struct _MafwGstRendererStateClass MafwGstRendererStateClass;
+#include "mafw-gst-renderer.h"
+
+G_BEGIN_DECLS
+
+/*----------------------------------------------------------------------------
+  GObject type conversion macros
+  ----------------------------------------------------------------------------*/
+
+#define MAFW_TYPE_GST_RENDERER_STATE            \
+        (mafw_gst_renderer_state_get_type())
+#define MAFW_GST_RENDERER_STATE(obj)                                    \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER_STATE, \
+                                   MafwGstRendererState))
+#define MAFW_IS_GST_RENDERER_STATE(obj)                                 \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER_STATE))
+#define MAFW_GST_RENDERER_STATE_CLASS(klass)                            \
+       (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER_STATE, \
+                                MafwGstRendererStateClass))
+#define MAFW_GST_RENDERER_STATE_GET_CLASS(obj)                          \
+       (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER_STATE, \
+                                  MafwGstRendererStateClass))
+#define MAFW_IS_GST_RENDERER_STATE_CLASS(klass)                         \
+       (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER_STATE))
+
+/*----------------------------------------------------------------------------
+  Type definitions
+  ----------------------------------------------------------------------------*/
+
+
+struct _MafwGstRendererStateClass {
+       GObjectClass parent_class;
+       const gchar* name;
+
+       /* Playback */
+
+       void (*play)(MafwGstRendererState *self, GError **error);
+       void (*play_object)(MafwGstRendererState *self, const gchar *object_id,
+                           GError **error);
+       void (*stop)(MafwGstRendererState *self, GError **error);
+       void (*pause)(MafwGstRendererState *self, GError **error);
+       void (*resume)(MafwGstRendererState *self, GError **error);
+       void (*set_position) (MafwGstRendererState *self,
+                             MafwRendererSeekMode mode, gint seconds,
+                             GError **error);
+       void (*get_position) (MafwGstRendererState *self,
+                             gint *seconds,
+                             GError **error);
+
+       /* Playlist */
+
+       void (*next)(MafwGstRendererState *self, GError **error);
+       void (*previous)(MafwGstRendererState *self, GError **error);
+       void (*goto_index)(MafwGstRendererState *self, guint index,
+                          GError **error);
+
+       /* Notification metadata */
+
+       void (*notify_metadata)(MafwGstRendererState *self,
+                               const gchar *object_id,
+                               GHashTable *metadata,
+                               GError **error);
+
+
+       /* Notifications */
+
+       void (*notify_play)(MafwGstRendererState *self, GError **error);
+       void (*notify_pause)(MafwGstRendererState *self, GError **error);
+       void (*notify_seek)(MafwGstRendererState *self, GError **error);
+       void (*notify_buffer_status)(MafwGstRendererState *self, gdouble percent,
+                                    GError **error);
+       void (*notify_eos) (MafwGstRendererState *self, GError **error);
+
+       /* Playlist editing signals */
+
+       void (*playlist_contents_changed)(MafwGstRendererState *self,
+                                         gboolean clip_changed,
+                                         GError **error);
+       /* Property methods */
+
+       GValue* (*get_property_value)(MafwGstRendererState *self,
+                                     const gchar *name);
+
+       /* Memory card event handlers */
+
+       void (*handle_pre_unmount)(MafwGstRendererState *self,
+                                  const gchar *mount_point);
+};
+
+struct _MafwGstRendererState {
+       GObject parent;
+
+       MafwGstRenderer *renderer;
+};
+
+GType mafw_gst_renderer_state_get_type(void);
+
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_play(MafwGstRendererState *self, GError **error);
+void mafw_gst_renderer_state_play_object(MafwGstRendererState *self,
+                                         const gchar *object_id,
+                                         GError **error);
+void mafw_gst_renderer_state_stop(MafwGstRendererState *self, GError **error);
+void mafw_gst_renderer_state_pause(MafwGstRendererState *self, GError **error);
+void mafw_gst_renderer_state_resume(MafwGstRendererState *self, GError **error);
+void mafw_gst_renderer_state_set_position(MafwGstRendererState *self,
+                                          MafwRendererSeekMode mode, gint seconds,
+                                          GError **error);
+void mafw_gst_renderer_state_get_position(MafwGstRendererState *self,
+                                          gint *seconds,
+                                          GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_next(MafwGstRendererState *self, GError **error);
+void mafw_gst_renderer_state_previous(MafwGstRendererState *self, GError **error);
+void mafw_gst_renderer_state_goto_index(MafwGstRendererState *self, guint index,
+                                        GError **error);
+
+
+/*----------------------------------------------------------------------------
+  Notification metatada
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_notify_metadata(MafwGstRendererState *self,
+                                             const gchar *object_id,
+                                             GHashTable *metadata,
+                                             GError **error);
+
+/*----------------------------------------------------------------------------
+  Notification worker
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_notify_play(MafwGstRendererState *self,
+                                         GError **error);
+void mafw_gst_renderer_state_notify_pause(MafwGstRendererState *self,
+                                          GError **error);
+void mafw_gst_renderer_state_notify_seek(MafwGstRendererState *self,
+                                         GError **error);
+void mafw_gst_renderer_state_notify_buffer_status(MafwGstRendererState *self,
+                                                  gdouble percent,
+                                                  GError **error);
+void mafw_gst_renderer_state_notify_eos(MafwGstRendererState *self,
+                                        GError **error);
+
+/*----------------------------------------------------------------------------
+  Playlist editing handlers
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_playlist_contents_changed_handler(
+       MafwGstRendererState *self,
+       gboolean clip_changed,
+       GError **error);
+
+/*----------------------------------------------------------------------------
+  Property methods
+  ----------------------------------------------------------------------------*/
+
+GValue* mafw_gst_renderer_state_get_property_value(MafwGstRendererState *self,
+                                                  const gchar *name);
+
+/*----------------------------------------------------------------------------
+  Memory card event handlers
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_handle_pre_unmount(MafwGstRendererState *self,
+                                               const gchar *mount_point);
+
+/*----------------------------------------------------------------------------
+  Helpers
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_state_do_play(MafwGstRendererState *self, GError **error);
+void mafw_gst_renderer_state_do_play_object(MafwGstRendererState *self,
+                                            const gchar *object_id,
+                                            GError **error);
+void mafw_gst_renderer_state_do_stop(MafwGstRendererState *self,
+                                     GError **error);
+void mafw_gst_renderer_state_do_next(MafwGstRendererState *self,
+                                     GError **error);
+void mafw_gst_renderer_state_do_prev(MafwGstRendererState *self,
+                                     GError **error);
+void mafw_gst_renderer_state_do_goto_index(MafwGstRendererState *self,
+                                           guint index,
+                                           GError **error);
+void mafw_gst_renderer_state_do_set_position(MafwGstRendererState *self,
+                                             MafwRendererSeekMode mode, gint seconds,
+                                             GError **error);
+void mafw_gst_renderer_state_do_get_position(MafwGstRendererState *self,
+                                             gint *seconds,
+                                             GError **error);
+void mafw_gst_renderer_state_do_notify_seek(MafwGstRendererState *self,
+                                            GError **error);
+void mafw_gst_renderer_state_do_notify_buffer_status(MafwGstRendererState *self,
+                                                     gdouble percent,
+                                                     GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-utils.c b/libmafw-gst-renderer/mafw-gst-renderer-utils.c
new file mode 100644 (file)
index 0000000..0169862
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+
+#include "mafw-gst-renderer-utils.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-utils"
+
+/**
+ * convert_utf8:
+ * @src: string.
+ * @dst: location for utf8 version of @src.
+ *
+ * Tries to convert @src into UTF-8, placing it into @dst.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean convert_utf8(const gchar *src, gchar **dst)
+{
+       GError *error;
+
+       if (!src)
+               return FALSE;
+       if (g_utf8_validate(src, -1, NULL)) {
+               *dst = g_strdup(src);
+               return TRUE;
+       }
+       error = NULL;
+       *dst = g_locale_to_utf8(src, -1, NULL, NULL, &error);
+       if (error) {
+               g_warning("utf8 conversion failed '%s' (%d: %s)",
+                         src, error->code, error->message);
+               g_error_free(error);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+gboolean uri_is_playlist(const gchar *uri) {
+       /* TODO: Return if the uri is a playlist or not, using the mime type
+          instead of the file extension. */
+       if ((g_str_has_suffix(uri, ".pls")) ||
+           (g_str_has_suffix(uri, ".m3u")) ||
+           (g_str_has_suffix(uri, ".smil")) ||
+           (g_str_has_suffix(uri, ".smi")) ||
+           (g_str_has_suffix(uri, ".wpl")) ||
+           (g_str_has_suffix(uri, ".wax")) ||
+           (g_str_has_suffix(uri, ".uni")) ||
+           (g_str_has_suffix(uri, ".ram")) ||
+/*         (g_str_has_suffix(uri, ".ra")) || */
+           (g_str_has_suffix(uri, ".asx")) ||
+           (g_str_has_suffix(uri, ".rpm")))
+               {
+                       return TRUE;
+               }
+       return FALSE;
+}
+
+/**
+ * uri_is_stream:
+ * @uri: the URI to be checked.
+ *
+ * Check if given URI is a stream (not a local resource).  To not depend on
+ * gnomevfs for this, we assume everything that doesn't start with "file://" is
+ * a stream.
+ *
+ * Returns: TRUE if the URI is not local.
+ */
+gboolean uri_is_stream(const gchar *uri)
+{
+       if (uri == NULL) {
+               return FALSE;
+       } else {
+               return !g_str_has_prefix(uri, "file://");
+       }
+}
+
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-utils.h b/libmafw-gst-renderer/mafw-gst-renderer-utils.h
new file mode 100644 (file)
index 0000000..57bbe81
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#ifndef MAFW_GST_RENDERER_UTILS_H
+#define MAFW_GST_RENDERER_UTILS_H
+
+G_BEGIN_DECLS
+
+gboolean convert_utf8(const gchar *src, gchar **dst);
+gboolean uri_is_playlist(const gchar *uri);
+gboolean uri_is_stream(const gchar *uri);
+
+G_END_DECLS
+#endif
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c b/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c
new file mode 100644 (file)
index 0000000..4d17ea9
--- /dev/null
@@ -0,0 +1,702 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME
+
+#include <pulse/pulseaudio.h>
+#include <pulse/glib-mainloop.h>
+#include <pulse/ext-stream-restore.h>
+#include <string.h>
+
+#include "mafw-gst-renderer-worker-volume.h"
+#include "config.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-worker-volume"
+
+#define MAFW_GST_RENDERER_WORKER_VOLUME_SERVER NULL
+
+#define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PROPERTY "PULSE_PROP_media.role"
+#define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX "sink-input-by-media-role:"
+#define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE "x-maemo"
+
+#define MAFW_GST_RENDERER_WORKER_SET_TIMEOUT 200
+
+
+struct _MafwGstRendererWorkerVolume {
+       pa_glib_mainloop *mainloop;
+       pa_context *context;
+       gdouble pulse_volume;
+       gboolean pulse_mute;
+       MafwGstRendererWorkerVolumeChangedCb cb;
+       gpointer user_data;
+       MafwGstRendererWorkerVolumeMuteCb mute_cb;
+       gpointer mute_user_data;
+       gdouble current_volume;
+       gboolean current_mute;
+       gboolean pending_operation;
+       gdouble pending_operation_volume;
+       gboolean pending_operation_mute;
+       guint change_request_id;
+       pa_operation *pa_operation;
+};
+
+typedef struct {
+       MafwGstRendererWorkerVolume *wvolume;
+       MafwGstRendererWorkerVolumeInitCb cb;
+       gpointer user_data;
+} InitCbClosure;
+
+#define _pa_volume_to_per_one(volume) \
+       ((guint) ((((gdouble)(volume) / (gdouble) PA_VOLUME_NORM) + \
+                  (gdouble) 0.005) * (gdouble) 100.0) / (gdouble) 100.0)
+#define _pa_volume_from_per_one(volume) \
+       ((pa_volume_t)((gdouble)(volume) * (gdouble) PA_VOLUME_NORM))
+
+#define _pa_operation_running(wvolume) \
+       (wvolume->pa_operation != NULL && \
+        pa_operation_get_state(wvolume->pa_operation) == PA_OPERATION_RUNNING)
+
+static void _state_cb_init(pa_context *c, void *data);
+
+
+static gchar *_get_client_name(void) {
+       gchar buf[PATH_MAX];
+       gchar *name = NULL;
+
+       if (pa_get_binary_name(buf, sizeof(buf)))
+               name = g_strdup_printf("mafw-gst-renderer[%s]", buf);
+       else
+               name = g_strdup("mafw-gst-renderer");
+
+       return name;
+}
+
+static void _ext_stream_restore_read_cb(pa_context *c,
+                                       const pa_ext_stream_restore2_info *i,
+                                       int eol,
+                                       void *userdata)
+{
+       MafwGstRendererWorkerVolume *wvolume = userdata;
+       gdouble volume;
+       gboolean mute;
+
+       if (eol < 0) {
+               g_critical("eol parameter should not be < 1. "
+                          "Discarding volume event");
+               return;
+       }
+
+       if (i == NULL ||
+           strcmp(i->name, MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX
+                  MAFW_GST_RENDERER_WORKER_VOLUME_ROLE) != 0) {
+               return;
+       }
+
+       volume = _pa_volume_to_per_one(pa_cvolume_max(&i->volume));
+       mute = i->mute != 0 ? TRUE : FALSE;
+
+       if (_pa_operation_running(wvolume) ||
+           (wvolume->pending_operation &&
+            (wvolume->pending_operation_volume != volume ||
+             wvolume->pending_operation_mute != mute))) {
+               g_debug("volume notification, but operation running, ignoring");
+               return;
+       }
+
+       wvolume->pulse_volume = volume;
+       wvolume->pulse_mute = mute;
+
+       /* EMIT VOLUME */
+       g_debug("ext stream volume is %lf (mute: %d) for role %s in device %s",
+               wvolume->pulse_volume, wvolume->pulse_mute, i->name, i->device);
+       if (!wvolume->pending_operation &&
+           wvolume->pulse_volume != wvolume->current_volume) {
+               wvolume->current_volume = wvolume->pulse_volume;
+               if (wvolume->cb != NULL) {
+                       g_debug("signalling volume");
+                       wvolume->cb(wvolume, wvolume->current_volume,
+                                   wvolume->user_data);
+               }
+       }
+       if (!wvolume->pending_operation &&
+           wvolume->pulse_mute != wvolume->current_mute) {
+               wvolume->current_mute = wvolume->pulse_mute;
+               if (wvolume->mute_cb != NULL) {
+                       g_debug("signalling mute");
+                       wvolume->mute_cb(wvolume, wvolume->current_mute,
+                                        wvolume->mute_user_data);
+               }
+       }
+
+       wvolume->pending_operation = FALSE;
+}
+
+static void _destroy_context(MafwGstRendererWorkerVolume *wvolume)
+{
+       if (wvolume->pa_operation != NULL) {
+               if (pa_operation_get_state(wvolume->pa_operation) ==
+                   PA_OPERATION_RUNNING) {
+                       pa_operation_cancel(wvolume->pa_operation);
+               }
+               pa_operation_unref(wvolume->pa_operation);
+               wvolume->pa_operation = NULL;
+       }
+       pa_context_unref(wvolume->context);
+}
+
+static InitCbClosure *_init_cb_closure_new(MafwGstRendererWorkerVolume *wvolume,
+                                          MafwGstRendererWorkerVolumeInitCb cb,
+                                          gpointer user_data)
+{
+       InitCbClosure *closure;
+
+       closure = g_new(InitCbClosure, 1);
+       closure->wvolume = wvolume;
+       closure->cb = cb;
+       closure->user_data = user_data;
+
+       return closure;
+}
+
+static void _connect(gpointer user_data)
+{
+       gchar *name = NULL;
+       pa_mainloop_api *api = NULL;
+       InitCbClosure *closure = user_data;
+       MafwGstRendererWorkerVolume *wvolume = closure->wvolume;
+
+       name = _get_client_name();
+
+       /* get the mainloop api and create a context */
+       api = pa_glib_mainloop_get_api(wvolume->mainloop);
+       wvolume->context = pa_context_new(api, name);
+       g_assert(wvolume->context != NULL);
+
+       /* register some essential callbacks */
+       pa_context_set_state_callback(wvolume->context, _state_cb_init,
+                                     closure);
+
+       g_debug("connecting to pulse");
+
+       g_assert(pa_context_connect(wvolume->context,
+                                   MAFW_GST_RENDERER_WORKER_VOLUME_SERVER,
+                                   PA_CONTEXT_NOAUTOSPAWN | PA_CONTEXT_NOFAIL,
+                                   NULL) >= 0);
+       g_free(name);
+}
+
+static gboolean _reconnect(gpointer user_data)
+{
+       InitCbClosure *closure = user_data;
+       MafwGstRendererWorkerVolume *wvolume = closure->wvolume;
+
+       g_warning("got disconnected from pulse, reconnecting");
+       _destroy_context(wvolume);
+       _connect(user_data);
+
+       return FALSE;
+}
+
+static void
+_state_cb(pa_context *c, void *data)
+{
+       MafwGstRendererWorkerVolume *wvolume = data;
+       pa_context_state_t state;
+
+       state = pa_context_get_state(c);
+
+       switch (state) {
+       case PA_CONTEXT_TERMINATED:
+       case PA_CONTEXT_FAILED:
+       {
+               InitCbClosure *closure;
+
+               closure = _init_cb_closure_new(wvolume, NULL, NULL);
+               g_idle_add(_reconnect, closure);
+               break;
+       }
+       case PA_CONTEXT_READY: {
+               pa_operation *o;
+
+               o = pa_ext_stream_restore2_read(c, _ext_stream_restore_read_cb,
+                                              wvolume);
+               g_assert(o != NULL);
+               pa_operation_unref(o);
+
+               break;
+       }
+       default:
+               break;
+       }
+}
+
+static void _ext_stream_restore_read_cb_init(pa_context *c,
+                                            const pa_ext_stream_restore2_info *i,
+                                            int eol,
+                                            void *userdata)
+{
+       InitCbClosure *closure = userdata;
+
+       if (eol < 0) {
+               g_critical("eol parameter should not be < 1");
+       }
+
+       if (i == NULL ||
+           strcmp(i->name, MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX
+                  MAFW_GST_RENDERER_WORKER_VOLUME_ROLE) != 0)
+               return;
+
+       closure->wvolume->pulse_volume =
+               _pa_volume_to_per_one(pa_cvolume_max(&i->volume));
+       closure->wvolume->pulse_mute = i->mute != 0 ? TRUE : FALSE;
+       closure->wvolume->current_volume = closure->wvolume->pulse_volume;
+       closure->wvolume->current_mute = closure->wvolume->pulse_mute;
+
+       /* NOT EMIT VOLUME, BUT DEBUG */
+       g_debug("ext stream volume is %lf (mute: %d) for role %s in device %s",
+               closure->wvolume->pulse_volume, i->mute, i->name, i->device);
+
+       if (closure->cb != NULL) {
+               g_debug("initialized: returning volume manager");
+               closure->cb(closure->wvolume, closure->user_data);
+       } else {
+               if (closure->wvolume->cb != NULL) {
+                       g_debug("signalling volume after reconnection");
+                       closure->wvolume->cb(closure->wvolume,
+                                            closure->wvolume->current_volume,
+                                            closure->wvolume->user_data);
+               }
+               if (closure->wvolume->mute_cb != NULL) {
+                       g_debug("signalling mute after reconnection");
+                       closure->wvolume->mute_cb(closure->wvolume,
+                                                 closure->wvolume->
+                                                 current_mute,
+                                                 closure->wvolume->
+                                                 mute_user_data);
+               }
+       }
+
+       pa_context_set_state_callback(closure->wvolume->context, _state_cb,
+                                     closure->wvolume);
+
+       g_free(closure);
+}
+
+static void _ext_stream_restore_subscribe_cb(pa_context *c, void *userdata)
+{
+    pa_operation *o;
+
+    o = pa_ext_stream_restore2_read(c, _ext_stream_restore_read_cb, userdata);
+    g_assert(o != NULL);
+    pa_operation_unref(o);
+}
+
+static void
+_state_cb_init(pa_context *c, void *data)
+{
+       InitCbClosure *closure = data;
+       MafwGstRendererWorkerVolume *wvolume = closure->wvolume;
+       pa_context_state_t state;
+
+       state = pa_context_get_state(c);
+
+       g_debug("state: %d", state);
+
+       switch (state) {
+       case PA_CONTEXT_TERMINATED:
+       case PA_CONTEXT_FAILED:
+               g_critical("Connection to pulse failed, reconnection in 1 "
+                          "second");
+               g_timeout_add_seconds(1, _reconnect, closure);
+               break;
+       case PA_CONTEXT_READY: {
+               pa_operation *o;
+
+               g_debug("PA_CONTEXT_READY");
+
+               o = pa_ext_stream_restore2_read(c,
+                                              _ext_stream_restore_read_cb_init,
+                                              closure);
+               g_assert(o != NULL);
+               pa_operation_unref(o);
+
+               pa_ext_stream_restore_set_subscribe_cb(
+                       c, _ext_stream_restore_subscribe_cb, wvolume);
+
+               o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL);
+               g_assert(o != NULL);
+               pa_operation_unref(o);
+
+               break;
+       }
+       default:
+               break;
+       }
+}
+
+static gboolean _destroy_idle(gpointer data)
+{
+       MafwGstRendererWorkerVolume *wvolume = data;
+
+       g_debug("destroying");
+
+       _destroy_context(wvolume);
+       pa_glib_mainloop_free(wvolume->mainloop);
+       g_free(wvolume);
+
+       return FALSE;
+}
+
+static void
+_state_cb_destroy(pa_context *c, void *data)
+{
+       pa_context_state_t state;
+
+       state = pa_context_get_state(c);
+
+       switch (state) {
+       case PA_CONTEXT_TERMINATED:
+               g_idle_add(_destroy_idle, data);
+               break;
+       case PA_CONTEXT_FAILED:
+               g_error("Unexpected problem in volume management");
+               break;
+       default:
+               break;
+       }
+}
+
+static void _success_cb(pa_context *c, int success, void *userdata)
+{
+       if (success == 0) {
+               g_critical("Setting volume to pulse operation failed");
+       }
+}
+
+static void _remove_set_timeout(MafwGstRendererWorkerVolume *wvolume)
+{
+       if (wvolume->change_request_id != 0) {
+               g_source_remove(wvolume->change_request_id);
+       }
+       wvolume->change_request_id = 0;
+}
+
+static gboolean _set_timeout(gpointer data)
+{
+       pa_ext_stream_restore2_info info;
+        pa_ext_stream_restore2_info *infos[1];
+       MafwGstRendererWorkerVolume *wvolume = data;
+
+       if (wvolume->pending_operation) {
+               g_debug("setting volume ignored as there is still a pending "
+                       "operation. Waiting till next iteration");
+       } else if (wvolume->pulse_mute != wvolume->current_mute ||
+                  wvolume->pulse_volume != wvolume->current_volume) {
+
+               info.name = MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX
+                       MAFW_GST_RENDERER_WORKER_VOLUME_ROLE;
+               info.channel_map.channels = 1;
+               info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
+               info.device = NULL;
+               info.volume_is_absolute = TRUE;
+               infos[0] = &info;
+
+               info.mute = wvolume->current_mute;
+               pa_cvolume_init(&info.volume);
+               pa_cvolume_set(&info.volume, info.channel_map.channels,
+                              _pa_volume_from_per_one(wvolume->
+                                                      current_volume));
+
+               g_debug("setting volume to %lf and mute to %d",
+                       wvolume->current_volume, wvolume->current_mute);
+
+               if (wvolume->pa_operation != NULL) {
+                       pa_operation_unref(wvolume->pa_operation);
+               }
+
+               wvolume->pending_operation = TRUE;
+               wvolume->pending_operation_volume = wvolume->current_volume;
+               wvolume->pending_operation_mute = wvolume->current_mute;
+
+               wvolume->pa_operation = pa_ext_stream_restore2_write(
+                       wvolume->context,
+                       PA_UPDATE_REPLACE,
+                       (const pa_ext_stream_restore2_info*
+                        const *)infos,
+                       1, TRUE, _success_cb, wvolume);
+
+               if (wvolume->pa_operation == NULL) {
+                       g_critical("NULL operation when writing volume to "
+                                  "pulse");
+                       _remove_set_timeout(wvolume);
+               }
+       } else {
+               g_debug("removing volume timeout");
+               _remove_set_timeout(wvolume);
+       }
+
+       return wvolume->change_request_id != 0;
+}
+
+void mafw_gst_renderer_worker_volume_init(GMainContext *main_context,
+                                         MafwGstRendererWorkerVolumeInitCb cb,
+                                         gpointer user_data,
+                                         MafwGstRendererWorkerVolumeChangedCb
+                                         changed_cb,
+                                         gpointer changed_user_data,
+                                         MafwGstRendererWorkerVolumeMuteCb
+                                         mute_cb, gpointer mute_user_data)
+{
+       MafwGstRendererWorkerVolume *wvolume = NULL;
+       InitCbClosure *closure;
+
+       g_return_if_fail(cb != NULL);
+
+       g_assert(g_setenv(MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PROPERTY,
+                         MAFW_GST_RENDERER_WORKER_VOLUME_ROLE, FALSE));
+
+       g_debug("initializing volume manager");
+
+       wvolume = g_new0(MafwGstRendererWorkerVolume, 1);
+
+       wvolume->pulse_volume = 1.0;
+       wvolume->pulse_mute = FALSE;
+       wvolume->cb = changed_cb;
+       wvolume->user_data = changed_user_data;
+       wvolume->mute_cb = mute_cb;
+       wvolume->mute_user_data = mute_user_data;
+
+       wvolume->mainloop = pa_glib_mainloop_new(main_context);
+       g_assert(wvolume->mainloop != NULL);
+
+       closure = _init_cb_closure_new(wvolume, cb, user_data);
+       _connect(closure);
+}
+
+void mafw_gst_renderer_worker_volume_set(MafwGstRendererWorkerVolume *wvolume,
+                                        gdouble volume, gboolean mute)
+{
+       gboolean signal_volume, signal_mute;
+
+       g_return_if_fail(wvolume != NULL);
+       g_return_if_fail(pa_context_get_state(wvolume->context) ==
+                        PA_CONTEXT_READY);
+
+#ifndef MAFW_GST_RENDERER_ENABLE_MUTE
+       mute = FALSE;
+#endif
+
+       signal_volume = wvolume->current_volume != volume &&
+               wvolume->cb != NULL;
+       signal_mute = wvolume->current_mute != mute && wvolume->mute_cb != NULL;
+
+       wvolume->current_volume = volume;
+       wvolume->current_mute = mute;
+
+       g_debug("volume set: %lf (mute %d)", volume, mute);
+
+       if (signal_volume) {
+               g_debug("signalling volume");
+               wvolume->cb(wvolume, volume, wvolume->user_data);
+       }
+
+       if (signal_mute) {
+               g_debug("signalling mute");
+               wvolume->mute_cb(wvolume, mute, wvolume->mute_user_data);
+       }
+
+       if ((signal_mute || signal_volume) && wvolume->change_request_id == 0) {
+               wvolume->change_request_id =
+                       g_timeout_add(MAFW_GST_RENDERER_WORKER_SET_TIMEOUT,
+                                     _set_timeout, wvolume);
+
+               _set_timeout(wvolume);
+       }
+}
+
+gdouble mafw_gst_renderer_worker_volume_get(
+       MafwGstRendererWorkerVolume *wvolume)
+{
+       g_return_val_if_fail(wvolume != NULL, 0.0);
+
+       g_debug("getting volume; %lf", wvolume->current_volume);
+
+       return wvolume->current_volume;
+}
+
+gboolean mafw_gst_renderer_worker_volume_is_muted(
+       MafwGstRendererWorkerVolume *wvolume)
+{
+       g_return_val_if_fail(wvolume != NULL, FALSE);
+
+       g_debug("getting mute; %d", wvolume->current_mute);
+
+       return wvolume->current_mute;
+}
+
+void mafw_gst_renderer_worker_volume_destroy(
+       MafwGstRendererWorkerVolume *wvolume)
+{
+       g_return_if_fail(wvolume != NULL);
+
+       g_debug("disconnecting");
+
+       pa_ext_stream_restore_set_subscribe_cb(wvolume->context, NULL, NULL);
+       pa_context_set_state_callback(wvolume->context, _state_cb_destroy,
+                                     wvolume);
+       pa_context_disconnect(wvolume->context);
+}
+
+
+
+#else
+
+
+#include "mafw-gst-renderer-worker-volume.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-worker-volume-fake"
+
+struct _MafwGstRendererWorkerVolume {
+       MafwGstRendererWorkerVolumeChangedCb cb;
+       gpointer user_data;
+       MafwGstRendererWorkerVolumeMuteCb mute_cb;
+       gpointer mute_user_data;
+       gdouble current_volume;
+       gboolean current_mute;
+};
+
+typedef struct {
+       MafwGstRendererWorkerVolume *wvolume;
+       MafwGstRendererWorkerVolumeInitCb cb;
+       gpointer user_data;
+} InitCbClosure;
+
+static gboolean _init_cb_closure(gpointer user_data)
+{
+       InitCbClosure *closure = user_data;
+
+       if (closure->cb != NULL) {
+               closure->cb(closure->wvolume, closure->user_data);
+       }
+       g_free(closure);
+
+       return FALSE;
+}
+
+void mafw_gst_renderer_worker_volume_init(GMainContext *main_context,
+                                         MafwGstRendererWorkerVolumeInitCb cb,
+                                         gpointer user_data,
+                                         MafwGstRendererWorkerVolumeChangedCb
+                                         changed_cb,
+                                         gpointer changed_user_data,
+                                         MafwGstRendererWorkerVolumeMuteCb
+                                         mute_cb, gpointer mute_user_data)
+{
+       MafwGstRendererWorkerVolume *wvolume = NULL;
+       InitCbClosure *closure;
+
+       g_return_if_fail(cb != NULL);
+
+       g_debug("initializing volume manager");
+
+       wvolume = g_new0(MafwGstRendererWorkerVolume, 1);
+
+       wvolume->cb = changed_cb;
+       wvolume->user_data = changed_user_data;
+       wvolume->mute_cb = mute_cb;
+       wvolume->mute_user_data = mute_user_data;
+       wvolume->current_volume = 0.485;
+
+       closure = g_new0(InitCbClosure, 1);
+       closure->wvolume = wvolume;
+       closure->cb = cb;
+       closure->user_data = user_data;
+       g_idle_add(_init_cb_closure, closure);
+}
+
+void mafw_gst_renderer_worker_volume_set(MafwGstRendererWorkerVolume *wvolume,
+                                        gdouble volume, gboolean mute)
+{
+       gboolean signal_volume, signal_mute;
+
+       g_return_if_fail(wvolume != NULL);
+
+#ifndef MAFW_GST_RENDERER_ENABLE_MUTE
+       mute = FALSE;
+#endif
+
+       signal_volume = wvolume->current_volume != volume &&
+               wvolume->cb != NULL;
+       signal_mute = wvolume->current_mute != mute && wvolume->mute_cb != NULL;
+
+       wvolume->current_volume = volume;
+       wvolume->current_mute = mute;
+
+       g_debug("volume set: %lf (mute %d)", volume, mute);
+
+       if (signal_volume) {
+               g_debug("signalling volume");
+               wvolume->cb(wvolume, volume, wvolume->user_data);
+       }
+
+       if (signal_mute) {
+               g_debug("signalling mute");
+               wvolume->mute_cb(wvolume, mute, wvolume->mute_user_data);
+       }
+}
+
+gdouble mafw_gst_renderer_worker_volume_get(
+       MafwGstRendererWorkerVolume *wvolume)
+{
+       g_return_val_if_fail(wvolume != NULL, 0.0);
+
+       g_debug("getting volume; %lf", wvolume->current_volume);
+
+       return wvolume->current_volume;
+}
+
+gboolean mafw_gst_renderer_worker_volume_is_muted(
+       MafwGstRendererWorkerVolume *wvolume)
+{
+       g_return_val_if_fail(wvolume != NULL, FALSE);
+
+       g_debug("getting mute; %d", wvolume->current_mute);
+
+       return wvolume->current_mute;
+}
+
+void mafw_gst_renderer_worker_volume_destroy(
+       MafwGstRendererWorkerVolume *wvolume)
+{
+       g_return_if_fail(wvolume != NULL);
+
+       g_free(wvolume);
+}
+
+#endif
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h b/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h
new file mode 100644 (file)
index 0000000..c1e860e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_GST_RENDERER_WORKER_VOLUME_H
+#define MAFW_GST_RENDERER_WORKER_VOLUME_H
+
+#include <glib.h>
+
+typedef struct _MafwGstRendererWorkerVolume MafwGstRendererWorkerVolume;
+
+typedef void(*MafwGstRendererWorkerVolumeChangedCb)(
+       MafwGstRendererWorkerVolume *wvolume, gdouble volume, gpointer data);
+
+typedef void(*MafwGstRendererWorkerVolumeMuteCb)(
+       MafwGstRendererWorkerVolume *wvolume, gboolean mute, gpointer data);
+
+typedef void(*MafwGstRendererWorkerVolumeInitCb)(
+       MafwGstRendererWorkerVolume *volume, gpointer data);
+
+G_BEGIN_DECLS
+
+void mafw_gst_renderer_worker_volume_init(GMainContext *main_context,
+                                         MafwGstRendererWorkerVolumeInitCb cb,
+                                         gpointer user_data,
+                                         MafwGstRendererWorkerVolumeChangedCb
+                                         changed_cb,
+                                         gpointer changed_user_data,
+                                         MafwGstRendererWorkerVolumeMuteCb
+                                         mute_cb, gpointer mute_user_data);
+
+void mafw_gst_renderer_worker_volume_set(MafwGstRendererWorkerVolume *wvolume,
+                                        gdouble volume, gboolean mute);
+
+gdouble mafw_gst_renderer_worker_volume_get(
+       MafwGstRendererWorkerVolume *wvolume);
+gboolean mafw_gst_renderer_worker_volume_is_muted(
+       MafwGstRendererWorkerVolume *wvolume);
+
+void mafw_gst_renderer_worker_volume_destroy(
+       MafwGstRendererWorkerVolume *wvolume);
+
+G_END_DECLS
+#endif
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-worker.c b/libmafw-gst-renderer/mafw-gst-renderer-worker.c
new file mode 100644 (file)
index 0000000..0cd1ec4
--- /dev/null
@@ -0,0 +1,2373 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <gst/interfaces/xoverlay.h>
+#include <gst/pbutils/missing-plugins.h>
+#include <gst/base/gstbasesink.h>
+#include <libmafw/mafw.h>
+
+#ifdef HAVE_GDKPIXBUF
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glib/gstdio.h>
+#include <unistd.h>
+#include "gstscreenshot.h"
+#endif
+
+#include <totem-pl-parser.h>
+#include "mafw-gst-renderer.h"
+#include "mafw-gst-renderer-worker.h"
+#include "mafw-gst-renderer-utils.h"
+#include "blanking.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-worker"
+
+#define MAFW_GST_RENDERER_WORKER_SECONDS_READY 60
+#define MAFW_GST_RENDERER_WORKER_SECONDS_DURATION_AND_SEEKABILITY 4
+
+#define MAFW_GST_MISSING_TYPE_DECODER "decoder"
+#define MAFW_GST_MISSING_TYPE_ENCODER "encoder"
+
+#define MAFW_GST_BUFFER_TIME  600000L
+#define MAFW_GST_LATENCY_TIME (MAFW_GST_BUFFER_TIME / 2)
+
+#define NSECONDS_TO_SECONDS(ns) ((ns)%1000000000 < 500000000?\
+                                 GST_TIME_AS_SECONDS((ns)):\
+                                 GST_TIME_AS_SECONDS((ns))+1)
+
+/* Private variables. */
+/* Global reference to worker instance, needed for Xerror handler */
+static MafwGstRendererWorker *Global_worker = NULL;
+
+/* Forward declarations. */
+static void _do_play(MafwGstRendererWorker *worker);
+static void _do_seek(MafwGstRendererWorker *worker, GstSeekType seek_type,
+                    gint position, GError **error);
+static void _play_pl_next(MafwGstRendererWorker *worker);
+
+static void _emit_metadatas(MafwGstRendererWorker *worker);
+
+static void _current_metadata_add(MafwGstRendererWorker *worker,
+                                 const gchar *key, GType type,
+                                 const gpointer value);
+
+/*
+ * Sends @error to MafwGstRenderer.  Only call this from the glib main thread, or
+ * face the consequences.  @err is free'd.
+ */
+static void _send_error(MafwGstRendererWorker *worker, GError *err)
+{
+       worker->is_error = TRUE;
+        if (worker->notify_error_handler)
+                worker->notify_error_handler(worker, worker->owner, err);
+       g_error_free(err);
+}
+
+/*
+ * Posts an @error on the gst bus.  _async_bus_handler will then pick it up and
+ * forward to MafwGstRenderer.  @err is free'd.
+ */
+static void _post_error(MafwGstRendererWorker *worker, GError *err)
+{
+       gst_bus_post(worker->bus,
+                    gst_message_new_error(GST_OBJECT(worker->pipeline),
+                                          err, NULL));
+       g_error_free(err);
+}
+
+#ifdef HAVE_GDKPIXBUF
+typedef struct {
+       MafwGstRendererWorker *worker;
+       gchar *metadata_key;
+       GdkPixbuf *pixbuf;
+} SaveGraphicData;
+
+static gchar *_init_tmp_file(void)
+{
+       gint fd;
+       gchar *path = NULL;
+
+       fd = g_file_open_tmp("mafw-gst-renderer-XXXXXX.jpeg", &path, NULL);
+       close(fd);
+
+       return path;
+}
+
+static void _init_tmp_files_pool(MafwGstRendererWorker *worker)
+{
+       guint8 i;
+
+       worker->tmp_files_pool_index = 0;
+
+       for (i = 0; i < MAFW_GST_RENDERER_MAX_TMP_FILES; i++) {
+               worker->tmp_files_pool[i] = NULL;
+       }
+}
+
+static void _destroy_tmp_files_pool(MafwGstRendererWorker *worker)
+{
+       guint8 i;
+
+       for (i = 0; (i < MAFW_GST_RENDERER_MAX_TMP_FILES) &&
+                    (worker->tmp_files_pool[i] != NULL); i++) {
+               g_unlink(worker->tmp_files_pool[i]);
+               g_free(worker->tmp_files_pool[i]);
+       }
+}
+
+static const gchar *_get_tmp_file_from_pool(
+                        MafwGstRendererWorker *worker)
+{
+       gchar *path = worker->tmp_files_pool[worker->tmp_files_pool_index];
+
+       if (path == NULL) {
+               path = _init_tmp_file();
+               worker->tmp_files_pool[worker->tmp_files_pool_index] = path;
+       }
+
+       if (++(worker->tmp_files_pool_index) >=
+           MAFW_GST_RENDERER_MAX_TMP_FILES) {
+               worker->tmp_files_pool_index = 0;
+       }
+
+       return path;
+}
+
+static void _destroy_pixbuf (guchar *pixbuf, gpointer data)
+{
+       gst_buffer_unref(GST_BUFFER(data));
+}
+
+static void _emit_gst_buffer_as_graphic_file_cb(GstBuffer *new_buffer,
+                                               gpointer user_data)
+{
+       SaveGraphicData *sgd = user_data;
+       GdkPixbuf *pixbuf = NULL;
+
+       if (new_buffer != NULL) {
+               gint width, height;
+               GstStructure *structure;
+
+               structure =
+                       gst_caps_get_structure(GST_BUFFER_CAPS(new_buffer), 0);
+
+               gst_structure_get_int(structure, "width", &width);
+               gst_structure_get_int(structure, "height", &height);
+
+               pixbuf = gdk_pixbuf_new_from_data(
+                       GST_BUFFER_DATA(new_buffer), GDK_COLORSPACE_RGB,
+                       FALSE, 8, width, height,
+                       GST_ROUND_UP_4(3 * width), _destroy_pixbuf,
+                       new_buffer);
+
+               if (sgd->pixbuf != NULL) {
+                       g_object_unref(sgd->pixbuf);
+                       sgd->pixbuf = NULL;
+               }
+       } else {
+               pixbuf = sgd->pixbuf;
+       }
+
+       if (pixbuf != NULL) {
+               gboolean save_ok;
+               GError *error = NULL;
+               const gchar *filename;
+
+               filename = _get_tmp_file_from_pool(sgd->worker);
+
+               save_ok = gdk_pixbuf_save (pixbuf, filename, "jpeg", &error,
+                                          NULL);
+
+               g_object_unref (pixbuf);
+
+               if (save_ok) {
+                       /* Add the info to the current metadata. */
+                       _current_metadata_add(sgd->worker, sgd->metadata_key,
+                                             G_TYPE_STRING,
+                                             (const gpointer) filename);
+
+                       /* Emit the metadata. */
+                       mafw_renderer_emit_metadata_string(sgd->worker->owner,
+                                                          sgd->metadata_key,
+                                                          (gchar *) filename);
+               } else {
+                       if (error != NULL) {
+                               g_warning ("%s\n", error->message);
+                               g_error_free (error);
+                       } else {
+                               g_critical("Unknown error when saving pixbuf "
+                                          "with GStreamer data");
+                       }
+               }
+       } else {
+               g_warning("Could not create pixbuf from GstBuffer");
+       }
+
+       g_free(sgd->metadata_key);
+       g_free(sgd);
+}
+
+static void _pixbuf_size_prepared_cb (GdkPixbufLoader *loader, 
+                                     gint width, gint height,
+                                     gpointer user_data)
+{
+       /* Be sure the image size is reasonable */
+       if (width > 512 || height > 512) {
+               g_debug ("pixbuf: image is too big: %dx%d", width, height);
+               gdouble ar;
+               ar = (gdouble) width / height;
+               if (width > height) {
+                       width = 512;
+                       height = width / ar;
+               } else {
+                       height = 512;
+                       width = height * ar;
+               }
+               g_debug ("pixbuf: scaled image to %dx%d", width, height);
+               gdk_pixbuf_loader_set_size (loader, width, height);
+       }
+}
+
+static void _emit_gst_buffer_as_graphic_file(MafwGstRendererWorker *worker,
+                                            GstBuffer *buffer,
+                                            const gchar *metadata_key)
+{
+       GdkPixbufLoader *loader;
+       GstStructure *structure;
+       const gchar *mime = NULL;
+       GError *error = NULL;
+
+       g_return_if_fail((buffer != NULL) && GST_IS_BUFFER(buffer));
+
+       structure = gst_caps_get_structure(GST_BUFFER_CAPS(buffer), 0);
+       mime = gst_structure_get_name(structure);
+
+       if (g_str_has_prefix(mime, "video/x-raw")) {
+               gint framerate_d, framerate_n;
+               GstCaps *to_caps;
+               SaveGraphicData *sgd;
+
+               gst_structure_get_fraction (structure, "framerate",
+                                           &framerate_n, &framerate_d);
+
+               to_caps = gst_caps_new_simple ("video/x-raw-rgb",
+                                              "bpp", G_TYPE_INT, 24,
+                                              "depth", G_TYPE_INT, 24,
+                                              "framerate", GST_TYPE_FRACTION,
+                                              framerate_n, framerate_d,
+                                              "pixel-aspect-ratio",
+                                              GST_TYPE_FRACTION, 1, 1,
+                                              "endianness",
+                                              G_TYPE_INT, G_BIG_ENDIAN,
+                                              "red_mask", G_TYPE_INT,
+                                              0xff0000,
+                                              "green_mask",
+                                              G_TYPE_INT, 0x00ff00,
+                                              "blue_mask",
+                                              G_TYPE_INT, 0x0000ff,
+                                              NULL);
+
+               sgd = g_new0(SaveGraphicData, 1);
+               sgd->worker = worker;
+               sgd->metadata_key = g_strdup(metadata_key);
+
+               g_debug("pixbuf: using bvw to convert image format");
+               bvw_frame_conv_convert (buffer, to_caps,
+                                       _emit_gst_buffer_as_graphic_file_cb,
+                                       sgd);
+       } else {
+               GdkPixbuf *pixbuf = NULL;
+               loader = gdk_pixbuf_loader_new_with_mime_type(mime, &error);
+               g_signal_connect (G_OBJECT (loader), "size-prepared", 
+                                (GCallback)_pixbuf_size_prepared_cb, NULL);
+
+               if (loader == NULL) {
+                       g_warning ("%s\n", error->message);
+                       g_error_free (error);
+               } else {
+                       if (!gdk_pixbuf_loader_write (loader,
+                                                     GST_BUFFER_DATA(buffer),
+                                                     GST_BUFFER_SIZE(buffer),
+                                                     &error)) {
+                               g_warning ("%s\n", error->message);
+                               g_error_free (error);
+
+                               gdk_pixbuf_loader_close (loader, NULL);
+                       } else {
+                               pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+
+                               if (!gdk_pixbuf_loader_close (loader, &error)) {
+                                       g_warning ("%s\n", error->message);
+                                       g_error_free (error);
+
+                                       g_object_unref(pixbuf);
+                               } else {
+                                       SaveGraphicData *sgd;
+
+                                       sgd = g_new0(SaveGraphicData, 1);
+
+                                       sgd->worker = worker;
+                                       sgd->metadata_key =
+                                               g_strdup(metadata_key);
+                                       sgd->pixbuf = pixbuf;
+
+                                       _emit_gst_buffer_as_graphic_file_cb(
+                                               NULL, sgd);
+                               }
+                       }
+               }
+       }
+}
+#endif
+
+static gboolean _go_to_gst_ready(gpointer user_data)
+{
+       MafwGstRendererWorker *worker = user_data;
+
+       g_return_val_if_fail(worker->state == GST_STATE_PAUSED ||
+                            worker->prerolling, FALSE);
+
+       worker->seek_position =
+               mafw_gst_renderer_worker_get_position(worker);
+
+       g_debug("going to GST_STATE_READY");
+       gst_element_set_state(worker->pipeline, GST_STATE_READY);
+       worker->in_ready = TRUE;
+        worker->ready_timeout = 0;
+
+       return FALSE;
+}
+
+static void _add_ready_timeout(MafwGstRendererWorker *worker)
+{
+       if (worker->media.seekable) {
+               if (!worker->ready_timeout)
+               {
+                       g_debug("Adding timeout to go to GST_STATE_READY");
+                       worker->ready_timeout =
+                               g_timeout_add_seconds(
+                                       MAFW_GST_RENDERER_WORKER_SECONDS_READY,
+                                       _go_to_gst_ready,
+                                       worker);
+               }
+       } else {
+               g_debug("Not adding timeout to go to GST_STATE_READY as media "
+                       "is not seekable");
+               worker->ready_timeout = 0;
+       }
+}
+
+static void _remove_ready_timeout(MafwGstRendererWorker *worker)
+{
+       g_debug("removing timeout for READY");
+       if (worker->ready_timeout != 0) {
+               g_source_remove(worker->ready_timeout);
+               worker->ready_timeout = 0;
+       }
+       worker->in_ready = FALSE;
+}
+
+static gboolean _emit_video_info(MafwGstRendererWorker *worker)
+{
+       mafw_renderer_emit_metadata_int(worker->owner,
+                                   MAFW_METADATA_KEY_RES_X,
+                                   worker->media.video_width);
+       mafw_renderer_emit_metadata_int(worker->owner,
+                                   MAFW_METADATA_KEY_RES_Y,
+                                   worker->media.video_height);
+       mafw_renderer_emit_metadata_double(worker->owner,
+                                      MAFW_METADATA_KEY_VIDEO_FRAMERATE,
+                                      worker->media.fps);
+       return FALSE;
+}
+
+/*
+ * Checks if the video details are supported.  It also extracts other useful
+ * information (such as PAR and framerate) from the caps, if available.  NOTE:
+ * this will be called from a different thread than glib's mainloop (when
+ * invoked via _stream_info_cb);  don't call MafwGstRenderer directly.
+ *
+ * Returns: TRUE if video details are acceptable.
+ */
+static gboolean _handle_video_info(MafwGstRendererWorker *worker,
+                                  const GstStructure *structure)
+{
+       gint width, height;
+       gdouble fps;
+
+       width = height = 0;
+       gst_structure_get_int(structure, "width", &width);
+       gst_structure_get_int(structure, "height", &height);
+       g_debug("video size: %d x %d", width, height);
+       if (gst_structure_has_field(structure, "pixel-aspect-ratio"))
+       {
+               gst_structure_get_fraction(structure, "pixel-aspect-ratio",
+                                          &worker->media.par_n,
+                                          &worker->media.par_d);
+               g_debug("video PAR: %d:%d", worker->media.par_n,
+                       worker->media.par_d);
+               width = width * worker->media.par_n / worker->media.par_d;
+       }
+
+       fps = 1.0;
+       if (gst_structure_has_field(structure, "framerate"))
+       {
+               gint fps_n, fps_d;
+
+               gst_structure_get_fraction(structure, "framerate",
+                                          &fps_n, &fps_d);
+               if (fps_d > 0)
+                       fps = (gdouble)fps_n / (gdouble)fps_d;
+               g_debug("video fps: %f", fps);
+       }
+
+       worker->media.video_width = width;
+       worker->media.video_height = height;
+       worker->media.fps = fps;
+
+       /* Add the info to the current metadata. */
+       gint *p_width = g_new0(gint, 1);
+       gint *p_height = g_new0(gint, 1);
+       gdouble *p_fps = g_new0(gdouble, 1);
+
+       *p_width = width;* p_height = height; *p_fps = fps;
+
+       _current_metadata_add(worker, MAFW_METADATA_KEY_RES_X, G_TYPE_INT,
+                             (const gpointer) p_width);
+       _current_metadata_add(worker, MAFW_METADATA_KEY_RES_Y, G_TYPE_INT,
+                             (const gpointer) p_height);
+       _current_metadata_add(worker, MAFW_METADATA_KEY_VIDEO_FRAMERATE,
+                             G_TYPE_DOUBLE,
+                             (const gpointer) p_fps);
+
+       g_free(p_width); g_free(p_height); g_free(p_fps);
+
+       /* Emit the metadata.*/
+       g_idle_add((GSourceFunc)_emit_video_info, worker);
+
+       return TRUE;
+}
+
+static void _parse_stream_info_item(MafwGstRendererWorker *worker, GObject *obj)
+{
+       GParamSpec *pspec;
+       GEnumValue *val;
+       gint type;
+
+       g_object_get(obj, "type", &type, NULL);
+       pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), "type");
+       val = g_enum_get_value(G_PARAM_SPEC_ENUM(pspec)->enum_class, type);
+       if (!val)
+               return;
+       if (!g_ascii_strcasecmp(val->value_nick, "video") ||
+           !g_ascii_strcasecmp(val->value_name, "video"))
+       {
+               GstCaps *vcaps;
+               GstObject *object;
+
+               object = NULL;
+               g_object_get(obj, "object", &object, NULL);
+               vcaps = NULL;
+               if (object) {
+                       vcaps = gst_pad_get_caps(GST_PAD_CAST(object));
+               } else {
+                       g_object_get(obj, "caps", &vcaps, NULL);
+                       gst_caps_ref(vcaps);
+               }
+               if (vcaps) {
+                       if (gst_caps_is_fixed(vcaps))
+                       {
+                               _handle_video_info(
+                                       worker,
+                                       gst_caps_get_structure(vcaps, 0));
+                       }
+                       gst_caps_unref(vcaps);
+               }
+       }
+}
+
+/* It always returns FALSE, because it is used as an idle callback as well. */
+static gboolean _parse_stream_info(MafwGstRendererWorker *worker)
+{
+       GList *stream_info, *s;
+
+       stream_info = NULL;
+       if (g_object_class_find_property(G_OBJECT_GET_CLASS(worker->pipeline),
+                                        "stream-info"))
+       {
+               g_object_get(worker->pipeline,
+                            "stream-info", &stream_info, NULL);
+       }
+       for (s = stream_info; s; s = g_list_next(s))
+               _parse_stream_info_item(worker, G_OBJECT(s->data));
+       return FALSE;
+}
+
+static void mafw_gst_renderer_worker_apply_xid(MafwGstRendererWorker *worker)
+{
+       /* Set sink to render on the provided XID if we have do have
+          a XID a valid video sink and we are rendeing video content */
+       if (worker->xid && 
+           worker->vsink && 
+           worker->media.has_visual_content)
+       {
+               g_debug ("Setting overlay, window id: %x", 
+                        (gint) worker->xid);
+               gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(worker->vsink), 
+                                            worker->xid);
+               /* Ask the gst to redraw the frame if we are paused */
+               /* TODO: in MTG this works only in non-fs -> fs way. */
+               if (worker->state == GST_STATE_PAUSED)
+               {
+                       gst_x_overlay_expose(GST_X_OVERLAY(worker->vsink));
+               }
+       } else {
+               g_debug("Not setting overlay for window id: %x", 
+                       (gint) worker->xid);
+       }
+}
+
+/*
+ * GstBus synchronous message handler.  NOTE that this handler is NOT invoked
+ * from the glib thread, so be careful what you do here.
+ */
+static GstBusSyncReply _sync_bus_handler(GstBus *bus, GstMessage *msg,
+                                        MafwGstRendererWorker *worker)
+{
+       if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
+           gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
+       {
+               g_debug("got prepare-xwindow-id");
+               worker->media.has_visual_content = TRUE;
+               /* The user has to preset the XID, we don't create windows by
+                * ourselves. */
+               if (!worker->xid) {
+                       /* We must post an error message to the bus that will
+                        * be picked up by _async_bus_handler.  Calling the
+                        * notification function directly from here (different
+                        * thread) is not healthy. */
+                       g_warning("No video window set!");
+                       _post_error(worker,
+                                   g_error_new_literal(
+                                           MAFW_RENDERER_ERROR,
+                                           MAFW_RENDERER_ERROR_PLAYBACK,
+                                           "No video window XID set"));
+                       return GST_BUS_DROP;
+               } else {
+                       g_debug ("Video window to use is: %x", 
+                                (gint) worker->xid);
+               }
+
+               /* Instruct vsink to use the client-provided window */
+               mafw_gst_renderer_worker_apply_xid(worker);
+
+               /* Handle colorkey and autopaint */
+               mafw_gst_renderer_worker_set_autopaint(
+                       worker,
+                       worker->autopaint);
+               g_object_get(worker->vsink,
+                            "colorkey", &worker->colorkey, NULL);
+               /* Defer the signal emission to the thread running the
+                * mainloop. */
+               if (worker->colorkey != -1) {
+                       gst_bus_post(worker->bus,
+                                    gst_message_new_application(
+                                            GST_OBJECT(worker->vsink),
+                                            gst_structure_empty_new("ckey")));
+               }
+               return GST_BUS_DROP;
+       }
+       return GST_BUS_PASS;
+}
+
+static void _free_taglist_item(GstMessage *msg, gpointer data)
+{
+       gst_message_unref(msg);
+}
+
+static void _free_taglist(MafwGstRendererWorker *worker)
+{
+       if (worker->tag_list != NULL)
+       {
+               g_ptr_array_foreach(worker->tag_list, (GFunc)_free_taglist_item,
+                                   NULL);
+               g_ptr_array_free(worker->tag_list, TRUE);
+               worker->tag_list = NULL;
+       }
+}
+
+static gboolean _seconds_duration_equal(gint64 duration1, gint64 duration2)
+{
+       gint64 duration1_seconds, duration2_seconds;
+
+       duration1_seconds = NSECONDS_TO_SECONDS(duration1);
+       duration2_seconds = NSECONDS_TO_SECONDS(duration2);
+
+       return duration1_seconds == duration2_seconds;
+}
+
+static void _check_duration(MafwGstRendererWorker *worker, gint64 value)
+{
+       MafwGstRenderer *renderer = worker->owner;
+       gboolean right_query = TRUE;
+
+       if (value == -1) {
+               GstFormat format = GST_FORMAT_TIME;
+               right_query =
+                       gst_element_query_duration(worker->pipeline, &format,
+                                                  &value);
+       }
+
+       if (right_query && value > 0) {
+               gint duration_seconds = NSECONDS_TO_SECONDS(value);
+
+               if (!_seconds_duration_equal(worker->media.length_nanos,
+                                            value)) {
+                       gint64 *duration = g_new0(gint64, 1);
+                       *duration = duration_seconds;
+
+                       /* Add the duration to the current metadata. */
+                       _current_metadata_add(worker,
+                                             MAFW_METADATA_KEY_DURATION,
+                                             G_TYPE_INT64,
+                                             (const gpointer) duration);
+
+                       /* Emit the duration. */
+                       mafw_renderer_emit_metadata_int64(
+                               worker->owner, MAFW_METADATA_KEY_DURATION,
+                               *duration);
+                       g_free(duration);
+               }
+
+               /* We compare this duration we just got with the
+                * source one and update it in the source if needed */
+               if (duration_seconds != renderer->media->duration) {
+                       mafw_gst_renderer_update_source_duration(
+                               renderer,
+                               duration_seconds);
+               }
+       }
+
+       worker->media.length_nanos = value;
+       g_debug("media duration: %lld", worker->media.length_nanos);
+}
+
+static void _check_seekability(MafwGstRendererWorker *worker)
+{
+       MafwGstRenderer *renderer = worker->owner;
+       SeekabilityType seekable = SEEKABILITY_NO_SEEKABLE;
+
+       if (worker->media.length_nanos != -1)
+       {
+               g_debug("source seekability %d", renderer->media->seekability);
+
+               if (renderer->media->seekability != SEEKABILITY_NO_SEEKABLE) {
+                       g_debug("Quering GStreamer for seekability");
+                       GstQuery *seek_query;
+                       GstFormat format = GST_FORMAT_TIME;
+                       /* Query the seekability of the stream */
+                       seek_query = gst_query_new_seeking(format);
+                       if (gst_element_query(worker->pipeline, seek_query)) {
+                               gboolean renderer_seekable = FALSE;
+                               gst_query_parse_seeking(seek_query, NULL,
+                                                       &renderer_seekable,
+                                                       NULL, NULL);
+                               g_debug("GStreamer seekability %d",
+                                       renderer_seekable);
+                               seekable = renderer_seekable ?
+                                       SEEKABILITY_SEEKABLE :
+                                       SEEKABILITY_NO_SEEKABLE;
+                       }
+                       gst_query_unref(seek_query);
+               }
+       }
+
+       if (worker->media.seekable != seekable) {
+               gboolean *is_seekable = g_new0(gboolean, 1);
+               *is_seekable = (seekable == SEEKABILITY_SEEKABLE) ? TRUE : FALSE;
+
+               /* Add the seekability to the current metadata. */
+               _current_metadata_add(worker, MAFW_METADATA_KEY_IS_SEEKABLE,
+                       G_TYPE_BOOLEAN, (const gpointer) is_seekable);
+
+               /* Emit. */
+               mafw_renderer_emit_metadata_boolean(
+                       worker->owner, MAFW_METADATA_KEY_IS_SEEKABLE,
+                       *is_seekable);
+
+               g_free(is_seekable);
+       }
+
+       g_debug("media seekable: %d", seekable);
+       worker->media.seekable = seekable;
+}
+
+static gboolean _query_duration_and_seekability_timeout(gpointer data)
+{
+       MafwGstRendererWorker *worker = data;
+
+       _check_duration(worker, -1);
+       _check_seekability(worker);
+
+       worker->duration_seek_timeout = 0;
+
+       return FALSE;
+}
+
+/*
+ * Called when the pipeline transitions into PAUSED state.  It extracts more
+ * information from Gst.
+ */
+static void _finalize_startup(MafwGstRendererWorker *worker)
+{
+       /* Check video caps */
+       if (worker->media.has_visual_content) {
+               GstPad *pad = GST_BASE_SINK_PAD(worker->vsink);
+               GstCaps *caps = GST_PAD_CAPS(pad);
+               if (caps && gst_caps_is_fixed(caps)) {
+                       GstStructure *structure;
+                       structure = gst_caps_get_structure(caps, 0);
+                       if (!_handle_video_info(worker, structure))
+                               return;
+               }
+       }
+
+       /* Something might have gone wrong at this point already. */
+       if (worker->is_error) {
+               g_debug("Error occured during preroll");
+               return;
+       }
+
+       /* Streaminfo might reveal the media to be unsupported.  Therefore we
+        * need to check the error again. */
+       _parse_stream_info(worker);
+       if (worker->is_error) {
+               g_debug("Error occured. Leaving");
+               return;
+       }
+
+       /* Check duration and seekability */
+       if (worker->duration_seek_timeout != 0) {
+               g_source_remove(worker->duration_seek_timeout);
+               worker->duration_seek_timeout = 0;
+       }
+       _check_duration(worker, -1);
+       _check_seekability(worker);
+}
+
+static void _add_duration_seek_query_timeout(MafwGstRendererWorker *worker)
+{
+       if (worker->duration_seek_timeout != 0) {
+               g_source_remove(worker->duration_seek_timeout);
+       }
+       worker->duration_seek_timeout = g_timeout_add_seconds(
+               MAFW_GST_RENDERER_WORKER_SECONDS_DURATION_AND_SEEKABILITY,
+               _query_duration_and_seekability_timeout,
+               worker);
+}
+
+static void _do_pause_postprocessing(MafwGstRendererWorker *worker)
+{
+       if (worker->notify_pause_handler) {
+               worker->notify_pause_handler(worker, worker->owner);
+       }
+
+#ifdef HAVE_GDKPIXBUF
+       if (worker->media.has_visual_content &&
+           worker->current_frame_on_pause) {
+               GstBuffer *buffer = NULL;
+
+               g_object_get(worker->pipeline, "frame", &buffer, NULL);
+
+               if (buffer != NULL) {
+                       _emit_gst_buffer_as_graphic_file(
+                               worker, buffer,
+                               MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI);
+               }
+       }
+#endif
+
+       _add_ready_timeout(worker);
+}
+
+static void _report_playing_state(MafwGstRendererWorker * worker)
+{
+       if (worker->report_statechanges) {
+               switch (worker->mode) {
+               case WORKER_MODE_SINGLE_PLAY:
+                       /* Notify play if we are playing in
+                        * single mode */
+                       if (worker->notify_play_handler)
+                               worker->notify_play_handler(
+                                       worker,
+                                       worker->owner);
+                       break;
+               case WORKER_MODE_PLAYLIST:
+               case WORKER_MODE_REDUNDANT:
+                       /* Only notify play when the "playlist"
+                          playback starts, don't notify play for each
+                          individual element of the playlist. */
+                       if (worker->pl.notify_play_pending) {
+                               if (worker->notify_play_handler)
+                                       worker->notify_play_handler(
+                                               worker,
+                                               worker->owner);
+                               worker->pl.notify_play_pending = FALSE;
+                       }
+                       break;
+               default: break;
+               }
+       }
+}
+
+static void _handle_state_changed(GstMessage *msg, MafwGstRendererWorker *worker)
+{
+       GstState newstate, oldstate;
+       GstStateChange statetrans;
+       MafwGstRenderer *renderer = (MafwGstRenderer*)worker->owner;
+
+       gst_message_parse_state_changed(msg, &oldstate, &newstate, NULL);
+       statetrans = GST_STATE_TRANSITION(oldstate, newstate);
+       g_debug ("State changed: %d: %d -> %d", worker->state, oldstate, newstate);
+
+       /* If the state is the same we do nothing, otherwise, we keep
+        * it */
+       if (worker->state == newstate) {
+               return;
+       } else {
+               worker->state = newstate;
+       }
+
+        if (statetrans == GST_STATE_CHANGE_READY_TO_PAUSED &&
+            worker->in_ready) {
+                /* Woken up from READY, resume stream position and playback */
+                g_debug("State changed to pause after ready");
+                if (worker->seek_position > 0) {
+                        _check_seekability(worker);
+                        if (worker->media.seekable) {
+                                g_debug("performing a seek");
+                                _do_seek(worker, GST_SEEK_TYPE_SET,
+                                         worker->seek_position, NULL);
+                        } else {
+                                g_critical("media is not seekable (and should)");
+                        }
+                }
+
+                /* If playing a stream wait for buffering to finish before
+                   starting to play */
+                if (!worker->is_stream || worker->is_live) {
+                        _do_play(worker);
+                }
+                return;
+        }
+
+       /* While buffering, we have to wait in PAUSED 
+          until we reach 100% before doing anything */
+       if (worker->buffering) {
+               if (statetrans == GST_STATE_CHANGE_PAUSED_TO_PLAYING) {
+                       /* Mmm... probably the client issued a seek on the
+                        * stream and then a play/resume command right away,
+                        * so the stream got into PLAYING state while
+                        * buffering. When the next buffering signal arrives,
+                        * the stream will be PAUSED silently and resumed when
+                        * buffering is done (silently too), so let's signal
+                        * the state change to PLAYING here. */
+                       _report_playing_state(worker);                  
+               }
+               return;
+       }
+
+       switch (statetrans) {
+       case GST_STATE_CHANGE_READY_TO_PAUSED:
+               if (worker->prerolling && worker->report_statechanges) {
+                       /* PAUSED after pipeline has been
+                        * constructed. We check caps, seek and
+                        * duration and if staying in pause is needed,
+                        * we perform operations for pausing, such as
+                        * current frame on pause and signalling state
+                        * change and adding the timeout to go to ready */
+                       g_debug ("Prerolling done, finalizaing startup");
+                       _finalize_startup(worker);
+                       _do_play(worker);
+                       renderer->play_failed_count = 0;
+
+                       if (worker->stay_paused) {
+                               _do_pause_postprocessing(worker);
+                       }
+                       worker->prerolling = FALSE;
+               }
+               break;
+       case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+               /* When pausing we do the stuff, like signalling
+                * state, current frame on pause and timeout to go to
+                * ready */
+               if (worker->report_statechanges) {
+                       _do_pause_postprocessing(worker);
+               }
+               break;
+       case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+               /* if seek was called, at this point it is really ended */
+               worker->seek_position = -1;
+                worker->eos = FALSE;
+
+               /* Signal state change if needed */
+               _report_playing_state(worker);
+
+               /* Prevent blanking if we are playing video */
+                if (worker->media.has_visual_content) {
+                        blanking_prohibit();
+                }
+               /* Remove the ready timeout if we are playing [again] */
+               _remove_ready_timeout(worker);
+                /* If mode is redundant we are trying to play one of several
+                 * candidates, so when we get a successful playback, we notify
+                 * the real URI that we are playing */
+                if (worker->mode == WORKER_MODE_REDUNDANT) {
+                        mafw_renderer_emit_metadata_string(
+                                worker->owner,
+                                MAFW_METADATA_KEY_URI,
+                                worker->media.location);
+                }
+
+               /* Emit metadata. We wait until we reach the playing
+                  state because this speeds up playback start time */
+               _emit_metadatas(worker);
+               /* Query duration and seekability. Useful for vbr
+                * clips or streams. */
+               _add_duration_seek_query_timeout(worker);
+               break;
+       case GST_STATE_CHANGE_PAUSED_TO_READY:
+               /* If we went to READY, we free the taglist and
+                * deassign the timout it */
+               if (worker->in_ready) {
+                       g_debug("changed to GST_STATE_READY");
+                       _free_taglist(worker);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void _handle_duration(MafwGstRendererWorker *worker, GstMessage *msg)
+{
+       GstFormat fmt;
+       gint64 duration;
+
+       gst_message_parse_duration(msg, &fmt, &duration);
+
+       if (worker->duration_seek_timeout != 0) {
+               g_source_remove(worker->duration_seek_timeout);
+               worker->duration_seek_timeout = 0;
+       }
+
+       _check_duration(worker,
+                       duration != GST_CLOCK_TIME_NONE ? duration : -1);
+       _check_seekability(worker);
+}
+
+#ifdef HAVE_GDKPIXBUF
+static void _emit_renderer_art(MafwGstRendererWorker *worker,
+                              const GstTagList *list)
+{
+       GstBuffer *buffer = NULL;
+       const GValue *value = NULL;
+
+       g_return_if_fail(gst_tag_list_get_tag_size(list, GST_TAG_IMAGE) > 0);
+
+       value = gst_tag_list_get_value_index(list, GST_TAG_IMAGE, 0);
+
+       g_return_if_fail((value != NULL) && G_VALUE_HOLDS(value, GST_TYPE_BUFFER));
+
+       buffer = g_value_peek_pointer(value);
+
+       g_return_if_fail((buffer != NULL) && GST_IS_BUFFER(buffer));
+
+       _emit_gst_buffer_as_graphic_file(worker, buffer,
+                                        MAFW_METADATA_KEY_RENDERER_ART_URI);
+}
+#endif
+
+
+
+static void _current_metadata_add(MafwGstRendererWorker *worker,
+                                 const gchar *key, GType type,
+                                 const gpointer value)
+{
+       g_return_if_fail(value != NULL);
+
+       if (!worker->current_metadata)
+               worker->current_metadata = mafw_metadata_new();
+
+       mafw_metadata_add_something(worker->current_metadata, key, type, 1, value);
+}
+
+static GHashTable* _build_tagmap(void)
+{
+       GHashTable *hash_table = NULL;
+
+       hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+                                          g_free);
+
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_TITLE),
+                           g_strdup(MAFW_METADATA_KEY_TITLE));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_ARTIST),
+                           g_strdup(MAFW_METADATA_KEY_ARTIST));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_AUDIO_CODEC),
+                           g_strdup(MAFW_METADATA_KEY_AUDIO_CODEC));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_VIDEO_CODEC),
+                           g_strdup(MAFW_METADATA_KEY_VIDEO_CODEC));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_BITRATE),
+                           g_strdup(MAFW_METADATA_KEY_BITRATE));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_LANGUAGE_CODE),
+                           g_strdup(MAFW_METADATA_KEY_ENCODING));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_ALBUM),
+                           g_strdup(MAFW_METADATA_KEY_ALBUM));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_GENRE),
+                           g_strdup(MAFW_METADATA_KEY_GENRE));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_TRACK_NUMBER),
+                           g_strdup(MAFW_METADATA_KEY_TRACK));
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_ORGANIZATION),
+                           g_strdup(MAFW_METADATA_KEY_ORGANIZATION));
+#ifdef HAVE_GDKPIXBUF
+       g_hash_table_insert(hash_table, g_strdup(GST_TAG_IMAGE),
+                           g_strdup(MAFW_METADATA_KEY_RENDERER_ART_URI));
+#endif
+
+       return hash_table;
+}
+
+/*
+ * Emits metadata-changed signals for gst tags.
+ */
+static void _emit_tag(const GstTagList *list, const gchar *tag,
+                     MafwGstRendererWorker *worker)
+{
+       /* Mapping between Gst <-> MAFW metadata tags
+        * NOTE: This assumes that GTypes matches between GST and MAFW. */
+       static GHashTable *tagmap = NULL;
+       gint i, count;
+       const gchar *mafwtag;
+       GType type;
+       GValueArray *values;
+
+       if (tagmap == NULL) {
+               tagmap = _build_tagmap();
+       }
+
+       g_debug("tag: '%s' (type: %s)", tag,
+               g_type_name(gst_tag_get_type(tag)));
+       /* Is there a mapping for this tag? */
+       mafwtag = g_hash_table_lookup(tagmap, tag);
+       if (!mafwtag)
+               return;
+
+#ifdef HAVE_GDKPIXBUF
+       if (strcmp (mafwtag, MAFW_METADATA_KEY_RENDERER_ART_URI) == 0) {
+               _emit_renderer_art(worker, list);
+               return;
+       }
+#endif
+
+       /* Build a value array of this tag.  We need to make sure that strings
+        * are UTF-8.  GstTagList API says that the value is always UTF8, but it
+        * looks like the ID3 demuxer still might sometimes produce non-UTF-8
+        * strings. */
+       count = gst_tag_list_get_tag_size(list, tag);
+       type = gst_tag_get_type(tag);
+       values = g_value_array_new(count);
+       for (i = 0; i < count; ++i) {
+               GValue *v = (GValue *)
+                       gst_tag_list_get_value_index(list, tag, i);
+               if (type == G_TYPE_STRING) {
+                       gchar *orig, *utf8;
+
+                       gst_tag_list_get_string_index(list, tag, i, &orig);
+                       if (convert_utf8(orig, &utf8)) {
+                               GValue utf8gval = {0};
+
+                               g_value_init(&utf8gval, G_TYPE_STRING);
+                               g_value_take_string(&utf8gval, utf8);
+                               _current_metadata_add(worker, mafwtag, G_TYPE_VALUE,
+                                       (const gpointer) &utf8gval);
+                               g_value_array_append(values, &utf8gval);
+                               g_value_unset(&utf8gval);
+                       }
+                       g_free(orig);
+               } else if (type == G_TYPE_UINT) {
+                       GValue intgval = {0};
+
+                       g_value_init(&intgval, G_TYPE_INT);
+                       g_value_transform(v, &intgval);
+                       _current_metadata_add(worker, mafwtag, G_TYPE_VALUE,
+                                       (const gpointer) &intgval);
+                       g_value_array_append(values, &intgval);
+                       g_value_unset(&intgval);
+               } else {
+                       _current_metadata_add(worker, mafwtag, G_TYPE_VALUE,
+                                       (const gpointer) v);
+                       g_value_array_append(values, v);
+               }
+       }
+
+       /* Emit the metadata. */
+       g_signal_emit_by_name(worker->owner, "metadata-changed", mafwtag,
+                             values);
+
+       g_value_array_free(values);
+}
+
+/**
+ * Collect tag-messages, parse it later, when playing is ongoing
+ */
+static void _handle_tag(MafwGstRendererWorker *worker, GstMessage *msg)
+{
+       /* Do not emit metadata until we get to PLAYING state to speed up
+          playback start */
+       if (worker->tag_list == NULL)
+               worker->tag_list = g_ptr_array_new();
+       g_ptr_array_add(worker->tag_list, gst_message_ref(msg));
+
+       /* Some tags come in playing state, so in this case we have
+          to emit them right away (example: radio stations) */
+       if (worker->state == GST_STATE_PLAYING) {
+               _emit_metadatas(worker);
+       }
+}
+
+/**
+ * Parses the list of tag-messages
+ */
+static void _parse_tagmsg(GstMessage *msg, MafwGstRendererWorker *worker)
+{
+       GstTagList *new_tags;
+
+       gst_message_parse_tag(msg, &new_tags);
+       gst_tag_list_foreach(new_tags, (gpointer)_emit_tag, worker);
+       gst_tag_list_free(new_tags);
+       gst_message_unref(msg);
+}
+
+/**
+ * Parses the collected tag messages, and emits the metadatas
+ */
+static void _emit_metadatas(MafwGstRendererWorker *worker)
+{
+       if (worker->tag_list != NULL)
+       {
+               g_ptr_array_foreach(worker->tag_list, (GFunc)_parse_tagmsg,
+                                   worker);
+               g_ptr_array_free(worker->tag_list, TRUE);
+               worker->tag_list = NULL;
+       }
+}
+
+static void _reset_volume_and_mute_to_pipeline(MafwGstRendererWorker *worker)
+{
+#ifdef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME
+       g_debug("resetting volume and mute to pipeline");
+
+       if (worker->pipeline != NULL) {
+               g_object_set(
+                       G_OBJECT(worker->pipeline), "volume",
+                       mafw_gst_renderer_worker_volume_get(worker->wvolume),
+                       "mute",
+                       mafw_gst_renderer_worker_volume_is_muted(worker->wvolume),
+                       NULL);
+       }
+#endif
+}
+
+static void _handle_buffering(MafwGstRendererWorker *worker, GstMessage *msg)
+{
+       gint percent;
+       MafwGstRenderer *renderer = (MafwGstRenderer*)worker->owner;
+
+       gst_message_parse_buffering(msg, &percent);
+       g_debug("buffering: %d", percent);
+
+        /* No state management needed for live pipelines */
+        if (!worker->is_live) {
+               worker->buffering = TRUE;
+               if (worker->state == GST_STATE_PLAYING) {
+                       g_debug("setting pipeline to PAUSED not to wolf the "
+                               "buffer down");
+                       worker->report_statechanges = FALSE;
+                       /* We can't call _pause() here, since it sets
+                        * the "report_statechanges" to TRUE.  We don't
+                        * want that, application doesn't need to know
+                        * that internally the state changed to
+                        * PAUSED. */
+                       if (gst_element_set_state(worker->pipeline,
+                                             GST_STATE_PAUSED) ==
+                                       GST_STATE_CHANGE_ASYNC)
+                       {
+                               /* XXX this blocks at most 2 seconds. */
+                               gst_element_get_state(worker->pipeline, NULL,
+                                             NULL,
+                                             2 * GST_SECOND);
+                       }
+               }
+
+                if (percent >= 100) {
+                        /* On buffering we go to PAUSED, so here we move back to
+                           PLAYING */
+                        worker->buffering = FALSE;
+                        if (worker->state == GST_STATE_PAUSED) {
+                                /* If buffering more than once, do this only the
+                                   first time we are done with buffering */
+                                if (worker->prerolling) {
+                                       g_debug("buffering concluded during "
+                                               "prerolling");
+                                       _finalize_startup(worker);
+                                       _do_play(worker);
+                                       renderer->play_failed_count = 0;
+                                       /* Send the paused notification */
+                                       if (worker->stay_paused &&
+                                           worker->notify_pause_handler) {
+                                               worker->notify_pause_handler(
+                                                       worker,
+                                                       worker->owner);
+                                       }
+                                       worker->prerolling = FALSE;
+                                } else if (worker->in_ready) {
+                                       /* If we had been woken up from READY
+                                          and we have finish our buffering,
+                                          check if we have to play or stay
+                                          paused and if we have to play,
+                                          signal the state change. */
+                                        g_debug("buffering concluded, "
+                                                "continuing playing");
+                                        _do_play(worker);
+                                } else if (!worker->stay_paused) {
+                                       /* This means, that we were playing but
+                                          ran out of buffer, so we silently
+                                          paused waited for buffering to
+                                          finish and now we continue silently
+                                          (silently meaning we do not expose
+                                          state changes) */
+                                       g_debug("buffering concluded, setting "
+                                               "pipeline to PLAYING again");
+                                       _reset_volume_and_mute_to_pipeline(
+                                               worker);
+                                       if (gst_element_set_state(
+                                               worker->pipeline,
+                                               GST_STATE_PLAYING) ==
+                                                       GST_STATE_CHANGE_ASYNC)
+                                       {
+                                               /* XXX this blocks at most 2 seconds. */
+                                               gst_element_get_state(
+                                                       worker->pipeline, NULL, NULL,
+                                                       2 * GST_SECOND);
+                                       }
+                               }
+                        } else if (worker->state == GST_STATE_PLAYING) {
+                               g_debug("buffering concluded, signalling "
+                                       "state change");
+                               /* In this case we got a PLAY command while 
+                                  buffering, likely because it was issued
+                                  before we got the first buffering signal.
+                                  The UI should not do this, but if it does,
+                                  we have to signal that we have executed
+                                  the state change, since in 
+                                  _handle_state_changed we do not do anything 
+                                  if we are buffering  */
+                               if (worker->report_statechanges &&
+                                   worker->notify_play_handler) {
+                                       worker->notify_play_handler(
+                                                               worker,
+                                                       worker->owner);
+                               }
+                                _add_duration_seek_query_timeout(worker);
+                        }
+                }
+        }
+
+       /* Send buffer percentage */
+        if (worker->notify_buffer_status_handler)
+                worker->notify_buffer_status_handler(worker, worker->owner,
+                                                    percent);
+}
+
+static void _handle_element_msg(MafwGstRendererWorker *worker, GstMessage *msg)
+{
+       /* Only HelixBin sends "resolution" messages. */
+       if (gst_structure_has_name(msg->structure, "resolution") &&
+           _handle_video_info(worker, msg->structure))
+       {
+               worker->media.has_visual_content = TRUE;
+       }
+}
+
+static void _reset_pl_info(MafwGstRendererWorker *worker)
+{
+       if (worker->pl.items) {
+               g_slist_foreach(worker->pl.items, (GFunc) g_free, NULL);
+               g_slist_free(worker->pl.items);
+               worker->pl.items = NULL;
+       }
+
+       worker->pl.current = 0;
+       worker->pl.notify_play_pending = TRUE;
+}
+
+static GError * _get_specific_missing_plugin_error(GstMessage *msg)
+{
+       const GstStructure *gst_struct;
+       const gchar *type;
+
+       GError *error;
+       gchar *desc;
+
+       desc = gst_missing_plugin_message_get_description(msg);
+
+       gst_struct = gst_message_get_structure(msg);
+       type = gst_structure_get_string(gst_struct, "type");
+
+       if ((type) && ((strcmp(type, MAFW_GST_MISSING_TYPE_DECODER) == 0) ||
+                      (strcmp(type, MAFW_GST_MISSING_TYPE_ENCODER) == 0))) {
+
+               /* Missing codec error. */
+               const GValue *val;
+               const GstCaps *caps;
+               GstStructure *caps_struct;
+               const gchar *mime;
+
+               val = gst_structure_get_value(gst_struct, "detail");
+               caps = gst_value_get_caps(val);
+               caps_struct = gst_caps_get_structure(caps, 0);
+               mime = gst_structure_get_name(caps_struct);
+
+               if (g_strrstr(mime, "video")) {
+                       error = g_error_new_literal(
+                               MAFW_RENDERER_ERROR,
+                               MAFW_RENDERER_ERROR_VIDEO_CODEC_NOT_FOUND,
+                               desc);
+               } else if (g_strrstr(mime, "audio")) {
+                       error = g_error_new_literal(
+                               MAFW_RENDERER_ERROR,
+                               MAFW_RENDERER_ERROR_AUDIO_CODEC_NOT_FOUND,
+                               desc);
+               } else {
+                       error = g_error_new_literal(
+                               MAFW_RENDERER_ERROR,
+                               MAFW_RENDERER_ERROR_CODEC_NOT_FOUND,
+                               desc);
+               }
+       } else {
+               /* Unsupported type error. */
+               error = g_error_new(
+                       MAFW_RENDERER_ERROR,
+                       MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE,
+                       "missing plugin: %s", desc);
+       }
+
+       g_free(desc);
+
+       return error;
+}
+
+/*
+ * Asynchronous message handler.  It gets removed from if it returns FALSE.
+ */
+static gboolean _async_bus_handler(GstBus *bus, GstMessage *msg,
+                                  MafwGstRendererWorker *worker)
+{
+       /* No need to handle message if error has already occured. */
+       if (worker->is_error)
+               return TRUE;
+
+       /* Handle missing-plugin (element) messages separately, relaying more
+        * details. */
+       if (gst_is_missing_plugin_message(msg)) {
+               GError *err = _get_specific_missing_plugin_error(msg);
+               /* FIXME?: for some reason, calling the error handler directly
+                * (_send_error) causes problems.  On the other hand, turning
+                * the error into a new GstMessage and letting the next
+                * iteration handle it seems to work. */
+               _post_error(worker, err);
+               return TRUE;
+       }
+
+       switch (GST_MESSAGE_TYPE(msg)) {
+       case GST_MESSAGE_ERROR:
+               if (!worker->is_error) {
+                       gchar *debug;
+                       GError *err;
+
+                       debug = NULL;
+                       gst_message_parse_error(msg, &err, &debug);
+                       g_debug("gst error: domain = %d, code = %d, "
+                               "message = '%s', debug = '%s'",
+                               err->domain, err->code, err->message, debug);
+                       if (debug)
+                               g_free(debug);
+
+                       /* If we are in playlist/radio mode, we silently
+                          ignore the error and continue with the next
+                          item until we end the playlist. If no
+                          playable elements we raise the error and
+                          after finishing we go to normal mode */
+
+                       if (worker->mode == WORKER_MODE_PLAYLIST ||
+                            worker->mode == WORKER_MODE_REDUNDANT) {
+                               if (worker->pl.current <
+                                   (g_slist_length(worker->pl.items) - 1)) {
+                                       /* If the error is "no space left"
+                                          notify, otherwise try to play the
+                                          next item */
+                                       if (err->code ==
+                                           GST_RESOURCE_ERROR_NO_SPACE_LEFT) {
+                                               _send_error(worker, err);
+
+                                       } else {
+                                               _play_pl_next(worker);
+                                       }
+                               } else {
+                                        /* Playlist EOS. We cannot try another
+                                         * URI, so we have to go back to normal
+                                         * mode and signal the error (done
+                                         * below) */
+                                       worker->mode = WORKER_MODE_SINGLE_PLAY;
+                                       _reset_pl_info(worker);
+                               }
+                       }
+
+                       if (worker->mode == WORKER_MODE_SINGLE_PLAY) {
+                               _send_error(worker, err);
+                       }
+               }
+               break;
+       case GST_MESSAGE_EOS:
+               if (!worker->is_error) {
+                       worker->eos = TRUE;
+
+                       if (worker->mode == WORKER_MODE_PLAYLIST) {
+                               if (worker->pl.current <
+                                   (g_slist_length(worker->pl.items) - 1)) {
+                                       /* If the playlist EOS is not reached
+                                          continue playing */
+                                       _play_pl_next(worker);
+                               } else {
+                                       /* Playlist EOS, go back to normal
+                                          mode */
+                                       worker->mode = WORKER_MODE_SINGLE_PLAY;
+                                       _reset_pl_info(worker);
+                               }
+                       }
+
+                       if (worker->mode == WORKER_MODE_SINGLE_PLAY ||
+                            worker->mode == WORKER_MODE_REDUNDANT) {
+                               if (worker->notify_eos_handler)
+                                       worker->notify_eos_handler(
+                                               worker,
+                                               worker->owner);
+
+                               /* We can remove the message handlers now, we
+                                  are not interested in bus messages
+                                  anymore. */
+                               if (worker->bus) {
+                                       gst_bus_set_sync_handler(worker->bus,
+                                                                 NULL,
+                                                                NULL);
+                               }
+                               if (worker->async_bus_id) {
+                                       g_source_remove(worker->async_bus_id);
+                                       worker->async_bus_id = 0;
+                               }
+
+                                if (worker->mode == WORKER_MODE_REDUNDANT) {
+                                        /* Go to normal mode */
+                                        worker->mode = WORKER_MODE_SINGLE_PLAY;
+                                        _reset_pl_info(worker);
+                                }
+                       }
+               }
+               break;
+       case GST_MESSAGE_TAG:
+               _handle_tag(worker, msg);
+               break;
+       case GST_MESSAGE_BUFFERING:
+               _handle_buffering(worker, msg);
+               break;
+       case GST_MESSAGE_DURATION:
+               _handle_duration(worker, msg);
+               break;
+       case GST_MESSAGE_ELEMENT:
+               _handle_element_msg(worker, msg);
+               break;
+       case GST_MESSAGE_STATE_CHANGED:
+               if ((GstElement *)GST_MESSAGE_SRC(msg) == worker->pipeline)
+                       _handle_state_changed(msg, worker);
+               break;
+       case GST_MESSAGE_APPLICATION:
+               if (gst_structure_has_name(gst_message_get_structure(msg),
+                                          "ckey"))
+               {
+                       GValue v = {0};
+                       g_value_init(&v, G_TYPE_INT);
+                       g_value_set_int(&v, worker->colorkey);
+                       mafw_extension_emit_property_changed(
+                               MAFW_EXTENSION(worker->owner),
+                               MAFW_PROPERTY_RENDERER_COLORKEY,
+                               &v);
+               }
+       default: break;
+       }
+       return TRUE;
+}
+
+/* NOTE this function will possibly be called from a different thread than the
+ * glib main thread. */
+static void _stream_info_cb(GstObject *pipeline, GParamSpec *unused,
+                           MafwGstRendererWorker *worker)
+{
+       g_debug("stream-info changed");
+       _parse_stream_info(worker);
+}
+
+static void _volume_cb(MafwGstRendererWorkerVolume *wvolume, gdouble volume,
+                      gpointer data)
+{
+       MafwGstRendererWorker *worker = data;
+       GValue value = {0, };
+
+       _reset_volume_and_mute_to_pipeline(worker);
+
+       g_value_init(&value, G_TYPE_UINT);
+       g_value_set_uint(&value, (guint) (volume * 100.0));
+       mafw_extension_emit_property_changed(MAFW_EXTENSION(worker->owner),
+                                            MAFW_PROPERTY_RENDERER_VOLUME,
+                                            &value);
+}
+
+static void _mute_cb(MafwGstRendererWorkerVolume *wvolume, gboolean mute,
+                    gpointer data)
+{
+       MafwGstRendererWorker *worker = data;
+       GValue value = {0, };
+
+       _reset_volume_and_mute_to_pipeline(worker);
+
+       g_value_init(&value, G_TYPE_BOOLEAN);
+       g_value_set_boolean(&value, mute);
+       mafw_extension_emit_property_changed(MAFW_EXTENSION(worker->owner),
+                                            MAFW_PROPERTY_RENDERER_MUTE,
+                                            &value);
+}
+
+/* TODO: I think it's not enought to act on error, we need to handle
+ * DestroyNotify on the given window ourselves, because for example helixbin
+ * does it and silently stops the decoder thread.  But it doesn't notify
+ * us... */
+static int xerror(Display *dpy, XErrorEvent *xev)
+{
+       MafwGstRendererWorker *worker;
+
+       if (Global_worker == NULL) {
+               return -1;
+       } else {
+               worker = Global_worker;
+       }
+
+       /* Swallow BadWindow and stop pipeline when the error is about the
+        * currently set xid. */
+       if (worker->xid &&
+           xev->resourceid == worker->xid &&
+           xev->error_code == BadWindow)
+       {
+               g_warning("BadWindow received for current xid (%x).",
+                       (gint)xev->resourceid);
+               worker->xid = 0;
+               /* We must post a message to the bus, because this function is
+                * invoked from a different thread (xvimagerenderer's queue). */
+               _post_error(worker, g_error_new_literal(
+                                   MAFW_RENDERER_ERROR,
+                                   MAFW_RENDERER_ERROR_PLAYBACK,
+                                   "Video window gone"));
+       }
+       return 0;
+}
+
+/*
+ * Resets the media information.
+ */
+static void _reset_media_info(MafwGstRendererWorker *worker)
+{
+       if (worker->media.location) {
+               g_free(worker->media.location);
+               worker->media.location = NULL;
+       }
+       worker->media.length_nanos = -1;
+       worker->media.has_visual_content = FALSE;
+       worker->media.seekable = SEEKABILITY_UNKNOWN;
+       worker->media.video_width = 0;
+       worker->media.video_height = 0;
+       worker->media.fps = 0.0;
+}
+
+static void _set_volume_and_mute(MafwGstRendererWorker *worker, gdouble vol,
+                                gboolean mute)
+{
+       g_return_if_fail(worker->wvolume != NULL);
+
+       mafw_gst_renderer_worker_volume_set(worker->wvolume, vol, mute);
+}
+
+static void _set_volume(MafwGstRendererWorker *worker, gdouble new_vol)
+{
+       g_return_if_fail(worker->wvolume != NULL);
+
+       _set_volume_and_mute(
+               worker, new_vol,
+               mafw_gst_renderer_worker_volume_is_muted(worker->wvolume));
+}
+
+static void _set_mute(MafwGstRendererWorker *worker, gboolean mute)
+{
+       g_return_if_fail(worker->wvolume != NULL);
+
+       _set_volume_and_mute(
+               worker, mafw_gst_renderer_worker_volume_get(worker->wvolume),
+               mute);
+}
+
+/*
+ * Start to play the media
+ */
+static void _start_play(MafwGstRendererWorker *worker)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) worker->owner;
+       GstStateChangeReturn state_change_info;
+
+       g_assert(worker->pipeline);
+       g_object_set(G_OBJECT(worker->pipeline),
+                    "uri", worker->media.location, NULL);
+
+       g_debug("URI: %s", worker->media.location);
+       g_debug("setting pipeline to PAUSED");
+
+       worker->report_statechanges = TRUE;
+       state_change_info = gst_element_set_state(worker->pipeline, 
+                                                 GST_STATE_PAUSED);
+       if (state_change_info == GST_STATE_CHANGE_NO_PREROLL) {
+               /* FIXME:  for live sources we may have to handle
+                  buffering and prerolling differently */
+               g_debug ("Source is live!");
+               worker->is_live = TRUE;
+       }
+        worker->prerolling = TRUE;
+
+       worker->is_stream = uri_is_stream(worker->media.location);
+
+        if (renderer->update_playcount_id > 0) {
+                g_source_remove(renderer->update_playcount_id);
+                renderer->update_playcount_id = 0;
+        }
+
+}
+
+/*
+ * Constructs gst pipeline
+ *
+ * FIXME: Could the same pipeline be used for playing all media instead of
+ *  constantly deleting and reconstructing it again?
+ */
+static void _construct_pipeline(MafwGstRendererWorker *worker)
+{
+       g_debug("constructing pipeline");
+       g_assert(worker != NULL);
+
+       /* Return if we have already one */
+       if (worker->pipeline)
+               return;
+
+       _free_taglist(worker);
+
+       g_debug("Creating a new instance of playbin2");
+       worker->pipeline = gst_element_factory_make("playbin2",
+                                                   "playbin");
+       if (worker->pipeline == NULL)
+       {
+               /* Let's try with playbin */
+               g_warning ("playbin2 failed, falling back to playbin");
+               worker->pipeline = gst_element_factory_make("playbin",
+                                                           "playbin");
+
+               if (worker->pipeline) {
+                       /* Use nwqueue only for non-rtsp and non-mms(h)
+                          streams. */
+                       gboolean use_nw;
+                       use_nw = worker->media.location && 
+                               !g_str_has_prefix(worker->media.location, 
+                                                 "rtsp://") &&
+                               !g_str_has_prefix(worker->media.location, 
+                                                 "mms://") &&
+                               !g_str_has_prefix(worker->media.location, 
+                                                 "mmsh://");
+                       
+                       g_debug("playbin using network queue: %d", use_nw);
+
+                       /* These need a modified version of playbin. */
+                       g_object_set(G_OBJECT(worker->pipeline),
+                                    "nw-queue", use_nw, NULL);
+                       g_object_set(G_OBJECT(worker->pipeline),
+                                    "no-video-transform", TRUE, NULL);
+               }
+       }
+
+       if (!worker->pipeline) {
+               g_critical("failed to create playback pipeline");
+               g_signal_emit_by_name(MAFW_EXTENSION (worker->owner), 
+                                     "error",
+                                     MAFW_RENDERER_ERROR,
+                                     MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM,
+                                     "Could not create pipeline");
+               g_assert_not_reached();
+       }
+
+
+       worker->bus = gst_pipeline_get_bus(GST_PIPELINE(worker->pipeline));
+       gst_bus_set_sync_handler(worker->bus,
+                                (GstBusSyncHandler)_sync_bus_handler, worker);
+       worker->async_bus_id = gst_bus_add_watch_full(worker->bus,G_PRIORITY_HIGH,
+                                                (GstBusFunc)_async_bus_handler,
+                                                worker, NULL);
+
+       /* Listen for changes in stream-info object to find out whether the
+        * media contains video and throw error if application has not provided
+        * video window. */
+       g_signal_connect(worker->pipeline, "notify::stream-info",
+                        G_CALLBACK(_stream_info_cb), worker);
+
+#ifndef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME
+       g_object_set(worker->pipeline, "flags", 99, NULL);
+
+       /* Set audio and video sinks ourselves. We create and configure
+          them only once. */
+       if (!worker->asink) {
+               worker->asink = gst_element_factory_make("pulsesink", NULL);
+               if (!worker->asink) {
+                       g_critical("Failed to create pipeline audio sink");
+                       g_signal_emit_by_name(MAFW_EXTENSION (worker->owner), 
+                                             "error",
+                                             MAFW_RENDERER_ERROR,
+                                             MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM,
+                                             "Could not create audio sink");
+                       g_assert_not_reached();
+               }
+               gst_object_ref(worker->asink);
+               g_object_set(worker->asink, "buffer-time", 
+                            (gint64) MAFW_GST_BUFFER_TIME, NULL);
+               g_object_set(worker->asink, "latency-time", 
+                            (gint64) MAFW_GST_LATENCY_TIME, NULL);
+       }
+       g_object_set(worker->pipeline, "audio-sink", worker->asink, NULL);
+#endif
+
+       if (!worker->vsink) {
+               worker->vsink = gst_element_factory_make("xvimagesink", NULL);
+               if (!worker->vsink) {
+                       g_critical("Failed to create pipeline video sink");
+                       g_signal_emit_by_name(MAFW_EXTENSION (worker->owner), 
+                                             "error",
+                                             MAFW_RENDERER_ERROR,
+                                             MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM,
+                                             "Could not create video sink");
+                       g_assert_not_reached();
+               }
+               gst_object_ref(worker->vsink);
+               g_object_set(G_OBJECT(worker->vsink), "handle-events",
+                            TRUE, NULL);
+               g_object_set(worker->vsink, "force-aspect-ratio",
+                            TRUE, NULL);
+       }
+       g_object_set(worker->pipeline, "video-sink", worker->vsink, NULL);
+}
+
+/*
+ * @seek_type: GstSeekType
+ * @position: Time in seconds where to seek
+ */
+static void _do_seek(MafwGstRendererWorker *worker, GstSeekType seek_type,
+                    gint position, GError **error)
+{
+       gboolean ret;
+       gint64 spos;
+
+       g_assert(worker != NULL);
+
+       if (worker->eos || !worker->media.seekable)
+               goto err;
+
+       /* According to the docs, relative seeking is not so easy:
+       GST_SEEK_TYPE_CUR - change relative to currently configured segment.
+       This can't be used to seek relative to the current playback position -
+       do a position query, calculate the desired position and then do an
+       absolute position seek instead if that's what you want to do. */
+       if (seek_type == GST_SEEK_TYPE_CUR)
+       {
+               gint curpos = mafw_gst_renderer_worker_get_position(worker);
+               position = curpos + position;
+               seek_type = GST_SEEK_TYPE_SET;
+       }
+
+       if (position < 0) {
+               position = 0;
+       }
+
+       worker->seek_position = position;
+       worker->report_statechanges = FALSE;
+       spos = (gint64)position * GST_SECOND;
+       g_debug("seek: type = %d, offset = %lld", seek_type, spos);
+
+        /* If the pipeline has been set to READY by us, then wake it up by
+          setting it to PAUSED (when we get the READY->PAUSED transition
+          we will execute the seek). This way when we seek we disable the
+          READY state (logical, since the player is not idle anymore)
+          allowing the sink to render the destination frame in case of
+          video playback */
+        if (worker->in_ready && worker->state == GST_STATE_READY) {
+                gst_element_set_state(worker->pipeline, GST_STATE_PAUSED);
+        } else {
+                ret = gst_element_seek(worker->pipeline, 1.0, GST_FORMAT_TIME,
+                                       GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_KEY_UNIT,
+                                       seek_type, spos,
+                                       GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+                if (!ret) {
+                        /* Seeking is async, so seek_position should not be
+                           invalidated here */
+                        goto err;
+                }
+       }
+        return;
+
+err:    g_set_error(error,
+                   MAFW_RENDERER_ERROR,
+                   MAFW_RENDERER_ERROR_CANNOT_SET_POSITION,
+                   "Seeking to %d failed", position);
+}
+
+/* @vol should be between [0 .. 100], higher values (up to 1000) are allowed,
+ * but probably cause distortion. */
+void mafw_gst_renderer_worker_set_volume(
+       MafwGstRendererWorker *worker, guint volume)
+{
+        _set_volume(worker, CLAMP((gdouble)volume / 100.0, 0.0, 1.0));
+}
+
+guint mafw_gst_renderer_worker_get_volume(
+       MafwGstRendererWorker *worker)
+{
+        return (guint)
+               (mafw_gst_renderer_worker_volume_get(worker->wvolume) * 100);
+}
+
+void mafw_gst_renderer_worker_set_mute(MafwGstRendererWorker *worker,
+                                     gboolean mute)
+{
+        _set_mute(worker, mute);
+}
+
+gboolean mafw_gst_renderer_worker_get_mute(MafwGstRendererWorker *worker)
+{
+       return mafw_gst_renderer_worker_volume_is_muted(worker->wvolume);
+}
+
+#ifdef HAVE_GDKPIXBUF
+void mafw_gst_renderer_worker_set_current_frame_on_pause(MafwGstRendererWorker *worker,
+                                                               gboolean current_frame_on_pause)
+{
+        worker->current_frame_on_pause = current_frame_on_pause;
+}
+
+gboolean mafw_gst_renderer_worker_get_current_frame_on_pause(MafwGstRendererWorker *worker)
+{
+       return worker->current_frame_on_pause;
+}
+#endif
+
+void mafw_gst_renderer_worker_set_position(MafwGstRendererWorker *worker,
+                                         GstSeekType seek_type,
+                                         gint position, GError **error)
+{
+        /* If player is paused and we have a timeout for going to ready
+        * restart it. This is logical, since the user is seeking and
+        * thus, the player is not idle anymore. Also this prevents that
+        * when seeking streams we enter buffering and in the middle of
+        * the buffering process we set the pipeline to ready (which stops
+        * the buffering before it reaches 100%, making the client think
+        * buffering is still going on).
+        */
+        if (worker->ready_timeout) {
+                _remove_ready_timeout(worker);
+                _add_ready_timeout(worker);
+        }
+
+        _do_seek(worker, seek_type, position, error);
+        if (worker->notify_seek_handler)
+                worker->notify_seek_handler(worker, worker->owner);
+}
+
+/*
+ * Gets current position, rounded down into precision of one second.  If a seek
+ * is pending, returns the position we are going to seek.  Returns -1 on
+ * failure.
+ */
+gint mafw_gst_renderer_worker_get_position(MafwGstRendererWorker *worker)
+{
+       GstFormat format;
+       gint64 time = 0;
+       g_assert(worker != NULL);
+
+       /* If seek is ongoing, return the position where we are seeking. */
+       if (worker->seek_position != -1)
+       {
+               return worker->seek_position;
+       }
+       /* Otherwise query position from pipeline. */
+       format = GST_FORMAT_TIME;
+       if (worker->pipeline &&
+            gst_element_query_position(worker->pipeline, &format, &time))
+       {
+               return (gint)(NSECONDS_TO_SECONDS(time));
+       }
+       return -1;
+}
+
+GHashTable *mafw_gst_renderer_worker_get_current_metadata(
+       MafwGstRendererWorker *worker)
+{
+       return worker->current_metadata;
+}
+
+void mafw_gst_renderer_worker_set_xid(MafwGstRendererWorker *worker, XID xid)
+{
+       /* Check for errors on the target window */
+       XSetErrorHandler(xerror);
+
+       /* Store the target window id */
+       g_debug("Setting xid: %x", (guint)xid);
+       worker->xid = xid;
+
+       /* Check if we should use it right away */
+       mafw_gst_renderer_worker_apply_xid(worker);
+}
+
+XID mafw_gst_renderer_worker_get_xid(MafwGstRendererWorker *worker)
+{
+       return worker->xid;
+}
+
+gboolean mafw_gst_renderer_worker_get_autopaint(
+       MafwGstRendererWorker *worker)
+{
+       return worker->autopaint;
+}
+void mafw_gst_renderer_worker_set_autopaint(
+       MafwGstRendererWorker *worker, gboolean autopaint)
+{
+       worker->autopaint = autopaint;
+       if (worker->vsink)
+               g_object_set(worker->vsink, "autopaint-colorkey",
+                            autopaint, NULL);
+}
+
+gint mafw_gst_renderer_worker_get_colorkey(
+       MafwGstRendererWorker *worker)
+{
+       return worker->colorkey;
+}
+
+gboolean mafw_gst_renderer_worker_get_seekable(MafwGstRendererWorker *worker)
+{
+       return worker->media.seekable;
+}
+
+static void _play_pl_next(MafwGstRendererWorker *worker) {
+       gchar *next;
+
+       g_assert(worker != NULL);
+       g_return_if_fail(worker->pl.items != NULL);
+
+       next = (gchar *) g_slist_nth_data(worker->pl.items,
+                                         ++worker->pl.current);
+       mafw_gst_renderer_worker_stop(worker);
+       _reset_media_info(worker);
+
+       worker->media.location = g_strdup(next);
+       _construct_pipeline(worker);
+       _start_play(worker);
+}
+
+static void _on_pl_entry_parsed(TotemPlParser *parser, gchar *uri,
+                               gpointer metadata, gpointer user_data)
+{
+       MafwGstRendererWorker *worker = user_data;
+
+       if (uri != NULL) {
+               worker->pl.items =
+                       g_slist_append(worker->pl.items, g_strdup(uri));
+       }
+}
+
+static void _do_play(MafwGstRendererWorker *worker)
+{
+       g_assert(worker != NULL);
+
+       if (worker->pipeline == NULL) {
+               g_debug("play without a pipeline!");
+               return;
+       }
+       worker->report_statechanges = TRUE;
+
+       /* If we have to stay paused, we do and add the ready
+        * timeout. Otherwise, we move the pipeline */
+       if (!worker->stay_paused) {
+               /* If pipeline is READY, we move it to PAUSED,
+                * otherwise, to PLAYING */
+               if (worker->state == GST_STATE_READY) {
+                       gst_element_set_state(worker->pipeline,
+                                             GST_STATE_PAUSED);
+                       g_debug("setting pipeline to PAUSED");
+               } else {
+                       _reset_volume_and_mute_to_pipeline(worker);
+                       gst_element_set_state(worker->pipeline,
+                                             GST_STATE_PLAYING);
+                       g_debug("setting pipeline to PLAYING");
+               }
+       }
+       else {
+               g_debug("staying in PAUSED state");
+               _add_ready_timeout(worker);
+       }
+}
+
+void mafw_gst_renderer_worker_play(MafwGstRendererWorker *worker,
+                                 const gchar *uri)
+{
+       g_assert(uri);
+
+       mafw_gst_renderer_worker_stop(worker);
+       _reset_media_info(worker);
+       _reset_pl_info(worker);
+       /* Check if the item to play is a single item or a playlist. */
+       if (uri_is_playlist(uri)){
+               /* In case of a playlist we parse it and start playing the first
+                  item of the playlist. */
+               TotemPlParser *pl_parser;
+               gchar *item;
+
+               /* Initialize the playlist parser */
+               pl_parser = totem_pl_parser_new ();
+               g_object_set(pl_parser, "recurse", TRUE, "disable-unsafe",
+                            TRUE, NULL);
+               g_signal_connect(G_OBJECT(pl_parser), "entry-parsed",
+                                G_CALLBACK(_on_pl_entry_parsed), worker);
+
+               /* Parsing */
+               if (totem_pl_parser_parse(pl_parser, uri, FALSE) !=
+                   TOTEM_PL_PARSER_RESULT_SUCCESS) {
+                       /* An error happens while parsing */
+                       _send_error(worker,
+                                   g_error_new(MAFW_RENDERER_ERROR,
+                                               MAFW_RENDERER_ERROR_PLAYLIST_PARSING,
+                                               "Playlist parsing failed: %s",
+                                               uri));
+                       return;
+               }
+
+               if (!worker->pl.items) {
+                       /* The playlist is empty */
+                       _send_error(worker,
+                                   g_error_new(MAFW_RENDERER_ERROR,
+                                               MAFW_RENDERER_ERROR_PLAYLIST_PARSING,
+                                               "The playlist %s is empty.",
+                                               uri));
+                       return;
+               }
+
+               /* Set the playback mode */
+               worker->mode = WORKER_MODE_PLAYLIST;
+               worker->pl.notify_play_pending = TRUE;
+
+               /* Set the item to be played */
+               worker->pl.current = 0;
+               item = (gchar *) g_slist_nth_data(worker->pl.items, 0);
+               worker->media.location = g_strdup(item);
+
+               /* Free the playlist parser */
+               g_object_unref(pl_parser);
+       } else {
+               /* Single item. Set the playback mode according to that */
+               worker->mode = WORKER_MODE_SINGLE_PLAY;
+
+               /* Set the item to be played */
+               worker->media.location = g_strdup(uri);
+       }
+       _construct_pipeline(worker);
+       _start_play(worker);
+}
+
+void mafw_gst_renderer_worker_play_alternatives(MafwGstRendererWorker *worker,
+                                                gchar **uris)
+{
+        gint i;
+        gchar *item;
+
+        g_assert(uris && uris[0]);
+
+        mafw_gst_renderer_worker_stop(worker);
+        _reset_media_info(worker);
+        _reset_pl_info(worker);
+
+        /* Add the uris to playlist */
+        i = 0;
+        while (uris[i]) {
+                worker->pl.items =
+                        g_slist_append(worker->pl.items, g_strdup(uris[i]));
+                i++;
+        }
+
+        /* Set the playback mode */
+        worker->mode = WORKER_MODE_REDUNDANT;
+        worker->pl.notify_play_pending = TRUE;
+
+        /* Set the item to be played */
+        worker->pl.current = 0;
+        item = (gchar *) g_slist_nth_data(worker->pl.items, 0);
+        worker->media.location = g_strdup(item);
+
+        /* Start playing */
+        _construct_pipeline(worker);
+        _start_play(worker);
+}
+
+/*
+ * Currently, stop destroys the Gst pipeline and resets the worker into
+ * default startup configuration.
+ */
+void mafw_gst_renderer_worker_stop(MafwGstRendererWorker *worker)
+{
+       g_debug("worker stop");
+       g_assert(worker != NULL);
+
+       /* If location is NULL, this is a pre-created pipeline */
+       if (worker->async_bus_id && worker->pipeline && !worker->media.location)
+               return;
+
+       if (worker->pipeline) {
+               g_debug("destroying pipeline");
+               if (worker->async_bus_id) {
+                       g_source_remove(worker->async_bus_id);
+                       worker->async_bus_id = 0;
+               }
+               gst_bus_set_sync_handler(worker->bus, NULL, NULL);
+               gst_element_set_state(worker->pipeline, GST_STATE_NULL);
+               if (worker->bus) {
+                       gst_object_unref(GST_OBJECT_CAST(worker->bus));
+                       worker->bus = NULL;
+               }
+               gst_object_unref(GST_OBJECT(worker->pipeline));
+               worker->pipeline = NULL;
+       }
+
+       /* Reset worker */
+       worker->report_statechanges = TRUE;
+       worker->state = GST_STATE_NULL;
+       worker->prerolling = FALSE;
+       worker->is_live = FALSE;
+       worker->buffering = FALSE;
+       worker->is_stream = FALSE;
+       worker->is_error = FALSE;
+       worker->eos = FALSE;
+       worker->seek_position = -1;
+       _remove_ready_timeout(worker);
+       _free_taglist(worker);
+       if (worker->current_metadata) {
+               g_hash_table_destroy(worker->current_metadata);
+               worker->current_metadata = NULL;
+       }
+
+       if (worker->duration_seek_timeout != 0) {
+               g_source_remove(worker->duration_seek_timeout);
+               worker->duration_seek_timeout = 0;
+       }
+
+       /* Reset media iformation */
+       _reset_media_info(worker);
+
+       /* We are not playing, so we can let the screen blank */
+       blanking_allow();
+
+       /* And now get a fresh pipeline ready */
+       _construct_pipeline(worker);
+}
+
+void mafw_gst_renderer_worker_pause(MafwGstRendererWorker *worker)
+{
+       g_assert(worker != NULL);
+
+       if (worker->buffering && worker->state == GST_STATE_PAUSED &&
+           !worker->prerolling) {
+               /* If we are buffering and get a pause, we have to
+                * signal state change and stay_paused */
+               g_debug("Pausing while buffering, signalling state change");
+               worker->stay_paused = TRUE;
+               if (worker->notify_pause_handler) {
+                       worker->notify_pause_handler(
+                               worker,
+                               worker->owner);
+               }
+       } else {
+               worker->report_statechanges = TRUE;
+
+               if (gst_element_set_state(worker->pipeline, GST_STATE_PAUSED) ==
+                   GST_STATE_CHANGE_ASYNC)
+               {
+                       /* XXX this blocks at most 2 seconds. */
+                       gst_element_get_state(worker->pipeline, NULL, NULL,
+                                     2 * GST_SECOND);
+               }
+               blanking_allow();
+       }
+}
+
+void mafw_gst_renderer_worker_resume(MafwGstRendererWorker *worker)
+{
+       if (worker->mode == WORKER_MODE_PLAYLIST ||
+            worker->mode == WORKER_MODE_REDUNDANT) {
+               /* We must notify play if the "playlist" playback
+                  is resumed */
+               worker->pl.notify_play_pending = TRUE;
+       }
+       if (worker->buffering && worker->state == GST_STATE_PAUSED &&
+           !worker->prerolling) {
+               /* If we are buffering we cannot resume, but we know
+                * that the pipeline will be moved to PLAYING as
+                * stay_paused is FALSE, so we just activate the state
+                * change report, this way as soon as buffering is finished
+                * the pipeline will be set to PLAYING and the state
+                * change will be reported */
+               worker->report_statechanges = TRUE;
+               g_debug("Resumed while buffering, activating pipeline state "
+                       "changes");
+               /* Notice though that we can receive the Resume before
+                  we get any buffering information. In that case
+                  we go with the "else" branch and set the pipeline to
+                  to PLAYING. However, it is possible that in this case
+                  we get the fist buffering signal before the
+                  PAUSED -> PLAYING state change. In that case, since we
+                  ignore state changes while buffering we never signal
+                  the state change to PLAYING. We can only fix this by
+                  checking, when we receive a PAUSED -> PLAYING transition
+                  if we are buffering, and in that case signal the state
+                  change (if we get that transition while buffering
+                  is on, it can only mean that the client resumed playback
+                  while buffering, and we must notify the state change) */
+       } else {
+               _do_play(worker);
+       }
+}
+
+static void _volume_init_cb(MafwGstRendererWorkerVolume *wvolume,
+                           gpointer data)
+{
+       MafwGstRendererWorker *worker = data;
+       gdouble volume;
+       gboolean mute;
+
+       worker->wvolume = wvolume;
+
+       g_debug("volume manager initialized");
+
+       volume = mafw_gst_renderer_worker_volume_get(wvolume);
+       mute = mafw_gst_renderer_worker_volume_is_muted(wvolume);
+       _volume_cb(wvolume, volume, worker);
+       _mute_cb(wvolume, mute, worker);
+}
+
+MafwGstRendererWorker *mafw_gst_renderer_worker_new(gpointer owner)
+{
+        MafwGstRendererWorker *worker;
+       GMainContext *main_context;
+
+       worker = g_new0(MafwGstRendererWorker, 1);
+       worker->mode = WORKER_MODE_SINGLE_PLAY;
+       worker->pl.items = NULL;
+       worker->pl.current = 0;
+       worker->pl.notify_play_pending = TRUE;
+       worker->owner = owner;
+       worker->report_statechanges = TRUE;
+       worker->state = GST_STATE_NULL;
+       worker->seek_position = -1;
+       worker->ready_timeout = 0;
+       worker->in_ready = FALSE;
+       worker->xid = 0;
+       worker->autopaint = TRUE;
+       worker->colorkey = -1;
+       worker->vsink = NULL;
+       worker->asink = NULL;
+       worker->tag_list = NULL;
+       worker->current_metadata = NULL;
+
+#ifdef HAVE_GDKPIXBUF
+       worker->current_frame_on_pause = FALSE;
+       _init_tmp_files_pool(worker);
+#endif
+       worker->notify_seek_handler = NULL;
+       worker->notify_pause_handler = NULL;
+       worker->notify_play_handler = NULL;
+       worker->notify_buffer_status_handler = NULL;
+       worker->notify_eos_handler = NULL;
+       worker->notify_error_handler = NULL;
+       Global_worker = worker;
+       main_context = g_main_context_default();
+       worker->wvolume = NULL;
+       mafw_gst_renderer_worker_volume_init(main_context,
+                                            _volume_init_cb, worker,
+                                            _volume_cb, worker,
+                                            _mute_cb, worker);
+       blanking_init();
+       _construct_pipeline(worker);
+
+       return worker;
+}
+
+void mafw_gst_renderer_worker_exit(MafwGstRendererWorker *worker)
+{
+       blanking_deinit();
+#ifdef HAVE_GDKPIXBUF
+       _destroy_tmp_files_pool(worker);
+#endif
+       mafw_gst_renderer_worker_volume_destroy(worker->wvolume);
+        mafw_gst_renderer_worker_stop(worker);
+}
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/libmafw-gst-renderer/mafw-gst-renderer-worker.h b/libmafw-gst-renderer/mafw-gst-renderer-worker.h
new file mode 100644 (file)
index 0000000..d59c09a
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef MAFW_GST_RENDERER_WORKER_H
+#define MAFW_GST_RENDERER_WORKER_H
+
+#include <X11/Xdefs.h>
+#include <glib-object.h>
+#include <gst/gst.h>
+#include "mafw-gst-renderer-worker-volume.h"
+
+#define MAFW_GST_RENDERER_MAX_TMP_FILES 5
+
+typedef struct _MafwGstRendererWorker MafwGstRendererWorker;
+
+typedef void (*MafwGstRendererWorkerNotifySeekCb)(MafwGstRendererWorker *worker, gpointer owner);
+typedef void (*MafwGstRendererWorkerNotifyPauseCb)(MafwGstRendererWorker *worker, gpointer owner);
+typedef void (*MafwGstRendererWorkerNotifyPlayCb)(MafwGstRendererWorker *worker, gpointer owner);
+typedef void (*MafwGstRendererWorkerNotifyBufferStatusCb)(MafwGstRendererWorker *worker, gpointer owner, gdouble percent);
+typedef void (*MafwGstRendererWorkerNotifyEOSCb)(MafwGstRendererWorker *worker, gpointer owner);
+typedef void (*MafwGstRendererWorkerNotifyErrorCb)(MafwGstRendererWorker *worker,
+                                                   gpointer owner,
+                                                   const GError *error);
+
+typedef enum {
+       WORKER_MODE_SINGLE_PLAY,
+        WORKER_MODE_PLAYLIST,
+        WORKER_MODE_REDUNDANT,
+} PlaybackMode;
+
+typedef enum {
+       SEEKABILITY_UNKNOWN = -1,
+       SEEKABILITY_NO_SEEKABLE,
+       SEEKABILITY_SEEKABLE,
+} SeekabilityType;
+
+/*
+ * media:        Information about currently selected media.
+ *   location:           Current media location
+ *   length_nanos:       Length of the media, in nanoseconds
+ *   has_visual_content: the clip contains some visual content (video)
+ *   video_width:        If media contains video, this tells the video width
+ *   video_height:       If media contains video, this tells the video height
+ *   seekable:           Tells whether the media can be seeked
+ *   par_n:              Video pixel aspect ratio numerator
+ *   par_d:              Video pixel aspect ratio denominator
+ * owner:        Owner of the worker; usually a MafwGstRenderer (FIXME USUALLY?)
+ * pipeline:     Playback pipeline
+ * bus:          Message bus
+ * state:        Current playback pipeline state
+ * is_stream:    Is currently playing media a stream
+ * muted:        Is the audio muted
+ * eos:          Has playback reached EOS already
+ * is_error:     Has there been an error situation
+ * buffering:    Indicates the buffering state
+ * prerolling:   Indicates the prerolling state (NULL -> PAUSED)
+ * report_statechanges: Report state change bus messages
+ * current_volume:      Current audio volume [0.0 .. 1.0], see playbin:volume
+ * async_bus_id:        ID handle for GstBus
+ * buffer_probe_id:     ID of the video renderer buffer probe
+ * seek_position:       Indicates the pos where to seek, in seconds
+ * vsink:               Video sink element of the pipeline
+ * asink:               Audio sink element of the pipeline
+ * xid:                 XID for video playback
+ * current_frame_on_pause: whether to emit current frame when pausing
+ */
+struct _MafwGstRendererWorker {
+       struct {
+               gchar *location;
+               gint64 length_nanos;
+               gboolean has_visual_content;
+               gint video_width;
+               gint video_height;
+               gdouble fps;
+               SeekabilityType seekable;
+               gint par_n;
+               gint par_d;
+       } media;
+       PlaybackMode mode;
+       struct {
+               GSList *items;
+               gint current;
+               gboolean notify_play_pending;
+       } pl;
+        gpointer owner;
+       GstElement *pipeline;
+       GstBus *bus;
+       /* GStreamer state we are considering right now */
+       GstState state;
+       MafwGstRendererWorkerVolume *wvolume;
+       gboolean is_stream;
+       gboolean muted;
+       /* we are handing eos or we did */
+       gboolean eos;
+       /* if we are handling (or handled) and error */
+       gboolean is_error;
+       /* pipeline is buffering */
+       gboolean buffering;
+       /* pipeline is prerolling */
+       gboolean prerolling;
+       /* stream is live and doesn't need prerolling */
+       gboolean is_live;
+       /* if we have to stay in paused though a do_play was
+        * requested. Usually used when pausing in transitioning */
+       gboolean stay_paused;
+       /* this variable should be FALSE while we are hiding state
+        * changed to the UI. This is that GStreamer can perform
+        * state_changes without us requiring it, for example, then
+        * seeking, buffering and so on and we have to hide those
+        * changes */
+       gboolean report_statechanges;
+       guint async_bus_id;
+       gint seek_position;
+       guint ready_timeout;
+       guint duration_seek_timeout;
+       /* After some time PAUSED, we set the pipeline to READY in order to
+        * save resources. This field states if we are in this special
+        * situation.
+        * It is set to TRUE when the state change to READY is requested
+        * and stays like that until we reach again PLAYING state (not PAUSED).
+        * The reason for this is that when resuming streams, we have to 
+        * move from READY to PAUSED, then seek to the position where the
+        * stream had been paused, then wait for buffering to finish, and then
+        * play (and notify the state change to PLAYING), and we have to
+        * differentiate this case from the one in which we have entered PAUSED
+        * silently (when we ran out of buffer while playing, because in that
+        * case, when we are done buffering we want to resume playback silently
+        * again.
+        */
+       gboolean in_ready;
+       GstElement *vsink;
+       GstElement *asink;
+       XID xid;
+       gboolean autopaint;
+       gint colorkey;
+       GPtrArray *tag_list;
+       GHashTable *current_metadata;
+
+#ifdef HAVE_GDKPIXBUF
+       gboolean current_frame_on_pause;
+       gchar *tmp_files_pool[MAFW_GST_RENDERER_MAX_TMP_FILES];
+       guint8 tmp_files_pool_index;
+#endif
+
+        /* Handlers for notifications */
+        MafwGstRendererWorkerNotifySeekCb notify_seek_handler;
+        MafwGstRendererWorkerNotifyPauseCb notify_pause_handler;
+        MafwGstRendererWorkerNotifyPlayCb notify_play_handler;
+        MafwGstRendererWorkerNotifyBufferStatusCb notify_buffer_status_handler;
+        MafwGstRendererWorkerNotifyEOSCb notify_eos_handler;
+        MafwGstRendererWorkerNotifyErrorCb notify_error_handler;
+};
+
+G_BEGIN_DECLS
+
+MafwGstRendererWorker *mafw_gst_renderer_worker_new(gpointer owner);
+void mafw_gst_renderer_worker_exit(MafwGstRendererWorker *worker);
+
+void mafw_gst_renderer_worker_set_volume(MafwGstRendererWorker *worker,
+                                         guint vol);
+guint mafw_gst_renderer_worker_get_volume(MafwGstRendererWorker *worker);
+void mafw_gst_renderer_worker_set_mute(MafwGstRendererWorker *worker,
+                                       gboolean mute);
+gboolean mafw_gst_renderer_worker_get_mute(MafwGstRendererWorker *worker);
+#ifdef HAVE_GDKPIXBUF
+void mafw_gst_renderer_worker_set_current_frame_on_pause(MafwGstRendererWorker *worker,
+                                                         gboolean current_frame_on_pause);
+gboolean mafw_gst_renderer_worker_get_current_frame_on_pause(MafwGstRendererWorker *worker);
+#endif
+void mafw_gst_renderer_worker_set_position(MafwGstRendererWorker *worker,
+                                           GstSeekType seek_type,
+                                           gint position,
+                                           GError **error);
+gint mafw_gst_renderer_worker_get_position(MafwGstRendererWorker *worker);
+void mafw_gst_renderer_worker_set_xid(MafwGstRendererWorker *worker, XID xid);
+XID mafw_gst_renderer_worker_get_xid(MafwGstRendererWorker *worker);
+gboolean mafw_gst_renderer_worker_get_autopaint(MafwGstRendererWorker *worker);
+void mafw_gst_renderer_worker_set_autopaint(MafwGstRendererWorker *worker, gboolean autopaint);
+gint mafw_gst_renderer_worker_get_colorkey(MafwGstRendererWorker *worker);
+gboolean mafw_gst_renderer_worker_get_seekable(MafwGstRendererWorker *worker);
+GHashTable *mafw_gst_renderer_worker_get_current_metadata(MafwGstRendererWorker *worker);
+void mafw_gst_renderer_worker_play(MafwGstRendererWorker *worker, const gchar *uri);
+void mafw_gst_renderer_worker_play_alternatives(MafwGstRendererWorker *worker, gchar **uris);
+void mafw_gst_renderer_worker_stop(MafwGstRendererWorker *worker);
+void mafw_gst_renderer_worker_pause(MafwGstRendererWorker *worker);
+void mafw_gst_renderer_worker_resume(MafwGstRendererWorker *worker);
+
+G_END_DECLS
+#endif
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/libmafw-gst-renderer/mafw-gst-renderer.c b/libmafw-gst-renderer/mafw-gst-renderer.c
new file mode 100644 (file)
index 0000000..6412b13
--- /dev/null
@@ -0,0 +1,2191 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dbus/dbus.h>
+#include <libgnomevfs/gnome-vfs.h>
+
+#include <libmafw/mafw.h>
+#include "mafw-gst-renderer.h"
+#include "mafw-gst-renderer-utils.h"
+#include "mafw-gst-renderer-worker.h"
+
+#include "mafw-gst-renderer-state-playing.h"
+#include "mafw-gst-renderer-state-stopped.h"
+#include "mafw-gst-renderer-state-paused.h"
+#include "mafw-gst-renderer-state-transitioning.h"
+
+#include "blanking.h"
+
+#ifdef HAVE_CONIC
+#include <conicconnectionevent.h>
+#endif
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer"
+
+#define is_current_uri_stream(self) \
+       (((self)->media != NULL) && ((self)->media->uri != NULL) &&     \
+        uri_is_stream((self)->media->uri))
+
+#define GCONF_OSSO_AF "/system/osso/af"
+#define GCONF_BATTERY_COVER_OPEN "/system/osso/af/mmc-cover-open"
+#define HAL_VIDEOOUT_UDI "/org/freedesktop/Hal/devices" \
+        "/platform_soc_audio_logicaldev_input"
+
+/*----------------------------------------------------------------------------
+  Static variable definitions
+  ----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Plugin initialization
+  ----------------------------------------------------------------------------*/
+
+static gboolean mafw_gst_renderer_initialize(MafwRegistry *registry,
+                                          GError **error);
+static void mafw_gst_renderer_deinitialize(GError **error);
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+static void mafw_gst_renderer_dispose(GObject *object);
+static void mafw_gst_renderer_finalize(GObject *object);
+
+/*----------------------------------------------------------------------------
+  Hal callbacks
+  ----------------------------------------------------------------------------*/
+static void _property_modified(LibHalContext *ctx, const char *udi,
+                               const char *key, dbus_bool_t is_removed,
+                               dbus_bool_t is_added);
+static gboolean _tv_out_is_connected(LibHalContext *ctx, const char *udi);
+
+/*----------------------------------------------------------------------------
+  GConf notifications
+  ----------------------------------------------------------------------------*/
+
+static void _battery_cover_open_cb(GConfClient *client,
+                                  guint cnxn_id,
+                                  GConfEntry *entry,
+                                  MafwGstRenderer *renderer);
+
+/*----------------------------------------------------------------------------
+  Gnome VFS notifications
+  ----------------------------------------------------------------------------*/
+
+static void _volume_pre_unmount_cb(GnomeVFSVolumeMonitor *monitor,
+                                  GnomeVFSVolume *volume,
+                                   MafwGstRenderer *renderer);
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _signal_state_changed(MafwGstRenderer * self);
+static void _signal_media_changed(MafwGstRenderer * self);
+static void _signal_playlist_changed(MafwGstRenderer * self);
+static void _signal_transport_actions_property_changed(MafwGstRenderer * self);
+
+/*----------------------------------------------------------------------------
+  Properties
+  ----------------------------------------------------------------------------*/
+
+static void _set_error_policy(MafwGstRenderer *renderer, MafwRendererErrorPolicy policy);
+static MafwRendererErrorPolicy _get_error_policy(MafwGstRenderer *renderer);
+
+static void mafw_gst_renderer_set_property(MafwExtension *self, const gchar *key,
+                                        const GValue *value);
+static void mafw_gst_renderer_get_property(MafwExtension *self, const gchar *key,
+                                        MafwExtensionPropertyCallback callback,
+                                        gpointer user_data);
+
+/*----------------------------------------------------------------------------
+  Metadata
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwSource *cb_source,
+                            const gchar *cb_object_id,
+                            GHashTable *cb_metadata,
+                            gpointer cb_user_data,
+                            const GError *cb_error);
+
+/*----------------------------------------------------------------------------
+  Notification operations
+  ----------------------------------------------------------------------------*/
+
+static void _notify_play(MafwGstRendererWorker *worker, gpointer owner);
+static void _notify_pause(MafwGstRendererWorker *worker, gpointer owner);
+static void _notify_seek(MafwGstRendererWorker *worker, gpointer owner);
+static void _notify_buffer_status(MafwGstRendererWorker *worker, gpointer owner,
+                                 gdouble percent);
+static void _notify_eos(MafwGstRendererWorker *worker, gpointer owner);
+static void _error_handler(MafwGstRendererWorker *worker, gpointer owner,
+                          const GError *error);
+
+#ifdef HAVE_CONIC
+/*----------------------------------------------------------------------------
+  Connection
+  ----------------------------------------------------------------------------*/
+
+static void _connection_init(MafwGstRenderer *renderer);
+#endif
+
+/*----------------------------------------------------------------------------
+  Plugin initialization
+  ----------------------------------------------------------------------------*/
+
+/*
+ * Registers the plugin descriptor making this plugin available to the
+ * framework and applications
+ */
+G_MODULE_EXPORT MafwPluginDescriptor mafw_gst_renderer_plugin_description = {
+       { .name         = MAFW_GST_RENDERER_PLUGIN_NAME },
+       .initialize     = mafw_gst_renderer_initialize,
+       .deinitialize   = mafw_gst_renderer_deinitialize,
+};
+
+static gboolean mafw_gst_renderer_initialize(MafwRegistry *registry,
+                                          GError **error)
+{
+       MafwGstRenderer *self;
+
+       g_assert(registry != NULL);
+       self = MAFW_GST_RENDERER(mafw_gst_renderer_new(registry));
+       mafw_registry_add_extension(registry, MAFW_EXTENSION(self));
+
+       return TRUE;
+}
+
+static void mafw_gst_renderer_deinitialize(GError **error)
+{
+}
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+G_DEFINE_TYPE(MafwGstRenderer, mafw_gst_renderer, MAFW_TYPE_RENDERER);
+
+static void mafw_gst_renderer_class_init(MafwGstRendererClass *klass)
+{
+       GObjectClass *gclass = NULL;
+       MafwRendererClass *renderer_class = NULL;
+       const gchar *preloaded_plugins[] = {"playback", "uridecodebin",
+               "coreelements", "typefindfunctions", "omx", "selector",
+               "autodetect", "pulseaudio", "audioconvert", "audioresample",
+               "xvimagesink", "ffmpegcolorspace", "videoscale", NULL};
+       gint i = 0;
+       GObject *plugin;
+
+       gclass = G_OBJECT_CLASS(klass);
+       g_return_if_fail(gclass != NULL);
+
+       renderer_class = MAFW_RENDERER_CLASS(klass);
+       g_return_if_fail(renderer_class != NULL);
+
+       /* GObject */
+
+       gclass->dispose = mafw_gst_renderer_dispose;
+       gclass->finalize = mafw_gst_renderer_finalize;
+
+       /* Playback */
+
+       renderer_class->play = mafw_gst_renderer_play;
+       renderer_class->play_object = mafw_gst_renderer_play_object;
+       renderer_class->stop = mafw_gst_renderer_stop;
+       renderer_class->pause = mafw_gst_renderer_pause;
+       renderer_class->resume = mafw_gst_renderer_resume;
+       renderer_class->get_status = mafw_gst_renderer_get_status;
+
+       /* Playlist operations */
+
+       renderer_class->assign_playlist = mafw_gst_renderer_assign_playlist;
+       renderer_class->next = mafw_gst_renderer_next;
+       renderer_class->previous = mafw_gst_renderer_previous;
+       renderer_class->goto_index = mafw_gst_renderer_goto_index;
+
+       /* Playback position */
+
+       renderer_class->set_position = mafw_gst_renderer_set_position;
+       renderer_class->get_position = mafw_gst_renderer_get_position;
+
+       /* Metadata */
+
+       renderer_class->get_current_metadata =
+               mafw_gst_renderer_get_current_metadata;
+
+       /* Properties */
+
+       MAFW_EXTENSION_CLASS(klass)->get_extension_property =
+               (gpointer) mafw_gst_renderer_get_property;
+       MAFW_EXTENSION_CLASS(klass)->set_extension_property =
+               (gpointer) mafw_gst_renderer_set_property;
+
+       gst_init(NULL, NULL);
+       gst_pb_utils_init();
+
+       /* Pre-load some common plugins */
+       while (preloaded_plugins[i])
+       {
+               plugin = G_OBJECT(gst_plugin_load_by_name(preloaded_plugins[i]));
+               if (plugin)
+                       g_object_unref(plugin);
+               else
+                       g_debug("Can not load plugin: %s", preloaded_plugins[i]);
+               i++;
+       }
+}
+
+static void mafw_gst_renderer_init(MafwGstRenderer *self)
+{
+       MafwGstRenderer *renderer = NULL;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       renderer = MAFW_GST_RENDERER(self);
+       g_return_if_fail(renderer != NULL);
+
+       mafw_extension_add_property(MAFW_EXTENSION(self), "volume", G_TYPE_UINT);
+       mafw_extension_add_property(MAFW_EXTENSION(self), "mute", G_TYPE_BOOLEAN);
+       mafw_extension_add_property(MAFW_EXTENSION(self), "xid", G_TYPE_UINT);
+       mafw_extension_add_property(MAFW_EXTENSION(self), "error-policy", G_TYPE_UINT);
+       MAFW_EXTENSION_SUPPORTS_AUTOPAINT(self);
+       MAFW_EXTENSION_SUPPORTS_COLORKEY(self);
+#ifdef HAVE_GDKPIXBUF
+       mafw_extension_add_property(MAFW_EXTENSION(self),
+                                    "current-frame-on-pause",
+                                    G_TYPE_BOOLEAN);
+#endif
+        mafw_extension_add_property(MAFW_EXTENSION(self),
+                                    MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED,
+                                    G_TYPE_BOOLEAN);
+       MAFW_EXTENSION_SUPPORTS_TRANSPORT_ACTIONS(self);
+       renderer->media = g_new0(MafwGstRendererMedia, 1);
+       renderer->media->seekability = SEEKABILITY_UNKNOWN;
+       renderer->current_state = Stopped;
+
+       renderer->playlist = NULL;
+       renderer->iterator = NULL;
+       renderer->seeking_to = -1;
+        renderer->update_playcount_id = 0;
+
+        self->worker = mafw_gst_renderer_worker_new(self);
+
+        /* Set notification handlers for worker */
+        renderer->worker->notify_play_handler = _notify_play;
+        renderer->worker->notify_pause_handler = _notify_pause;
+        renderer->worker->notify_seek_handler = _notify_seek;
+        renderer->worker->notify_error_handler = _error_handler;
+        renderer->worker->notify_eos_handler = _notify_eos;
+       renderer->worker->notify_buffer_status_handler = _notify_buffer_status;
+
+       renderer->states = g_new0 (MafwGstRendererState*, _LastMafwPlayState);
+       renderer->states[Stopped] =
+               MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_stopped_new(self));
+       renderer->states[Transitioning] =
+               MAFW_GST_RENDERER_STATE(
+                       mafw_gst_renderer_state_transitioning_new(self));
+       renderer->states[Playing] =
+               MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_playing_new(self));
+       renderer->states[Paused] =
+               MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_paused_new(self));
+
+       renderer->current_state = Stopped;
+       renderer->resume_playlist = FALSE;
+       renderer->playback_mode = MAFW_GST_RENDERER_MODE_PLAYLIST;
+
+#ifdef HAVE_CONIC
+       renderer->connected = FALSE;
+       renderer->connection = NULL;
+
+       _connection_init(renderer);
+#endif
+       renderer->gconf_client = gconf_client_get_default();
+       gconf_client_add_dir(renderer->gconf_client, GCONF_OSSO_AF,
+                            GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
+       if (error) {
+               g_warning("%s", error->message);
+               g_error_free(error);
+               error = NULL;
+       }
+
+       gconf_client_notify_add(renderer->gconf_client,
+                               GCONF_BATTERY_COVER_OPEN,
+                               (GConfClientNotifyFunc) _battery_cover_open_cb,
+                               renderer,
+                               NULL, &error);
+
+       if (error) {
+               g_warning("%s", error->message);
+               g_error_free(error);
+       }
+
+       if (gnome_vfs_init()) {
+               GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
+               g_signal_connect(monitor, "volume-pre-unmount", 
+                                G_CALLBACK(_volume_pre_unmount_cb), renderer);
+       } else {
+               g_warning("Failed to initialize gnome-vfs");
+       }
+}
+
+static void mafw_gst_renderer_dispose(GObject *object)
+{
+       MafwGstRenderer *renderer;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(object));
+
+       renderer = MAFW_GST_RENDERER(object);
+
+       if (renderer->worker != NULL) {
+               mafw_gst_renderer_worker_exit(renderer->worker);
+               renderer->seek_pending = FALSE;
+               g_free(renderer->worker);
+               renderer->worker = NULL;
+       }
+
+       if (renderer->registry != NULL) {
+               g_object_unref(renderer->registry);
+               renderer->registry = NULL;
+       }
+
+       if (renderer->states != NULL) {
+               guint i = 0;
+
+               for (i = 0; i < _LastMafwPlayState; i++) {
+                       if (renderer->states[i] != NULL)
+                               g_object_unref(renderer->states[i]);
+               }
+               g_free(renderer->states);
+               renderer->states = NULL;
+       }
+
+       if (renderer->hal_ctx != NULL) {
+                libhal_device_remove_property_watch(renderer->hal_ctx,
+                                                    HAL_VIDEOOUT_UDI,
+                                                    NULL);
+               libhal_ctx_shutdown(renderer->hal_ctx, NULL);
+               libhal_ctx_free(renderer->hal_ctx);
+       }
+
+#ifdef HAVE_CONIC
+       if (renderer->connection != NULL) {
+               g_object_unref(renderer->connection);
+               renderer->connection = NULL;
+       }
+#endif
+
+       if (renderer->gconf_client != NULL) {
+               g_object_unref(renderer->gconf_client);
+               renderer->gconf_client = NULL;
+       }
+
+       G_OBJECT_CLASS(mafw_gst_renderer_parent_class)->dispose(object);
+}
+
+static void mafw_gst_renderer_finalize(GObject *object)
+{
+       MafwGstRenderer *self = (MafwGstRenderer*) object;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       mafw_gst_renderer_clear_media(self);
+
+       if (self->media)
+       {
+               g_free(self->media);
+               self->media = NULL;
+       }
+
+       G_OBJECT_CLASS(mafw_gst_renderer_parent_class)->finalize(object);
+}
+
+/**
+ * mafw_gst_renderer_new:
+ * @registry: The registry that owns this renderer.
+ *
+ * Creates a new MafwGstRenderer object
+ */
+GObject *mafw_gst_renderer_new(MafwRegistry* registry)
+{
+       GObject* object;
+       LibHalContext *ctx;
+       DBusConnection *conn;
+       DBusError err;
+        char **jackets;
+        char **jack;
+        gint num_jacks;
+
+       object = g_object_new(MAFW_TYPE_GST_RENDERER,
+                             "uuid", MAFW_GST_RENDERER_UUID,
+                             "name", MAFW_GST_RENDERER_NAME,
+                             "plugin", MAFW_GST_RENDERER_PLUGIN_NAME,
+                             NULL);
+       g_assert(object != NULL);
+       MAFW_GST_RENDERER(object)->registry = g_object_ref(registry);
+
+       /* Set default error policy */
+       MAFW_GST_RENDERER(object)->error_policy =
+               MAFW_RENDERER_ERROR_POLICY_CONTINUE;
+
+        MAFW_GST_RENDERER(object)->tv_connected = FALSE;
+
+       /* Setup hal connection for reacting usb cable connected event */
+       dbus_error_init(&err);
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+
+        if (dbus_error_is_set(&err)) {
+               g_warning("Couldn't setup HAL connection: %s", err.message);
+                dbus_error_free(&err);
+
+               goto err1;
+        }
+       ctx = libhal_ctx_new();
+       libhal_ctx_set_dbus_connection(ctx, conn);
+       libhal_ctx_set_user_data(ctx, object);
+
+       if (libhal_ctx_init(ctx, &err) == FALSE) {
+               if (dbus_error_is_set(&err)) {
+                       g_warning("Could not initialize hal: %s", err.message);
+                       dbus_error_free(&err);
+               } else {
+                       g_warning("Could not initialize hal");
+               }
+               goto err2;
+       }
+
+       libhal_device_add_property_watch(ctx, HAL_VIDEOOUT_UDI, &err);
+
+       if (dbus_error_is_set(&err)) {
+               g_warning("Could not start watching usb device: %s",
+                          err.message);
+               dbus_error_free(&err);
+
+               goto err3;
+       }
+        libhal_ctx_set_device_property_modified(ctx, _property_modified);
+
+        /* Initializes blanking policy */
+        jackets = libhal_find_device_by_capability(ctx,
+                                                   "input.jack.video-out",
+                                                   &num_jacks, NULL);
+       if (jackets != NULL) {
+                jack = jackets;
+                while (*jack) {
+                        if (_tv_out_is_connected(ctx, *jack)) {
+                                MAFW_GST_RENDERER(object)->tv_connected = TRUE;
+                                break;
+                        }
+                        jack++;
+                }
+
+                blanking_control(*jack == NULL);
+                libhal_free_string_array(jackets);
+        }
+
+       MAFW_GST_RENDERER(object)->hal_ctx = ctx;
+
+       return object;
+err3:
+       libhal_ctx_shutdown(ctx, NULL);
+err2:
+       libhal_ctx_free(ctx);
+err1:
+       return object;
+}
+
+/**
+ * mafw_gst_renderer_error_quark:
+ *
+ * Fetches the quark representing the domain of the errors in the
+ * gst renderer
+ *
+ * Return value: a quark identifying the error domain of the
+ * #MafwGstRenderer objects.
+ *
+ **/
+GQuark mafw_gst_renderer_error_quark(void)
+{
+       return g_quark_from_static_string("mafw-gst-renderer-error-quark");
+}
+
+void mafw_gst_renderer_set_playback_mode(MafwGstRenderer *self,
+                                      MafwGstRendererPlaybackMode mode)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       self->playback_mode = mode;
+}
+
+MafwGstRendererPlaybackMode mafw_gst_renderer_get_playback_mode(
+       MafwGstRenderer *self)
+{
+       g_return_val_if_fail(MAFW_IS_GST_RENDERER(self),
+                            MAFW_GST_RENDERER_MODE_STANDALONE);
+       return self->playback_mode;
+}
+
+/*----------------------------------------------------------------------------
+  Set Media
+  ----------------------------------------------------------------------------*/
+
+static MafwSource* _get_source(MafwGstRenderer *renderer,
+                              const gchar *object_id)
+{
+       MafwSource* source;
+       gchar* sourceid = NULL;
+
+       g_assert(object_id != NULL);
+
+       /* Attempt to find a source that provided the object ID */
+       mafw_source_split_objectid(object_id, &sourceid, NULL);
+       source = MAFW_SOURCE(mafw_registry_get_extension_by_uuid(
+                                    renderer->registry, sourceid));
+       g_free(sourceid);
+
+       return source;
+}
+
+void mafw_gst_renderer_get_metadata(MafwGstRenderer* self,
+                                 const gchar* objectid,
+                                 GError **error)
+{
+       MafwSource* source;
+
+       g_assert(self != NULL);
+
+       /*
+        * Any error here is an error when trying to Play, so
+        * it must be handled by error policy.
+        * Problem: if we get an error here and we are not in
+        * Transitioning yet (maybe we are still in Stopped state)
+        * then the policy may move to next and stay Stopped (instead of
+        * trying to play), so  errors need to be handled by the policy
+         * in an idle callback, so that any error that may happen here
+         * is not processed until we have moved to Transitioning state
+        */
+
+       source = _get_source(self, objectid);
+       if (source != NULL)
+       {
+               /* List of metadata keys that we are interested in when going to
+                  Transitioning state */
+               static const gchar * const keys[] =
+                       { MAFW_METADATA_KEY_URI,
+                         MAFW_METADATA_KEY_IS_SEEKABLE,
+                         MAFW_METADATA_KEY_DURATION,
+                               NULL };
+
+               /* Source found, get metadata */
+               mafw_source_get_metadata(source, objectid,
+                                        keys,
+                                        _notify_metadata,
+                                        self);
+
+       }
+       else
+       {
+               /* This is a playback error: execute error policy */
+               MafwGstRendererErrorClosure *error_closure;
+               error_closure = g_new0(MafwGstRendererErrorClosure, 1);
+               error_closure->renderer = self;
+               g_set_error (&(error_closure->error),
+                            MAFW_EXTENSION_ERROR,
+                            MAFW_EXTENSION_ERROR_EXTENSION_NOT_AVAILABLE,
+                            "Unable to find source for current object ID");
+               g_idle_add(mafw_gst_renderer_manage_error_idle, error_closure);
+       }
+}
+
+void mafw_gst_renderer_set_object(MafwGstRenderer *self, const gchar *object_id)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(object_id != NULL);
+
+       /* This is intended to be called only when using play_object(),
+        * as for playlists we use set_media_playlist()
+        */
+
+       /* Stop any ongoing playback */
+       mafw_gst_renderer_clear_media(renderer);
+
+       /* Set new object */
+       renderer->media->object_id = g_strdup(object_id);
+
+       /* Signal media changed */
+       _signal_media_changed(renderer);
+}
+
+
+/**
+ * mafw_gst_renderer_clear_media:
+ *
+ * @renderer A #MafwGstRenderer whose media to clear
+ *
+ * Clears & frees the renderer's current media details
+ **/
+void mafw_gst_renderer_clear_media(MafwGstRenderer *self)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(self->media != NULL);
+
+       g_free(self->media->object_id);
+       self->media->object_id = NULL;
+
+       g_free(self->media->uri);
+       self->media->uri = NULL;
+
+       g_free(self->media->title);
+       self->media->title = NULL;
+
+       g_free(self->media->artist);
+       self->media->artist = NULL;
+
+       g_free(self->media->album);
+       self->media->album = NULL;
+
+       self->media->duration = 0;
+       self->media->position = 0;
+}
+
+
+/**
+ * mafw_gst_renderer_set_media_playlist:
+ *
+ * @self A #MafwGstRenderer, whose media to set
+ *
+ * Set current media from the renderer's playlist, using the current playlist index.
+ **/
+void mafw_gst_renderer_set_media_playlist(MafwGstRenderer* self)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       /* Get rid of old media details */
+       mafw_gst_renderer_clear_media(self);
+
+        if (self->playlist != NULL &&
+            mafw_playlist_iterator_get_size(self->iterator, NULL) > 0) {
+                /* Get the current item from playlist */
+                self->media->object_id =
+                       g_strdup(mafw_playlist_iterator_get_current_objectid(self->iterator));
+        } else {
+                self->media->object_id = NULL;
+       }
+
+       _signal_media_changed(self);
+}
+
+#ifdef HAVE_CONIC
+/*----------------------------------------------------------------------------
+  Connection
+  ----------------------------------------------------------------------------*/
+
+static void
+_con_ic_status_handler(ConIcConnection *conn, ConIcConnectionEvent *event,
+                      gpointer data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) data;
+
+       g_assert(MAFW_IS_GST_RENDERER(renderer));
+
+       renderer->connected =
+               con_ic_connection_event_get_status(event) ==
+               CON_IC_STATUS_CONNECTED;
+}
+
+static void
+_connection_init(MafwGstRenderer *renderer)
+{
+       g_assert (MAFW_IS_GST_RENDERER(renderer));
+
+       if (renderer->connection == NULL) {
+               renderer->connection = con_ic_connection_new();
+               renderer->connected = FALSE;
+
+               g_assert(renderer->connection != NULL);
+       }
+
+       g_object_set(renderer->connection, "automatic-connection-events",
+                     TRUE, NULL);
+        g_signal_connect(renderer->connection, "connection-event",
+                        G_CALLBACK (_con_ic_status_handler), renderer);
+
+       con_ic_connection_connect(renderer->connection,
+                                 CON_IC_CONNECT_FLAG_AUTOMATICALLY_TRIGGERED);
+}
+#endif
+
+/*----------------------------------------------------------------------------
+  Hal callbacks
+  ----------------------------------------------------------------------------*/
+
+static gboolean _tv_out_is_connected(LibHalContext *ctx, const char *udi)
+{
+        gboolean is_tv_out_jack = FALSE;
+        char **jack_types;
+        char **jack;
+
+        if (udi == NULL) {
+                return FALSE;
+        }
+
+        jack_types = libhal_device_get_property_strlist(ctx, udi,
+                                                        "input.jack.type",
+                                                        NULL);
+        if (jack_types == NULL) {
+                return FALSE;
+        }
+
+        jack = jack_types;
+        while (*jack) {
+                if (strcmp(*jack, "video-out") == 0) {
+                        is_tv_out_jack = TRUE;
+                        break;
+                } else {
+                        jack++;
+                }
+        }
+
+        libhal_free_string_array(jack_types);
+
+        return is_tv_out_jack;
+}
+
+static void _property_modified(LibHalContext *ctx, const char *udi,
+                               const char *key, dbus_bool_t is_removed,
+                               dbus_bool_t is_added)
+{
+        MafwGstRenderer *renderer;
+        gboolean connected;
+        GValue value = { 0 };
+
+        g_debug("HAL property modified! jack changed\n");
+        connected = _tv_out_is_connected(ctx, udi);
+        renderer = MAFW_GST_RENDERER(libhal_ctx_get_user_data(ctx));
+        if (renderer->tv_connected != connected) {
+                /* Notify the change */
+                renderer->tv_connected = connected;
+                g_value_init(&value, G_TYPE_BOOLEAN);
+                g_value_set_boolean(&value, renderer->tv_connected);
+                mafw_extension_emit_property_changed(
+                        MAFW_EXTENSION(renderer),
+                        MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED,
+                        &value);
+                g_value_unset(&value);
+        }
+        blanking_control(connected == FALSE);
+}
+
+/*----------------------------------------------------------------------------
+  GConf notifications
+  ----------------------------------------------------------------------------*/
+
+static void _battery_cover_open_cb(GConfClient *client,
+                                  guint cnxn_id,
+                                  GConfEntry *entry,
+                                  MafwGstRenderer *renderer)
+{
+       GConfValue *value = NULL;
+       gboolean is_cover_open;
+
+       value = gconf_entry_get_value(entry);
+       is_cover_open = gconf_value_get_bool(value);
+
+       if (is_cover_open) {
+               /* External mmc could be removed!. */
+               const gchar *emmc_path = g_getenv("MMC_MOUNTPOINT");
+
+               mafw_gst_renderer_state_handle_pre_unmount(
+                       MAFW_GST_RENDERER_STATE(
+                               renderer->states[renderer->current_state]),
+                                emmc_path);
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Gnome VFS notifications
+  ----------------------------------------------------------------------------*/
+
+static void _volume_pre_unmount_cb(GnomeVFSVolumeMonitor *monitor, 
+                                  GnomeVFSVolume *volume,
+                                  MafwGstRenderer *renderer)
+{
+       gchar *location = gnome_vfs_volume_get_activation_uri(volume);
+       if (!location) {
+               return;
+       }
+       
+       mafw_gst_renderer_state_handle_pre_unmount(
+               MAFW_GST_RENDERER_STATE(
+                       renderer->states[renderer->current_state]),
+               location);
+       
+       g_free(location);
+}
+
+/*----------------------------------------------------------------------------
+  Signals
+  ----------------------------------------------------------------------------*/
+
+
+/**
+ * _signal_state_changed:
+ * @self: A #MafwGstRenderer
+ *
+ * Signals state_changed to all UIs
+ **/
+static void _signal_state_changed(MafwGstRenderer * self)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_signal_emit_by_name(MAFW_RENDERER(self),
+                             "state-changed", self->current_state);
+}
+
+/**
+ * _signal_playlist_changed:
+ * @self: A #MafwGstRenderer
+ *
+ * Signals playlist update to all UIs
+ **/
+static void _signal_playlist_changed(MafwGstRenderer * self)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_signal_emit_by_name(MAFW_RENDERER(self),
+                             "playlist-changed", self->playlist);
+}
+
+/**
+ * _signal_media_changed:
+ * @self: A #MafwGstRenderer
+ *
+ * Signals media_changed to all UIs
+ **/
+static void _signal_media_changed(MafwGstRenderer *self)
+{
+
+       MafwGstRendererPlaybackMode mode;
+       gint index;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       mode = mafw_gst_renderer_get_playback_mode(MAFW_GST_RENDERER(self));
+       if ((mode == MAFW_GST_RENDERER_MODE_STANDALONE) ||
+            (self->iterator == NULL)) {
+               index = -1;
+       } else {
+               index = mafw_playlist_iterator_get_current_index(self->iterator);
+       }
+
+       g_signal_emit_by_name(MAFW_RENDERER(self),
+                             "media-changed",
+                             index,
+                             self->media->object_id);
+}
+
+/**
+ * _signal_transport_actions_property_changed:
+ * @self: A #MafwGstRenderer
+ *
+ * Signals transport_actions property_changed to all UIs
+ **/
+static void _signal_transport_actions_property_changed(MafwGstRenderer * self)
+{
+       GValue *value;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       value = mafw_gst_renderer_state_get_property_value(
+               MAFW_GST_RENDERER_STATE(
+                       self->states[self->current_state]),
+               MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS);
+
+        if (value) {
+                mafw_extension_emit_property_changed(
+                        MAFW_EXTENSION(self),
+                        MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS,
+                        value);
+                g_value_unset(value);
+                g_free(value);
+        }
+}
+
+
+/*----------------------------------------------------------------------------
+  State pattern support
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_set_state(MafwGstRenderer *self, MafwPlayState state)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       self->current_state = state;
+       _signal_state_changed(self);
+       _signal_transport_actions_property_changed(self);
+}
+
+void mafw_gst_renderer_play(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                         gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_play(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_play_object(MafwRenderer *self,
+                                const gchar *object_id,
+                                MafwRendererPlaybackCB callback,
+                                gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(object_id != NULL);
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_play_object(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               object_id,
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_stop(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                         gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       renderer->play_failed_count = 0;
+       mafw_gst_renderer_state_stop(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+
+void mafw_gst_renderer_pause(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                          gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_pause(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_resume(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                           gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_resume(
+               MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_next(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                         gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       renderer->play_failed_count = 0;
+       mafw_gst_renderer_state_next(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_previous(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                             gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       renderer->play_failed_count = 0;
+       mafw_gst_renderer_state_previous(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_goto_index(MafwRenderer *self, guint index,
+                               MafwRendererPlaybackCB callback,
+                               gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       renderer->play_failed_count = 0;
+       mafw_gst_renderer_state_goto_index(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               index,
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_get_position(MafwRenderer *self, MafwRendererPositionCB callback,
+                                 gpointer user_data)
+{
+       MafwGstRenderer *renderer;
+       gint pos;
+       GError *error = NULL;
+
+       g_return_if_fail(callback != NULL);
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       renderer = MAFW_GST_RENDERER(self);
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_get_position(
+                MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]),
+               &pos,
+               &error);
+       
+       callback(self, pos, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_set_position(MafwRenderer *self, MafwRendererSeekMode mode,
+                                  gint seconds, MafwRendererPositionCB callback,
+                                  gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_set_position(
+               MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]),
+               mode,
+               seconds,
+               &error);
+
+       if (callback != NULL)
+               callback(self, seconds, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+gboolean mafw_gst_renderer_manage_error_idle(gpointer data)
+{
+        MafwGstRendererErrorClosure *mec = (MafwGstRendererErrorClosure *) data;
+
+        mafw_gst_renderer_manage_error(mec->renderer, mec->error);
+       if (mec->error)
+               g_error_free(mec->error);
+        g_free(mec);
+
+        return FALSE;
+}
+
+static void _run_error_policy(MafwGstRenderer *self, const GError *in_err,
+                             GError **out_err)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+        gboolean play_next = FALSE;
+
+        /* Check what to do on error */
+       if (in_err->code == MAFW_EXTENSION_ERROR_OUT_OF_MEMORY) {
+                play_next = FALSE;
+       } else {
+               MafwGstRendererPlaybackMode mode;
+
+               mode = mafw_gst_renderer_get_playback_mode(self);
+
+               if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST) {
+                       /* In playlist mode we try to play next if
+                          error policy suggests so */
+                       play_next =
+                               (_get_error_policy(self) ==
+                                MAFW_RENDERER_ERROR_POLICY_CONTINUE);
+               } else {
+                       /* In standalone mode, then switch back to playlist
+                          mode and resume if necessary or move to Stopped
+                          otherwise */
+                       mafw_gst_renderer_set_playback_mode(
+                               self, MAFW_GST_RENDERER_MODE_PLAYLIST);
+                       mafw_gst_renderer_set_media_playlist(self);
+                       if (self->resume_playlist) {
+                               mafw_gst_renderer_play(MAFW_RENDERER(self),
+                                                    NULL, NULL);
+                       } else {
+                               mafw_gst_renderer_worker_stop(self->worker);
+                               mafw_gst_renderer_set_state(self, Stopped);
+                       }
+                       if (out_err) *out_err = g_error_copy(in_err);
+
+                       /* Bail out, he have already managed the error
+                          for the case of standalone mode */
+                       return;
+               }
+       }
+
+       if (play_next) {
+               if (self->playlist){
+                       MafwPlaylistIteratorMovementResult result;
+
+                       result = mafw_playlist_iterator_move_to_next(self->iterator,
+                                                                     NULL);
+                       self->play_failed_count++;
+
+                       if (mafw_playlist_iterator_get_size(self->iterator,
+                               NULL) <=
+                               self->play_failed_count)
+                       {
+                               mafw_gst_renderer_state_stop(
+                                       MAFW_GST_RENDERER_STATE(self->states[self->current_state]),
+                                       NULL);
+                               self->play_failed_count = 0;
+                               mafw_gst_renderer_set_media_playlist(self);
+                       } else if (result !=
+                                  MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK) {
+                               mafw_playlist_iterator_reset(self->iterator, NULL);
+                               mafw_gst_renderer_set_media_playlist(self);
+                               mafw_gst_renderer_stop(MAFW_RENDERER(self), NULL, NULL);
+                       } else {
+                               mafw_gst_renderer_set_media_playlist(self);
+                               mafw_gst_renderer_play(MAFW_RENDERER(self), NULL, NULL);
+                       }
+
+                       if (out_err) *out_err = g_error_copy(in_err);
+               }
+       } else {
+               /* We cannot move to next in the playlist or decided
+                  we do not want to do it, just stop on error */
+                mafw_gst_renderer_stop(MAFW_RENDERER(self), NULL, NULL);
+                if (out_err) *out_err = g_error_copy(in_err);
+       }
+}
+
+static void _metadata_set_cb(MafwSource *self, const gchar *object_id,
+                               const gchar **failed_keys, gpointer user_data,
+                               const GError *error)
+{
+       if (error != NULL) {
+               g_debug("Ignoring error received when setting metadata: "
+                       "%s (%d): %s", g_quark_to_string(error->domain),
+                       error->code, error->message);
+       } else {
+               g_debug("Metadata set correctly");
+       }
+}
+
+/**
+ * _update_playcount_metadata_cb:
+ * @cb_source:   The #MafwSource that sent the metadata results
+ * @cb_object_id: The object ID, whose metadata results were received
+ * @cb_metadata: GHashTable containing metadata key-value pairs
+ * @cb_user_data: Optional user data pointer (self)
+ * @cb_error:    Set if any errors occurred during metadata browsing
+ *
+ * Receives the results of a metadata request about the playcount. It increases
+ * it, or sets to 1, and sets the metadata to that.
+ */
+static void _update_playcount_metadata_cb (MafwSource *cb_source,
+                                          const gchar *cb_object_id,
+                                          GHashTable *cb_metadata,
+                                          gpointer cb_user_data,
+                                          const GError *cb_error)
+{
+       GValue *curval = NULL;
+       gint curplaycount = -1;
+       GHashTable *mdata = cb_user_data;
+
+       if (cb_error == NULL) {
+               if (cb_metadata)
+                       curval = mafw_metadata_first(cb_metadata,
+                               MAFW_METADATA_KEY_PLAY_COUNT);
+               if (curval && !G_VALUE_HOLDS(curval, G_TYPE_INT))
+                       goto set_data;
+               if (curval)
+               {
+                       curplaycount = g_value_get_int(curval);
+                       curplaycount++;
+               }
+               else
+               { /* Playing at first time, or not supported... */
+                       curplaycount = 1;
+               }
+               if (!mdata)
+                       mdata = mafw_metadata_new();
+               mafw_metadata_add_int(mdata,
+                                       MAFW_METADATA_KEY_PLAY_COUNT,
+                                       curplaycount);
+
+       } else {
+               g_warning("_playcount_metadata received an error: "
+                         "%s (%d): %s", g_quark_to_string(cb_error->domain),
+                         cb_error->code, cb_error->message);
+               if (mdata)
+                       g_hash_table_unref(mdata);
+               return;
+       }
+set_data:
+       
+       if (mdata)
+       {
+               mafw_source_set_metadata(cb_source, cb_object_id, mdata,
+                                               _metadata_set_cb, NULL);
+               g_hash_table_unref(mdata);
+       }
+}
+
+/**
+ * mafw_gst_renderer_add_lastplayed:
+ * @mdata:   Exisiting mdata, or NULL
+ *
+ * Sets the MAFW_METADATA_KEY_LAST_PLAYED metadata in the given metadata-table,
+ * or creates a new metadata-table, and sets the current time there.
+ */
+static GHashTable *mafw_gst_renderer_add_lastplayed(GHashTable *mdata)
+{
+       GHashTable *metadata;
+       GTimeVal timeval;
+
+       
+       if (!mdata)
+               metadata = mafw_metadata_new();
+       else
+               metadata = mdata;
+       
+               
+
+       g_get_current_time(&timeval);
+               
+       mafw_metadata_add_long(metadata,
+                                       MAFW_METADATA_KEY_LAST_PLAYED,
+                                       timeval.tv_sec);
+       return metadata;
+}
+
+/**
+ * mafw_gst_renderer_increase_playcount:
+ * @self:   Gst renderer
+ * @object_id: The object ID of the touched object
+ * @mdat: Existing metadatas to add the playcount to, or NULL
+ *
+ * Increases the playcount of the given object.
+ */
+static void mafw_gst_renderer_increase_playcount(MafwGstRenderer* self,
+                                       const gchar *object_id, GHashTable *mdat)
+{
+       MafwSource* source;
+
+       g_assert(self != NULL);
+       source = _get_source(self, object_id);
+       if (source != NULL)
+       {
+               static const gchar * const keys[] =
+                       { MAFW_METADATA_KEY_PLAY_COUNT, NULL };
+
+               mafw_source_get_metadata(source, object_id,
+                                        keys,
+                                        _update_playcount_metadata_cb,
+                                        mdat);
+
+       }
+}
+
+/**
+ * mafw_gst_renderer_update_stats:
+ * @data: user data
+ *
+ * Updates both playcount and lastplayed after a while.
+ **/
+gboolean mafw_gst_renderer_update_stats(gpointer data)
+{
+        MafwGstRenderer *renderer = (MafwGstRenderer *) data;
+
+        /* Update stats only for audio content */
+        if (renderer->media->object_id &&
+            !renderer->worker->media.has_visual_content) {
+               GHashTable *mdata = mafw_gst_renderer_add_lastplayed(NULL);
+               mafw_gst_renderer_increase_playcount(renderer,
+                                                     renderer->media->object_id,
+                                                     mdata);
+       }
+        renderer->update_playcount_id = 0;
+        return FALSE;
+}
+
+void mafw_gst_renderer_update_source_duration(MafwGstRenderer *renderer,
+                                             gint duration)
+{
+       GHashTable *metadata;
+       MafwSource* source;
+
+       source = _get_source(renderer, renderer->media->object_id);
+       g_return_if_fail(source != NULL);
+
+       renderer->media->duration = duration;
+
+       g_debug("updated source duration to %d", duration);
+
+       metadata = mafw_metadata_new();
+       mafw_metadata_add_int(metadata, MAFW_METADATA_KEY_DURATION, duration);
+
+       mafw_source_set_metadata(source, renderer->media->object_id, metadata,
+                                _metadata_set_cb, NULL);
+       g_hash_table_unref(metadata);
+}
+
+/**
+ * _notify_metadata:
+ * @source:   The #MafwSource that sent the metadata results
+ * @objectid: The object ID, whose metadata results were received
+ * @metadata: GHashTable containing metadata key-value pairs
+ * @userdata: Optional user data pointer (self)
+ * @error:    Set if any errors occurred during metadata browsing
+ *
+ * Receives the results of a metadata request.
+ */
+static void _notify_metadata (MafwSource *cb_source,
+                             const gchar *cb_object_id,
+                             GHashTable *cb_metadata,
+                             gpointer cb_user_data,
+                             const GError *cb_error)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) cb_user_data;
+        GError *mafw_error = NULL;
+       GError *error = NULL;
+       GValue *mval;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       g_debug("running _notify_metadata...");
+
+       mval = mafw_metadata_first(cb_metadata, MAFW_METADATA_KEY_URI);
+
+       if (cb_error == NULL && mval != NULL) {
+               mafw_gst_renderer_state_notify_metadata(
+                       MAFW_GST_RENDERER_STATE(
+                               renderer->states[renderer->current_state]),
+                       cb_object_id,
+                       cb_metadata,
+                       &error);
+       }
+       else {
+                g_set_error(&mafw_error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_URI_NOT_AVAILABLE, "%s",
+                           cb_error ? cb_error->message : "URI not available");
+               mafw_gst_renderer_manage_error(renderer, mafw_error);
+                g_error_free(mafw_error);
+       }
+}
+
+static void _notify_play(MafwGstRendererWorker *worker, gpointer owner)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       g_debug("running _notify_play...");
+
+       mafw_gst_renderer_state_notify_play(renderer->states[renderer->current_state],
+                                         &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error",
+                                     error->domain,
+                                     error->code,
+                                     error->message);
+               g_error_free (error);
+       }
+}
+
+static void _notify_pause(MafwGstRendererWorker *worker, gpointer owner)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER (renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_notify_pause(renderer->states[renderer->current_state],
+                                          &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error",
+                                     error->domain, error->code,
+                                     error->message);
+               g_error_free(error);
+       }
+}
+
+static void _notify_buffer_status (MafwGstRendererWorker *worker,
+                                  gpointer owner,
+                                  gdouble percent)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_notify_buffer_status(
+               renderer->states[renderer->current_state],
+               percent,
+               &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error",
+                                     error->domain, error->code,
+                                     error->message);
+               g_error_free(error);
+       }
+}
+
+static void _notify_seek(MafwGstRendererWorker *worker, gpointer owner)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_notify_seek(renderer->states[renderer->current_state],
+                                         &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error",
+                                     error->domain, error->code,
+                                     error->message);
+               g_error_free(error);
+       }
+}
+
+static void _playlist_changed_handler(MafwPlaylistIterator *iterator,
+                                     gboolean clip_changed, GQuark domain,
+                                     gint code, const gchar *message,
+                                     gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) user_data;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       /* We update the current index and media here,  for this is
+          the same for all the states. Then we delegate in the state
+          to finish the task (for example, start playback if needed) */
+
+       if (renderer->playlist == NULL) {
+               g_critical("Got iterator:contents-changed but renderer has no" \
+                          "playlist assigned!. Skipping...");
+               return;
+       }
+
+       if (domain != 0) {
+               g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error",
+                                     domain, code, message);
+       } else {
+               GError *error = NULL;
+               MafwGstRendererPlaybackMode mode;
+               
+               mode = mafw_gst_renderer_get_playback_mode(renderer);
+
+               /* Only in non-playobject mode */               
+               if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST)
+                       mafw_gst_renderer_set_media_playlist(renderer);
+
+               /* We let the state know if the current clip has changed as
+                  result of this operation, so it can do its work */
+               mafw_gst_renderer_state_playlist_contents_changed_handler(
+                       renderer->states[renderer->current_state],
+                       clip_changed,
+                       &error);
+
+               if (error != NULL) {
+                       g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error",
+                                             error->domain, error->code,
+                                             error->message);
+                       g_error_free(error);
+               }
+       }
+}
+
+static void _error_handler(MafwGstRendererWorker *worker, gpointer owner,
+                          const GError *error)
+{
+        MafwGstRenderer *renderer = MAFW_GST_RENDERER(owner);
+
+        mafw_gst_renderer_manage_error(renderer, error);
+}
+
+void mafw_gst_renderer_manage_error(MafwGstRenderer *self, const GError *error)
+{
+        GError *new_err = NULL;
+       GError *raise_error = NULL;
+        GQuark new_err_domain = MAFW_RENDERER_ERROR;
+        gint new_err_code = 0;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((self->states != 0) &&
+                        (self->current_state != _LastMafwPlayState) &&
+                        (self->states[self->current_state] != NULL));
+
+       g_warning("Got error in renderer:\n\tdomain: %d, code: %d, message: %s",
+                 error->domain, error->code, error->message);
+
+        /* Get a MAFW error */
+       if (error->domain == GST_RESOURCE_ERROR) {
+               /* handle RESOURCE errors */
+               switch (error->code) {
+               case GST_RESOURCE_ERROR_READ:
+                       if (is_current_uri_stream(self)) {
+#ifdef HAVE_CONIC
+                               if (self->connected) {
+                                       new_err_code = MAFW_RENDERER_ERROR_STREAM_DISCONNECTED;
+                               } else {
+                                       new_err_domain = MAFW_EXTENSION_ERROR;
+                                       new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN;
+                               }
+#else
+                               /* Stream + cannot read resource ->
+                                  disconnected */
+                               new_err_code = MAFW_RENDERER_ERROR_STREAM_DISCONNECTED;
+#endif
+                       } else {
+                               /* This shouldn't happen */
+                               /* Unknown RESOURCE error */
+                               new_err_domain = MAFW_EXTENSION_ERROR;
+                               new_err_code = MAFW_EXTENSION_ERROR_FAILED;
+                       }
+                       break;
+               case GST_RESOURCE_ERROR_NOT_FOUND:
+#ifdef HAVE_CONIC
+                       if (!is_current_uri_stream(self) || self->connected) {
+                               new_err_code =
+                                       MAFW_RENDERER_ERROR_INVALID_URI;
+                       } else {
+                               new_err_domain = MAFW_EXTENSION_ERROR;
+                               new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN;
+                       }
+#else
+                       new_err_code =
+                               MAFW_RENDERER_ERROR_INVALID_URI;
+#endif
+                       break;
+               case GST_RESOURCE_ERROR_OPEN_READ_WRITE:
+               case GST_RESOURCE_ERROR_OPEN_READ:
+#ifdef HAVE_CONIC
+                       if (!is_current_uri_stream(self) || self->connected) {
+                               new_err_code =
+                                       MAFW_RENDERER_ERROR_MEDIA_NOT_FOUND;
+                       } else {
+                               new_err_domain = MAFW_EXTENSION_ERROR;
+                               new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN;
+                       }
+#else
+                       new_err_code =
+                               MAFW_RENDERER_ERROR_MEDIA_NOT_FOUND;
+#endif
+                       break;
+               case GST_RESOURCE_ERROR_NO_SPACE_LEFT:
+                       new_err_domain = MAFW_EXTENSION_ERROR;
+                       new_err_code = MAFW_EXTENSION_ERROR_OUT_OF_MEMORY;
+                       break;
+               case GST_RESOURCE_ERROR_WRITE:
+                       /* DSP renderers send ERROR_WRITE when they find
+                          corrupted data */
+                       new_err_code = MAFW_RENDERER_ERROR_CORRUPTED_FILE;
+                       break;
+               case GST_RESOURCE_ERROR_SEEK:
+                       new_err_code = MAFW_RENDERER_ERROR_CANNOT_SET_POSITION;
+                       break;
+               default:
+                       /* Unknown RESOURCE error */
+                       new_err_domain = MAFW_EXTENSION_ERROR;
+                       new_err_code = MAFW_EXTENSION_ERROR_FAILED;
+               }
+
+       } else if (error->domain == GST_STREAM_ERROR) {
+               /* handle STREAM errors */
+               switch (error->code) {
+               case GST_STREAM_ERROR_TYPE_NOT_FOUND:
+                       new_err_code = MAFW_RENDERER_ERROR_TYPE_NOT_AVAILABLE;
+                       break;
+               case GST_STREAM_ERROR_FORMAT:
+               case GST_STREAM_ERROR_WRONG_TYPE:
+               case GST_STREAM_ERROR_FAILED:
+                        new_err_code = MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE;
+                       break;
+               case GST_STREAM_ERROR_DECODE:
+               case GST_STREAM_ERROR_DEMUX:
+                        new_err_code = MAFW_RENDERER_ERROR_CORRUPTED_FILE;
+                       break;
+               case GST_STREAM_ERROR_CODEC_NOT_FOUND:
+                       new_err_code = MAFW_RENDERER_ERROR_CODEC_NOT_FOUND;
+                       break;
+               case GST_STREAM_ERROR_DECRYPT:
+               case GST_STREAM_ERROR_DECRYPT_NOKEY:
+                       new_err_code = MAFW_RENDERER_ERROR_DRM;
+                       break;
+               default:
+                       /* Unknown STREAM error */
+                        new_err_domain = MAFW_EXTENSION_ERROR;
+                        new_err_code = MAFW_EXTENSION_ERROR_FAILED;
+                }
+       } else if (error->domain == MAFW_GST_RENDERER_ERROR) {
+               /* Handle own errors. Errors that belong to this domain:
+                     - MAFW_GST_RENDERER_ERROR_PLUGIN_NOT_FOUND,
+                     - MAFW_GST_RENDERER_ERROR_VIDEO_CODEC_NOT_SUPPORTED,
+                     - MAFW_GST_RENDERER_ERROR_AUDIO_CODEC_NOT_SUPPORTED */
+               new_err_code = MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE;
+       } else if (error->domain == MAFW_RENDERER_ERROR) {
+               /* Worker may have sent MAFW_RENDERER_ERROR as well.
+                  No processing needed */
+               new_err_code = error->code;
+       } else {
+                /* default */
+               /* Unknown error */
+               new_err_domain = MAFW_EXTENSION_ERROR;
+               new_err_code = MAFW_EXTENSION_ERROR_FAILED;
+       }
+
+       g_set_error(&new_err, new_err_domain, new_err_code, "%s", error->message);
+
+        _run_error_policy(self, new_err, &raise_error);
+        g_error_free(new_err);
+
+        if (raise_error) {
+                g_signal_emit_by_name(MAFW_EXTENSION (self), "error",
+                                     raise_error->domain,
+                                     raise_error->code,
+                                     raise_error->message);
+                g_error_free(raise_error);
+        }
+}
+
+static void _notify_eos(MafwGstRendererWorker *worker, gpointer owner)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER (renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_notify_eos(renderer->states[renderer->current_state],
+                                        &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error",
+                                     error->domain, error->code,
+                                     error->message);
+               g_error_free(error);
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Status
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_get_status(MafwRenderer *self, MafwRendererStatusCB callback,
+                               gpointer user_data)
+{
+       MafwGstRenderer* renderer;
+       gint index;
+       MafwGstRendererPlaybackMode mode;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(callback != NULL);
+       renderer = MAFW_GST_RENDERER(self);
+
+       mode = mafw_gst_renderer_get_playback_mode(MAFW_GST_RENDERER(self));
+       if ((mode == MAFW_GST_RENDERER_MODE_STANDALONE) || (renderer->iterator == NULL)) {
+               index = -1;
+       } else {
+               index =
+                       mafw_playlist_iterator_get_current_index(renderer->iterator);
+       }
+
+       /* TODO: Set error parameter */
+       callback(self, renderer->playlist, index, renderer->current_state,
+                (const gchar*) renderer->media->object_id, user_data, NULL);
+}
+
+void mafw_gst_renderer_get_current_metadata(MafwRenderer *self,
+                                           MafwRendererMetadataResultCB callback,
+                                           gpointer user_data)
+{
+       MafwGstRenderer *renderer;
+       GHashTable *metadata;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       renderer = MAFW_GST_RENDERER(self);
+
+       metadata = mafw_gst_renderer_worker_get_current_metadata(
+                       renderer->worker);
+
+       callback(self,
+                (const gchar*) renderer->media->object_id,
+                metadata,
+                user_data,
+                NULL);
+}
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void
+_playlist_contents_changed_handler(MafwPlaylist *playlist,
+                                       guint from, guint nremove,
+                                       guint nreplace,
+                                       MafwGstRenderer *renderer)
+{
+       /* Item(s) added to playlist, so new playable items could come */
+       if (nreplace)
+               renderer->play_failed_count = 0;
+}
+
+gboolean mafw_gst_renderer_assign_playlist(MafwRenderer *self,
+                                          MafwPlaylist *playlist,
+                                          GError **error)
+{
+       MafwGstRenderer* renderer = (MafwGstRenderer*) self;
+
+       g_return_val_if_fail(MAFW_IS_GST_RENDERER(self), FALSE);
+
+       /* Get rid of previously assigned playlist  */
+       if (renderer->playlist != NULL) {
+               g_signal_handlers_disconnect_matched(renderer->iterator,
+                                                    (GSignalMatchType) G_SIGNAL_MATCH_FUNC,
+                                                    0, 0, NULL,
+                                                    _playlist_changed_handler,
+                                                    NULL);
+               g_signal_handlers_disconnect_matched(renderer->playlist,
+                                       (GSignalMatchType) G_SIGNAL_MATCH_FUNC,
+                                       0, 0, NULL,
+                                       G_CALLBACK(_playlist_contents_changed_handler),
+                                       NULL);
+               /* Decrement the use count of the previous playlist because the
+                  renderer isn't going to use it more */
+               mafw_playlist_decrement_use_count(renderer->playlist, NULL);
+
+               g_object_unref(renderer->iterator);
+               g_object_unref(renderer->playlist);
+       }
+
+       /* Assign the new playlist */
+        if (playlist == NULL) {
+               renderer->playlist = NULL;
+               renderer->iterator = NULL;
+       } else {
+               GError *new_error = NULL;
+               MafwPlaylistIterator *iterator = NULL;
+
+               iterator = mafw_playlist_iterator_new();
+               mafw_playlist_iterator_initialize(iterator, playlist,
+                                                  &new_error);
+               
+               g_object_ref(playlist);
+
+               if (new_error == NULL) {
+
+                       renderer->playlist = playlist;
+                       renderer->iterator = iterator;
+
+                       /* Increment the use_count to avoid the playlist destruction
+                          while the playlist is assigned to some renderer */
+                       mafw_playlist_increment_use_count(renderer->playlist, NULL);
+
+                       g_signal_connect(iterator,
+                                        "playlist-changed",
+                                        G_CALLBACK(_playlist_changed_handler),
+                                        renderer);
+                       g_signal_connect(renderer->playlist,
+                                        "contents-changed",
+                                        G_CALLBACK(_playlist_contents_changed_handler),
+                                        renderer);
+               }
+               else {
+                       g_propagate_error (error, new_error);
+               }
+       }
+
+       /* Set the new media and signal playlist changed signal */
+       _signal_playlist_changed(renderer);
+       mafw_gst_renderer_set_media_playlist(renderer);
+
+
+       /* Stop playback */
+       mafw_gst_renderer_stop(MAFW_RENDERER(renderer), NULL , NULL);
+
+       return TRUE;
+}
+
+MafwGstRendererMovementResult mafw_gst_renderer_move(MafwGstRenderer *renderer,
+                                                  MafwGstRendererMovementType type,
+                                                  guint index,
+                                                  GError **error)
+{
+       MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK;
+
+        if (renderer->playlist == NULL) {
+               value = MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST;
+        } else {
+               MafwPlaylistIteratorMovementResult result;
+
+               switch (type) {
+               case MAFW_GST_RENDERER_MOVE_TYPE_INDEX:
+                       result =
+                               mafw_playlist_iterator_move_to_index(renderer->iterator,
+                                                                     index,
+                                                                     error);
+                       break;
+               case MAFW_GST_RENDERER_MOVE_TYPE_PREV:
+                       result =
+                               mafw_playlist_iterator_move_to_prev(renderer->iterator,
+                                                                    error);
+                       break;
+               case MAFW_GST_RENDERER_MOVE_TYPE_NEXT:
+                       result =
+                               mafw_playlist_iterator_move_to_next(renderer->iterator,
+                                                                    error);
+                       break;
+               }
+
+               switch (result) {
+               case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK:
+                       value = MAFW_GST_RENDERER_MOVE_RESULT_OK;
+                       mafw_gst_renderer_set_media_playlist(renderer);
+                       break;
+               case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID:
+                       g_critical("Iterator is invalid!");
+                       value = MAFW_GST_RENDERER_MOVE_RESULT_ERROR;
+                       break;
+               case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR:
+                       value = MAFW_GST_RENDERER_MOVE_RESULT_ERROR;
+                       break;
+               case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT:
+                       value = MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT;
+                       break;
+               }
+       }
+
+       return value;
+}
+
+/*----------------------------------------------------------------------------
+  Properties
+  ----------------------------------------------------------------------------*/
+
+static void _set_error_policy(MafwGstRenderer *renderer, MafwRendererErrorPolicy policy)
+{
+       renderer->error_policy = policy;
+}
+
+static MafwRendererErrorPolicy _get_error_policy(MafwGstRenderer *renderer)
+{
+       return renderer->error_policy;
+}
+
+static void mafw_gst_renderer_get_property(MafwExtension *self,
+                                        const gchar *key,
+                                        MafwExtensionPropertyCallback callback,
+                                        gpointer user_data)
+{
+       MafwGstRenderer *renderer;
+       GValue *value = NULL;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(callback != NULL);
+       g_return_if_fail(key != NULL);
+
+       renderer = MAFW_GST_RENDERER(self);
+       if (!strcmp(key, MAFW_PROPERTY_RENDERER_VOLUME)) {
+               guint volume;
+
+               volume = mafw_gst_renderer_worker_get_volume(
+                       renderer->worker);
+
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_UINT);
+               g_value_set_uint(value, volume);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_MUTE)) {
+               gboolean mute;
+               mute = mafw_gst_renderer_worker_get_mute(renderer->worker);
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_BOOLEAN);
+               g_value_set_boolean(value, mute);
+       }
+       else if (!strcmp (key, MAFW_PROPERTY_RENDERER_XID)) {
+               guint xid;
+               xid = mafw_gst_renderer_worker_get_xid(renderer->worker);
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_UINT);
+               g_value_set_uint(value, xid);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_ERROR_POLICY)) {
+               guint policy;
+               policy = _get_error_policy(renderer);
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_UINT);
+               g_value_set_uint(value, policy);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_AUTOPAINT)) {
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_BOOLEAN);
+               g_value_set_boolean(
+                       value,
+                       mafw_gst_renderer_worker_get_autopaint(
+                               renderer->worker));
+       } else if (!strcmp(key, MAFW_PROPERTY_RENDERER_COLORKEY)) {
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_INT);
+               g_value_set_int(
+                       value,
+                       mafw_gst_renderer_worker_get_colorkey(
+                               renderer->worker));
+       }
+#ifdef HAVE_GDKPIXBUF
+       else if (!strcmp(key,
+                        MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE)) {
+               gboolean current_frame_on_pause;
+               current_frame_on_pause =
+                       mafw_gst_renderer_worker_get_current_frame_on_pause(renderer->worker);
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_BOOLEAN);
+               g_value_set_boolean(value, current_frame_on_pause);
+       }
+#endif
+        else if (!strcmp(key,
+                         MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED)) {
+                value = g_new0(GValue, 1);
+                g_value_init(value, G_TYPE_BOOLEAN);
+                g_value_set_boolean(value, renderer->tv_connected);
+        }
+       else if (!strcmp(key,
+                        MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)){
+               /* Delegate in the state. */
+               value = mafw_gst_renderer_state_get_property_value(
+                       MAFW_GST_RENDERER_STATE(
+                               renderer->states[renderer->current_state]),
+                       MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS);
+
+               if (!value) {
+                       /* Something goes wrong. */
+                       error = g_error_new(
+                               MAFW_GST_RENDERER_ERROR,
+                               MAFW_EXTENSION_ERROR_GET_PROPERTY,
+                               "Error while getting the property value");
+               }
+       }
+       else {
+               /* Unsupported property */
+               error = g_error_new(MAFW_GST_RENDERER_ERROR,
+                                   MAFW_EXTENSION_ERROR_GET_PROPERTY,
+                                   "Unsupported property");
+       }
+
+       callback(self, key, value, user_data, error);
+}
+
+static void mafw_gst_renderer_set_property(MafwExtension *self,
+                                        const gchar *key,
+                                        const GValue *value)
+{
+       MafwGstRenderer *renderer;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(key != NULL);
+
+       renderer = MAFW_GST_RENDERER(self);
+
+       if (!strcmp(key, MAFW_PROPERTY_RENDERER_VOLUME)) {
+               guint volume = g_value_get_uint(value);
+               if (volume > 100)
+                       volume = 100;
+                mafw_gst_renderer_worker_set_volume(renderer->worker,
+                                                          volume);
+                /* Property-changed emision is done by worker */
+                return;
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_MUTE)) {
+               gboolean mute = g_value_get_boolean(value);
+                mafw_gst_renderer_worker_set_mute(renderer->worker, mute);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_XID)) {
+               XID xid = g_value_get_uint(value);
+               mafw_gst_renderer_worker_set_xid(renderer->worker, xid);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_ERROR_POLICY)) {
+               MafwRendererErrorPolicy policy = g_value_get_uint(value);
+               _set_error_policy(renderer, policy);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_AUTOPAINT)) {
+               mafw_gst_renderer_worker_set_autopaint(
+                       renderer->worker,
+                       g_value_get_boolean(value));
+       }
+#ifdef HAVE_GDKPIXBUF
+       else if (!strcmp(key,
+                        MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE)) {
+               gboolean current_frame_on_pause = g_value_get_boolean(value);
+               mafw_gst_renderer_worker_set_current_frame_on_pause(renderer->worker,
+                                                                          current_frame_on_pause);
+       }
+#endif
+       else return;
+
+       /* FIXME I'm not sure when to emit property-changed signals.
+        * Maybe we should let the worker do it, when the change
+        * reached the hardware... */
+       mafw_extension_emit_property_changed(self, key, value);
+}
+
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/libmafw-gst-renderer/mafw-gst-renderer.h b/libmafw-gst-renderer/mafw-gst-renderer.h
new file mode 100644 (file)
index 0000000..0350e34
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef MAFW_GST_RENDERER_H
+#define MAFW_GST_RENDERER_H
+
+#include <glib-object.h>
+#include <libmafw/mafw-renderer.h>
+#include <libmafw/mafw-registry.h>
+#include <gst/gst.h>
+#include <gst/pbutils/pbutils.h>
+#include <libhal.h>
+#include <gconf/gconf-client.h>
+
+#include "mafw-gst-renderer-utils.h"
+#include "mafw-gst-renderer-worker.h"
+#include "mafw-playlist-iterator.h"
+/* Solving the cyclic dependencies */
+typedef struct _MafwGstRenderer MafwGstRenderer;
+typedef struct _MafwGstRendererClass MafwGstRendererClass;
+#include "mafw-gst-renderer-state.h"
+
+#ifdef HAVE_CONIC
+#include <conicconnection.h>
+#endif
+
+typedef enum {
+       MAFW_GST_RENDERER_ERROR_PLUGIN_NOT_FOUND,
+       MAFW_GST_RENDERER_ERROR_VIDEO_CODEC_NOT_SUPPORTED,
+       MAFW_GST_RENDERER_ERROR_AUDIO_CODEC_NOT_SUPPORTED,
+} MafwGstRendererError;
+
+typedef enum {
+       MAFW_GST_RENDERER_MODE_PLAYLIST,
+       MAFW_GST_RENDERER_MODE_STANDALONE,
+} MafwGstRendererPlaybackMode;
+
+typedef enum {
+       MAFW_GST_RENDERER_MOVE_RESULT_OK,
+       MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST,
+       MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT,
+       MAFW_GST_RENDERER_MOVE_RESULT_ERROR,
+} MafwGstRendererMovementResult;
+
+typedef enum {
+       MAFW_GST_RENDERER_MOVE_TYPE_INDEX,
+       MAFW_GST_RENDERER_MOVE_TYPE_PREV,
+       MAFW_GST_RENDERER_MOVE_TYPE_NEXT,
+} MafwGstRendererMovementType;
+
+#ifdef HAVE_GDKPIXBUF
+#define MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE       \
+       "current-frame-on-pause"
+#endif
+
+#define MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED "tv-connected"
+
+/*----------------------------------------------------------------------------
+  GObject type conversion macros
+  ----------------------------------------------------------------------------*/
+
+#define MAFW_TYPE_GST_RENDERER                  \
+        (mafw_gst_renderer_get_type())
+#define MAFW_GST_RENDERER(obj)                                          \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER, MafwGstRenderer))
+#define MAFW_IS_GST_RENDERER(obj)                                       \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER))
+#define MAFW_GST_RENDERER_CLASS(klass)                                  \
+       (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER, MafwGstRenderer))
+#define MAFW_GST_RENDERER_GET_CLASS(obj)                                \
+       (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER,       \
+                                  MafwGstRendererClass))
+#define MAFW_IS_GST_RENDERER_CLASS(klass)                               \
+       (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER))
+
+#define MAFW_GST_RENDERER_ERROR (mafw_gst_renderer_error_quark ())
+
+/* Gst renderer plugin name for the plugin descriptor */
+#define MAFW_GST_RENDERER_PLUGIN_NAME "Mafw-Gst-Renderer-Plugin"
+/* Gst renderer name */
+#define MAFW_GST_RENDERER_NAME "Mafw-Gst-Renderer"
+/* Gst renderer UUID */
+#define MAFW_GST_RENDERER_UUID "gstrenderer"
+
+/*----------------------------------------------------------------------------
+  Type definitions
+  ----------------------------------------------------------------------------*/
+
+typedef struct {
+       gchar *object_id;
+       gchar *uri;
+       gchar *title;
+       gchar *artist;
+       gchar *album;
+
+       gint duration;
+       gint position;
+
+       /* Seekability coming from source */
+       SeekabilityType seekability;
+} MafwGstRendererMedia;
+
+struct _MafwGstRendererClass {
+       MafwRendererClass parent;
+};
+
+/*
+ * media:             Current media details
+ * worker:            Worker
+ * registry:          The registry that owns this renderer
+ * media_timer:      Stream timer data
+ * current_state:     The renderer's current state
+ * playlist:          The renderer's playlist
+ * play_index:        A playlist index that is currently playing
+ * seek_pending:      Seek is pending or ongoing
+ * seek_type_pending: Type of the pending seek
+ * seeking_to:        The position of pending seek (milliseconds)
+ * is_stream:         is the URI a stream?
+ * play_failed_count: The number of unably played items from the playlist.
+ * playback_mode:     Playback mode
+ * resume_playlist:   Do we want to resume playlist playback when play_object
+ *                    is finished
+ * states:            State array
+ * error_policy:      error policy
+ * tv_connected:      if TV-out cable is connected
+ */
+struct _MafwGstRenderer{
+       MafwRenderer parent;
+
+       MafwGstRendererMedia *media;
+       MafwGstRendererWorker *worker;
+       MafwRegistry *registry;
+       LibHalContext *hal_ctx;
+       MafwPlayState current_state;
+       MafwPlaylist *playlist;
+       MafwPlaylistIterator *iterator;
+       gboolean seek_pending;
+       GstSeekType seek_type_pending;
+       gint seeking_to;
+       gboolean is_stream;
+        gint update_playcount_id;
+       guint play_failed_count;
+
+       MafwGstRendererPlaybackMode playback_mode;
+       gboolean resume_playlist;
+       MafwGstRendererState **states;
+       MafwRendererErrorPolicy error_policy;
+        gboolean tv_connected;
+
+#ifdef HAVE_CONIC
+       gboolean connected;
+       ConIcConnection *connection;
+#endif
+       GConfClient *gconf_client;
+};
+
+typedef struct {
+        MafwGstRenderer *renderer;
+        GError *error;
+} MafwGstRendererErrorClosure;
+
+G_BEGIN_DECLS
+
+GType mafw_gst_renderer_get_type(void);
+GObject *mafw_gst_renderer_new(MafwRegistry *registry);
+GQuark mafw_gst_renderer_error_quark(void);
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_play(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                            gpointer user_data);
+void mafw_gst_renderer_play_object(MafwRenderer *self, const gchar *object_id,
+                                   MafwRendererPlaybackCB callback,
+                                   gpointer user_data);
+void mafw_gst_renderer_stop(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                            gpointer user_data);
+void mafw_gst_renderer_pause(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                             gpointer user_data);
+void mafw_gst_renderer_resume(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                              gpointer user_data);
+
+/*----------------------------------------------------------------------------
+  Status
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_get_status(MafwRenderer *self, MafwRendererStatusCB callback,
+                                  gpointer user_data);
+
+/*----------------------------------------------------------------------------
+  Set Media
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_set_object(MafwGstRenderer *self, const gchar *object_id);
+void mafw_gst_renderer_clear_media(MafwGstRenderer *self);
+
+/*----------------------------------------------------------------------------
+  Metadata
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_get_metadata(MafwGstRenderer* self, const gchar* objectid,
+                                    GError **error);
+gboolean mafw_gst_renderer_update_stats(gpointer data);
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+gboolean mafw_gst_renderer_assign_playlist(MafwRenderer *self,
+                                           MafwPlaylist *playlist,
+                                           GError **error);
+void mafw_gst_renderer_next(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                            gpointer user_data);
+void mafw_gst_renderer_previous(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                                gpointer user_data);
+void mafw_gst_renderer_goto_index(MafwRenderer *self, guint index,
+                                  MafwRendererPlaybackCB callback,
+                                  gpointer user_data);
+MafwGstRendererMovementResult mafw_gst_renderer_move(MafwGstRenderer *renderer,
+                                                     MafwGstRendererMovementType type,
+                                                     guint index,
+                                                     GError **error);
+
+/*----------------------------------------------------------------------------
+  Set media
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_set_media_playlist(MafwGstRenderer* self);
+
+/*----------------------------------------------------------------------------
+  Position
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_set_position(MafwRenderer *self,
+                                    MafwRendererSeekMode mode, gint seconds,
+                                    MafwRendererPositionCB callback,
+                                    gpointer user_data);
+void mafw_gst_renderer_get_position(MafwRenderer *self, MafwRendererPositionCB callback,
+                                    gpointer user_data);
+
+/*----------------------------------------------------------------------------
+  Metadata
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_get_current_metadata(MafwRenderer *self,
+                                           MafwRendererMetadataResultCB callback,
+                                           gpointer user_data);
+
+/*----------------------------------------------------------------------------
+  Local API
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_set_state(MafwGstRenderer *self, MafwPlayState state);
+
+gboolean mafw_gst_renderer_manage_error_idle(gpointer data);
+
+void mafw_gst_renderer_manage_error(MafwGstRenderer *self, const GError *error);
+
+void mafw_gst_renderer_set_playback_mode(MafwGstRenderer *self,
+                                         MafwGstRendererPlaybackMode mode);
+
+MafwGstRendererPlaybackMode mafw_gst_renderer_get_playback_mode(
+       MafwGstRenderer *self);
+
+void mafw_gst_renderer_update_source_duration(MafwGstRenderer *renderer,
+                                             gint duration);
+
+G_END_DECLS
+
+#endif
+
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/libmafw-gst-renderer/mafw-playlist-iterator.c b/libmafw-gst-renderer/mafw-playlist-iterator.c
new file mode 100644 (file)
index 0000000..4a8b869
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "mafw-playlist-iterator.h"
+#include "mafw-gst-renderer-marshal.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer-playlist-iterator"
+
+struct _MafwPlaylistIteratorPrivate {
+       MafwPlaylist *playlist;
+       gint current_index;
+       gchar *current_objectid;
+       gint size;
+};
+
+typedef gboolean (*movement_function) (MafwPlaylist *playlist,
+                                      guint *index,
+                                      gchar **objectid,
+                                      GError **error);
+
+enum {
+       PLAYLIST_CHANGED = 0,
+       LAST_SIGNAL,
+};
+
+static guint mafw_playlist_iterator_signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE(MafwPlaylistIterator, mafw_playlist_iterator, G_TYPE_OBJECT);
+
+static void
+mafw_playlist_iterator_dispose(GObject *object)
+{
+       MafwPlaylistIterator *iterator = (MafwPlaylistIterator *) object;
+
+       g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator));
+
+       mafw_playlist_iterator_invalidate(iterator);
+
+       G_OBJECT_CLASS(mafw_playlist_iterator_parent_class)->dispose(object);
+}
+
+static void
+mafw_playlist_iterator_class_init(MafwPlaylistIteratorClass *klass)
+{
+       GObjectClass *gclass = NULL;
+
+       gclass = G_OBJECT_CLASS(klass);
+       g_return_if_fail(gclass != NULL);
+
+       g_type_class_add_private(klass, sizeof(MafwPlaylistIteratorPrivate));
+
+       gclass->dispose = mafw_playlist_iterator_dispose;
+
+       mafw_playlist_iterator_signals[PLAYLIST_CHANGED] =
+           g_signal_new("playlist-changed",
+                        G_TYPE_FROM_CLASS(klass),
+                        G_SIGNAL_RUN_FIRST,
+                        G_STRUCT_OFFSET(MafwPlaylistIteratorClass, playlist_changed),
+                        NULL,
+                        NULL,
+                        mafw_gst_renderer_marshal_VOID__BOOLEAN_UINT_INT_STRING,
+                        G_TYPE_NONE,
+                        4,
+                        G_TYPE_BOOLEAN,
+                        G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING);
+}
+
+static void
+mafw_playlist_iterator_init(MafwPlaylistIterator *self)
+{
+       self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
+                                                MAFW_TYPE_PLAYLIST_ITERATOR,
+                                                MafwPlaylistIteratorPrivate);
+}
+
+static void
+mafw_playlist_iterator_set_data(MafwPlaylistIterator *iterator, gint index,
+                                gchar *objectid)
+{
+       g_assert(mafw_playlist_iterator_is_valid(iterator));
+
+       g_free(iterator->priv->current_objectid);
+       iterator->priv->current_index = index;
+       iterator->priv->current_objectid = objectid;
+}
+
+static MafwPlaylistIteratorMovementResult
+mafw_playlist_iterator_move_to_next_in_direction(MafwPlaylistIterator *iterator,
+                                                 movement_function get_next_in_direction,
+                                                 GError **error)
+{
+       gint index;
+       gchar *objectid = NULL;
+       GError *new_error = NULL;
+       gboolean playlist_movement_result = FALSE;
+       MafwPlaylistIteratorMovementResult iterator_movement_result =
+               MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK;
+
+       g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator),
+                            MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID);
+
+       index = iterator->priv->current_index;
+
+       playlist_movement_result =
+               get_next_in_direction (iterator->priv->playlist,
+                                      (guint *) &index,
+                                      &objectid, &new_error);
+
+       if (new_error != NULL) {
+               g_propagate_error(error, new_error);
+               iterator_movement_result =
+                       MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR;
+       } else if (playlist_movement_result) {
+               mafw_playlist_iterator_set_data(iterator, index, objectid);
+       } else {
+               iterator_movement_result =
+                       MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT;
+       }
+
+       return iterator_movement_result;
+}
+
+static void
+mafw_playlist_iterator_playlist_contents_changed_handler(MafwPlaylist *playlist,
+                                                         guint from,
+                                                         guint nremove,
+                                                         guint nreplace,
+                                                         gpointer user_data)
+{
+       gint play_index;
+       gboolean clip_changed = FALSE;
+       GError *error = NULL;
+       MafwPlaylistIterator *iterator = (MafwPlaylistIterator*) user_data;
+
+       g_return_if_fail(MAFW_IS_PLAYLIST(playlist));
+       g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator));
+
+       if (iterator->priv->playlist == NULL) {
+               g_critical("Got playlist:contents-changed but renderer has no" \
+                          "playlist assigned!. Skipping...");
+               return;
+       }
+
+       play_index = iterator->priv->current_index;
+       iterator->priv->size += nreplace;
+
+       if (nremove > 0) {
+               /* Items have been removed from the playlist */
+               iterator->priv->size -= nremove;
+               if ((play_index >= from) &&
+                   (play_index < from + nremove)) {
+                       /* The current index has been removed */
+                       guint pls_size =
+                               mafw_playlist_iterator_get_size(iterator,
+                                                                &error);
+                       if (error == NULL) {
+                               /* Is the current index invalid now? If not,
+                                  set current item to the last in the playlist,
+                                  otherwise the keep the index and update the
+                                  media */
+                               if (pls_size == 0) {
+                                       mafw_playlist_iterator_set_data(iterator, -1, NULL);
+                               } else if (play_index >= pls_size) {
+                                       mafw_playlist_iterator_move_to_index(iterator,
+                                                                             pls_size - 1,
+                                                                             &error);
+                               } else {
+                                       mafw_playlist_iterator_update(iterator,
+                                                                      &error);
+                               }
+
+                               clip_changed = TRUE;
+                       }
+               } else if (from < play_index) {
+                       /* The current index has been moved towards
+                          the head of the playlist */
+                       play_index -= nremove;
+                       if (play_index < 0) {
+                               play_index = 0;
+                       }
+                       mafw_playlist_iterator_move_to_index(iterator,
+                                                             play_index,
+                                                             &error);
+               }
+       } else if (nremove == 0) {
+               /* Items have been inserted in the playlist */
+               if (play_index == -1) {
+                       /* First item has been added to an empty playlist */
+                       mafw_playlist_iterator_reset(iterator,
+                                                     &error);
+                       clip_changed = TRUE;
+               } else if (play_index >= from) {
+                       /* The current item has been moved towards the
+                          tail of the playlist */
+                       mafw_playlist_iterator_move_to_index(iterator,
+                                                             play_index + nreplace,
+                                                             &error);
+               }
+       }
+
+       if (error != NULL) {
+               g_critical("playlist::contents-changed handler failed "
+                          "with \"%s\"", error->message);
+               g_signal_emit(iterator,
+                             mafw_playlist_iterator_signals[PLAYLIST_CHANGED],
+                             0, FALSE, error->domain, error->code, error->message);
+               g_error_free (error);
+       } else {
+               g_signal_emit(iterator,
+                             mafw_playlist_iterator_signals[PLAYLIST_CHANGED],
+                             0, clip_changed, 0, 0, NULL);
+       }
+}
+
+static void
+mafw_playlist_iterator_playlist_item_moved_handler(MafwPlaylist *playlist,
+                                                   guint from,
+                                                   guint to,
+                                                   gpointer user_data)
+{
+       MafwPlaylistIterator *iterator = (MafwPlaylistIterator *) user_data;
+       gint play_index;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_PLAYLIST(playlist));
+       g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator));
+
+       if (iterator->priv->playlist == NULL) {
+               g_critical("Got playlist:item-moved but renderer has not a " \
+                         "playlist assigned! Skipping...");
+               return;
+       }
+
+       play_index = iterator->priv->current_index;
+
+       if (play_index == from) {
+               /* So the current item has been moved, let's update the
+                  the current index to the new location  */
+               mafw_playlist_iterator_move_to_index(iterator, to, &error);
+       } else if (play_index > from && play_index <= to) {
+               /* So we current item  has been pushed one position towards
+                  the head, let's update the current index */
+               mafw_playlist_iterator_move_to_prev(iterator, &error);
+       }  else if (play_index >= to && play_index < from) {
+               /* So we current item  has been pushed one position towards
+                  the head, let's update the current index */
+               mafw_playlist_iterator_move_to_next(iterator, &error);
+       }
+
+       if (error != NULL) {
+               g_critical("playlist::item-moved handler failed "
+                          "with \"%s\"", error->message);
+               g_error_free (error);
+       }
+}
+
+MafwPlaylistIterator *
+mafw_playlist_iterator_new(void)
+{
+       MafwPlaylistIterator *iterator = (MafwPlaylistIterator *)
+               g_object_new(MAFW_TYPE_PLAYLIST_ITERATOR, NULL);
+
+       g_assert(iterator != NULL);
+
+       iterator->priv->playlist = NULL;
+       iterator->priv->current_index = -1;
+       iterator->priv->current_objectid = NULL;
+       iterator->priv->size = -1;
+
+       return iterator;
+}
+
+void
+mafw_playlist_iterator_initialize(MafwPlaylistIterator *iterator,
+                                  MafwPlaylist *playlist, GError **error)
+{
+       guint size;
+       gint index = -1;
+       gchar *objectid = NULL;
+       GError *new_error = NULL;
+
+       g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator));
+       g_return_if_fail(iterator->priv->playlist == NULL);
+
+       iterator->priv->size = -1;
+
+       mafw_playlist_get_starting_index(playlist, (guint *) &index, &objectid,
+                                         &new_error);
+
+       if (new_error == NULL) {
+               size = mafw_playlist_get_size(playlist, &new_error);
+       }
+
+       if (new_error == NULL) {
+               iterator->priv->playlist = g_object_ref(playlist);
+               iterator->priv->current_index = index;
+               iterator->priv->current_objectid = objectid;
+               iterator->priv->size = size;
+
+               g_signal_connect(playlist,
+                                "item-moved",
+                                G_CALLBACK(mafw_playlist_iterator_playlist_item_moved_handler),
+                                iterator);
+               g_signal_connect(playlist,
+                                "contents-changed",
+                                G_CALLBACK(mafw_playlist_iterator_playlist_contents_changed_handler),
+                                iterator);
+       }
+       else {
+               g_propagate_error (error, new_error);
+       }
+}
+
+void
+mafw_playlist_iterator_invalidate(MafwPlaylistIterator *iterator)
+{
+       g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator));
+
+       if (iterator->priv->playlist != NULL) {
+               g_signal_handlers_disconnect_matched(iterator->priv->playlist,
+                                                    (GSignalMatchType) G_SIGNAL_MATCH_FUNC,
+                                                    0, 0, NULL,
+                                                    mafw_playlist_iterator_playlist_item_moved_handler,
+                                                    NULL);
+
+               g_signal_handlers_disconnect_matched(iterator->priv->playlist,
+                                                    (GSignalMatchType) G_SIGNAL_MATCH_FUNC,
+                                                    0, 0, NULL,
+                                                    mafw_playlist_iterator_playlist_contents_changed_handler,
+                                                    NULL);
+
+               g_object_unref(iterator->priv->playlist);
+               g_free(iterator->priv->current_objectid);
+               iterator->priv->playlist = NULL;
+               iterator->priv->current_index = -1;
+               iterator->priv->current_objectid = NULL;
+               iterator->priv->size = -1;
+       }
+}
+
+gboolean
+mafw_playlist_iterator_is_valid(MafwPlaylistIterator *iterator)
+{
+       g_return_val_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator), FALSE);
+
+       return iterator->priv->playlist != NULL;
+}
+
+void
+mafw_playlist_iterator_reset(MafwPlaylistIterator *iterator, GError **error)
+{
+       gint index = -1;
+       gchar *objectid = NULL;
+       GError *new_error = NULL;
+
+       g_return_if_fail(mafw_playlist_iterator_is_valid(iterator));
+
+       mafw_playlist_get_starting_index(iterator->priv->playlist,
+                                         (guint *) &index,
+                                         &objectid, &new_error);
+
+       if (new_error == NULL) {
+               mafw_playlist_iterator_set_data(iterator, index, objectid);
+       }
+       else {
+               g_propagate_error (error, new_error);
+       }
+}
+
+void
+mafw_playlist_iterator_move_to_last(MafwPlaylistIterator *iterator,
+                                    GError **error)
+{
+       GError *new_error = NULL;
+       gint index = -1;
+       gchar *objectid = NULL;
+
+       g_return_if_fail(mafw_playlist_iterator_is_valid(iterator));
+
+       mafw_playlist_get_last_index(iterator->priv->playlist,
+                                     (guint *) &index,
+                                     &objectid, &new_error);
+
+       if (new_error == NULL) {
+               mafw_playlist_iterator_set_data(iterator, index, objectid);
+       }
+       else {
+               g_propagate_error (error, new_error);
+       }
+}
+
+MafwPlaylistIteratorMovementResult
+mafw_playlist_iterator_move_to_next(MafwPlaylistIterator *iterator,
+                                    GError **error)
+{
+       return  mafw_playlist_iterator_move_to_next_in_direction(iterator,
+                                                                 mafw_playlist_get_next,
+                                                                 error);
+}
+
+MafwPlaylistIteratorMovementResult
+mafw_playlist_iterator_move_to_prev(MafwPlaylistIterator *iterator,
+                                    GError **error)
+{
+       return  mafw_playlist_iterator_move_to_next_in_direction(iterator,
+                                                                 mafw_playlist_get_prev,
+                                                                 error);
+}
+
+MafwPlaylistIteratorMovementResult
+mafw_playlist_iterator_move_to_index(MafwPlaylistIterator *iterator,
+                                     gint index,
+                                     GError **error)
+{
+       GError *new_error = NULL;
+       MafwPlaylistIteratorMovementResult iterator_movement_result =
+               MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK;
+       gint playlist_size;
+
+       g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator),
+                            MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID);
+
+       playlist_size = mafw_playlist_iterator_get_size(iterator, &new_error);
+
+       if (new_error != NULL) {
+               g_propagate_error(error, new_error);
+               iterator_movement_result =
+                       MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR;
+       } else if ((index < 0) || (index >= playlist_size)) {
+               iterator_movement_result =
+                       MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT;
+       } else {
+               gchar *objectid =
+                       mafw_playlist_get_item(iterator->priv->playlist,
+                                               index,
+                                               &new_error);
+
+               if (new_error != NULL) {
+                       g_propagate_error(error, new_error);
+                       iterator_movement_result =
+                               MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR;
+               } else {
+                       mafw_playlist_iterator_set_data(iterator, index, objectid);
+               }
+       }
+
+       return iterator_movement_result;
+}
+
+void
+mafw_playlist_iterator_update(MafwPlaylistIterator *iterator, GError **error)
+{
+       GError *new_error = NULL;
+       gchar *objectid = NULL;
+
+       objectid =
+               mafw_playlist_get_item(iterator->priv->playlist,
+                                       iterator->priv->current_index,
+                                       &new_error);
+
+       if (new_error != NULL) {
+               g_propagate_error(error, new_error);
+       } else {
+               mafw_playlist_iterator_set_data(iterator,
+                                                iterator->priv->current_index,
+                                                objectid);
+       }
+}
+
+const gchar *
+mafw_playlist_iterator_get_current_objectid(MafwPlaylistIterator *iterator)
+{
+       g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator), NULL);
+
+       return iterator->priv->current_objectid;
+}
+
+gint
+mafw_playlist_iterator_get_current_index(MafwPlaylistIterator *iterator)
+{
+       g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator), 0);
+
+       return iterator->priv->current_index;
+}
+
+gint
+mafw_playlist_iterator_get_size(MafwPlaylistIterator *iterator,
+                                GError **error)
+{
+       g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator), -1);
+
+       if (iterator->priv->size == -1) {
+               iterator->priv->size =
+                       mafw_playlist_get_size(iterator->priv->playlist,
+                                               error);
+       }
+
+       return iterator->priv->size;
+}
diff --git a/libmafw-gst-renderer/mafw-playlist-iterator.h b/libmafw-gst-renderer/mafw-playlist-iterator.h
new file mode 100644 (file)
index 0000000..6e88360
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_PLAYLIST_ITERATOR_H
+#define MAFW_PLAYLIST_ITERATOR_H
+
+#include <glib-object.h>
+#include <libmafw/mafw.h>
+
+G_BEGIN_DECLS
+
+typedef struct _MafwPlaylistIteratorPrivate MafwPlaylistIteratorPrivate;
+
+typedef struct {
+       GObject g_object;
+
+       MafwPlaylistIteratorPrivate *priv;
+} MafwPlaylistIterator;
+
+typedef struct {
+       GObjectClass g_object_class;
+
+       /* Signals */
+       void (*playlist_changed)(MafwPlaylistIterator *iterator,
+                                gboolean current_item_changed,
+                                GQuark domain, gint code, const gchar *message);
+} MafwPlaylistIteratorClass;
+
+typedef enum {
+       MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK,
+       MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT,
+       MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID,
+       MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR,
+} MafwPlaylistIteratorMovementResult;
+
+#define MAFW_TYPE_PLAYLIST_ITERATOR \
+        (mafw_playlist_iterator_get_type())
+#define MAFW_PLAYLIST_ITERATOR(obj) \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_PLAYLIST_ITERATOR, MafwPlaylistIterator))
+#define MAFW_IS_PLAYLIST_ITERATOR(obj) \
+        (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_PLAYLIST_ITERATOR))
+#define MAFW_PLAYLIST_ITERATOR_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_PLAYLIST_ITERATOR, MafwPlaylistIterator))
+#define MAFW_PLAYLIST_ITERATOR_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_PLAYLIST_ITERATOR, \
+                                  MafwPlaylistIteratorClass))
+#define MAFW_IS_PLAYLIST_ITERATOR_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_PLAYLIST_ITERATOR))
+
+G_END_DECLS
+
+GType mafw_playlist_iterator_get_type(void);
+MafwPlaylistIterator *mafw_playlist_iterator_new(void);
+void mafw_playlist_iterator_initialize(MafwPlaylistIterator *iterator,
+                                       MafwPlaylist *playlist,
+                                       GError **error);
+void mafw_playlist_iterator_invalidate(MafwPlaylistIterator *iterator);
+gboolean mafw_playlist_iterator_is_valid(MafwPlaylistIterator *iterator);
+void mafw_playlist_iterator_reset(MafwPlaylistIterator *iterator, GError **error);
+void mafw_playlist_iterator_move_to_last(MafwPlaylistIterator *iterator, GError **error);
+MafwPlaylistIteratorMovementResult mafw_playlist_iterator_move_to_next(MafwPlaylistIterator *iterator,
+                                                                        GError **error);
+MafwPlaylistIteratorMovementResult mafw_playlist_iterator_move_to_prev(MafwPlaylistIterator *iterator,
+                                                                        GError **error);
+MafwPlaylistIteratorMovementResult mafw_playlist_iterator_move_to_index(MafwPlaylistIterator *iterator,
+                                                                         gint index,
+                                                                         GError **error);
+void mafw_playlist_iterator_update(MafwPlaylistIterator *iterator, GError **error);
+const gchar *mafw_playlist_iterator_get_current_objectid(MafwPlaylistIterator *iterator);
+gint mafw_playlist_iterator_get_current_index(MafwPlaylistIterator *iterator);
+gint mafw_playlist_iterator_get_size(MafwPlaylistIterator *iterator,
+                                     GError **error);
+
+#endif
diff --git a/mafw-gst-renderer-uninstalled.pc.in b/mafw-gst-renderer-uninstalled.pc.in
new file mode 100644 (file)
index 0000000..8496267
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=
+exec_prefix=
+libdir=${pcfiledir}/libmafw-gst-renderer
+includedir=${pcfiledir}/
+
+Name: mafw-gst-renderer
+Description: MAFW local renderer
+Version: @VERSION@
+Libs: ${libdir}/mafw-gst-renderer.la
+Cflags: -I${includedir}
+Requires: gobject-2.0 gstreamer-0.10 mafw
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644 (file)
index 0000000..a894fbf
--- /dev/null
@@ -0,0 +1,60 @@
+#
+# Makefile.am for MAFW gst renderer library.
+#
+# Author: Visa Smolander <visa.smolander@nokia.com>
+#
+# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved.
+
+TESTS                          = check-mafw-gst-renderer
+TESTS_ENVIRONMENT              = CK_FORK=yes \
+                                 TESTS_DIR=@abs_srcdir@
+
+noinst_PROGRAMS                        = $(TESTS)
+
+AM_CFLAGS                      = $(_CFLAGS)
+AM_LDFLAGS                     = $(_LDFLAGS)
+
+INCLUDES                       = -I$(top_srcdir)/libmafw-gst-renderer \
+                                 $(DEPS_CFLAGS) \
+                                 $(DEPS_TESTS_CFLAGS) \
+                                 $(CHECKMORE_CFLAGS)
+
+LDADD                          = $(CHECKMORE_LIBS) \
+                                 $(DEPS_LIBS) \
+                                 $(DEPS_TESTS_LIBS) \
+                                 $(top_builddir)/libmafw-gst-renderer/mafw-gst-renderer.la \
+                                 -lgstinterfaces-0.10 -lgsttag-0.10
+
+if HAVE_GDKPIXBUF
+INCLUDES += $(GDKPIXBUF_CFLAGS)
+LDADD += $(GDKPIXBUF_LIBS)
+endif
+
+if HAVE_CONIC
+INCLUDES += $(CONIC_CFLAGS)
+LDADD += $(CONIC_LIBS)
+endif
+
+EXTRA_DIST                     = media/test.wav media/test.avi media/testframe.png
+
+# -----------------------------------------------
+# Test programs build specs
+# -----------------------------------------------
+
+check_mafw_gst_renderer_SOURCES        = check-main.c \
+                                 check-mafw-gst-renderer.c \
+                                 mafw-mock-playlist.c mafw-mock-playlist.h \
+                                 mafw-mock-pulseaudio.c mafw-mock-pulseaudio.h
+
+CLEANFILES                     = $(TESTS) mafw.db *.gcno *.gcda
+MAINTAINERCLEANFILES           = Makefile.in
+
+# Run valgrind on tests.
+VG_OPTS                                :=  --suppressions=test.suppressions --tool=memcheck \
+                                   --leak-check=full --show-reachable=yes
+vg: $(TESTS)
+       for p in $^; do \
+               G_SLICE=always-malloc G_DEBUG=gc-friendly WAIT_TIMEOUT=25000 \
+               libtool --mode=execute valgrind $(VG_OPTS) $$p 2>vglog.$$p; \
+       done;
+       -rm -f vgcore.*
diff --git a/tests/check-mafw-gst-renderer.c b/tests/check-mafw-gst-renderer.c
new file mode 100644 (file)
index 0000000..ae4982b
--- /dev/null
@@ -0,0 +1,4174 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/*
+ * check-gst-renderer.c
+ *
+ * Gst Renderer unit tests
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ */
+
+#include <glib.h>
+
+#include <check.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sqlite3.h>
+#include <gst/tag/tag.h>
+
+#include <libmafw/mafw.h>
+#include <checkmore.h>
+
+#include "config.h"
+
+#include "mafw-gst-renderer.h"
+#include "mafw-mock-playlist.h"
+#include "mafw-mock-pulseaudio.h"
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "check-mafw-gstreamer-renderer"
+
+#define SAMPLE_AUDIO_CLIP "test.wav"
+#define SAMPLE_VIDEO_CLIP "test.avi"
+#define SAMPLE_IMAGE "testframe.png"
+
+/* Base timeout used when waiting for state transitions or execution of
+   user function callbacks associated to each mafw-renderer function */
+#define DEFAULT_WAIT_TOUT 2000
+
+/* EOS timeout must be longer than the clip duration */
+#define EOS_TIMEOUT 7000
+
+SRunner *configure_tests(void);
+
+typedef struct {
+       gint index;
+       MafwPlayState state;
+} RendererInfo;
+
+typedef struct {
+       gboolean called;
+       gboolean error;
+       gint err_code;
+       gchar *err_msg;
+       gint seek_position;
+       gboolean error_signal_expected;
+       GError *error_signal_received;
+       const gchar *property_expected;
+       GValue *property_received;
+} CallbackInfo;                
+
+typedef struct {
+       const gchar *expected_key;
+       GValue *value;
+} MetadataChangedInfo;
+
+typedef struct {
+       const gchar *expected;
+       GValue *received;
+} PropertyChangedInfo;
+
+typedef struct {
+       gboolean requested;
+       gboolean received;
+       gfloat value;
+} BufferingInfo;
+
+static gint wait_tout_val;
+
+/* Globals. */
+
+static MafwRenderer *g_gst_renderer = NULL;
+
+/* Error messages. */
+
+static const gchar *callback_err_msg = "Error received when %s: (%d) %s";
+static const gchar *callback_no_err_msg = "No error received when %s: (%d) %s";
+static const gchar *no_callback_msg = "We forgot to call the user callback";
+static const gchar *state_err_msg = "Call %s didn't change state to %s. " \
+"Current state is: %d";
+static const gchar *index_err_msg = "Actual index is (%d) instead of the " \
+"expected index (%d)";
+
+
+/*----------------------------------------------------------------------------
+  Signal handlers
+  ----------------------------------------------------------------------------*/
+
+static void error_cb(MafwRenderer *s, GQuark domain, gint code, gchar *msg, 
+                    gpointer user_data)
+{
+       CallbackInfo* c = (CallbackInfo*) user_data;
+
+       /* "MafwExtension::error" signal handler */     
+       if (user_data == NULL || !c->error_signal_expected) {
+               fail("Signal error received: (%d) %s", code, msg);
+       } else {
+               if (c->error_signal_received != NULL) {
+                       fail("Error received already initialized");
+               } else {
+                       c->error_signal_received =
+                               g_error_new_literal(domain, code, msg);
+               }
+       }
+}
+
+static void state_changed_cb(MafwRenderer *s,  MafwPlayState state,
+                            gpointer user_data)
+{
+       /* "MafwRenderer::state-changed" signal handler */
+       RendererInfo *si = (RendererInfo *) user_data;
+       gchar *states[] = {"Stopped","Playing","Paused","Transitioning"};
+
+       si->state = state;
+       g_debug("state changed (%s) ---", states[state]);
+}
+
+static gboolean media_changed_called;
+
+static void media_changed_cb(MafwRenderer *s, gint index, gchar *objectid,
+                            gpointer user_data)
+{
+       /* "MafwRenderer::media-changed" signal handler */
+       RendererInfo *si = (RendererInfo *) user_data;
+
+       si->index = index;
+       g_debug("media changed (%d) ---", index);
+       media_changed_called = TRUE;
+}
+static void playlist_changed_cb (MafwRenderer *self,
+                                                        GObject       *playlist,
+                                                        gpointer       user_data)
+{
+       g_debug("playlist changed");
+       fail_if(media_changed_called, "At first playlist-changed should be called");
+}
+
+static void metadata_changed_cb(MafwRenderer *self, const gchar *key,
+                               GValueArray *value, gpointer user_data)
+{
+       MetadataChangedInfo *m = user_data;
+
+       if (m->expected_key != NULL && strcmp(key, m->expected_key) == 0)
+       {
+               GValue *original;
+
+               original = g_value_array_get_nth(value, 0);
+
+               m->value = g_new0(GValue, 1);
+               g_value_init(m->value, G_VALUE_TYPE(original));
+               g_value_copy(original, m->value);
+       }
+}
+
+static void property_changed_cb(MafwExtension *extension, const gchar *name,
+                               const GValue *value, gpointer user_data)
+{
+       PropertyChangedInfo* p = (PropertyChangedInfo*) user_data;
+       gchar *value_string;
+
+       value_string = g_strdup_value_contents(value);
+
+       g_debug("property_changed_cb: %s (%s)", name, value_string);
+       g_free(value_string);
+
+       if (p->expected != NULL &&
+           strcmp(p->expected, name) == 0) {
+               p->received = g_new0(GValue, 1);
+               g_value_init(p->received, G_VALUE_TYPE(value));
+               g_value_copy(value, p->received);
+       }
+}
+
+static void buffering_info_cb(MafwRenderer *self, gfloat status,
+                             gpointer user_data)
+{
+       BufferingInfo *b = user_data;
+
+       if (b->requested) {
+               b->received = TRUE;
+               b->value = status;
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Function callbacks
+  ----------------------------------------------------------------------------*/
+
+
+static void status_cb(MafwRenderer* renderer, MafwPlaylist* playlist, guint index,
+                     MafwPlayState state,
+                     const gchar* object_id, 
+                     gpointer user_data,
+                     const GError *error)
+{
+       /* MafwRendererStatusCB */
+       RendererInfo* s = (RendererInfo*) user_data;
+       g_assert(s != NULL);
+
+       if (error != NULL) {
+               fail("Error received while trying to get renderer status: (%d) %s",
+                    error->code, error->message);
+       } 
+       s->state = state;
+
+}
+
+static void playback_cb(MafwRenderer* renderer, gpointer user_data, const GError* error)
+{
+       /* MafwRendererPlaybackCB:
+          
+       Called after mafw_renderer_play(), mafw_renderer_play_uri(),
+       mafw_renderer_play_object(), mafw_renderer_stop(), mafw_renderer_pause(),
+       mafw_renderer_resume(), mafw_renderer_next(),   mafw_renderer_previous() or 
+       mafw_renderer_goto_index() has been called. */
+       CallbackInfo* c = (CallbackInfo*) user_data;
+       g_assert(c != NULL);
+
+       c->called = TRUE;
+       if (error != NULL) {
+               c->error = TRUE;
+               c->err_code = error->code;
+               c->err_msg = g_strdup(error->message);
+       }
+}
+
+static void seek_cb (MafwRenderer *self, gint position, gpointer user_data,
+                    const GError *error)
+{
+       /* Called when seeking */
+
+       CallbackInfo* c = (CallbackInfo*) user_data;
+       g_assert(c != NULL);
+
+       c->called = TRUE;
+       c->seek_position = position;
+       if (error != NULL) {
+               c->error = TRUE;
+               c->err_code = error->code;
+               c->err_msg = g_strdup(error->message);
+       }
+}
+
+static void  get_position_cb(MafwRenderer *self, gint position,
+                            gpointer user_data, const GError *error)
+{
+       CallbackInfo* c = (CallbackInfo*) user_data;
+
+       g_debug("get position cb: %d", position);
+
+       if (error != NULL) {
+               c->error = TRUE;
+               c->err_code = error->code;
+               c->err_msg = g_strdup(error->message);
+       }
+       c->called = TRUE;
+}
+
+static void get_property_cb(MafwExtension *self,
+                           const gchar *name,
+                           GValue *value,
+                           gpointer user_data,
+                           const GError *error)
+{
+       CallbackInfo* c = (CallbackInfo*) user_data;
+       gchar *value_string;
+
+       value_string = g_strdup_value_contents(value);
+
+       g_debug("get property cb: %s (%s)", name, value_string);
+       g_free(value_string);
+
+       if (error != NULL) {
+               c->error = TRUE;
+               c->err_code = error->code;
+               c->err_msg = g_strdup(error->message);
+       }
+
+       if (c->property_expected != NULL &&
+           strcmp(c->property_expected, name) == 0) {
+               c->property_received = g_new0(GValue, 1);
+               g_value_init(c->property_received, G_VALUE_TYPE(value));
+               g_value_copy(value, c->property_received);
+
+               c->called = TRUE;
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Helpers
+  ----------------------------------------------------------------------------*/
+
+static gchar *get_sample_clip_path(const gchar *clip)
+{
+       gchar *my_dir, *media;
+
+       /* Makefile.am sets TESTS_DIR, required for VPATH builds (like make
+        * distcheck).  Otherwise assume we are running in-place. */
+       my_dir = g_strdup(g_getenv("TESTS_DIR"));
+       if (!my_dir)
+               my_dir = g_get_current_dir();
+       media = g_strconcat("file://", my_dir, G_DIR_SEPARATOR_S,
+                           "media" G_DIR_SEPARATOR_S, clip,
+                           NULL);
+       g_free(my_dir);
+       return media;
+}
+
+static gchar *get_sample_clip_objectid(const gchar *clip)
+{
+       gchar *path = NULL;
+       gchar *objectid = NULL;
+
+       path = get_sample_clip_path(clip);
+        objectid = mafw_source_create_objectid(path);
+       g_free(path);
+
+       return objectid;
+}
+
+static gboolean stop_wait_timeout(gpointer user_data) 
+{
+       gboolean *do_stop = (gboolean *) user_data;
+       g_debug("stop wait timeout");
+       *do_stop = TRUE;
+
+       return FALSE;
+}
+
+static gboolean wait_until_timeout_finishes(guint millis)
+{
+       guint timeout = 0;
+       gboolean stop_wait = FALSE;
+       gboolean result = FALSE;
+
+       g_debug("Init wait_");
+       /* We'll wait a limitted ammount of time */
+       timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait);
+       while(!stop_wait) {
+               result= g_main_context_iteration(NULL, TRUE);
+       }
+
+       g_debug("End wait_");
+       return TRUE;
+}
+
+static gboolean wait_for_state(RendererInfo *renderer_info,
+                              MafwPlayState expected_state, guint millis)
+{
+       guint timeout = 0;
+       gboolean stop_wait = FALSE;
+       
+       g_debug("Init wait for state");
+       /* We'll wait a limitted ammount of time */
+       timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait);
+
+       while(renderer_info->state != expected_state  && !stop_wait) {
+                       g_main_context_iteration(NULL, TRUE);
+       }
+       
+       if (!stop_wait) {
+               g_source_remove(timeout);
+       }
+
+       g_debug("End wait for state");
+       return (renderer_info->state == expected_state);
+}
+
+static gboolean wait_for_callback(CallbackInfo *callback, guint millis)
+{
+       guint timeout = 0;
+       gboolean stop_wait = FALSE;
+
+       g_debug("Init wait for callback");
+       /* We'll wait a limitted ammount of time */
+       timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait);
+
+       while (callback->called == FALSE && !stop_wait) {
+               g_main_context_iteration(NULL, TRUE);
+       }
+       if (!stop_wait) {
+               g_source_remove(timeout);
+       }
+       g_debug("End wait for callback");
+       return callback->called;
+}
+
+static gboolean wait_for_metadata(MetadataChangedInfo *callback, guint millis)
+{
+       guint timeout = 0;
+       gboolean stop_wait = FALSE;
+
+       g_debug("Init wait for metadata");
+       /* We'll wait a limitted ammount of time */
+       timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait);
+
+       while (callback->value == NULL && !stop_wait) {
+               g_main_context_iteration(NULL, TRUE);
+       }
+       if (!stop_wait) {
+               g_source_remove(timeout);
+       }
+       g_debug("End wait for metadata");
+       return callback->value != NULL;
+}
+
+static gboolean wait_for_property(PropertyChangedInfo *callback, guint millis)
+{
+       guint timeout = 0;
+       gboolean stop_wait = FALSE;
+
+       g_debug("Init wait for property changed");
+       /* We'll wait a limitted ammount of time */
+       timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait);
+
+       while (callback->received == NULL && !stop_wait) {
+               g_main_context_iteration(NULL, TRUE);
+       }
+       if (!stop_wait) {
+               g_source_remove(timeout);
+       }
+       g_debug("End wait for callback");
+       return callback->received != NULL;
+}
+
+static gboolean wait_for_buffering(BufferingInfo *callback, guint millis)
+{
+       guint timeout = 0;
+       gboolean stop_wait = FALSE;
+
+       g_debug("Init wait for buffering info");
+       /* We'll wait a limitted ammount of time */
+       timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait);
+
+       while (!callback->received && !stop_wait) {
+               g_main_context_iteration(NULL, TRUE);
+       }
+       if (!stop_wait) {
+               g_source_remove(timeout);
+       }
+       g_debug("End wait for buffering info");
+       return callback->received;
+}
+
+static void reset_callback_info(CallbackInfo *callback_info)
+{
+       if (callback_info->err_msg != NULL) 
+               g_free(callback_info->err_msg);
+
+       callback_info->err_msg = NULL;
+       callback_info->called = FALSE;
+       callback_info->error = FALSE;
+       callback_info->seek_position = 0;
+       callback_info->error_signal_expected = FALSE;
+       if (callback_info->error_signal_received != NULL) {
+               g_error_free(callback_info->error_signal_received);
+               callback_info->error_signal_received = NULL;
+       }
+       callback_info->property_expected = NULL;
+       if (callback_info->property_received != NULL) {
+               g_value_unset(callback_info->property_received);
+               callback_info->property_received = NULL;
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Fixtures
+  ----------------------------------------------------------------------------*/
+
+static void fx_setup_dummy_gst_renderer(void)
+{
+       MafwRegistry *registry;
+
+       /* Setup GLib */
+       g_type_init();
+
+       /* Create a gst renderer instance */
+       registry = MAFW_REGISTRY(mafw_registry_get_instance());
+       fail_if(registry == NULL,
+               "Error: cannot get MAFW registry");
+               
+       g_gst_renderer = MAFW_RENDERER(mafw_gst_renderer_new(registry));
+       fail_if(!MAFW_IS_GST_RENDERER(g_gst_renderer),
+               "Could not create gst renderer instance");
+}
+
+static void fx_teardown_dummy_gst_renderer(void)
+{
+       g_object_unref(g_gst_renderer);
+}
+
+/*----------------------------------------------------------------------------
+  Mockups
+  ----------------------------------------------------------------------------*/
+
+/* GStreamer mock */
+
+GstElement * gst_element_factory_make(const gchar * factoryname,
+                                     const gchar * name)
+{
+       GstElementFactory *factory;
+       GstElement *element;
+        const gchar *use_factoryname;
+       
+       g_return_val_if_fail(factoryname != NULL, NULL);
+       
+        /* For testing, use playbin instead of playbin2 */
+        if (g_ascii_strcasecmp(factoryname, "playbin2") == 0)
+                use_factoryname = "playbin";
+        else
+                use_factoryname = factoryname;
+
+       GST_LOG("gstelementfactory: make \"%s\" \"%s\"",
+               use_factoryname, GST_STR_NULL (name));
+
+       factory = gst_element_factory_find(use_factoryname);
+       if (factory == NULL) {
+               /* No factory */
+               GST_INFO("no such element factory \"%s\"!", use_factoryname);
+               return NULL;    
+       }
+
+       GST_LOG_OBJECT(factory, "found factory %p", factory);
+       if (g_ascii_strcasecmp(use_factoryname, "pulsesink") == 0) {
+               element = gst_element_factory_make("fakesink", "pulsesink");
+               g_object_set(G_OBJECT(element), "sync", TRUE, NULL);
+       } else if (g_ascii_strcasecmp(use_factoryname, "xvimagesink") == 0) {
+               element = gst_element_factory_make("fakesink", "xvimagesink");
+               g_object_set(G_OBJECT(element), "sync", TRUE, NULL);
+       } else {
+               element = gst_element_factory_create(factory, name);
+       }
+       gst_object_unref(factory);
+
+       if (element == NULL) {
+               /* Create failed */
+               GST_INFO_OBJECT(factory, "couldn't create instance!");
+               return NULL;
+       }
+
+       GST_LOG("gstelementfactory: make \"%s\" \"%s\"",use_factoryname,
+               GST_STR_NULL(name));
+
+       /* Playbin will use fake renderer */
+       if (g_ascii_strcasecmp(use_factoryname, "playbin") == 0) {
+               GstElement *audiorenderer = gst_element_factory_make("fakesink",
+                                                                "audiorenderer");
+               
+               g_object_set(G_OBJECT(audiorenderer), "sync", TRUE, NULL);
+               g_object_set(G_OBJECT(element),
+                            "audio-sink",
+                            audiorenderer,
+                            NULL);
+               g_object_set(G_OBJECT(element),
+                            "video-sink",
+                            audiorenderer,
+                            NULL);
+       }
+       return element;
+}
+
+
+/*----------------------------------------------------------------------------
+  Test cases
+  ----------------------------------------------------------------------------*/
+
+START_TEST(test_basic_playback)
+{
+       RendererInfo s;
+       CallbackInfo c;     
+       MetadataChangedInfo m;
+       GstBus *bus = NULL;
+       GstMessage *message = NULL;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       m.expected_key = NULL;
+       m.value = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "metadata-changed",
+                        G_CALLBACK(metadata_changed_cb),
+                        &m);
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               /* No media item has been set so, we should get an error */
+               if (c.error == FALSE)
+                       fail("Play of unset media did not return an error");
+       } else {
+               fail(no_callback_msg);
+       }
+       
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       gchar *objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c);
+
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Transitioning",
+                    s.state);
+       }
+               
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       g_free(objectid);
+
+       /* --- Get position --- */
+
+       reset_callback_info(&c);
+
+       mafw_renderer_get_position(g_gst_renderer, get_position_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "get_position", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Duration emission --- */
+
+       m.expected_key = MAFW_METADATA_KEY_DURATION;
+
+       bus = MAFW_GST_RENDERER(g_gst_renderer)->worker->bus;
+       fail_if(bus == NULL, "No GstBus");
+
+       message = gst_message_new_duration(NULL, GST_FORMAT_TIME,
+                                          5 * GST_SECOND);
+       gst_bus_post(bus, message);
+
+       if (wait_for_metadata(&m, wait_tout_val) == FALSE) {
+               fail("Expected " MAFW_METADATA_KEY_DURATION
+                    ", but not received");
+       }
+
+       fail_if(m.value == NULL, "Metadata " MAFW_METADATA_KEY_DURATION
+               " not received");
+
+       g_value_unset(m.value);
+       g_free(m.value);
+       m.value = NULL;
+       m.expected_key = NULL;
+
+       /* --- Pause --- */
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_pause", "Paused", s.state);
+       }
+
+       /* --- Resume --- */
+
+       reset_callback_info(&c);
+
+       g_debug("resume...");
+       mafw_renderer_resume(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "resuming", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_resume", "Playing", s.state);
+       }
+
+       /* --- Stop --- */
+
+       reset_callback_info(&c);
+
+       g_debug("stop...");
+       mafw_renderer_stop(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "stopping", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state);
+       }
+       
+}
+END_TEST
+
+START_TEST(test_playlist_playback)
+{
+       MafwPlaylist *playlist = NULL;
+       gint i = 0;
+       RendererInfo s = {0, };
+       CallbackInfo c = {0, };
+       gchar *cur_item_oid = NULL;
+       
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+
+       /* --- Create and assign a playlist --- */
+
+       g_debug("assign playlist...");
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+       cur_item_oid =
+               get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       for (i=0; i<10; i++) {
+               mafw_playlist_insert_item(
+                       playlist, i, cur_item_oid, NULL);
+       }
+       g_free(cur_item_oid);
+       cur_item_oid =  get_sample_clip_objectid("unexisting.wav");
+       mafw_playlist_insert_item(playlist, 9, cur_item_oid, NULL);
+       g_free(cur_item_oid);
+
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Playing", s.state);
+       }
+
+       /* --- Stop --- */
+
+       reset_callback_info(&c);
+
+       g_debug("stop...");
+       mafw_renderer_stop(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "stopping", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_stop", "Stopped", s.state);
+       }
+
+       /* --- Next --- */
+
+       /* Get actual index */
+
+       gint initial_index = s.index;
+
+       for (i=0; i<3; i++) {
+
+               reset_callback_info(&c);
+
+               g_debug("move to next...");
+               mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+               if (wait_for_callback(&c, wait_tout_val)) {
+                       if (c.error)
+                               fail(callback_err_msg, "moving to next", c.err_code,
+                                    c.err_msg);
+               } else {
+                       fail(no_callback_msg);
+               }
+       
+               /* Check if the playlist index is correct */
+               fail_if(s.index != initial_index + (i+1), index_err_msg, s.index,
+                       initial_index + (i+1));                 
+       }
+
+
+       /* --- Prev --- */
+
+       /* Get actual index */
+       initial_index = s.index;
+
+       for (i=0; i<3; i++) {
+
+               reset_callback_info(&c);
+
+               g_debug("move to prev...");
+               mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+               if (wait_for_callback(&c, wait_tout_val)) {
+                       if (c.error)
+                               fail(callback_err_msg, "moving to prev", c.err_code,
+                                    c.err_msg);
+               } else {
+                       fail(no_callback_msg);
+               }               
+
+               /* Check if the playlist index is correct */
+               fail_if(s.index != initial_index - (i+1), index_err_msg, s.index,
+                       initial_index - (i+1));                 
+       }
+
+       /* Check if renderer remains in Stopped state after some Prev operations */
+       fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped state "
+               "after doing prev. The actual state is %s and must be %s",
+               s.state, "Stopped");
+
+       /* --- Stop --- */
+
+       reset_callback_info(&c);
+
+       g_debug("stop...");
+       mafw_renderer_stop(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "stopping playback",
+                            c.err_code, c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+       
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg,"mafw_renderer_stop","Stopped", s.state);
+       }
+
+       /* --- Go to index in Stopped state --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 3...");
+       mafw_renderer_goto_index(g_gst_renderer, 3, playback_cb, &c);
+               
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index 3", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+       
+       /* Check if the playlist index is correct */
+       fail_if(s.index != 3, index_err_msg, s.index, 3);
+
+       /* Check if renderer remains in Stopped state after running go to index */
+       fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped state "
+               "after running go to index. The actual state is %s and must be"
+               " %s", s.state, "Stopped");
+
+       /* --- Play (playlist index is 3) --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Playing", s.state);
+       }
+       
+       /* --- Goto index in Playing state --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 5...");
+       mafw_renderer_goto_index(g_gst_renderer, 5, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_goto_index", "Playing", s.state);
+       }
+
+       /* Check if the index if correct */
+       fail_if(s.index != 5, index_err_msg, s.index, 5);
+
+       /* Check if renderer remains in Playing state after running go to index */
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail("Gst renderer didn't remain in Playing state after running "
+                    "go to index. The actual state is %s and must be %s",
+                    s.state, "Playing");
+       }
+
+       /* --- Goto an invalid index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto the invalid index 20...");
+       mafw_renderer_goto_index(g_gst_renderer, 20, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error == FALSE)
+                       fail("Error not received when we go to an incorrect" 
+                            "index");
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* Check if the previous index (5) remains after an incorrect go to
+          index request */
+       fail_if(s.index != 5, index_err_msg, 5, s.index);
+       
+       reset_callback_info(&c);
+
+       /* --- Reassigning playlist --- */
+
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer,
+                                          g_object_ref(playlist), NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* --- Go to index with invalid media --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 9...");
+       mafw_renderer_goto_index(g_gst_renderer, 9, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index 9", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* Check if the playlist index is correct */
+       fail_if(s.index != 9, index_err_msg, s.index, 9);
+
+       /* Check if renderer remains in Stopped state after running go
+        * to index */
+       fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped "
+               "state after running go to index. The actual state is %d and "
+               "must be %s", s.state, "Stopped");
+
+       /* --- Play (playlist index is 9) --- */
+
+       reset_callback_info(&c);
+
+       c.error_signal_expected = TRUE;
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Transitioning",
+                    s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Playing", s.state);
+       }
+
+       fail_if(c.error_signal_received == NULL ||
+               !g_error_matches(c.error_signal_received, MAFW_RENDERER_ERROR,
+                                MAFW_RENDERER_ERROR_INVALID_URI),
+               "No error received or incorrect one");
+
+       if (c.error_signal_received != NULL) {
+               g_error_free(c.error_signal_received);
+               c.error_signal_received = NULL;
+       }
+       c.error_signal_expected = FALSE;
+
+       /* --- Stop --- */
+
+       reset_callback_info(&c);
+
+       g_debug("stop...");
+       mafw_renderer_stop(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "stopping playback",
+                            c.err_code, c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg,"mafw_renderer_stop","Stopped", s.state);
+       }
+
+       /* --- Remove last media --- */
+
+       mafw_playlist_remove_item(playlist, 10, NULL);
+
+       /* --- Go to index with invalid media --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 9...");
+       mafw_renderer_goto_index(g_gst_renderer, 9, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index 9", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* Check if the playlist index is correct */
+       fail_if(s.index != 9, index_err_msg, s.index, 9);
+
+       /* Check if renderer remains in Stopped state after running go
+        * to index */
+       fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped "
+               "state after running go to index. The actual state is %d and "
+               "must be %s", s.state, "Stopped");
+
+       /* --- Play (playlist index is 9) --- */
+
+       reset_callback_info(&c);
+
+       c.error_signal_expected = TRUE;
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Transitioning",
+                    s.state);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Stopped", s.state);
+       }
+
+       fail_if(c.error_signal_received == NULL ||
+               !g_error_matches(c.error_signal_received, MAFW_RENDERER_ERROR,
+                                MAFW_RENDERER_ERROR_INVALID_URI),
+               "No error received or incorrect one");
+
+       if (c.error_signal_received != NULL) {
+               g_error_free(c.error_signal_received);
+               c.error_signal_received = NULL;
+       }
+       c.error_signal_expected = FALSE;
+
+       /* --- Play incorrect object --- */
+
+       reset_callback_info(&c);
+
+       c.error_signal_expected = TRUE;
+
+       gchar *objectid = get_sample_clip_objectid("unexisting.wav");
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning",
+                    s.state);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Stopped",
+                    s.state);
+       }
+
+       fail_if(c.error_signal_received == NULL ||
+               !g_error_matches(c.error_signal_received, MAFW_RENDERER_ERROR,
+                                MAFW_RENDERER_ERROR_INVALID_URI),
+               "No error received or incorrect one");
+
+       if (c.error_signal_received != NULL) {
+               g_error_free(c.error_signal_received);
+               c.error_signal_received = NULL;
+       }
+       c.error_signal_expected = FALSE;
+
+       g_free(objectid);
+
+}
+END_TEST
+
+
+START_TEST(test_repeat_mode_playback)
+{
+       MafwPlaylist *playlist = NULL;
+       gint i = 0;
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+
+       /* --- Create playlist --- */
+
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+       for (i=0; i<10; i++) {
+               gchar *cur_item_oid =
+                       get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+               mafw_playlist_insert_item(
+                       playlist, i, cur_item_oid, NULL);
+               g_free(cur_item_oid);
+       }
+        
+        /* Set repeat mode */
+       mafw_playlist_set_repeat(playlist, TRUE);
+
+       /* --- Assign playlist --- */   
+
+       g_debug("assign playlist...");
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Playing", s.state);
+       }
+
+       /* --- Go to index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 9...");
+       /* go to the end of the playlist */
+       mafw_renderer_goto_index(g_gst_renderer, 9, playback_cb, &c);
+       
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index 9", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+       
+       /* check if the movement was successful */
+       fail_if(s.index != 9, index_err_msg, 9, s.index);
+       
+       /* ---  Stop --- */
+
+       reset_callback_info(&c);
+
+       g_debug("stop...");
+       mafw_renderer_stop(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "stopping playback", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_stop", "Stopped", s.state);
+       }
+       /* --- Next --- */
+
+       reset_callback_info(&c);
+
+       g_debug("next...");
+       /* The actual index is 9 */
+       mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+               if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to next", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* check if the movement was successful */
+       fail_if(s.index != 0, index_err_msg, s.index, 0);
+
+       /* Check if renderer remains in Stopped state after moving to next */
+       fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped state "
+               "after doing next. The actual state is %s and must be %s",
+               s.state, "Stopped");
+
+       /* --- Prev --- */
+
+       reset_callback_info(&c);
+
+       g_debug("prev...");
+       /* The actual index is 0 */
+       mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to prev", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* check if the movement was successful */
+       fail_if(s.index != 9, index_err_msg, s.index, 9);
+
+       /* Check if renderer remains in Stopped state after moving to next */
+       fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped state "
+               "after doing next. The actual state is %s and must be %s",
+               s.state, "Stopped");
+}
+END_TEST
+
+
+START_TEST(test_gst_renderer_mode)
+{
+       MafwPlaylist *playlist = NULL;
+       MafwGstRenderer *renderer = NULL;
+       MafwGstRendererPlaybackMode play_mode;
+       gchar *objectid = NULL;
+       gint i = 0;
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+       gchar *modes[] = {"MAFW_GST_RENDERER_MODE_PLAYLIST",
+                         "MAFW_GST_RENDERER_MODE_STANDALONE"};
+
+       renderer = MAFW_GST_RENDERER(g_gst_renderer);
+
+       /* Initiliaze callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+
+       /* --- Create playlist --- */
+
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+       for (i=0; i<10; i++) {
+               gchar *cur_item_oid =
+                       get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+               mafw_playlist_insert_item(
+                       playlist, i, cur_item_oid, NULL);
+               g_free(cur_item_oid);
+       }
+
+       /* --- Assign playlist --- */   
+
+       g_debug("assign playlist...");
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Transitioning", s.state);
+       }
+
+       /* Check that renderer is playing a playlist */
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Playing", s.state);
+       }
+       play_mode =  mafw_gst_renderer_get_playback_mode(renderer);
+       fail_if(play_mode != MAFW_GST_RENDERER_MODE_PLAYLIST,
+               "Incorrect value of playback_mode: %s", modes[play_mode]);
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s",objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c);
+       g_free(objectid);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Transitioning",
+                    s.state);
+       }
+
+       /* Check that renderer is playing an object */          
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       play_mode =  mafw_gst_renderer_get_playback_mode(renderer);
+       fail_if(play_mode != MAFW_GST_RENDERER_MODE_STANDALONE,
+               "Incorrect value of playback_mode: %s", modes[play_mode]);
+
+       /* Wait EOS_TIMEOUT to ensure that the play_object has finished */
+       wait_until_timeout_finishes(EOS_TIMEOUT);
+
+       /* Check that after playing the object, renderer returns to the playlist
+        playback */
+       play_mode =  mafw_gst_renderer_get_playback_mode(renderer);
+       fail_if(play_mode != MAFW_GST_RENDERER_MODE_PLAYLIST,
+               "Incorrect value of playback_mode: %s", modes[play_mode]);
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c);
+       g_free(objectid);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Transitioning",
+                    s.state);
+       }
+               
+       /* Check that renderer is playing an object */
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+       play_mode =  mafw_gst_renderer_get_playback_mode(renderer);
+       fail_if(play_mode != MAFW_GST_RENDERER_MODE_STANDALONE,
+               "Incorrect value of playback_mode: %s", modes[play_mode]);
+
+
+       /* --- Move to next when renderer is playing an object --- */
+
+       reset_callback_info(&c);
+
+       g_debug("next...");
+       mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+               if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to next", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* Check that "next" function finishes the object playback and returns
+          to the playlist playback */
+       play_mode =  mafw_gst_renderer_get_playback_mode(renderer);
+       fail_if(play_mode != MAFW_GST_RENDERER_MODE_PLAYLIST,
+               "Incorrect value of playback_mode: %s", modes[play_mode]);
+
+       /* Check that renderer is still in Playing state */
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- Stop --- */
+
+       reset_callback_info(&c);
+
+       g_debug("stop...");
+       mafw_renderer_stop(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "stopping", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state);
+       }
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Transitioning",
+                    s.state);
+       }
+
+       /* Check that renderer is playing an object */        
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+       play_mode =  mafw_gst_renderer_get_playback_mode(renderer);
+       fail_if(play_mode != MAFW_GST_RENDERER_MODE_STANDALONE,
+               "Incorrect value of playback_mode: %s", modes[play_mode]);
+       
+       /* Wait EOS_TIMEOUT to ensure that object playback finishes */
+       wait_until_timeout_finishes(EOS_TIMEOUT);
+
+       /* Check if renderer is in playlist mode and the renderer state is the state before
+          playing the object */
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state);
+       }
+       play_mode =  mafw_gst_renderer_get_playback_mode(renderer);
+       fail_if(play_mode != MAFW_GST_RENDERER_MODE_PLAYLIST,
+               "Incorrect value of playback_mode: %s", modes[play_mode]);
+
+       g_free(objectid);
+}
+END_TEST
+
+#define MOCK_SOURCE(o)                                                 \
+       (G_TYPE_CHECK_INSTANCE_CAST((o),                                \
+                                   mock_source_get_type(),             \
+                                   MockSource))
+
+typedef struct {
+       MafwSourceClass parent;
+} MockSourceClass;
+
+typedef struct {
+       MafwSource parent;
+
+
+} MockSource;
+
+GType mock_source_get_type(void);
+GObject* mock_source_new(void);
+
+G_DEFINE_TYPE(MockSource, mock_source, MAFW_TYPE_SOURCE);
+
+static GHashTable *get_md_ht;          /* Metadata hash-table for the metadata result */
+static GError *get_md_err;             /* Error value for the metadata result */
+static gboolean set_mdata_called;      /* Whether set_metadata was called or not */
+static gboolean get_mdata_called;      /* Whether get_metadata was called or not */
+static gint reference_pcount;          /* Reference playcount, what should come in set_metadata */
+static gboolean set_for_playcount;     /* TRUE, when the set_metadata is called to modify the playcount */
+static gboolean set_for_lastplayed;    /* TRUE, when the set_metadata is called to modify the last-played */
+
+static void get_metadata(MafwSource *self,
+                            const gchar *object_id,
+                            const gchar *const *metadata,
+                            MafwSourceMetadataResultCb callback,
+                            gpointer user_data)
+{
+       get_mdata_called = TRUE;
+       fail_if(strcmp(object_id, "mocksource::test"));
+       callback(self, object_id, get_md_ht, user_data, get_md_err);
+}
+
+static void set_metadata(MafwSource *self, const gchar *object_id,
+                            GHashTable *metadata,
+                            MafwSourceMetadataSetCb callback,
+                            gpointer user_data)
+{
+       GValue *curval;
+       gint htsize = 0;
+
+       if (set_for_playcount)
+               htsize++;
+       if (set_for_lastplayed)
+               htsize++;
+       fail_if(strcmp(object_id, "mocksource::test"));
+       fail_if(!metadata);
+       fail_if(g_hash_table_size(metadata) != htsize, "Hash table size: %d vs %d", g_hash_table_size(metadata), htsize);
+       if (set_for_playcount)
+       {
+               curval = mafw_metadata_first(metadata,
+                               MAFW_METADATA_KEY_PLAY_COUNT);
+               fail_if(!curval);
+               fail_if(g_value_get_int(curval) != reference_pcount);
+       }
+       if (set_for_lastplayed)
+       {
+               curval = mafw_metadata_first(metadata,
+                               MAFW_METADATA_KEY_LAST_PLAYED);
+               fail_if(!curval);
+               fail_if(!G_VALUE_HOLDS(curval, G_TYPE_LONG));
+       }
+       set_mdata_called = TRUE;
+}
+
+static void mock_source_class_init(MockSourceClass *klass)
+{
+       MafwSourceClass *sclass = MAFW_SOURCE_CLASS(klass);
+
+       sclass->get_metadata = get_metadata;
+       sclass->set_metadata = set_metadata;
+
+}
+
+static void mock_source_init(MockSource *source)
+{
+       /* NOP */
+}
+
+GObject* mock_source_new(void)
+{
+       GObject* object;
+       object = g_object_new(mock_source_get_type(),
+                             "plugin", "mockland",
+                             "uuid", "mocksource",
+                             "name", "mocksource",
+                             NULL);
+       return object;
+}
+
+
+START_TEST(test_update_stats)
+{
+       MafwGstRenderer *renderer = NULL;
+       MafwSource *src;
+       MafwRegistry *registry;
+
+       registry = MAFW_REGISTRY(mafw_registry_get_instance());
+       fail_if(registry == NULL,
+               "Error: cannot get MAFW registry");
+               
+
+       renderer = MAFW_GST_RENDERER(g_gst_renderer);
+       src = MAFW_SOURCE(mock_source_new());
+       
+       mafw_registry_add_extension(registry, MAFW_EXTENSION(src));
+
+       /* Error on get_mdata_cb*/
+       set_for_playcount = FALSE;
+       set_for_lastplayed = FALSE;
+        get_md_err = NULL;
+       g_set_error(&get_md_err, MAFW_SOURCE_ERROR,
+                    MAFW_SOURCE_ERROR_INVALID_OBJECT_ID,
+                    "Wrong object id mocksource::test");
+       renderer->media->object_id = g_strdup("mocksource::test");
+       mafw_gst_renderer_update_stats(renderer);
+        g_error_free(get_md_err);
+       fail_if(set_mdata_called);
+       fail_if(!get_mdata_called);
+
+       /* get_mdata ok, but HashTable is NULL */
+       reference_pcount = 1;
+       get_mdata_called = FALSE;
+       set_for_lastplayed = TRUE;
+       set_for_playcount = TRUE;
+       get_md_err = NULL;
+       mafw_gst_renderer_update_stats(renderer);
+       fail_if(!set_mdata_called);
+       fail_if(!get_mdata_called);
+       
+       /* get_mdata ok, but HashTable is empty */
+       get_mdata_called = FALSE;
+       set_mdata_called = FALSE;
+       set_for_lastplayed = TRUE;
+       set_for_playcount = TRUE;
+       get_md_ht = mafw_metadata_new();
+       mafw_gst_renderer_update_stats(renderer);
+       fail_if(!set_mdata_called);
+       fail_if(!get_mdata_called);
+       
+       /* get_mdata ok, but HashTable has valid value */
+       get_mdata_called = FALSE;
+       set_mdata_called = FALSE;
+       set_for_lastplayed = TRUE;
+       set_for_playcount = TRUE;
+       mafw_metadata_add_int(get_md_ht,
+                                               MAFW_METADATA_KEY_PLAY_COUNT,
+                                               1);
+       reference_pcount = 2;
+       mafw_gst_renderer_update_stats(renderer);
+       fail_if(!set_mdata_called);
+       fail_if(!get_mdata_called);
+}
+END_TEST
+
+START_TEST(test_play_state)
+{
+       MafwPlaylist *playlist = NULL;
+       gint i = 0;
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+       gchar *objectid = NULL;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       if (wait_for_state(&s, Stopped, 3000) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Stop",
+                    s.state);
+       }
+
+       g_free(objectid);
+
+
+       /* --- Create and assign a playlist --- */
+
+       g_debug("assign playlist...");
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+       for (i=0; i<10; i++) {
+               gchar *cur_item_oid =
+                       get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+               mafw_playlist_insert_item(
+                       playlist, i, cur_item_oid, NULL);
+               g_free(cur_item_oid);
+       }
+       mafw_playlist_set_repeat(playlist, FALSE);
+
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist,
+                                           NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail("Play after assigning playlist failed");
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Playing",
+                    s.state);
+       }
+
+       /* --- Prev --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to prev...");
+       mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to prev", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 9, index_err_msg, s.index, 9);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev", "Playing",
+                    s.state);
+       }
+
+       /* Removing last element */
+
+       g_debug("removing last element...");
+       fail_if(mafw_playlist_get_size(playlist, NULL) != 10,
+               "Playlist should have 10 elements");
+       mafw_playlist_remove_item(playlist, 9, NULL);
+       fail_if(mafw_playlist_get_size(playlist, NULL) != 9,
+               "Playlist should have 9 elements");
+       fail_if(s.index != 8, index_err_msg, s.index, 8);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_playlist_remove_element",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_playlist_remove_element", "Playing",
+                    s.state);
+       }
+
+       /* --- Next --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to next...");
+       mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to next", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 0, index_err_msg, s.index, 0);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_next",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_next", "Playing",
+                    s.state);
+       }
+
+       /* --- Go to index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 8...");
+       mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index 8", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 8, index_err_msg, s.index, 8);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_goto_index",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_goto_index", "Playing",
+                    s.state);
+       }
+
+       /* --- Seeking --- */
+
+       reset_callback_info(&c);
+
+       g_debug("seeking...");
+       mafw_renderer_set_position(g_gst_renderer, SeekAbsolute, 1,
+                                   seek_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "seeking failed", c.err_code,
+                            c.err_msg);
+               if (c.seek_position != 1) {
+                       fail("seeking failed");
+               }
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Waiting EOS --- */
+
+       if (wait_for_state(&s, Stopped, 2000) == FALSE) {
+               fail(state_err_msg, "EOS", "Stop",
+                    s.state);
+       }
+}
+END_TEST
+
+START_TEST(test_pause_state)
+{
+       MafwPlaylist *playlist = NULL;
+       gint i = 0;
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+       gchar *objectid = NULL;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Create and assign a playlist --- */
+
+       g_debug("assign playlist...");
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+       for (i=0; i<10; i++) {
+               gchar *cur_item_oid =
+                       get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+               mafw_playlist_insert_item(
+                       playlist, i, cur_item_oid, NULL);
+               g_free(cur_item_oid);
+       }
+       mafw_playlist_set_repeat(playlist, FALSE);
+
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist,
+                                           NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail("Play failed");
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play",
+                    "Transitioning", s.state);
+       }
+
+       /* Testing pause in transitioning */
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* Testing resume in transitioning */
+
+       reset_callback_info(&c);
+
+       g_debug("resume...");
+       mafw_renderer_resume(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "resuming", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       reset_callback_info(&c);
+
+       /* Testing resume without having paused in transitioning */
+
+       reset_callback_info(&c);
+
+       g_debug("resume...");
+       mafw_renderer_resume(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (!c.error)
+                       fail(callback_no_err_msg, "resuming", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_pause", "Paused",
+                    s.state);
+       }
+
+       /* --- Play object in pause --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_pause", "Paused",
+                    s.state);
+       }
+
+       g_free(objectid);
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail("Play failed");
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play",
+                    "Transitioning", s.state);
+       }
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_pause", "Paused",
+                    s.state);
+       }
+
+       /* --- Prev --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to prev...");
+       mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to prev", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* Check if the playlist index is correct */
+       fail_if(s.index != 9, index_err_msg, s.index, 9);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev",
+                    "Transitioning", s.state);
+       }
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev", "Playing",
+                    s.state);
+       }
+
+       /* Removing last element */
+
+       g_debug("removing last element...");
+       fail_if(mafw_playlist_get_size(playlist, NULL) != 10,
+               "Playlist should have 10 elements");
+       mafw_playlist_remove_item(playlist, 9, NULL);
+       fail_if(mafw_playlist_get_size(playlist, NULL) != 9,
+               "Playlist should have 9 elements");
+       fail_if(s.index != 8, index_err_msg, s.index, 8);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_playlist_remove_item",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_playlist_remove_item", "Playing",
+                    s.state);
+       }
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_playlist_remove_item", "Playing",
+                    s.state);
+       }
+
+       /* --- Next --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to next...");
+       mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to next", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* Check if the playlist index is correct */
+       fail_if(s.index != 0, index_err_msg, s.index, 0);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_next",
+                    "Transitioning", s.state);
+       }
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_next", "Playing",
+                    s.state);
+       }
+
+       /* --- Go to index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 8...");
+       mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index 8", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* Check if the playlist index is correct */
+       fail_if(s.index != 8, index_err_msg, s.index, 8);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_goto_index",
+                    "Transitioning", s.state);
+       }
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_goto_index", "Playing",
+                    s.state);
+       }
+
+       /* --- Seeking --- */
+
+       reset_callback_info(&c);
+
+       mafw_renderer_set_position(g_gst_renderer, SeekAbsolute, 1,
+                                   seek_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "seeking", c.err_code,
+                            c.err_msg);
+               if (c.seek_position != 1) {
+                       fail("seeking failed");
+               }
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Stop --- */
+
+       reset_callback_info(&c);
+
+       g_debug("stop...");
+       mafw_renderer_stop(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "stopping", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state);
+       }
+}
+END_TEST
+
+START_TEST(test_stop_state)
+{
+       MafwPlaylist *playlist = NULL;
+       gint i = 0;
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Prev --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to prev...");
+       mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (!c.error)
+                       fail(callback_no_err_msg, "moving to prev", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Next --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to next...");
+       mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (!c.error)
+                       fail(callback_no_err_msg, "moving to next", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Go to index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 8...");
+       mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (!c.error)
+                       fail(callback_no_err_msg, "going to index 8",
+                            c.err_code, c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Create and assign a playlist --- */
+
+       g_debug("assign playlist...");
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+       for (i=0; i<10; i++) {
+               gchar *cur_item_oid =
+                       get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+               mafw_playlist_insert_item(
+                       playlist, i, cur_item_oid, NULL);
+               g_free(cur_item_oid);
+       }
+       mafw_playlist_set_repeat(playlist, FALSE);
+
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist,
+                                           NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* Removing last element */
+
+       g_debug("removing last element...");
+       fail_if(mafw_playlist_get_size(playlist, NULL) != 10,
+               "Playlist should have 10 elements");
+       mafw_playlist_remove_item(playlist, 9, NULL);
+       fail_if(mafw_playlist_get_size(playlist, NULL) != 9,
+               "Playlist should have 9 elements");
+
+       /* --- Go to index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 9...");
+       mafw_renderer_goto_index(g_gst_renderer, 9, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (!c.error)
+                       fail(callback_no_err_msg, "going to index 9",
+                            c.err_code, c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+       reset_callback_info(&c);
+}
+END_TEST
+
+START_TEST(test_transitioning_state)
+{
+       MafwPlaylist *playlist = NULL;
+       gint i = 0;
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+       gchar *objectid = NULL;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Create and assign a playlist --- */
+
+       g_debug("assign playlist...");
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+       for (i=0; i<10; i++) {
+               gchar *cur_item_oid =
+                       get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+               mafw_playlist_insert_item(
+                       playlist, i, cur_item_oid, NULL);
+               g_free(cur_item_oid);
+       }
+       mafw_playlist_set_repeat(playlist, FALSE);
+
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist,
+                                           NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail("Play after assigning playlist failed");
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play",
+                    "Transitioning", s.state);
+       }
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       g_free(objectid);
+
+
+       /* --- Prev --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to prev...");
+       mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to prev", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 9, index_err_msg, s.index, 9);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev",
+                    "Transitioning", s.state);
+       }
+
+       /* Removing last element */
+
+       g_debug("removing last element...");
+       fail_if(mafw_playlist_get_size(playlist, NULL) != 10,
+               "Playlist should have 10 elements");
+       mafw_playlist_remove_item(playlist, 9, NULL);
+       fail_if(mafw_playlist_get_size(playlist, NULL) != 9,
+               "Playlist should have 9 elements");
+       fail_if(s.index != 8, index_err_msg, s.index, 8);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_playlist_remove_element",
+                    "Transitioning", s.state);
+       }
+
+       /* --- Next --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to next...");
+       mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to next", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 0, index_err_msg, s.index, 0);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_next",
+                    "Transitioning", s.state);
+       }
+
+       /* --- Go to index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 8...");
+       mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index 8", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 8, index_err_msg, s.index, 8);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_goto_index",
+                    "Transitioning", s.state);
+       }
+}
+END_TEST
+
+START_TEST(test_state_class)
+{
+       MafwPlaylist *playlist = NULL;
+       gint i = 0;
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+       gchar *objectid = NULL;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- Prev --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to prev...");
+       mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (!c.error)
+                       fail(callback_no_err_msg, "moving to prev", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- Next --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to next...");
+       mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (!c.error)
+                       fail(callback_no_err_msg, "moving to next", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- Go to index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 8...");
+       mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (!c.error)
+                       fail(callback_err_msg, "going to index 8", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Create and assign a playlist --- */
+
+       g_debug("assign playlist...");
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+       for (i=0; i<10; i++) {
+               gchar *cur_item_oid =
+                       get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+               mafw_playlist_insert_item(
+                       playlist, i, cur_item_oid, NULL);
+               g_free(cur_item_oid);
+       }
+       mafw_playlist_set_repeat(playlist, FALSE);
+
+       media_changed_called = FALSE;
+       if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist,
+                                           NULL))
+       {
+               fail("Assign playlist failed");
+       }
+
+       wait_for_state(&s, Stopped, wait_tout_val);
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- Next --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to next...");
+       mafw_renderer_next(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to next", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 1, index_err_msg, s.index, 1);
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_next", "Playing",
+                    s.state);
+       }
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- Go to index --- */
+
+       reset_callback_info(&c);
+
+       g_debug("goto index 8...");
+       mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "going to index 8", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 8, index_err_msg, s.index, 8);
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_goto_index", "Playing",
+                    s.state);
+       }
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- Prev --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to prev...");
+       mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to prev", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 7, index_err_msg, s.index, 7);
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev", "Playing",
+                    s.state);
+       }
+
+       /* --- Play --- */
+
+       reset_callback_info(&c);
+
+       g_debug("play...");
+       mafw_renderer_play(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail("Play after assigning playlist failed");
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play", "Playing",
+                    s.state);
+       }
+
+       /* --- Prev --- */
+
+       reset_callback_info(&c);
+
+       g_debug("move to prev...");
+       mafw_renderer_previous(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "moving to prev", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(s.index != 6, index_err_msg, s.index, 6);
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev",
+                    "Transitioning", s.state);
+       }
+
+       /* --- Seeking --- */
+
+       reset_callback_info(&c);
+
+       g_debug("seeking...");
+       mafw_renderer_set_position(g_gst_renderer, SeekRelative, 1,
+                                   seek_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "seeking failed", c.err_code,
+                            c.err_msg);
+               if (c.seek_position != 1) {
+                       fail("seeking failed");
+               }
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Seeking --- */
+
+       reset_callback_info(&c);
+
+       g_debug("seeking...");
+       mafw_renderer_set_position(g_gst_renderer, SeekAbsolute, -1,
+                                   seek_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "seeking failed", c.err_code,
+                            c.err_msg);
+               if (c.seek_position != -1) {
+                       fail("seeking failed");
+               }
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Seeking --- */
+
+       reset_callback_info(&c);
+
+       g_debug("seeking...");
+       mafw_renderer_set_position(g_gst_renderer, SeekAbsolute, 1,
+                                   seek_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "seeking failed", c.err_code,
+                            c.err_msg);
+               if (c.seek_position != 1) {
+                       fail("seeking failed");
+               }
+       } else {
+               fail(no_callback_msg);
+       }
+}
+END_TEST
+
+START_TEST(test_playlist_iterator)
+{
+       MafwPlaylist *playlist = NULL;
+       gint i = 0;
+       CallbackInfo c = {0, };;
+       MafwPlaylistIterator *iterator = NULL;
+       GError *error = NULL;
+       gint size;
+       gint index;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error = FALSE;
+       reset_callback_info(&c);
+
+       /* --- Create and assign a playlist --- */
+
+       g_debug("assign playlist...");
+       playlist = MAFW_PLAYLIST(mafw_mock_playlist_new());
+
+       iterator = mafw_playlist_iterator_new();
+       mafw_playlist_iterator_initialize(iterator, playlist, &error);
+       if (error != NULL) {
+               fail("Error found: %s, %d, %s",
+                    g_quark_to_string(error->domain),
+                    error->code, error->message);
+       }
+
+       for (i = 0; i < 3; i++) {
+               gchar *cur_item_oid = NULL;
+               cur_item_oid =
+                       get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+               mafw_playlist_insert_item(playlist, 0, cur_item_oid, NULL);
+               g_free(cur_item_oid);
+       }
+
+       size = mafw_playlist_iterator_get_size(iterator, NULL);
+       fail_if(size != 3, "Playlist should have 3 elements and it has %d",
+               size);
+       index = mafw_playlist_iterator_get_current_index(iterator);
+       fail_if(index != 2, "Index should be 2 and it is %d", index);
+
+       mafw_playlist_move_item(playlist, 1, 2, NULL);
+       index = mafw_playlist_iterator_get_current_index(iterator);
+       fail_if(index != 1, "Index should be 1 and it is %d", index);
+
+       mafw_playlist_move_item(playlist, 2, 1, NULL);
+       index = mafw_playlist_iterator_get_current_index(iterator);
+       fail_if(index != 2, "Index should be 2 and it is %d", index);
+
+       mafw_playlist_move_item(playlist, 2, 1, NULL);
+       index = mafw_playlist_iterator_get_current_index(iterator);
+       fail_if(index != 1, "Index should be 1 and it is %d", index);
+
+       mafw_playlist_remove_item(playlist, 0, &error);
+       if (error != NULL) {
+               fail("Error found: %s, %d, %s",
+                    g_quark_to_string(error->domain),
+                    error->code, error->message);
+       }
+
+       size = mafw_playlist_iterator_get_size(iterator, NULL);
+       fail_if(size != 2, "Playlist should have 2 elements and it has %d",
+               size);
+       index = mafw_playlist_iterator_get_current_index(iterator);
+       fail_if(index != 0, "Index should be 0 and it is %d", index);
+
+       mafw_playlist_iterator_reset(iterator, NULL);
+       index = mafw_playlist_iterator_get_current_index(iterator);
+       fail_if(index != 0, "Index should be 0 and it is %d", index);
+
+       mafw_playlist_remove_item(playlist, 0, &error);
+       if (error != NULL) {
+               fail("Error found: %s, %d, %s",
+                    g_quark_to_string(error->domain),
+                    error->code, error->message);
+       }
+
+       size = mafw_playlist_iterator_get_size(iterator, NULL);
+       fail_if(size != 1, "Playlist should have 1 elements and it has %d",
+               size);
+       index = mafw_playlist_iterator_get_current_index(iterator);
+       fail_if(index != 0, "Index should be 0 and it is %d", index);
+
+       mafw_playlist_remove_item(playlist, 0, &error);
+       if (error != NULL) {
+               fail("Error found: %s, %d, %s",
+                    g_quark_to_string(error->domain),
+                    error->code, error->message);
+       }
+
+       size = mafw_playlist_iterator_get_size(iterator, NULL);
+       fail_if(size != 0, "Playlist should have 0 elements and it has %d",
+               size);
+       index = mafw_playlist_iterator_get_current_index(iterator);
+       fail_if(index != -1, "Index should be -1 and it is %d", index);
+
+       g_object_unref(iterator);
+}
+END_TEST
+
+START_TEST(test_video)
+{
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+       MetadataChangedInfo m;
+       gchar *objectid = NULL;
+       GstBus *bus = NULL;
+       GstStructure *structure = NULL;
+       GstMessage *message = NULL;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       m.expected_key = NULL;
+       m.value = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+       g_signal_connect(g_gst_renderer, "metadata-changed",
+                        G_CALLBACK(metadata_changed_cb),
+                        &m);
+
+#ifdef HAVE_GDKPIXBUF
+       mafw_extension_set_property_boolean(
+               MAFW_EXTENSION(g_gst_renderer),
+               MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE,
+               TRUE);
+#endif
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_VIDEO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       MAFW_GST_RENDERER(g_gst_renderer)->worker->xid = 0x1;
+       bus = MAFW_GST_RENDERER(g_gst_renderer)->worker->bus;
+       fail_if(bus == NULL, "No GstBus");
+
+       structure = gst_structure_new("prepare-xwindow-id", "width",
+                                     G_TYPE_INT, 64, "height", G_TYPE_INT, 32,
+                                     NULL);
+       message = gst_message_new_element(NULL, structure);
+       gst_bus_post(bus, message);
+
+       /* --- Pause --- */
+
+       reset_callback_info(&c);
+
+       m.expected_key = MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI;
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev", "Playing",
+                    s.state);
+       }
+
+       if (wait_for_metadata(&m, wait_tout_val) == FALSE) {
+               fail("Expected " MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI
+                    ", but not received");
+       }
+
+       fail_if(m.value == NULL, "Metadata "
+               MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI " not received");
+
+       g_value_unset(m.value);
+       g_free(m.value);
+       m.value = NULL;
+       m.expected_key = NULL;
+
+       /* --- Resume --- */
+
+       reset_callback_info(&c);
+
+       g_debug("resume...");
+       mafw_renderer_resume(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "resuming", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       /* --- EOS --- */
+
+       if (wait_for_state(&s, Stopped, 3000) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Stop",
+                    s.state);
+       }
+
+       g_free(objectid);
+}
+END_TEST
+
+START_TEST(test_media_art)
+{
+       RendererInfo s = {0, };;
+       CallbackInfo c = {0, };;
+       MetadataChangedInfo m;
+       gchar *objectid = NULL;
+       GstBus *bus = NULL;
+       GstMessage *message = NULL;
+       GstTagList *list = NULL;
+       GstBuffer *buffer = NULL;
+       guchar *image = NULL;
+       gchar *image_path = NULL;
+       gsize image_length;
+       GstCaps *caps = NULL;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       m.expected_key = NULL;
+       m.value = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "media-changed",
+                        G_CALLBACK(media_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "playlist-changed",
+                        G_CALLBACK(playlist_changed_cb),
+                        NULL);
+       g_signal_connect(g_gst_renderer, "metadata-changed",
+                        G_CALLBACK(metadata_changed_cb),
+                        &m);
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb,
+                                  &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       /* --- Pause --- */
+
+       reset_callback_info(&c);
+
+       g_debug("pause...");
+       mafw_renderer_pause(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "pausing", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_prev", "Playing",
+                    s.state);
+       }
+
+       /* Emit image */
+
+       bus = MAFW_GST_RENDERER(g_gst_renderer)->worker->bus;
+       fail_if(bus == NULL, "No GstBus");
+
+       m.expected_key = MAFW_METADATA_KEY_RENDERER_ART_URI;
+
+       image_path = get_sample_clip_path(SAMPLE_IMAGE);
+       fail_if(!g_file_get_contents(image_path + 7, (gchar **) &image,
+                                    &image_length, NULL),
+               "Could not load test image");
+       g_free(image_path);
+
+       buffer = gst_buffer_new();
+       gst_buffer_set_data(buffer, image, image_length);
+       caps = gst_caps_new_simple("image/png", "image-type",
+                                  GST_TYPE_TAG_IMAGE_TYPE,
+                                  GST_TAG_IMAGE_TYPE_FRONT_COVER, NULL);
+       gst_buffer_set_caps(buffer, caps);
+       gst_caps_unref(caps);
+
+       list = gst_tag_list_new();
+       gst_tag_list_add(list, GST_TAG_MERGE_APPEND, GST_TAG_IMAGE, buffer,
+                        NULL);
+
+       message = gst_message_new_tag(NULL, list);
+       gst_bus_post(bus, message);
+
+       /* --- Resume --- */
+
+       reset_callback_info(&c);
+
+       g_debug("resume...");
+       mafw_renderer_resume(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "resuming", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       if (wait_for_metadata(&m, wait_tout_val) == FALSE) {
+               fail("Expected " MAFW_METADATA_KEY_RENDERER_ART_URI
+                    ", but not received");
+       }
+
+       fail_if(m.value == NULL, "Metadata "
+               MAFW_METADATA_KEY_RENDERER_ART_URI " not received");
+
+       g_value_unset(m.value);
+       g_free(m.value);
+       m.value = NULL;
+       m.expected_key = NULL;
+
+       /* --- EOS --- */
+
+       if (wait_for_state(&s, Stopped, 3000) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Stop",
+                    s.state);
+       }
+
+       g_free(objectid);
+}
+END_TEST
+
+START_TEST(test_properties_management)
+{
+       RendererInfo s;
+       CallbackInfo c = {0, };;
+       PropertyChangedInfo p;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+       p.expected = NULL;
+       p.received = NULL;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "property-changed",
+                        G_CALLBACK(property_changed_cb),
+                        &p);
+
+       /* Wait for the volume manager to be initialized */
+
+       /* Volume */
+
+       p.expected = MAFW_PROPERTY_RENDERER_VOLUME;
+
+       if (!wait_for_property(&p, wait_tout_val)) {
+               fail("No property %s received", p.expected);
+       }
+
+       fail_if(p.received == NULL, "No property %s received",
+               p.expected);
+       fail_if(p.received != NULL &&
+               g_value_get_uint(p.received) != 48,
+               "Property with value %d and %d expected",
+               g_value_get_uint(p.received), 48);
+
+       if (p.received != NULL) {
+               g_value_unset(p.received);
+               g_free(p.received);
+               p.received = NULL;
+       }
+       p.expected = NULL;
+
+       /* --- mute --- */
+
+       reset_callback_info(&c);
+
+       c.property_expected = MAFW_PROPERTY_RENDERER_MUTE;
+
+       mafw_extension_set_property_boolean(MAFW_EXTENSION(g_gst_renderer),
+                                           c.property_expected, TRUE);
+
+       p.expected = MAFW_PROPERTY_RENDERER_MUTE;
+
+#ifdef MAFW_GST_RENDERER_ENABLE_MUTE
+       if (!wait_for_property(&p, wait_tout_val)) {
+               fail("No property %s received", p.expected);
+       }
+
+       fail_if(p.received == NULL, "No property %s received",
+               p.expected);
+       fail_if(p.received != NULL &&
+               g_value_get_boolean(p.received) != TRUE,
+               "Property with value %d and %d expected",
+               g_value_get_boolean(p.received), TRUE);
+#else
+       if (wait_for_property(&p, wait_tout_val)) {
+               fail("Property %s received and it should not have been",
+                    p.expected);
+       }
+
+       fail_if(p.received != NULL,
+               "Property %s received and it should not have been",
+               p.expected);
+#endif
+
+       if (p.received != NULL) {
+               g_value_unset(p.received);
+               g_free(p.received);
+               p.received = NULL;
+       }
+       p.expected = NULL;
+
+       mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer),
+                                   c.property_expected, get_property_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "get_property", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(c.property_received == NULL,
+               "No property %s received and expected", c.property_expected);
+#ifdef MAFW_GST_RENDERER_ENABLE_MUTE
+       fail_if(c.property_received != NULL &&
+               g_value_get_boolean(c.property_received) != TRUE,
+               "Property with value %d and %d expected",
+               g_value_get_boolean(c.property_received), TRUE);
+#else
+       fail_if(c.property_received != NULL &&
+               g_value_get_boolean(c.property_received) != FALSE,
+               "Property with value %d and %d expected",
+               g_value_get_boolean(c.property_received), FALSE);
+#endif
+
+       /* --- xid --- */
+
+       reset_callback_info(&c);
+
+       c.property_expected = MAFW_PROPERTY_RENDERER_XID;
+
+       mafw_extension_set_property_uint(MAFW_EXTENSION(g_gst_renderer),
+                                        c.property_expected, 50);
+
+       mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer),
+                                   c.property_expected, get_property_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "get_property", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(c.property_received == NULL,
+               "No property %s received and expected", c.property_expected);
+       fail_if(c.property_received != NULL &&
+               g_value_get_uint(c.property_received) != 50,
+               "Property with value %d and %d expected",
+               g_value_get_uint(c.property_received), 50);
+
+       /* --- error policy --- */
+
+       reset_callback_info(&c);
+
+       c.property_expected = MAFW_PROPERTY_RENDERER_ERROR_POLICY;
+
+       mafw_extension_set_property_uint(MAFW_EXTENSION(g_gst_renderer),
+                                        c.property_expected, 1);
+
+       mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer),
+                                   c.property_expected, get_property_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "get_property", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(c.property_received == NULL,
+               "No property %s received and expected", c.property_expected);
+       fail_if(c.property_received != NULL &&
+               g_value_get_uint(c.property_received) != 1,
+               "Property with value %d and %d expected",
+               g_value_get_uint(c.property_received), 1);
+
+       /* --- autopaint --- */
+
+       reset_callback_info(&c);
+
+       c.property_expected = MAFW_PROPERTY_RENDERER_AUTOPAINT;
+
+       mafw_extension_set_property_boolean(MAFW_EXTENSION(g_gst_renderer),
+                                           c.property_expected, TRUE);
+
+       mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer),
+                                   c.property_expected, get_property_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "get_property", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(c.property_received == NULL,
+               "No property %s received and expected", c.property_expected);
+       fail_if(c.property_received != NULL &&
+               g_value_get_boolean(c.property_received) != TRUE,
+               "Property with value %d and %d expected",
+               g_value_get_boolean(c.property_received), TRUE);
+
+       /* --- colorkey --- */
+
+       reset_callback_info(&c);
+
+       c.property_expected = MAFW_PROPERTY_RENDERER_COLORKEY;
+
+       mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer),
+                                   c.property_expected, get_property_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "get_property", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(c.property_received == NULL,
+               "No property %s received and expected", c.property_expected);
+       fail_if(c.property_received != NULL &&
+               g_value_get_int(c.property_received) != -1,
+               "Property with value %d and %d expected",
+               g_value_get_int(c.property_received), -1);
+
+       /* --- current frame on pause --- */
+
+       reset_callback_info(&c);
+
+       c.property_expected = MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE;
+
+       mafw_extension_set_property_boolean(MAFW_EXTENSION(g_gst_renderer),
+                                           c.property_expected, TRUE);
+
+       mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer),
+                                   c.property_expected, get_property_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "get_property", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(c.property_received == NULL,
+               "No property %s received and expected", c.property_expected);
+       fail_if(c.property_received != NULL &&
+               g_value_get_boolean(c.property_received) != TRUE,
+               "Property with value %d and %d expected",
+               g_value_get_boolean(c.property_received), TRUE);
+
+       /* --- volume --- */
+
+       p.expected = MAFW_PROPERTY_RENDERER_VOLUME;
+
+       mafw_extension_set_property_uint(MAFW_EXTENSION(g_gst_renderer),
+                                        p.expected, 50);
+
+       if (!wait_for_property(&p, wait_tout_val)) {
+               fail("No property %s received", p.expected);
+       }
+
+       fail_if(p.received == NULL, "No property %s received",
+               p.expected);
+       fail_if(p.received != NULL &&
+               g_value_get_uint(p.received) != 50,
+               "Property with value %d and %d expected",
+               g_value_get_uint(p.received), 50);
+
+       if (p.received != NULL) {
+               g_value_unset(p.received);
+               g_free(p.received);
+               p.received = NULL;
+       }
+       p.expected = NULL;
+
+       c.property_expected = MAFW_PROPERTY_RENDERER_VOLUME;
+
+       mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer),
+                                   c.property_expected, get_property_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "get_property", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       fail_if(c.property_received == NULL,
+               "No property %s received and expected", c.property_expected);
+       fail_if(c.property_received != NULL &&
+               g_value_get_uint(c.property_received) != 50,
+               "Property with value %d and %d expected",
+               g_value_get_uint(c.property_received), 50);
+
+#ifndef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME
+       /* Test reconnection to pulse */
+
+       pa_context_disconnect(pa_context_get_instance());
+
+       /* Wait for the volume manager to be reinitialized */
+
+       /* Volume */
+
+       p.expected = MAFW_PROPERTY_RENDERER_VOLUME;
+
+       if (!wait_for_property(&p, wait_tout_val)) {
+               fail("No property %s received", p.expected);
+       }
+
+       fail_if(p.received == NULL, "No property %s received",
+               p.expected);
+       fail_if(p.received != NULL &&
+               g_value_get_uint(p.received) != 48,
+               "Property with value %d and %d expected",
+               g_value_get_uint(p.received), 48);
+
+       if (p.received != NULL) {
+               g_value_unset(p.received);
+               g_free(p.received);
+               p.received = NULL;
+       }
+       p.expected = NULL;
+
+       reset_callback_info(&c);
+#endif
+}
+END_TEST
+
+START_TEST(test_buffering)
+{
+       RendererInfo s;
+       CallbackInfo c;
+       BufferingInfo b;
+       GstBus *bus = NULL;
+       GstMessage *message = NULL;
+
+       /* Initialize callback info */
+       c.err_msg = NULL;
+       c.error_signal_expected = FALSE;
+       c.error_signal_received = NULL;
+       c.property_expected = NULL;
+       c.property_received = NULL;
+       b.requested = FALSE;
+       b.received = FALSE;
+       b.value = 0.0;
+
+       /* Connect to renderer signals */
+       g_signal_connect(g_gst_renderer, "error",
+                        G_CALLBACK(error_cb),
+                        &c);
+       g_signal_connect(g_gst_renderer, "state-changed",
+                        G_CALLBACK(state_changed_cb),
+                        &s);
+       g_signal_connect(g_gst_renderer, "buffering-info",
+                        G_CALLBACK(buffering_info_cb),
+                        &b);
+
+       /* --- Get initial status --- */
+
+       reset_callback_info(&c);
+
+       g_debug("get status...");
+       mafw_renderer_get_status(g_gst_renderer, status_cb, &s);
+
+       /* --- Play object --- */
+
+       reset_callback_info(&c);
+
+       gchar *objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP);
+       g_debug("play_object... %s", objectid);
+       mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "playing an object", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object",
+                    "Transitioning", s.state);
+       }
+
+       if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) {
+               fail(state_err_msg, "mafw_renderer_play_object", "Playing",
+                    s.state);
+       }
+
+       g_free(objectid);
+
+       /* --- Buffering info --- */
+
+       b.requested = TRUE;
+
+       bus = MAFW_GST_RENDERER(g_gst_renderer)->worker->bus;
+       fail_if(bus == NULL, "No GstBus");
+
+       message = gst_message_new_buffering(NULL, 50);
+       gst_bus_post(bus, message);
+
+       if (wait_for_buffering(&b, wait_tout_val) == FALSE) {
+               fail("Expected buffering message but not received");
+       }
+
+       fail_if(b.value != 0.5, "Expected buffering 0.50 and received %1.2f",
+               b.value);
+
+       b.requested = FALSE;
+       b.received = FALSE;
+       b.value = 0;
+
+       /* --- Buffering info --- */
+
+       b.requested = TRUE;
+
+       message = gst_message_new_buffering(NULL, 100);
+       gst_bus_post(bus, message);
+
+       if (wait_for_buffering(&b, wait_tout_val) == FALSE) {
+               fail("Expected buffering message but not received");
+       }
+
+       fail_if(b.value != 1.0, "Expected buffering 1.00 and received %1.2f",
+               b.value);
+
+       b.requested = FALSE;
+       b.received = FALSE;
+       b.value = 0;
+
+       /* --- Stop --- */
+
+       reset_callback_info(&c);
+
+       g_debug("stop...");
+       mafw_renderer_stop(g_gst_renderer, playback_cb, &c);
+
+       if (wait_for_callback(&c, wait_tout_val)) {
+               if (c.error)
+                       fail(callback_err_msg, "stopping", c.err_code,
+                            c.err_msg);
+       } else {
+               fail(no_callback_msg);
+       }
+
+       if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) {
+               fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state);
+       }
+}
+END_TEST
+
+/*----------------------------------------------------------------------------
+  Suit creation
+  ----------------------------------------------------------------------------*/
+
+SRunner * configure_tests(void)
+{
+       SRunner *sr = NULL;
+       Suite *s = NULL;
+       const gchar *tout = g_getenv("WAIT_TIMEOUT");
+       
+       if (!tout)
+               wait_tout_val = DEFAULT_WAIT_TOUT;
+       else
+       {
+               wait_tout_val = (gint)strtol(tout, NULL, 0);
+               if (wait_tout_val<=0)
+                       wait_tout_val = DEFAULT_WAIT_TOUT;
+       }
+
+       checkmore_wants_dbus();
+       mafw_log_init(":error");
+       /* Create the suite */
+       s = suite_create("MafwGstRenderer");
+
+       /* Create test cases */
+       TCase *tc1 = tcase_create("Playback");
+
+       /* Create unit tests for test case "Playback" */
+       tcase_add_checked_fixture(tc1, fx_setup_dummy_gst_renderer,
+                                 fx_teardown_dummy_gst_renderer);
+if (1) tcase_add_test(tc1, test_basic_playback);
+if (1) tcase_add_test(tc1, test_playlist_playback);
+if (1) tcase_add_test(tc1, test_repeat_mode_playback);
+if (1) tcase_add_test(tc1, test_gst_renderer_mode);
+if (1) tcase_add_test(tc1, test_update_stats);
+if (1)  tcase_add_test(tc1, test_play_state);
+if (1)  tcase_add_test(tc1, test_pause_state);
+if (1)  tcase_add_test(tc1, test_stop_state);
+if (1)  tcase_add_test(tc1, test_transitioning_state);
+if (1)  tcase_add_test(tc1, test_state_class);
+if (1)  tcase_add_test(tc1, test_playlist_iterator);
+if (1)  tcase_add_test(tc1, test_video);
+if (1)  tcase_add_test(tc1, test_media_art);
+if (1)  tcase_add_test(tc1, test_properties_management);
+if (1)  tcase_add_test(tc1, test_buffering);
+
+       tcase_set_timeout(tc1, 0);
+
+       suite_add_tcase(s, tc1);
+
+       /* Create srunner object with the test suite */
+       sr = srunner_create(s);
+
+       return sr;
+}
+
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/tests/check-main.c b/tests/check-main.c
new file mode 100644 (file)
index 0000000..c5c5172
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <check.h>
+
+
+/* This must be provided by the test suite implementation */
+SRunner *configure_tests(void);
+
+int main(void)
+{
+       int nf = 0;
+
+       /* Configure test suites to be executed */
+       SRunner *sr = configure_tests();
+
+       /* Run tests */
+       srunner_run_all(sr, CK_ENV);
+
+       /* Retrieve number of failed tests */
+       nf = srunner_ntests_failed(sr);
+
+       /* Free resouces */
+       srunner_free(sr);
+
+       /* Return global success or failure */
+       return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/tests/mafw-mock-playlist.c b/tests/mafw-mock-playlist.c
new file mode 100644 (file)
index 0000000..0720261
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libmafw/mafw-playlist.h>
+#include "mafw-mock-playlist.h"
+
+static GList *pl_list;
+static gchar *pl_name;
+static gboolean pl_rep;
+static gboolean pl_shuffle;
+
+/* Item manipulation */
+
+static gboolean mafw_mock_playlist_insert_item(MafwPlaylist *playlist,
+                                                    guint index,
+                                                    const gchar *objectid,
+                                                    GError **error);
+
+static gboolean mafw_mock_playlist_remove_item(MafwPlaylist *playlist,
+                                                    guint index,
+                                                    GError **error);
+
+static gchar *mafw_mock_playlist_get_item(MafwPlaylist *playlist,
+                                               guint index, GError **error);
+
+static gboolean mafw_mock_playlist_move_item(MafwPlaylist *playlist,
+                                                  guint from, guint to,
+                                                  GError **error);
+
+static guint mafw_mock_playlist_get_size(MafwPlaylist *playlist,
+                                              GError **error);
+
+static gboolean mafw_mock_playlist_clear(MafwPlaylist *playlist,
+                                              GError **error);
+
+static gboolean mafw_mock_playlist_increment_use_count(MafwPlaylist *playlist,
+                                                       GError **error);
+
+static gboolean mafw_mock_playlist_decrement_use_count(MafwPlaylist *playlist,
+                                                       GError **error);
+gboolean mafw_mock_playlist_get_prev(MafwPlaylist *playlist, guint *index,
+                               gchar **object_id, GError **error);
+gboolean mafw_mock_playlist_get_next(MafwPlaylist *playlist, guint *index,
+                               gchar **object_id, GError **error);
+static void mafw_mock_playlist_get_starting_index(MafwPlaylist *playlist, guint *index,
+                                       gchar **object_id, GError **error);
+static void mafw_mock_playlist_get_last_index(MafwPlaylist *playlist,
+                                              guint *index, gchar **object_id,
+                                              GError **error);
+
+enum {
+       PROP_0,
+       PROP_NAME,
+       PROP_REPEAT,
+       PROP_IS_SHUFFLED,
+};
+
+static void set_prop(MafwMockPlaylist *playlist, guint prop,
+                    const GValue *value, GParamSpec *spec)
+{
+       if (prop == PROP_NAME) {
+               pl_name = g_value_dup_string(value);
+       } else if (prop == PROP_REPEAT) {
+               pl_rep = g_value_get_boolean(value);
+       } else
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(playlist, prop, spec);
+}
+
+static void get_prop(MafwMockPlaylist *playlist, guint prop,
+                    GValue *value, GParamSpec *spec)
+{
+       if (prop == PROP_NAME) {
+               g_value_take_string(value, pl_name);
+       } else if (prop == PROP_REPEAT) {
+               g_value_set_boolean(value, pl_rep);
+       } else if (prop == PROP_IS_SHUFFLED) {
+               g_value_set_boolean(value, pl_shuffle);
+       } else
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(playlist, prop, spec);
+}
+
+static void mafw_mock_playlist_get_starting_index(MafwPlaylist *playlist, guint *index,
+                                       gchar **object_id, GError **error)
+{
+       if (g_list_length(pl_list) > 0) {
+               *index = 0;
+               *object_id = g_strdup(g_list_nth_data(pl_list, 0));
+       }
+}
+
+static void mafw_mock_playlist_get_last_index(MafwPlaylist *playlist,
+                                              guint *index, gchar **object_id,
+                                              GError **error)
+{
+       *index = g_list_length(pl_list) - 1;
+       *object_id = g_strdup(g_list_nth_data(pl_list, *index));
+}
+
+
+gboolean mafw_mock_playlist_get_next(MafwPlaylist *playlist, guint *index,
+                               gchar **object_id, GError **error)
+{
+       gint size;
+       gboolean return_value = TRUE;
+
+       size = g_list_length(pl_list);
+       
+       g_return_val_if_fail(size != 0, FALSE);
+
+       if (*index == (size - 1)) {
+               return_value = FALSE;
+       } else {
+               *object_id = g_strdup(g_list_nth_data(pl_list, ++(*index)));
+       }
+       
+       return return_value;
+}
+
+gboolean mafw_mock_playlist_get_prev(MafwPlaylist *playlist, guint *index,
+                               gchar **object_id, GError **error)
+{
+       gint size;
+       gboolean return_value = TRUE;
+
+       size = g_list_length(pl_list);
+       
+       g_return_val_if_fail(size != 0, FALSE);
+
+       if (*index == 0) {
+               return_value = FALSE;
+       } else {
+               *object_id = g_strdup(g_list_nth_data(pl_list, --(*index)));
+       }
+
+       return return_value;
+}
+
+static void playlist_iface_init(MafwPlaylistIface *iface)
+{
+       iface->get_item = mafw_mock_playlist_get_item;
+       iface->insert_item = mafw_mock_playlist_insert_item;
+       iface->clear = mafw_mock_playlist_clear;
+       iface->get_size = mafw_mock_playlist_get_size;
+       iface->remove_item = mafw_mock_playlist_remove_item;
+       iface->move_item = mafw_mock_playlist_move_item;
+       iface->get_starting_index = mafw_mock_playlist_get_starting_index;
+       iface->get_last_index = mafw_mock_playlist_get_last_index;
+       iface->get_next = mafw_mock_playlist_get_next;
+       iface->get_prev = mafw_mock_playlist_get_prev;
+       iface->increment_use_count = mafw_mock_playlist_increment_use_count;
+       iface->decrement_use_count = mafw_mock_playlist_decrement_use_count;
+}
+
+
+static void mafw_mock_playlist_finalize(GObject *object)
+{
+       g_debug(__FUNCTION__);
+       
+       while (pl_list)
+       {
+               g_free(pl_list->data);
+               pl_list = g_list_delete_link(pl_list, pl_list);
+       }
+       
+}
+
+static void mafw_mock_playlist_class_init(
+                                       MafwMockPlaylistClass *klass)
+{
+       GObjectClass *oclass = NULL;
+
+       oclass = G_OBJECT_CLASS(klass);
+
+       oclass->set_property = (gpointer)set_prop;
+       oclass->get_property = (gpointer)get_prop;
+       g_object_class_override_property(oclass, PROP_NAME, "name");
+       g_object_class_override_property(oclass, PROP_REPEAT, "repeat");
+       g_object_class_override_property(oclass,
+                                        PROP_IS_SHUFFLED, "is-shuffled");
+       
+       oclass -> finalize = mafw_mock_playlist_finalize;
+}
+
+static void mafw_mock_playlist_init(MafwMockPlaylist *self)
+{
+}
+
+
+
+G_DEFINE_TYPE_WITH_CODE(MafwMockPlaylist, mafw_mock_playlist,
+                       G_TYPE_OBJECT,
+                       G_IMPLEMENT_INTERFACE(MAFW_TYPE_PLAYLIST,
+                                             playlist_iface_init));
+
+
+GObject *mafw_mock_playlist_new(void)
+{
+       MafwMockPlaylist *self;
+
+       self = g_object_new(MAFW_TYPE_MOCK_PLAYLIST, NULL);
+       
+       return G_OBJECT(self);
+}
+
+gboolean mafw_mock_playlist_insert_item(MafwPlaylist *self, guint index,
+                                             const gchar *objectid,
+                                             GError **error)
+{
+       pl_list = g_list_insert(pl_list, g_strdup(objectid), index);
+
+       g_signal_emit_by_name(self, "contents-changed", index, 0, 1);
+       
+       return TRUE;
+}
+
+gboolean mafw_mock_playlist_remove_item(MafwPlaylist *self, guint index,
+                                        GError **error)
+{
+       GList *element;
+
+       g_return_val_if_fail(g_list_length(pl_list) > 0, FALSE);
+
+       element = g_list_nth(pl_list, index);
+       g_free(element->data);
+       pl_list = g_list_delete_link(pl_list, element);
+
+       g_signal_emit_by_name(self, "contents-changed", index, 1, 0);
+
+       return TRUE;
+}
+
+gchar *mafw_mock_playlist_get_item(MafwPlaylist *self, guint index,
+                                        GError **error)
+{
+       gchar *oid = g_list_nth_data(pl_list, index);
+       
+       if (oid)
+               oid = g_strdup(oid);
+       
+       return oid;
+}
+
+guint mafw_mock_playlist_get_size(MafwPlaylist *self, GError **error)
+{
+       return g_list_length(pl_list);
+}
+
+static gboolean mafw_mock_playlist_move_item(MafwPlaylist *playlist,
+                                                  guint from, guint to,
+                                                  GError **error)
+{
+       GList *element_from, *element_to;
+       gpointer data;
+       gint size;
+
+       size = g_list_length(pl_list);
+
+       g_return_val_if_fail(size > 0, FALSE);
+       g_return_val_if_fail(from != to, FALSE);
+       g_return_val_if_fail((from < size) && (to < size), FALSE);
+
+       element_from = g_list_nth(pl_list, from);
+       element_to = g_list_nth(pl_list, to);
+
+       data = element_from->data;
+       element_from->data = element_to->data;
+       element_to->data = data;
+
+       g_signal_emit_by_name(playlist, "item-moved", from, to);
+
+       return TRUE;
+}
+
+static gboolean mafw_mock_playlist_increment_use_count(MafwPlaylist *playlist,
+                                                       GError **error)
+{
+       return TRUE;
+}
+
+static gboolean mafw_mock_playlist_decrement_use_count(MafwPlaylist *playlist,
+                                                        GError **error)
+{
+       return TRUE;    
+}
+
+gboolean mafw_mock_playlist_clear(MafwPlaylist *self, GError **error)
+{
+       mafw_mock_playlist_finalize(NULL);
+       
+       return TRUE;
+}
+
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */
diff --git a/tests/mafw-mock-playlist.h b/tests/mafw-mock-playlist.h
new file mode 100644 (file)
index 0000000..dcc2da1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_MOCK_PLAYLIST_H
+#define MAFW_MOCK_PLAYLIST_H
+
+#include <glib-object.h>
+#include <libmafw/mafw-playlist.h>
+#include <libmafw/mafw-errors.h>
+
+/*----------------------------------------------------------------------------
+  GObject type conversion macros
+  ----------------------------------------------------------------------------*/
+
+#define MAFW_TYPE_MOCK_PLAYLIST \
+       (mafw_mock_playlist_get_type())
+
+#define MAFW_MOCK_PLAYLIST(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST(obj, MAFW_TYPE_MOCK_PLAYLIST, \
+                                   MafwMockPlaylist))
+
+#define MAFW_MOCK_PLAYLIST_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST(klass, MAFW_TYPE_MOCK_PLAYLIST, \
+                                MafwMockPlaylistClass))
+
+#define MAFW_IS_MOCK_PLAYLIST(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE(obj, MAFW_TYPE_MOCK_PLAYLIST))
+
+#define MAFW_IS_MOCK_PLAYLIST_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_MOCK_PLAYLIST))
+
+#define MAFW_MOCK_PLAYLIST_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_MOCK_PLAYLIST, \
+                                  MafwMockPlaylistClass))
+
+/*----------------------------------------------------------------------------
+  GObject type definitions
+  ----------------------------------------------------------------------------*/
+
+typedef struct _MafwMockPlaylist MafwMockPlaylist;
+typedef struct _MafwMockPlaylistClass MafwMockPlaylistClass;
+
+
+struct _MafwMockPlaylist
+{
+       GObject parent_instance;
+
+};
+
+struct _MafwMockPlaylistClass
+{
+       GObjectClass parent_class;
+
+};
+
+/*----------------------------------------------------------------------------
+  Shared playlist-specific functions
+  ----------------------------------------------------------------------------*/
+
+GType mafw_mock_playlist_get_type(void);
+GObject *mafw_mock_playlist_new(void);
+
+#endif
diff --git a/tests/mafw-mock-pulseaudio.c b/tests/mafw-mock-pulseaudio.c
new file mode 100644 (file)
index 0000000..1675073
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <glib.h>
+#include "mafw-mock-pulseaudio.h"
+
+typedef void pa_glib_mainloop;
+typedef void pa_mainloop_api;
+typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
+typedef guint pa_context_flags_t;
+typedef void pa_spawn_api;
+typedef void pa_operation;
+typedef guint32 pa_volume_t;
+typedef struct {
+       guint8 channels;
+       guint map[32];
+} pa_channel_map;
+typedef struct {
+       guint8 channels;
+       pa_volume_t values[32];
+} pa_cvolume;
+typedef struct {
+       const gchar *name;
+       pa_channel_map channel_map;
+       pa_cvolume volume;
+       const char *device;
+       gint mute;
+       gboolean volume_is_absolute;
+} pa_ext_stream_restore_info;
+typedef void (*pa_ext_stream_restore_read_cb_t)(
+       pa_context *c,
+       const pa_ext_stream_restore_info *info, int eol, void *userdata);
+typedef void (*pa_ext_stream_restore_subscribe_cb_t)(pa_context *c,
+                                                    void *userdata);
+typedef void (*pa_context_success_cb_t)(pa_context *c, int success,
+                                       void *userdata);
+enum pa_context_state {
+  PA_CONTEXT_UNCONNECTED = 0,
+  PA_CONTEXT_CONNECTING,
+  PA_CONTEXT_AUTHORIZING,
+  PA_CONTEXT_SETTING_NAME,
+  PA_CONTEXT_READY,
+  PA_CONTEXT_FAILED,
+  PA_CONTEXT_TERMINATED
+};
+struct _pa_context {
+       pa_context_notify_cb_t state_cb;
+       gpointer state_cb_userdata;
+       enum pa_context_state state;
+       pa_ext_stream_restore_read_cb_t read_cb;
+       gpointer read_cb_userdata;
+       pa_ext_stream_restore_subscribe_cb_t subscribe_cb;
+       gpointer subscribe_cb_userdata;
+       pa_cvolume volume;
+       gboolean mute;
+};
+
+static pa_context *context = NULL;
+
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
+char *pa_get_binary_name(char *s, size_t l);
+pa_mainloop_api *pa_glib_mainloop_get_api(pa_glib_mainloop *g);
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
+void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb,
+                                  void *userdata);
+int pa_context_connect(pa_context *c, const char *server,
+                      pa_context_flags_t flags, const pa_spawn_api *api);
+gint pa_context_get_state(pa_context *c);
+pa_operation *pa_ext_stream_restore2_read(pa_context *c,
+                                         pa_ext_stream_restore_read_cb_t cb,
+                                         void *userdata);
+void pa_operation_unref(pa_operation *o);
+pa_volume_t pa_cvolume_max(const pa_cvolume *volume);
+void pa_ext_stream_restore_set_subscribe_cb(
+       pa_context *c,
+       pa_ext_stream_restore_subscribe_cb_t cb, void *userdata);
+gint pa_operation_get_state(pa_operation *o);
+void pa_operation_cancel(pa_operation *o);
+void pa_glib_mainloop_free(pa_glib_mainloop *g);
+pa_cvolume *pa_cvolume_init(pa_cvolume *a);
+pa_cvolume *pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v);
+pa_operation *pa_ext_stream_restore2_write(
+       pa_context *c, gint mode, const pa_ext_stream_restore_info *data[],
+       unsigned n, int apply_immediately, pa_context_success_cb_t cb,
+       void *userdata);
+pa_operation *pa_ext_stream_restore_subscribe(pa_context *c, int enable,
+                                             pa_context_success_cb_t cb,
+                                             void *userdata);
+void pa_context_unref(pa_context *c);
+
+pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c)
+{
+       return (gpointer) 0x1;
+}
+
+char *pa_get_binary_name(char *s, size_t l)
+{
+       g_strlcpy(s, "mafw-gst-renderer-tests", l);
+
+       return NULL;
+}
+
+pa_mainloop_api *pa_glib_mainloop_get_api(pa_glib_mainloop *g)
+{
+       return (gpointer) 0x1;
+}
+
+pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name)
+{
+       pa_context *c = g_new0(pa_context, 1);
+
+       pa_cvolume_set(&c->volume, 1, 32000);
+
+       context = c;
+
+       return c;
+}
+
+void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb,
+                                  void *userdata)
+{
+       c->state_cb = cb;
+       c->state_cb_userdata = userdata;
+}
+
+static gboolean _pa_context_connect_idle(gpointer userdata)
+{
+       pa_context *c = userdata;
+       c->state++;
+       if (c->state_cb != NULL) {
+               c->state_cb(c, c->state_cb_userdata);
+       }
+       return c->state != PA_CONTEXT_READY;
+}
+
+int pa_context_connect(pa_context *c, const char *server,
+                      pa_context_flags_t flags, const pa_spawn_api *api)
+{
+       g_idle_add(_pa_context_connect_idle, c);
+       return 1;
+}
+
+gint pa_context_get_state(pa_context *c)
+{
+       return c->state;
+}
+
+static gboolean _pa_ext_stream_restore2_read_idle(gpointer userdata)
+{
+       pa_context *c = userdata;
+       pa_ext_stream_restore_info info = { 0, };
+
+       info.name = "sink-input-by-media-role:x-maemo";
+       pa_cvolume_set(&info.volume, 1, c->volume.values[0]);
+       info.mute = c->mute;
+
+       c->read_cb(c, &info, 1, c->read_cb_userdata);
+
+       return FALSE;
+}
+
+pa_operation *pa_ext_stream_restore2_read(pa_context *c,
+                                         pa_ext_stream_restore_read_cb_t cb,
+                                         void *userdata)
+{
+       c->read_cb = cb;
+       c->read_cb_userdata = userdata;
+       g_idle_add(_pa_ext_stream_restore2_read_idle, c);
+       return (gpointer) 0x1;
+}
+
+void pa_operation_unref(pa_operation *o)
+{
+}
+
+pa_volume_t pa_cvolume_max(const pa_cvolume *volume)
+{
+       return volume->values[0];
+}
+
+pa_operation *pa_ext_stream_restore_subscribe(pa_context *c, int enable,
+                                             pa_context_success_cb_t cb,
+                                             void *userdata)
+{
+       if (cb != NULL) {
+               cb(c, TRUE, userdata);
+       }
+       return (gpointer) 0x1;
+}
+
+void pa_ext_stream_restore_set_subscribe_cb(
+       pa_context *c,
+       pa_ext_stream_restore_subscribe_cb_t cb, void *userdata)
+{
+       c->subscribe_cb = cb;
+       c->subscribe_cb_userdata = userdata;
+}
+
+gint pa_operation_get_state(pa_operation *o)
+{
+       return 1;
+}
+
+void pa_operation_cancel(pa_operation *o)
+{
+}
+
+void pa_context_unref(pa_context *c)
+{
+       g_free(c);
+}
+
+void pa_glib_mainloop_free(pa_glib_mainloop *g)
+{
+}
+
+pa_cvolume *pa_cvolume_init(pa_cvolume *a)
+{
+       pa_cvolume_set(a, 1, 0);
+       return a;
+}
+
+pa_cvolume *pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v)
+{
+       a->channels = 1;
+       a->values[0] = v;
+       return a;
+}
+
+static gboolean _pa_ext_stream_restore_write_idle(gpointer userdata)
+{
+       pa_context *c = userdata;
+
+       if (c->subscribe_cb != NULL) {
+               c->subscribe_cb(c, c->subscribe_cb_userdata);
+       }
+
+       return FALSE;
+}
+
+pa_operation *pa_ext_stream_restore2_write(
+       pa_context *c, gint mode, const pa_ext_stream_restore_info *data[],
+       unsigned n, int apply_immediately, pa_context_success_cb_t cb,
+       void *userdata)
+{
+       const pa_ext_stream_restore_info *info = data[0];
+
+       pa_cvolume_set(&c->volume, 1, info->volume.values[0]);
+       c->mute = info->mute;
+
+       g_idle_add(_pa_ext_stream_restore_write_idle, c);
+
+       return (gpointer) 0x1;
+}
+
+static gboolean _pa_context_disconnect_idle(gpointer userdata)
+{
+       pa_context *c = userdata;
+       c->state = PA_CONTEXT_TERMINATED;
+       if (c->state_cb != NULL) {
+               c->state_cb(c, c->state_cb_userdata);
+       }
+       return FALSE;
+}
+
+void pa_context_disconnect(pa_context *c)
+{
+       g_idle_add(_pa_context_disconnect_idle, c);
+}
+
+pa_context *pa_context_get_instance(void)
+{
+       return context;
+}
diff --git a/tests/mafw-mock-pulseaudio.h b/tests/mafw-mock-pulseaudio.h
new file mode 100644 (file)
index 0000000..bfb30a7
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef MAFW_MOCK_PULSEAUDIO_H
+#define MAFW_MOCK_PULSEAUDIO_H
+
+typedef struct _pa_context pa_context;
+
+void pa_context_disconnect(pa_context *c);
+pa_context *pa_context_get_instance(void);
+
+#endif
diff --git a/tests/mafw-test-player.c b/tests/mafw-test-player.c
new file mode 100644 (file)
index 0000000..a340672
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/*
+ * test.c
+ *
+ * Test of the playback system
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <termios.h>
+
+#include "mafw-gst-renderer.h"
+#include <libmafw/mafw-metadata.h>
+#include <libmafw/mafw-registry.h>
+
+MafwGstRenderer *gst_renderer;
+GMainLoop *loop;
+static struct termios tio_orig;
+guint seek_delta = 2;
+gfloat volume = 0.7;
+gboolean muted = FALSE;
+
+
+/**
+ *@time: how long to wait, in microsecs
+ *
+ */
+int kbhit (int time) {
+       fd_set rfds;
+       struct timeval tv;
+       int retval;
+       char c;
+
+       FD_ZERO (&rfds);
+       FD_SET (0, &rfds);
+
+       /* Wait up to 'time' microseconds. */
+       tv.tv_sec=time / 1000;
+       tv.tv_usec = (time % 1000)*1000;
+
+       retval=select (1, &rfds, NULL, NULL, &tv);
+       if(retval < 1) return -1;
+       retval = read (0, &c, 1);
+       if (retval < 1) return -1;
+       return (int) c;
+}
+
+
+/**
+ *
+ *
+ */
+static void raw_kb_enable (void) {
+       struct termios tio_new;
+       tcgetattr(0, &tio_orig);
+       
+       tio_new = tio_orig;
+       tio_new.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
+       tio_new.c_cc[VMIN] = 1;
+       tio_new.c_cc[VTIME] = 0;
+       tcsetattr (0, TCSANOW, &tio_new);
+}
+
+static void raw_kb_disable (void)
+{
+       tcsetattr (0,TCSANOW, &tio_orig);
+}
+
+static void get_position_cb(MafwRenderer* self, gint position, gpointer user_data,
+                           const GError* error)
+{
+       guint* rpos = (guint*) user_data;
+       g_return_if_fail(rpos != NULL);
+
+       *rpos = position;
+}
+
+/**
+ *
+ *
+ */
+static gboolean idle_cb (gpointer data)
+{
+       gboolean ret = TRUE;
+       GError *error = NULL;
+
+       int c = kbhit (0);
+       if (c == -1) {
+               usleep (10 * 1000);
+               return TRUE;
+       }
+
+       printf ("c = %d\n", c);
+       /* '.' key */
+       if (c == 46) {
+               printf ("Seeking %d seconds forward\n", seek_delta);
+               gint pos = 0;
+
+               mafw_gst_renderer_get_position(MAFW_RENDERER(gst_renderer),
+                                            get_position_cb, &pos);
+
+               printf ("  Position before seek: %d\n", pos);
+               pos += seek_delta;
+               mafw_gst_renderer_set_position(MAFW_RENDERER(gst_renderer), pos,
+                                            NULL, NULL);
+
+               mafw_gst_renderer_get_position(MAFW_RENDERER(gst_renderer),
+                                            get_position_cb, &pos);
+
+               printf ("  Position after seek: %d\n", pos);
+       }
+       /* ',' key */
+       else if (c == 44) {
+               printf ("Seeking %d seconds backwards\n", seek_delta);
+               gint pos = 0;
+
+               mafw_gst_renderer_get_position(MAFW_RENDERER(gst_renderer),
+                                            get_position_cb, &pos);
+
+               printf ("  Position before seek: %d\n", pos);
+               pos -= seek_delta;
+               mafw_gst_renderer_set_position(MAFW_RENDERER(gst_renderer), pos,
+                                            NULL, NULL);
+
+               mafw_gst_renderer_get_position(MAFW_RENDERER(gst_renderer),
+                                            get_position_cb, &pos);
+
+               printf ("  Position after seek: %d\n", pos);
+       }
+       /* '' (space) key */
+       else if (c == 32) {
+               if (gst_renderer->current_state == Playing) {
+                       printf ("Pausing...\n");
+                       mafw_gst_renderer_pause(MAFW_RENDERER (gst_renderer), NULL, NULL);
+               }
+               else if (gst_renderer->current_state == Paused) {
+                       printf ("Resuming...\n");
+                       mafw_gst_renderer_resume(MAFW_RENDERER (gst_renderer), NULL, NULL);
+               }
+       }
+       /* 'p' key */
+       else if (c == 112) {
+               printf ("Playing...\n");
+               mafw_gst_renderer_play (MAFW_RENDERER (gst_renderer), NULL, NULL);
+       }
+       /* 's' key */
+       else if (c == 115) {
+               printf ("Stopping\n");
+               mafw_gst_renderer_stop (MAFW_RENDERER (gst_renderer), NULL, NULL);
+       }
+       /* 'g' key */
+       else if (c == 103) {
+               printf ("Getting position\n");
+               gint pos = 0;
+
+               mafw_gst_renderer_get_position(MAFW_RENDERER(gst_renderer),
+                                            get_position_cb, &pos);
+
+               printf ("Current position: %d\n", pos);
+       }
+       /* '+' key */
+       else if (c == 43) {
+               volume += 0.1;
+               printf ("Increasing volume to %lf\n", volume);
+               mafw_extension_set_property_float(MAFW_EXTENSION(gst_renderer),
+                                            "volume", volume);
+       }
+       /* '-' key */
+       else if (c == 45) {
+               volume -= 0.1;
+               printf ("Decreasing volume to %lf\n", volume);
+               mafw_extension_set_property_float(MAFW_EXTENSION(gst_renderer),
+                                            "volume", volume);
+       }
+       /* 'm' key */
+       else if (c == 109) {
+               muted = !muted;
+               printf ("(Un)Muting...\n");
+               mafw_extension_set_property_boolean(MAFW_EXTENSION(gst_renderer),
+                                              "mute", muted);
+       }
+       /* '?' key */
+       else if (c == 63) {
+               printf ("COMMANDS:\n" \
+                       "    s\t\tStop\n" \
+                       "    p\t\tPlay\n" \
+                       "    space\tPause/Resume\n" \
+                       "    +\t\tVolume up\n" \
+                       "    -\t\tVolume down\n" \
+                       "    m\t\tMute/Unmute\n" \
+                       "    .\t\tSeek forward 2 sec\n" \
+                       "    ,\t\tSeek backwards 2 sec\n" \
+                       "    q\t\tQuit\n");
+       }
+       /* 'q' key */
+       else if (c == 113) {
+               printf ("QUIT\n");
+               mafw_gst_renderer_stop (MAFW_RENDERER (gst_renderer), NULL, NULL);
+               raw_kb_disable ();
+               g_main_loop_quit (loop);
+               ret = FALSE;
+       }
+       if (error) {
+               printf ("Error occured during the operation\n");
+               g_error_free (error);
+       }
+       return ret;
+}
+
+
+/**
+ *
+ *
+ */
+static void metadata_changed (MafwGstRenderer *gst_renderer,
+                             GHashTable *metadata,
+                             gpointer user_data)
+{
+       g_print("Metadata changed:\n");
+       mafw_metadata_print (metadata, NULL);
+}
+
+
+/**
+ *
+ *
+ */
+static void buffering_cb (MafwGstRenderer *gst_renderer,
+                         gfloat percentage,
+                         gpointer user_data)
+{
+       g_print("Buffering: %f\n", percentage);
+}
+
+static void play_uri_cb(MafwRenderer* renderer, gpointer user_data, const GError* error)
+{
+       if (error != NULL) {
+               printf("Unable to play: %s\n", error->message);
+               exit(1);
+       }
+}
+
+/**
+ *
+ *
+ */
+gint main(gint argc, gchar ** argv)
+{
+       MafwRegistry *registry;
+       
+       g_type_init();
+       gst_init (&argc, &argv);
+
+       if (argc != 2) {
+               g_print("Usage: mafw-test-player <media-uri>\n");
+               exit(1);
+       }
+
+       raw_kb_enable();
+       
+       registry = MAFW_REGISTRY(mafw_registry_get_instance());
+       gst_renderer = MAFW_GST_RENDERER(mafw_gst_renderer_new(registry));
+       g_signal_connect (G_OBJECT (gst_renderer),
+                         "metadata_changed",
+                         G_CALLBACK (metadata_changed),
+                         gst_renderer);
+
+       g_signal_connect (G_OBJECT (gst_renderer),
+                         "buffering_info",
+                         G_CALLBACK (buffering_cb),
+                         gst_renderer);
+
+       mafw_renderer_play_uri(MAFW_RENDERER (gst_renderer), argv[1], play_uri_cb,
+                           NULL);
+       
+       loop = mafw_gst_renderer_get_loop(gst_renderer);
+
+       g_idle_add (idle_cb, NULL);
+       g_main_loop_run (loop);
+
+       g_object_unref (G_OBJECT (gst_renderer));
+       return 0;
+}
diff --git a/tests/media/test.avi b/tests/media/test.avi
new file mode 100644 (file)
index 0000000..f868c62
Binary files /dev/null and b/tests/media/test.avi differ
diff --git a/tests/media/test.wav b/tests/media/test.wav
new file mode 100644 (file)
index 0000000..943b040
Binary files /dev/null and b/tests/media/test.wav differ
diff --git a/tests/media/testframe.png b/tests/media/testframe.png
new file mode 100644 (file)
index 0000000..a0db335
Binary files /dev/null and b/tests/media/testframe.png differ
diff --git a/tests/test.suppressions b/tests/test.suppressions
new file mode 100644 (file)
index 0000000..8d0b36b
--- /dev/null
@@ -0,0 +1,864 @@
+{
+   <1>
+   Memcheck:Leak
+   fun:malloc
+   fun:g_malloc
+   fun:g_slice_alloc
+   fun:g_slice_alloc0
+   fun:g_type_create_instance
+   fun:g_object_constructor
+   fun:g_object_newv
+   fun:g_object_new_valist
+   fun:g_object_new
+   fun:gst_element_factory_create
+   fun:gst_element_make_from_uri
+   obj:*
+   obj:*
+   obj:*
+   fun:gst_element_change_state
+   fun:gst_element_continue_state
+   fun:gst_element_change_state
+   obj:*
+   fun:gst_element_set_state
+   fun:_construct_pipeline
+}
+{
+   <2>
+   Memcheck:Leak
+   fun:malloc
+   fun:g_malloc
+   fun:g_slice_alloc
+   fun:g_slice_alloc0
+   fun:g_type_create_instance
+   fun:g_object_constructor
+   fun:g_object_newv
+   fun:g_object_new_valist
+   fun:g_object_new
+   fun:gst_pad_new_from_template
+   obj:*
+   fun:g_type_create_instance
+   fun:g_object_constructor
+   fun:g_object_newv
+   fun:g_object_new_valist
+   fun:g_object_new
+   fun:gst_element_factory_create
+   fun:gst_element_make_from_uri
+   obj:*
+   obj:*
+}
+{
+   <3>
+   Memcheck:Cond
+   fun:_dl_relocate_object 
+   fun:dl_open_worker
+   fun:_dl_catch_error
+   fun:_dl_open
+   fun:do_dlopen
+   fun:_dl_catch_error
+   fun:dlerror_run
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   fun:__nss_lookup
+   fun:__nss_passwd_lookup
+   fun:getpwnam_r@@GLIBC_2.1.2
+}
+{
+   <4>
+   Memcheck:Leak
+   fun:realloc 
+   fun:g_realloc 
+   obj:*
+   fun:g_signal_newv 
+   fun:g_signal_new_valist 
+   fun:g_signal_new 
+   obj:*
+   fun:g_type_class_ref
+   fun:g_type_class_ref
+   fun:g_object_newv
+   fun:g_object_new_valist
+   fun:g_object_new
+}
+
+{
+   <5>
+   Memcheck:Leak
+   fun:calloc
+   fun:g_malloc0
+   obj:*
+   fun:g_type_create_instance
+   obj:*
+   fun:g_object_newv
+   fun:g_object_new_valist
+   fun:g_object_new
+   fun:gst_element_factory_create
+   fun:gst_element_make_from_uri
+   obj:*
+   obj:*
+}
+
+{
+   <6>
+   Memcheck:Leak
+   fun:vasprintf
+   fun:g_vasprintf
+   fun:g_strdup_vprintf
+   fun:g_strdup_printf
+   fun:gst_uri_construct
+   fun:g_object_newv
+   obj:*
+   obj:*
+   fun:gst_uri_handler_set_uri
+   fun:gst_element_make_from_uri
+   obj:*
+   obj:*
+}
+{
+   <7>
+   Memcheck:Leak
+   fun:malloc 
+   fun:open_path 
+   fun:_dl_map_object 
+   fun:openaux 
+   fun:_dl_catch_error 
+   fun:_dl_map_object_deps 
+   fun:dl_open_worker 
+   fun:_dl_catch_error 
+   fun:_dl_open 
+   fun:dlopen_doit 
+   fun:_dl_catch_error 
+   fun:_dlerror_run
+}
+{
+   <8>
+   Memcheck:Leak
+   fun:malloc 
+   fun:expand_dynamic_string_token 
+   fun:_dl_map_object 
+   fun:dl_open_worker 
+   fun:_dl_catch_error 
+   fun:_dl_open 
+   fun:dlopen_doit 
+   fun:_dl_catch_error 
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+   fun:gst_plugin_load_file
+}
+{
+   <9>
+   Memcheck:Leak
+   fun:malloc 
+   fun:_dl_new_object 
+   fun:_dl_map_object 
+   fun:dl_open_worker 
+   fun:_dl_catch_error 
+   fun:_dl_open 
+   fun:dlopen_doit 
+   fun:_dl_catch_error 
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+}
+{
+   <10>
+   Memcheck:Leak
+   fun:malloc 
+   fun:_dl_map_object_deps 
+   fun:_dl_map_object 
+   fun:dl_open_worker 
+   fun:_dl_catch_error 
+   fun:_dl_open 
+   fun:dlopen_doit 
+   fun:_dl_catch_error 
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+   fun:gst_plugin_load_file
+   fun:gst_plugin_load_by_name
+}
+{
+   <11>
+   Memcheck:Leak
+   fun:calloc 
+   fun:_dl_check_map_versions 
+   fun:dl_open_worker 
+   fun:_dl_catch_error 
+   fun:_dl_open 
+   fun:dlopen_doit 
+   fun:_dl_catch_error 
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+   fun:gst_plugin_load_file
+   fun:gst_plugin_load_by_name
+}
+{
+   <12>
+   Memcheck:Leak
+   fun:malloc
+   fun:g_malloc0
+   obj:*
+   obj:*
+   fun:g_type_create_instance
+   obj:*
+   fun:g_object_newv
+   fun:g_object_new_valist
+   fun:g_object_new
+   fun:gst_element_factory_create
+   fun:gst_element_make_from_uri
+   obj:*
+   obj:*
+}
+{
+   <13>
+   Memcheck:Leak
+   fun:malloc
+   fun:realloc 
+   fun:g_realloc 
+   obj:* 
+   fun:g_array_sized_new 
+   obj:* 
+   fun:gst_structure_copy 
+   fun:gst_caps_copy 
+   fun:gst_audio_filter_class_add_pad_templates 
+   obj:*
+   fun:g_type_class_ref 
+   fun:gst_element_register 
+}
+{
+   <14>
+   Memcheck:Cond
+   fun:strlen
+   fun:_dl_init_paths
+   fun:dl_main
+   fun:_dl_sysdep_start
+   fun:_dl_start
+}
+{
+   <15>
+   Memcheck:Cond
+   fun:_dl_relocate_object
+   fun:dl_main
+   fun:_dl_sysdep_start
+   fun:_dl_start
+}
+{
+   <15>
+   Memcheck:Leak
+   fun:calloc
+   fun:allocate_dtv 
+   fun:_dl_allocate_tls 
+   fun:pthread_create@@GLIBC_2.1 
+   obj:* 
+   fun:g_thread_create_full 
+   obj:* 
+   fun:g_thread_pool_push 
+   fun:gst_task_start 
+   fun:gst_pad_start_task 
+   obj:* 
+   fun:gst_pad_activate_pull 
+}
+{
+   <16>
+   Memcheck:Cond
+   fun:_dl_relocate_object 
+   fun:dl_open_worker
+   fun:_dl_catch_error
+   fun:_dl_open
+   fun:dlopen_doit
+   fun:_dl_catch_error
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+   fun:gst_plugin_load_file
+   fun:gst_plugin_load_by_name
+   fun:gst_plugin_feature_load
+}
+{
+   <17>
+   Memcheck:Leak
+   fun:malloc 
+   fun:dl_open_worker
+   fun:_dl_catch_error
+   fun:_dl_open
+   fun:dlopen_doit
+   fun:_dl_catch_error
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+   fun:gst_plugin_load_file
+   fun:gst_plugin_load_by_name
+   fun:gst_plugin_feature_load
+}
+{
+   <17>
+   Memcheck:Leak
+   fun:realloc 
+   fun:vasprintf 
+   fun:g_vasprintf 
+   fun:g_strdup_vprintf 
+   fun:g_strdup_printf 
+   fun:gst_uri_construct 
+   fun:gst_file_src_set_location 
+   fun:gst_file_src_uri_set_uri 
+   fun:gst_uri_handler_set_uri 
+   fun:gst_element_make_from_uri 
+   obj:* 
+   obj:* 
+}
+{
+   <18>
+   Memcheck:Leak
+   fun:calloc 
+   fun:parse_bracket_exp 
+   fun:parse_expression 
+   fun:parse_branch 
+   fun:parse_reg_exp 
+   fun:parse_expression 
+   fun:parse_branch 
+   fun:parse_reg_exp 
+   fun:parse_expression 
+   fun:parse_branch 
+   fun:parse_reg_exp 
+   fun:re_compile_internal
+}
+{
+   <19>
+   Memcheck:Leak
+   fun:malloc 
+   fun:_dl_map_object_deps
+   fun:dl_open_worker
+   fun:_dl_catch_error
+   fun:_dl_open
+   fun:dlopen_doit
+   fun:_dl_catch_error
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+   fun:gst_plugin_load_file
+   fun:gst_plugin_load_by_name
+}
+{
+   <20>
+   Memcheck:Leak
+   fun:malloc
+   fun:_dl_new_object
+   fun:_dl_map_object_from_fd
+   fun:_dl_map_object
+   fun:dl_open_worker
+   fun:_dl_catch_error
+   fun:_dl_open
+   fun:dlopen_doit
+   fun:_dl_catch_error
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+}
+{
+   <21>
+   Memcheck:Leak
+   fun:malloc
+   fun:g_malloc
+   obj:*
+   obj:*
+   fun:g_type_create_instance
+   obj:*
+   fun:g_object_newv
+   fun:g_object_new_valist
+   fun:g_object_new
+   fun:gst_element_factory_create
+   fun:gst_element_make_from_uri
+   obj:*
+}
+{
+   <22>
+   Memcheck:Leak
+   fun:calloc 
+   fun:dbus_malloc0
+   obj:*
+   obj:*
+   obj:* 
+   obj:* 
+   fun:dbus_parse_address
+   obj:* 
+   fun:dbus_connection_open
+   obj:* 
+   fun:dbus_bus_get
+   obj:*
+}
+{
+   <23>
+   Memcheck:Leak
+   fun:realloc
+   fun:g_realloc
+   obj:*
+   fun:g_array_append_vals
+   obj:*
+   fun:gst_structure_set_valist
+   fun:gst_caps_set_simple
+   fun:gst_riff_create_audio_caps
+   fun:gst_riff_create_audio_template_caps
+   obj:* 
+   fun:g_type_class_ref
+   fun:gst_element_register
+}
+{
+   <24>
+   Memcheck:Leak
+   fun:realloc
+   fun:dbus_realloc
+   obj:*
+   obj:*
+   obj:*
+   obj:*
+   obj:*
+   obj:*
+   obj:*
+   obj:*
+   obj:*
+   obj:*
+}
+{
+   <25>
+   Memcheck:Leak
+   fun:calloc
+   fun:_dl_new_object
+   fun:_dl_map_object_from_fd
+   fun:_dl_map_object
+   fun:dl_open_worker
+   fun:_dl_catch_error
+   fun:_dl_open
+   fun:dlopen_doit
+   fun:_dl_catch_error
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+}
+{
+   <26>
+   Memcheck:Leak
+   fun:realloc
+   fun:vasprintf
+   fun:g_vasprintf
+   fun:g_strdup_vprintf
+   fun:g_strdup_printf
+   fun:gst_uri_construct
+   obj:*
+   obj:*
+   fun:gst_uri_handler_set_uri
+   fun:gst_element_make_from_uri
+   obj:*
+   obj:*
+}
+{
+   <27>
+   Memcheck:Leak
+   fun:malloc 
+   fun:_dl_new_object 
+   fun:_dl_map_object_from_fd
+   fun:_dl_map_object 
+   fun:dl_open_worker 
+   fun:_dl_catch_error 
+   fun:_dl_open 
+   fun:dlopen_doit 
+   fun:_dl_catch_error 
+   fun:_dlerror_run
+   fun:dlopen@@GLIBC_2.1
+   fun:g_module_open
+}
+{
+   <28>
+   Memcheck:Leak
+   fun:realloc 
+   fun:vasprintf 
+   fun:g_vasprintf 
+   fun:g_strdup_vprintf 
+   fun:g_strdup_printf 
+   fun:gst_uri_construct 
+   obj:* 
+   obj:* 
+   fun:gst_uri_handler_set_uri 
+   fun:gst_element_make_from_uri 
+   obj:* 
+   obj:* 
+}
+{
+   <29>
+   Memcheck:Leak
+   fun:malloc
+   fun:g_malloc
+   fun:g_strdup 
+   fun:gst_object_set_name
+   fun:gst_element_factory_create
+   fun:gst_element_make_from_uri
+   obj:*
+   obj:*
+   obj:*
+   fun:gst_element_change_state
+   fun:gst_element_continue_state
+   fun:gst_element_change_state
+}
+{
+   <30>
+   Memcheck:Cond
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:*
+   obj:*
+   obj:*
+   obj:*
+}
+{
+   <31>
+   Memcheck:Cond
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+}
+{
+   <32>
+   Memcheck:Cond
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/targets/*/lib/libc-2.5.so
+   fun:__nss_passwd_lookup
+   fun:getpwnam_r
+   obj:/targets/*/usr/lib/libglib-2.0.so.0.1800.1
+}
+{
+   <33>
+   Memcheck:Cond
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/targets/*/lib/libc-2.5.so
+   fun:__nss_passwd_lookup
+   fun:getpwnam_r
+   obj:/targets/*/usr/lib/libglib-2.0.so.0.1800.1
+}
+{
+   <34>
+   Memcheck:Leak
+   fun:calloc
+   fun:g_malloc0
+   obj:/targets/*/usr/lib/libgobject-2.0.so.0.1800.1
+   obj:/targets/*/usr/lib/libgobject-2.0.so.0.1800.1
+   fun:g_type_init_with_debug_flags
+   fun:g_type_init
+   fun:fx_setup_dummy_gst_renderer
+   fun:tcase_run_checked_setup
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <35>
+   Memcheck:Leak
+   fun:calloc
+   fun:g_malloc0
+   obj:/targets/*/usr/lib/libglib-2.0.so.0.1800.1
+   fun:g_slice_alloc
+   fun:g_slist_prepend
+   fun:g_strsplit
+   fun:mafw_log_init
+   fun:configure_tests
+   fun:main
+}
+{
+   <36>
+   Memcheck:Addr4
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libdl-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libdl-2.5.so
+}
+{
+   <37>
+   Memcheck:Leak
+   fun:*
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   fun:reg*
+   fun:mafw_source_create_objectid
+   fun:get_sample_clip_objectid
+   fun:*
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <38>
+   Memcheck:Leak
+   fun:*
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+}
+{
+   <39>
+   Memcheck:Leak
+   fun:*
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   fun:reg*
+   fun:mafw_source_create_objectid
+   fun:get_sample_clip_objectid
+   fun:*
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <40>
+   Memcheck:Leak
+   fun:*
+   obj:/targets/*/lib/libc-2.5.so
+   fun:reg*
+   fun:mafw_source_create_objectid
+   fun:get_sample_clip_objectid
+   fun:*
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <41>
+   Memcheck:Leak
+   fun:*
+   fun:reg*
+   fun:mafw_source_create_objectid
+   fun:get_sample_clip_objectid
+   fun:*
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <42>
+   Memcheck:Leak
+   fun:realloc
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   fun:regcomp
+   fun:mafw_source_create_objectid
+   fun:get_sample_clip_objectid
+   fun:*
+   fun:srunner_run_all
+}
+{
+   <43>
+   Memcheck:Cond
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/targets/*/lib/libc-2.5.so
+   fun:__nss_passwd_lookup
+   fun:getpwnam_r
+}
+{
+   <44>
+   Memcheck:Addr4
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/targets/*/lib/libc-2.5.so
+}
+{
+   <45>
+   Memcheck:Leak
+   fun:malloc
+   fun:fdopen
+   fun:tmpfile
+   fun:setup_pipe
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <46>
+   Memcheck:Leak
+   fun:realloc
+   fun:erealloc
+   fun:maybe_grow
+   fun:list_add_end
+   fun:_tcase_add_test
+   fun:configure_tests
+   fun:main
+}
+{
+   <47>
+   Memcheck:Leak
+   fun:malloc
+   fun:realloc
+   fun:g_realloc
+   obj:/targets/*/usr/lib/libgobject-2.0.so.0.1800.1
+   fun:g_type_register_static
+   fun:g_type_plugin_get_type
+   fun:g_type_init_with_debug_flags
+   fun:g_type_init
+   fun:fx_setup_dummy_gst_renderer
+   fun:tcase_run_checked_setup
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <48>
+   Memcheck:Leak
+   fun:*
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   obj:/targets/*/lib/libc-2.5.so
+   fun:reg*
+   fun:mafw_source_create_objectid
+   fun:get_sample_clip_objectid
+   fun:*
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <49>
+   Memcheck:Leak
+   fun:malloc
+   fun:emalloc
+   fun:suite_create
+   fun:configure_tests
+   fun:main
+}
+{
+   <50>
+   Memcheck:Leak
+   fun:malloc
+   fun:dbus_malloc
+   obj:/targets/*/usr/lib/libdbus-1.so.3.4.0
+   obj:/targets/*/usr/lib/libdbus-1.so.3.4.0
+   fun:dbus_bus_get
+   obj:/targets/*/usr/lib/libosso.so.1.3.0
+   fun:osso_initialize
+   fun:blanking_init
+   fun:mafw_gst_renderer_worker_new
+   fun:mafw_gst_renderer_init
+   fun:g_type_create_instance
+   obj:/targets/*/usr/lib/libgobject-2.0.so.0.1800.1
+}
+{
+   <51>
+   Memcheck:Leak
+   fun:malloc
+   fun:g_malloc
+   fun:g_strdup
+   obj:/targets/*/usr/lib/libgstreamer-0.10.so.0.18.0
+   fun:gst_registry_binary_read_cache
+   obj:/targets/*/usr/lib/libgstreamer-0.10.so.0.18.0
+   obj:/targets/*/usr/lib/libgstreamer-0.10.so.0.18.0
+   fun:g_option_context_parse
+   fun:gst_init_check
+   fun:gst_init
+   fun:mafw_gst_renderer_class_init
+   fun:mafw_gst_renderer_class_intern_init
+}
+{
+   <52>
+   Memcheck:Leak
+   fun:*
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libdl-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libdl-2.5.so
+   fun:dlopen
+   fun:g_module_open
+}
+{
+   <53>
+   Memcheck:Leak
+   fun:malloc
+   fun:g_malloc
+   fun:g_slice_alloc
+   fun:g_hash_table_new_full
+   fun:g_hash_table_new
+   fun:g_quark_from_static_string
+   fun:g_type_init_with_debug_flags
+   fun:g_type_init
+   fun:fx_setup_dummy_gst_renderer
+   fun:tcase_run_checked_setup
+   fun:srunner_run_all
+   fun:main
+}
+{
+   <54>
+   Memcheck:Leak
+   fun:*
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libdl-2.5.so
+   obj:/targets/*/lib/ld-2.5.so
+   obj:/targets/*/lib/libdl-2.5.so
+}
+{
+   <55>
+   Memcheck:Leak
+   fun:malloc
+   fun:fdopen
+   fun:tmpfile
+   fun:setup_pipe
+   fun:receive_test_result
+   fun:srunner_run_all
+   fun:main
+}
diff --git a/welcome b/welcome
deleted file mode 100644 (file)
index e69de29..0000000