--- /dev/null
+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>
--- /dev/null
+ 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!
+
+
--- /dev/null
+#
+# 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
--- /dev/null
+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, µ) != 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"
+])
--- /dev/null
+#!/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 "$@"
--- /dev/null
+#
+# 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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+Copyright (C) 2007, 2008, 2009 Nokia Corporation. All rights reserved.
+
+Contact: Visa Smolander <visa.smolander@nokia.com>
+
+
+
--- /dev/null
+@plugindir@/*.so
--- /dev/null
+#!/bin/sh
+
+#DEBHELPER#
+
+test -x /usr/bin/mafw.sh && /usr/bin/mafw.sh start mafw-gst-renderer -7 \
+|| true;
--- /dev/null
+#!/bin/sh
+
+#DEBHELPER#
+
+test -x /usr/bin/mafw.sh && /usr/bin/mafw.sh stop mafw-gst-renderer \
+|| true;
--- /dev/null
+#!/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
+
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+# MafwPlaylistIterator::playlist-changed(clip_changed, domain, code, message)
+VOID: BOOLEAN, UINT, INT, STRING
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+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
--- /dev/null
+#
+# 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.*
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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: */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+{
+ <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
+}