From: Roman Moravcik Date: Mon, 18 Jan 2010 08:37:12 +0000 (+0100) Subject: Imported mafw-gst-renderer_0.1.2009.47-1+0m5 X-Git-Tag: 0.1.2009.47-1+0m5-0~11 X-Git-Url: http://git.maemo.org/git/?p=mafwsubrenderer;a=commitdiff_plain;h=431a51067d83801231daf9bdd8bd020e8ea13dda Imported mafw-gst-renderer_0.1.2009.47-1+0m5 --- diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..a2c188d --- /dev/null +++ b/AUTHORS @@ -0,0 +1,10 @@ +Visa Smolander +Zeeshan Ali +Mikael Saarenpää +Antía Puentes Felpeto +Iago Toral Quiroga +Juan Suárez Romero +Sandor Pinter +Xabier Rodríguez Calvar +Juha Kellokoski +Mika Tapojarvi diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..6ab6aea --- /dev/null +++ b/Makefile.am @@ -0,0 +1,41 @@ +# +# Makefile.am for MAFW gst renderer library. +# +# Author: Visa Smolander +# +# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved. + + +SUBDIRS = libmafw-gst-renderer + +if ENABLE_TESTS +SUBDIRS += tests +endif + +noinst_DATA = mafw-gst-renderer-uninstalled.pc +EXTRA_DIST = mafw-gst-renderer-uninstalled.pc.in + +# Extra clean files so that maintainer-clean removes *everything* +MAINTAINERCLEANFILES = aclocal.m4 compile config.guess \ + config.sub configure depcomp \ + install-sh ltmain.sh Makefile.in \ + missing config.h.in *-stamp omf.make + +if ENABLE_COVERAGE +LCOV_DATA_DIR = lcov-data +LCOV_DATA_FILE = lcov.info + +distclean-local: + -rm -rf $(LCOV_DATA_DIR) + +lcov-zero-counters: + $(LCOV) -z -d . + +lcov: + -mkdir -p $(LCOV_DATA_DIR) + $(LCOV) -c -d . -o $(LCOV_DATA_DIR)/$(LCOV_DATA_FILE) + genhtml -s $(LCOV_DATA_DIR)/$(LCOV_DATA_FILE) -o $(LCOV_DATA_DIR) + @echo + @echo "Please, have a look on ./$(LCOV_DATA_DIR)/index.html for coverage statistics" + @echo +endif diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..1457d2a --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,179 @@ +dnl AM_PATH_CHECK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for check, and define CHECK_CFLAGS and CHECK_LIBS +dnl + +AC_DEFUN([AM_PATH_CHECK], +[ + AC_ARG_WITH(check, + [ --with-check=PATH prefix where check is installed [default=auto]]) + + min_check_version=ifelse([$1], ,0.8.2,$1) + + AC_MSG_CHECKING(for check - version >= $min_check_version) + + if test x$with_check = xno; then + AC_MSG_RESULT(disabled) + ifelse([$3], , AC_MSG_ERROR([disabling check is not supported]), [$3]) + else + if test "x$with_check" != x; then + CHECK_CFLAGS="-I$with_check/include" + CHECK_LIBS="-L$with_check/lib -lcheck" + else + CHECK_CFLAGS="" + CHECK_LIBS="-lcheck" + fi + + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + + CFLAGS="$CFLAGS $CHECK_CFLAGS" + LIBS="$CHECK_LIBS $LIBS" + + rm -f conf.check-test + AC_TRY_RUN([ +#include +#include + +#include + +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 +#include + +#include +], , [ 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 + +int main () +{ + MafwRegistry *registry = NULL; + gboolean result; + + g_type_init(); + + registry = MAFW_REGISTRY(mafw_get_local_registry()); + if (!registry) { + printf ("Cannot get registry\n"); + return 1; + } + result = mafw_local_registry_load_plugin(MAFW_LOCAL_REGISTRY(registry), $1, NULL); + if (!result) { + printf ("Unable to load %s\n", $1); + return 1; + } else { + return 0; + } +} + ], [$2], [$3]) + + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + CC="$ac_save_CC" +]) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..d88de29 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +export AUTOMAKE="automake-1.9" +export ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/` + +autoreconf -v -f -i || exit 1 +test -n "$NOCONFIGURE" || ./configure \ + --enable-debug --enable-maintainer-mode "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..9b146ec --- /dev/null +++ b/configure.ac @@ -0,0 +1,222 @@ +# +# configure.ac for MAFW gstreamer renderer library +# +# Author: Visa Smolander +# +# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved. + +AC_PREREQ([2.53]) +AC_INIT([mafw-gst-renderer], [0.1.2009.47-1]) + +AC_CONFIG_SRCDIR([libmafw-gst-renderer/mafw-gst-renderer.h]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) + +AM_INIT_AUTOMAKE([foreign tar-ustar]) +AM_MAINTAINER_MODE + +AC_DISABLE_STATIC + +dnl Prevent AC_PROG_CC adding '-g -O2' to CFLAGS. +SAVEDCFLAGS="$CFLAGS" +AC_PROG_CC +if test "x$GCC" = xyes; then + CFLAGS="$SAVEDCFLAGS" +fi + +AC_PROG_LIBTOOL +AC_PROG_INSTALL + +# DISABLED_BY_DEFAULT(NAME, DESCRIPTION) +# --------------------------------- +# Creates a new --enable-* option, with default value `no'. +AC_DEFUN([DISABLED_BY_DEFAULT], [dnl + AC_ARG_ENABLE([$1], AS_HELP_STRING([--enable-$1], [$2]), [],[dnl + m4_bpatsubst([enable_$1], [[^0-9a-z]], [_])=no])dnl +])# DISABLED_BY_DEFAULT + +# ENABLED_BY_DEFAULT(NAME, DESCRIPTION) +# --------------------------------- +# Creates a new --disable-* option, with default value `yes'. +AC_DEFUN([ENABLED_BY_DEFAULT], [dnl + AC_ARG_ENABLE([$1], AS_HELP_STRING([--disable-$1], [$2]), [],[dnl + m4_bpatsubst([enable_$1], [[^0-9a-z]], [_])=yes])dnl +])# ENABLED_BY_DEFAULT + +dnl Prerequisites. + +GSTREAMER_VERSION=0.10.20 + +AM_PATH_GLIB_2_0(2.15.0, [], [], [glib]) +PKG_CHECK_MODULES(DEPS, + gobject-2.0 >= 2.0 + gstreamer-0.10 >= $GSTREAMER_VERSION + gstreamer-plugins-base-0.10 >= $GSTREAMER_VERSION + mafw >= 0.1 + libosso >= 2.0 + x11 + hal + totem-plparser + gconf-2.0 >= 2.0 + gnome-vfs-2.0 +) + +dnl Check for GdkPixbuf, needed for dumping current frame +GDKPIXBUF_REQUIRED=2.12.0 +AC_ARG_ENABLE(gdkpixbuf, + AS_HELP_STRING([--disable-gdkpixbuf], + [Disable GdkPixbuf support, required for current frame dumping]),, + [enable_gdkpixbuf=auto]) + +if test "x$enable_gdkpixbuf" != "xno"; then + PKG_CHECK_MODULES(GDKPIXBUF, + [gdk-pixbuf-2.0 >= $GDKPIXBUF_REQUIRED], + [have_gdkpixbuf=yes], + [have_gdkpixbuf=no]) + AC_SUBST(GDKPIXBUF_LIBS) + AC_SUBST(GDKPIXBUF_CFLAGS) + + if test "x$have_gdkpixbuf" = "xyes"; then + AC_DEFINE(HAVE_GDKPIXBUF, [], [Define if we have GdkPixbuf]) + fi +else + have_gdkpixbuf="no (disabled)" +fi + +if test "x$enable_gdkpixbuf" = "xyes"; then + if test "x$have_gdkpixbuf" != "xyes"; then + AC_MSG_ERROR([Couldn't find GdkPixbuf >= $GDKPIXBUF_REQUIRED.]) + fi +fi + +AM_CONDITIONAL(HAVE_GDKPIXBUF, test "x$have_gdkpixbuf" = "xyes") + + +dnl Check for Conic, needed connection error handling +CONIC_REQUIRED=0.16 +AC_ARG_ENABLE(conic, + AS_HELP_STRING([--disable-conic], + [Disable Conic support, required to handle network errors]),, + [enable_conic=auto]) + +if test "x$enable_conic" != "xno"; then + PKG_CHECK_MODULES(CONIC, + [conic >= $CONIC_REQUIRED], + [have_conic=yes], + [have_conic=no]) + AC_SUBST(CONIC_LIBS) + AC_SUBST(CONIC_CFLAGS) + + if test "x$have_conic" = "xyes"; then + AC_DEFINE(HAVE_CONIC, [], [Define if we have Conic]) + fi +else + have_conic="no (disabled)" +fi + +if test "x$enable_conic" = "xyes"; then + if test "x$have_conic" != "xyes"; then + AC_MSG_ERROR([Couldn't find Conic >= $CONIC_REQUIRED.]) + fi +fi + +AM_CONDITIONAL(HAVE_CONIC, test "x$have_conic" = "xyes") + + + +plugindir=`$PKG_CONFIG --variable=plugindir mafw` +AC_SUBST(plugindir) + +dnl Default compile flags. (NOTE: CFLAGS is reserved for the user!) + +AC_SUBST([_CFLAGS]) +AC_SUBST([_LDFLAGS]) +_CFLAGS="-Wall -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations" +_CFLAGS="$_CFLAGS -ggdb3" + +dnl Configure-time options. + +dnl Debugging. +DISABLED_BY_DEFAULT([debug], [compile with debug flags and extra output]) +if test "x$enable_debug" = xyes; then + AC_DEFINE([MAFW_DEBUG], [1], [Enable debugging related parts.]) + _CFLAGS="$_CFLAGS -O0 -Werror -DGTK_DISABLE_DEPRECATED" +else + AC_DEFINE([G_DEBUG_DISABLE], [1], [Disable g_debug() calls.]) + _CFLAGS="$_CFLAGS -O2" +fi +AS_IF([test "x$enable_debug" = xyes], + [AC_DEFINE([MAFW_DEBUG], [1], [Enable extra debug messages]) + CFLAGS="$CFLAGS -Werror -O0 -ggdb3 -DGTK_DISABLE_DEPRECATED"], + [AC_DEFINE([G_DEBUG_DISABLE], [1], [Disable g_debug calls]) + CFLAGS="$CFLAGS -O2"]) + + +dnl Tests. +DISABLED_BY_DEFAULT([tests], [disable unit tests]) +if test "x${SBOX_DPKG_INST_ARCH}" = "xarmel"; then + AC_MSG_WARN([Tests are disabled for compilation in armel]) + enable_tests="no" +fi +if test "x$enable_tests" = xyes; then + PKG_CHECK_MODULES(CHECKMORE, [checkmore, check >= 0.9.4]) + if test -z "$CHECKMORE_LIBS"; then + AC_MSG_WARN([checkmore is needed for unit tests!]) + fi +fi +AM_CONDITIONAL(ENABLE_TESTS, + [test "x$enable_tests" = xyes && test -n "$CHECKMORE_LIBS"]) + +dnl Volume handling +if test "x${SBOX_DPKG_INST_ARCH}" = "xi386"; then + DISABLED_BY_DEFAULT([pulse-volume], [enable volume handling with pulse]) +else + ENABLED_BY_DEFAULT([pulse-volume], [enable volume handling with pulse]) +fi +if test "x$enable_pulse_volume" = xno; then + AC_DEFINE([MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME], [1], [Disables volume handling with pulse.]) +else + PKG_CHECK_MODULES(VOLUME, libpulse-mainloop-glib >= 0.9.15) +fi + + +dnl Mute +DISABLED_BY_DEFAULT([mute], [enable mute handling]) +if test "x$enable_mute" = xyes; then + AC_DEFINE([MAFW_GST_RENDERER_ENABLE_MUTE], [1], [Enable mute.]) +fi + +dnl Tracing. +DISABLED_BY_DEFAULT([tracing], [enable function instrumentation (tracing)]) +if test "x$enable_tracing" = xyes; then + _CFLAGS="$_CFLAGS -finstrument-functions -rdynamic" +fi + +dnl Coverage. +DISABLED_BY_DEFAULT([coverage], [enable coverage data generation (gcov)]) +if test "x$enable_coverage" = xyes; then + AC_PATH_PROG(LCOV, [lcov], [lcov]) + if test "x$LCOV" = x; then + echo You need to install lcov to get actual reports! + echo See http://ltp.sf.net/coverage/lcov.php + fi + if test "x$SBOX_USE_CCACHE" == xyes; then + AC_MSG_ERROR([Please set SBOX_USE_CCACHE=no to use coverage.]) + fi + _CFLAGS="$_CFLAGS -fprofile-arcs -ftest-coverage" + _LDFLAGS="$_LDFLAGS -g -lgcov" +fi +AM_CONDITIONAL(ENABLE_COVERAGE, + [test "x$enable_coverage" != xno && test -n "$LCOV"]) + +dnl Output files. + +AC_CONFIG_FILES([ + Makefile + mafw-gst-renderer-uninstalled.pc + libmafw-gst-renderer/Makefile + tests/Makefile + debian/mafw-gst-renderer.install +]) + +AC_OUTPUT diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..9517254 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,1114 @@ +mafw-gst-renderer (0.1.2009.47-1+0m5) unstable; urgency=low + + * This entry has been added by BIFH queue processor + Suffix +0m5 added to package revision + + -- mika tapojarvi 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 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 Wed, 28 Oct 2009 23:48:22 +0200 + +mafw-gst-renderer (0.1.2009.42-2) unstable; urgency=low + + * Version increased. + + -- Mika Tapojärvi 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 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 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 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] mafw-gst-renderer.c + * Fixes: NB#134728 - [PR1.1 proposal] 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Fri, 05 Dec 2008 13:29:06 +0200 + +midas-gstreamer-renderer (0.1.2008.49) unstable; urgency=low + + * In development. + + -- Zeeshan Ali 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 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 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 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 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 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 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 Fri, 10 Oct 2008 16:10:02 +0300 + +midas-gstreamer-renderer (0.1.2008.40) unstable; urgency=low + + * Using playbin2. + + -- Zeeshan Ali 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 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 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 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 Mon, 01 Sep 2008 08:21:43 +0300 + +midas-gstreamer-renderer (0.1.2008.35) unstable; urgency=low + + * In development. + + -- Zeeshan Ali 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 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 Sun, 10 Aug 2008 19:52:39 +0300 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +4 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..f475874 --- /dev/null +++ b/debian/control @@ -0,0 +1,28 @@ +Source: mafw-gst-renderer +Section: misc +Priority: optional +Maintainer: Juha Kellokoski +Build-Depends: debhelper (>= 4.0.0), libglib2.0-dev, + libgstreamer0.10-dev (>= 0.10.20-0maemo3), + libgstreamer-plugins-base0.10-dev (>= 0.10.20-0maemo5), + libx11-dev, libosso-dev (>= 2.0), libmafw0-dev (>= 0.1), + checkmore, gstreamer0.10-plugins-base, + gstreamer0.10-plugins-good, + libhal-dev, libtotem-plparser-dev, libpulse-dev (>= 0.9.15-1), + libgconf2-dev, libosso-gnomevfs2-dev +Standards-Version: 3.7.2 + +Package: mafw-gst-renderer +Section: libs +Architecture: any +Depends: gconf2, ${shlibs:Depends}, ${misc:Depends} +Description: MAFW gst renderer plugin + Renderer plugin for MAFW-gst + +Package: mafw-gst-renderer-dbg +Section: devel +Architecture: any +Priority: extra +Depends: mafw-gst-renderer (= ${binary:Version}) +Description: debug symbols for mafw-gst-renderer + MAFW-gst renderer debug symbols diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..79772e7 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,6 @@ +Copyright (C) 2007, 2008, 2009 Nokia Corporation. All rights reserved. + +Contact: Visa Smolander + + + diff --git a/debian/mafw-gst-renderer.install.in b/debian/mafw-gst-renderer.install.in new file mode 100644 index 0000000..f350be5 --- /dev/null +++ b/debian/mafw-gst-renderer.install.in @@ -0,0 +1 @@ +@plugindir@/*.so diff --git a/debian/mafw-gst-renderer.postinst b/debian/mafw-gst-renderer.postinst new file mode 100644 index 0000000..924970d --- /dev/null +++ b/debian/mafw-gst-renderer.postinst @@ -0,0 +1,6 @@ +#!/bin/sh + +#DEBHELPER# + +test -x /usr/bin/mafw.sh && /usr/bin/mafw.sh start mafw-gst-renderer -7 \ +|| true; diff --git a/debian/mafw-gst-renderer.prerm b/debian/mafw-gst-renderer.prerm new file mode 100644 index 0000000..084ef87 --- /dev/null +++ b/debian/mafw-gst-renderer.prerm @@ -0,0 +1,6 @@ +#!/bin/sh + +#DEBHELPER# + +test -x /usr/bin/mafw.sh && /usr/bin/mafw.sh stop mafw-gst-renderer \ +|| true; diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..1e89b65 --- /dev/null +++ b/debian/rules @@ -0,0 +1,96 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +ifneq (,$(findstring thumb,$(DEB_BUILD_OPTIONS))) + CFLAGS += -mthumb +endif + +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 -ggdb3 -finstrument-functions +endif + +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif +maybe_coverage := $(if $(filter lcov,$(DEB_BUILD_OPTIONS)),--enable-coverage,) + +configure-stamp: + dh_testdir + CFLAGS="$(CFLAGS)" ./autogen.sh \ + --host=$(DEB_HOST_GNU_TYPE) \ + --build=$(DEB_BUILD_GNU_TYPE) \ + --disable-dependency-tracking \ + --prefix=/usr \ + $(maybe_coverage) + touch configure-stamp + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + $(MAKE) +ifeq ($(findstring nocheck,$(DEB_BUILD_OPTIONS)),) + $(MAKE) check +endif + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + [ ! -f Makefile ] || $(MAKE) distclean + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp + +binary-indep: build install + +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs ChangeLog + dh_installdocs + dh_install --sourcedir=debian/tmp -v + dh_link + dh_strip --dbg-package=mafw-gst-renderer + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch + +distcheck: build + dh_testdir + $(MAKE) distcheck + +vg: + dh_testdir + $(MAKE) -C "$(CURDIR)/tests" vg + + +.PHONY: build clean binary-indep binary-arch binary install distcheck vg + diff --git a/libmafw-gst-renderer/Makefile.am b/libmafw-gst-renderer/Makefile.am new file mode 100644 index 0000000..a564ab9 --- /dev/null +++ b/libmafw-gst-renderer/Makefile.am @@ -0,0 +1,55 @@ +# +# Makefile.am for MAFW gst renderer library. +# +# Author: Zeeshan Ali +# +# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved. + +plugin_LTLIBRARIES = mafw-gst-renderer.la + +BUILT_SOURCES = mafw-gst-renderer-marshal.c \ + mafw-gst-renderer-marshal.h + +mafw_gst_renderer_la_SOURCES = $(BUILT_SOURCES) \ + blanking.c blanking.h \ + mafw-gst-renderer.c mafw-gst-renderer.h \ + mafw-gst-renderer-utils.c mafw-gst-renderer-utils.h \ + mafw-gst-renderer-worker.c mafw-gst-renderer-worker.h \ + mafw-gst-renderer-worker-volume.c mafw-gst-renderer-worker-volume.h \ + mafw-gst-renderer-state.c mafw-gst-renderer-state.h \ + mafw-gst-renderer-state-playing.c mafw-gst-renderer-state-playing.h \ + mafw-gst-renderer-state-paused.c mafw-gst-renderer-state-paused.h \ + mafw-gst-renderer-state-stopped.c mafw-gst-renderer-state-stopped.h \ + mafw-gst-renderer-state-transitioning.c mafw-gst-renderer-state-transitioning.h \ + mafw-playlist-iterator.c mafw-playlist-iterator.h + +mafw_gst_renderer_la_CPPFLAGS = $(DEPS_CFLAGS) $(VOLUME_CFLAGS) \ + -DPREFIX=\"$(prefix)\" $(_CFLAGS) +mafw_gst_renderer_la_LDFLAGS = -avoid-version -module $(_LDFLAGS) +mafw_gst_renderer_la_LIBADD = $(DEPS_LIBS) $(VOLUME_LIBS) \ + -lgstinterfaces-0.10 -lgstpbutils-0.10 + +if HAVE_GDKPIXBUF +mafw_gst_renderer_la_SOURCES += gstscreenshot.c gstscreenshot.h +mafw_gst_renderer_la_CPPFLAGS += $(GDKPIXBUF_CFLAGS) +mafw_gst_renderer_la_LIBADD += $(GDKPIXBUF_LIBS) +endif + +if HAVE_CONIC +mafw_gst_renderer_la_CPPFLAGS += $(CONIC_CFLAGS) +mafw_gst_renderer_la_LIBADD += $(CONIC_LIBS) +endif + +mafw-gst-renderer-marshal.c: mafw-gst-renderer-marshal.list + ( \ + echo '#include "mafw-gst-renderer-marshal.h"'; \ + $(GLIB_GENMARSHAL) --prefix=mafw_gst_renderer_marshal --body $^ \ + ) > $@ + +mafw-gst-renderer-marshal.h: mafw-gst-renderer-marshal.list + $(GLIB_GENMARSHAL) --prefix=mafw_gst_renderer_marshal --header \ + $^ > $@ + +EXTRA_DIST = mafw-gst-renderer-marshal.list +CLEANFILES = *.gcno *.gcda +MAINTAINERCLEANFILES = Makefile.in *.loT diff --git a/libmafw-gst-renderer/blanking.c b/libmafw-gst-renderer/blanking.c new file mode 100644 index 0000000..4a12836 --- /dev/null +++ b/libmafw-gst-renderer/blanking.c @@ -0,0 +1,119 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include "blanking.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-blanking" + +/* In seconds */ +#define VIDEO_BLANKING_TIMER_INTERVAL 45 + +static guint blanking_timeout_id = 0; +static osso_context_t *osso_ctx = NULL; +static gboolean can_control_blanking = TRUE; +static gboolean is_blanking_prohibited = FALSE; + +static void remove_blanking_timeout(void) +{ + if (blanking_timeout_id) { + g_source_remove(blanking_timeout_id); + blanking_timeout_id = 0; + } +} + +/* + * Re-enables screen blanking. + */ +void blanking_allow(void) +{ + is_blanking_prohibited = FALSE; + remove_blanking_timeout(); +} + +static gboolean no_blanking_timeout(void) +{ + /* Stop trying if it fails. */ + return osso_display_blanking_pause(osso_ctx) == OSSO_OK; +} + +/* + * Adds a timeout to periodically disable screen blanking. + */ +void blanking_prohibit(void) +{ + is_blanking_prohibited = TRUE; + if ((!osso_ctx) || (!can_control_blanking)) + return; + osso_display_state_on(osso_ctx); + osso_display_blanking_pause(osso_ctx); + if (blanking_timeout_id == 0) { + blanking_timeout_id = + g_timeout_add_seconds(VIDEO_BLANKING_TIMER_INTERVAL, + (gpointer)no_blanking_timeout, + NULL); + } +} + +void blanking_init(void) +{ + /* It's enough to initialize it once for a process. */ + if (osso_ctx) + return; + osso_ctx = osso_initialize(PACKAGE, VERSION, 0, NULL); + if (!osso_ctx) + g_warning("osso_initialize failed, screen may go black"); + is_blanking_prohibited = FALSE; + /* Default policy is to allow user to control blanking */ + blanking_control(TRUE); +} + +void blanking_deinit(void) +{ + if (!osso_ctx) + return; + blanking_control(FALSE); + osso_deinitialize(osso_ctx); + osso_ctx = NULL; +} + +void blanking_control(gboolean activate) +{ + can_control_blanking = activate; + if (!can_control_blanking) { + remove_blanking_timeout(); + } else { + /* Restore the last state */ + if (is_blanking_prohibited) { + blanking_prohibit(); + } else { + blanking_allow(); + } + } +} diff --git a/libmafw-gst-renderer/blanking.h b/libmafw-gst-renderer/blanking.h new file mode 100644 index 0000000..6a69e61 --- /dev/null +++ b/libmafw-gst-renderer/blanking.h @@ -0,0 +1,37 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef BLANKING_H +#define BLANKING_H + +G_BEGIN_DECLS + +void blanking_init(void); +void blanking_deinit(void); +void blanking_allow(void); +void blanking_prohibit(void); +void blanking_control(gboolean activate); + +G_END_DECLS + +#endif diff --git a/libmafw-gst-renderer/gstscreenshot.c b/libmafw-gst-renderer/gstscreenshot.c new file mode 100644 index 0000000..f7f177e --- /dev/null +++ b/libmafw-gst-renderer/gstscreenshot.c @@ -0,0 +1,259 @@ +/* Small helper element for format conversion + * (c) 2004 Ronald Bultje + * 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 +#include + +#include "gstscreenshot.h" + +typedef struct { + GstBuffer *result; + GstElement *src; + GstElement *sink; + GstElement *pipeline; + BvwFrameConvCb cb; + gpointer cb_data; +} GstScreenshotData; + +/* GST_DEBUG_CATEGORY_EXTERN (_totem_gst_debug_cat); */ +/* #define GST_CAT_DEFAULT _totem_gst_debug_cat */ + +static void feed_fakesrc(GstElement *src, GstBuffer *buf, GstPad *pad, + gpointer data) +{ + GstBuffer *in_buf = GST_BUFFER(data); + + g_assert(GST_BUFFER_SIZE(buf) >= GST_BUFFER_SIZE(in_buf)); + g_assert(!GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_READONLY)); + + gst_buffer_set_caps(buf, GST_BUFFER_CAPS(in_buf)); + + memcpy(GST_BUFFER_DATA(buf), GST_BUFFER_DATA(in_buf), + GST_BUFFER_SIZE(in_buf)); + + GST_BUFFER_SIZE(buf) = GST_BUFFER_SIZE(in_buf); + + GST_DEBUG("feeding buffer %p, size %u, caps %" GST_PTR_FORMAT, + buf, GST_BUFFER_SIZE(buf), GST_BUFFER_CAPS(buf)); + + gst_buffer_unref(in_buf); +} + +static void save_result(GstElement *sink, GstBuffer *buf, GstPad *pad, + gpointer data) +{ + GstScreenshotData *gsd = data; + + gsd->result = gst_buffer_ref(buf); + + GST_DEBUG("received converted buffer %p with caps %" GST_PTR_FORMAT, + gsd->result, GST_BUFFER_CAPS(gsd->result)); +} + +static gboolean create_element(const gchar *factory_name, GstElement **element, + GError **err) +{ + *element = gst_element_factory_make(factory_name, NULL); + if (*element) + return TRUE; + + if (err && *err == NULL) { + *err = g_error_new( + GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN, + "cannot create element '%s' - please check your " + "GStreamer installation", factory_name); + } + + return FALSE; +} + +static gboolean finalize_process(GstScreenshotData *gsd) +{ + g_signal_handlers_disconnect_matched(gsd->sink, (GSignalMatchType) + G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + save_result, NULL); + g_signal_handlers_disconnect_matched(gsd->src, (GSignalMatchType) + G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + feed_fakesrc, NULL); + gst_element_set_state(gsd->pipeline, GST_STATE_NULL); + + g_free(gsd); + + return FALSE; +} + +static gboolean async_bus_handler(GstBus *bus, GstMessage *msg, + gpointer data) +{ + GstScreenshotData *gsd = data; + gboolean keep_watch = TRUE; + + switch (GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_EOS: { + if (gsd->result != NULL) { + GST_DEBUG("conversion successful: result = %p", + gsd->result); + } else { + GST_WARNING("EOS but no result frame?!"); + } + gsd->cb(gsd->result, gsd->cb_data); + keep_watch = finalize_process(gsd); + break; + } + case GST_MESSAGE_ERROR: { + gchar *dbg = NULL; + GError *error = NULL; + + gst_message_parse_error(msg, &error, &dbg); + if (error != NULL) { + g_warning("Could not take screenshot: %s", + error->message); + GST_DEBUG("%s [debug: %s]", error->message, + GST_STR_NULL(dbg)); + g_error_free(error); + } else { + g_warning("Could not take screenshot(and " + "NULL error!)"); + } + g_free(dbg); + gsd->result = NULL; + gsd->cb(gsd->result, gsd->cb_data); + keep_watch = finalize_process(gsd); + break; + } + default: + break; + } + + return keep_watch; +} + +/* takes ownership of the input buffer */ +gboolean bvw_frame_conv_convert(GstBuffer *buf, GstCaps *to_caps, + BvwFrameConvCb cb, gpointer cb_data) +{ + static GstElement *src = NULL, *sink = NULL, *pipeline = NULL, + *filter1 = NULL, *filter2 = NULL; + static GstBus *bus; + GError *error = NULL; + GstCaps *to_caps_no_par; + GstScreenshotData *gsd; + + g_return_val_if_fail(GST_BUFFER_CAPS(buf) != NULL, FALSE); + g_return_val_if_fail(cb != NULL, FALSE); + + if (pipeline == NULL) { + GstElement *csp, *vscale; + + pipeline = gst_pipeline_new("screenshot-pipeline"); + if(pipeline == NULL) { + g_warning("Could not take screenshot: " + "no pipeline (unknown error)"); + return FALSE; + } + + /* videoscale is here to correct for the + * pixel-aspect-ratio for us */ + GST_DEBUG("creating elements"); + if(!create_element("fakesrc", &src, &error) || + !create_element("ffmpegcolorspace", &csp, &error) || + !create_element("videoscale", &vscale, &error) || + !create_element("capsfilter", &filter1, &error) || + !create_element("capsfilter", &filter2, &error) || + !create_element("fakesink", &sink, &error)) { + g_warning("Could not take screenshot: %s", + error->message); + g_error_free(error); + return FALSE; + } + + GST_DEBUG("adding elements"); + gst_bin_add_many(GST_BIN(pipeline), src, csp, filter1, vscale, + filter2, sink, NULL); + + g_object_set(sink, "preroll-queue-len", 1, + "signal-handoffs", TRUE, NULL); + + /* set to 'fixed' sizetype */ + g_object_set(src, "sizetype", 2, "num-buffers", 1, + "signal-handoffs", TRUE, NULL); + + /* FIXME: linking is still way too expensive, profile + * this properly */ + GST_DEBUG("linking src->csp"); + if(!gst_element_link_pads(src, "src", csp, "sink")) + return FALSE; + + GST_DEBUG("linking csp->filter1"); + if(!gst_element_link_pads(csp, "src", filter1, "sink")) + return FALSE; + + GST_DEBUG("linking filter1->vscale"); + if(!gst_element_link_pads(filter1, "src", vscale, "sink")) + return FALSE; + + GST_DEBUG("linking vscale->capsfilter"); + if(!gst_element_link_pads(vscale, "src", filter2, "sink")) + return FALSE; + + GST_DEBUG("linking capsfilter->sink"); + if(!gst_element_link_pads(filter2, "src", sink, "sink")) + return FALSE; + + bus = gst_element_get_bus(pipeline); + } + + /* adding this superfluous capsfilter makes linking cheaper */ + to_caps_no_par = gst_caps_copy(to_caps); + gst_structure_remove_field(gst_caps_get_structure(to_caps_no_par, 0), + "pixel-aspect-ratio"); + g_object_set(filter1, "caps", to_caps_no_par, NULL); + gst_caps_unref(to_caps_no_par); + + g_object_set(filter2, "caps", to_caps, NULL); + gst_caps_unref(to_caps); + + gsd = g_new0(GstScreenshotData, 1); + + gsd->src = src; + gsd->sink = sink; + gsd->pipeline = pipeline; + gsd->cb = cb; + gsd->cb_data = cb_data; + + g_signal_connect(sink, "handoff", G_CALLBACK(save_result), gsd); + + g_signal_connect(src, "handoff", G_CALLBACK(feed_fakesrc), buf); + + gst_bus_add_watch(bus, async_bus_handler, gsd); + + /* set to 'fixed' sizetype */ + g_object_set(src, "sizemax", GST_BUFFER_SIZE(buf), NULL); + + GST_DEBUG("running conversion pipeline"); + gst_element_set_state(pipeline, GST_STATE_PLAYING); + + return TRUE; +} diff --git a/libmafw-gst-renderer/gstscreenshot.h b/libmafw-gst-renderer/gstscreenshot.h new file mode 100644 index 0000000..d3cf23c --- /dev/null +++ b/libmafw-gst-renderer/gstscreenshot.h @@ -0,0 +1,36 @@ +/* Small helper element for format conversion + * (c) 2004 Ronald Bultje + * 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 + +G_BEGIN_DECLS + +typedef void (*BvwFrameConvCb)(GstBuffer *result, gpointer user_data); + +gboolean bvw_frame_conv_convert (GstBuffer *buf, GstCaps *to, + BvwFrameConvCb cb, gpointer cb_data); + +G_END_DECLS + +#endif /* __BVW_FRAME_CONV_H__ */ diff --git a/libmafw-gst-renderer/mafw-gst-renderer-marshal.list b/libmafw-gst-renderer/mafw-gst-renderer-marshal.list new file mode 100644 index 0000000..113502f --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-marshal.list @@ -0,0 +1,2 @@ +# MafwPlaylistIterator::playlist-changed(clip_changed, domain, code, message) +VOID: BOOLEAN, UINT, INT, STRING diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c b/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c new file mode 100644 index 0000000..742d474 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c @@ -0,0 +1,379 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-state-paused" + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _do_play(MafwGstRendererState *self, GError **error); +static void _do_play_object(MafwGstRendererState *self, const gchar *object_id, + GError **error); +static void _do_stop(MafwGstRendererState *self, GError **error); +static void _do_resume(MafwGstRendererState *self, GError **error); +static void _do_set_position(MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error); +static void _do_get_position(MafwGstRendererState *self, + gint *seconds, + GError **error); + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void _do_next(MafwGstRendererState *self, GError **error); +static void _do_previous(MafwGstRendererState *self, GError **error); +static void _do_goto_index(MafwGstRendererState *self, guint index, + GError **error); +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error); + +/*---------------------------------------------------------------------------- + Notification worker + ----------------------------------------------------------------------------*/ + +static void _notify_play(MafwGstRendererState *self, GError **error); +static void _notify_seek(MafwGstRendererState *self, GError **error); +static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent, + GError **error); + +/*---------------------------------------------------------------------------- + Playlist editing signals + ----------------------------------------------------------------------------*/ + +static void _playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error); + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +static GValue* _get_property_value(MafwGstRendererState *self, + const gchar *name); + +/*---------------------------------------------------------------------------- + Memory card event handlers + ----------------------------------------------------------------------------*/ + +static void _handle_pre_unmount(MafwGstRendererState *self, + const gchar *mount_point); + +/*---------------------------------------------------------------------------- + GObject initialization + ----------------------------------------------------------------------------*/ + +G_DEFINE_TYPE(MafwGstRendererStatePaused, mafw_gst_renderer_state_paused, + MAFW_TYPE_GST_RENDERER_STATE); + +static void mafw_gst_renderer_state_paused_init(MafwGstRendererStatePaused *self) +{ +} + +static void mafw_gst_renderer_state_paused_class_init( + MafwGstRendererStatePausedClass *klass) +{ + MafwGstRendererStateClass *state_class; + + state_class = MAFW_GST_RENDERER_STATE_CLASS(klass); + g_return_if_fail(state_class != NULL); + + state_class->name = g_strdup("Paused"); + + /* Playback */ + + state_class->play = _do_play; + state_class->play_object = _do_play_object; + state_class->stop = _do_stop; + state_class->resume = _do_resume; + state_class->set_position = _do_set_position; + state_class->get_position = _do_get_position; + + /* Playlist */ + + state_class->next = _do_next; + state_class->previous = _do_previous; + state_class->goto_index = _do_goto_index; + + /* Notification metadata */ + + state_class->notify_metadata = _notify_metadata; + + /* Notification worker */ + + state_class->notify_play = _notify_play; + /* state_class->notify_pause is not allowed */ + state_class->notify_seek = _notify_seek; + state_class->notify_buffer_status = _notify_buffer_status; + + /* Playlist editing signals */ + + state_class->playlist_contents_changed = + _playlist_contents_changed; + + /* Property methods */ + + state_class->get_property_value = _get_property_value; + + /* Memory card event handlers */ + + state_class->handle_pre_unmount = _handle_pre_unmount; +} + +GObject *mafw_gst_renderer_state_paused_new(MafwGstRenderer *renderer) +{ + MafwGstRendererState *state; + + state = MAFW_GST_RENDERER_STATE( + g_object_new(MAFW_TYPE_GST_RENDERER_STATE_PAUSED, NULL)); + state->renderer = renderer; + + return G_OBJECT(state); +} + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _do_play(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + mafw_gst_renderer_state_do_play(self, error); +} + +static void _do_play_object(MafwGstRendererState *self, const gchar *object_id, + GError **error) +{ + MafwGstRendererPlaybackMode cur_mode, prev_mode; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + + self->renderer->worker->stay_paused = FALSE; + prev_mode = mafw_gst_renderer_get_playback_mode(self->renderer); + mafw_gst_renderer_state_do_play_object(self, object_id, error); + cur_mode = mafw_gst_renderer_get_playback_mode(self->renderer); + + /* If this happens it means that we interrupted playlist playback + so let's resume it when play_object is finished */ + if (cur_mode != prev_mode) { + self->renderer->resume_playlist = TRUE; + } +} + +static void _do_stop(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + + self->renderer->worker->stay_paused = FALSE; + /* Stop playback */ + mafw_gst_renderer_state_do_stop(self, error); +} + +static void _do_resume(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + + MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + self->renderer->worker->stay_paused = FALSE; + mafw_gst_renderer_worker_resume(renderer->worker); + + /* Transition will be done after receiving notify_play */ +} + +static void _do_set_position(MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + self->renderer->worker->stay_paused = TRUE; + mafw_gst_renderer_state_do_set_position(self, mode, seconds, error); +} + +static void _do_get_position(MafwGstRendererState *self, + gint *seconds, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + mafw_gst_renderer_state_do_get_position(self, seconds, error); +} + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void _do_next(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + self->renderer->worker->stay_paused = TRUE; + mafw_gst_renderer_state_do_next(self, error); +} + +static void _do_previous(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + self->renderer->worker->stay_paused = TRUE; + mafw_gst_renderer_state_do_prev(self, error); +} + +static void _do_goto_index(MafwGstRendererState *self, guint index, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + self->renderer->worker->stay_paused = FALSE; + mafw_gst_renderer_state_do_goto_index(self, index, error); +} + + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error) +{ + g_debug("running _notify_metadata..."); + /* Kindly Ignore this notification: + probably a delayed (now useless) metadata resolution */ +} + + +/*---------------------------------------------------------------------------- + Notification worker + ----------------------------------------------------------------------------*/ + +static void _notify_play(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + + MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* Change status to play */ + mafw_gst_renderer_set_state(renderer, Playing); +} + +static void _notify_seek(MafwGstRendererState *self, GError **error) +{ + mafw_gst_renderer_state_do_notify_seek(self, error); +} + +static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent, + GError **error) +{ + mafw_gst_renderer_state_do_notify_buffer_status (self, percent, error); +} + +/*---------------------------------------------------------------------------- + Playlist editing signals + ----------------------------------------------------------------------------*/ + +static void _playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error) +{ + MafwGstRendererPlaybackMode mode; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self)); + + /* Play the new index only if we are not in standalone mode. + Otherwise, when play_object finishes the new item will be + played if that's been suggested with renderer->resume_playlist */ + mode = mafw_gst_renderer_get_playback_mode(self->renderer); + if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST) { + mafw_gst_renderer_state_do_play(self, error); + } +} + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +GValue* _get_property_value(MafwGstRendererState *self, const gchar *name) +{ + GValue *value = NULL; + + g_return_val_if_fail(MAFW_IS_GST_RENDERER_STATE_PAUSED(self), value); + + if (!g_strcmp0(name, MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)) { + gboolean is_seekable = + mafw_gst_renderer_worker_get_seekable( + self->renderer->worker); + + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_STRING); + if (is_seekable) { + g_value_set_string(value, "seek"); + } else { + g_value_set_string(value, ""); + } + } + + return value; +} + +/*---------------------------------------------------------------------------- + Memory card event handlers + ----------------------------------------------------------------------------*/ + +static void _handle_pre_unmount(MafwGstRendererState *self, + const gchar *mount_point) +{ + gchar *mount_uri; + + /* If not playing anything, bail out */ + if (!self->renderer->media->uri) { + return; + } + + /* Check if mount point is URI or path, we need URI */ + if (!g_str_has_prefix(mount_point, "file://")) { + mount_uri = g_filename_to_uri(mount_point, NULL, NULL); + } else { + mount_uri = g_strdup(mount_point); + } + + /* Stop if playing from unmounted location */ + if (g_str_has_prefix(self->renderer->media->uri, mount_uri)) { + g_debug("PAUSED-STATE: stopping to mount card"); + mafw_gst_renderer_stop(MAFW_RENDERER(self->renderer), + NULL, + NULL); + } + + g_free(mount_uri); +} diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h b/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h new file mode 100644 index 0000000..f0098d8 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h @@ -0,0 +1,76 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MAFW_GST_RENDERER_STATE_PAUSED_H +#define MAFW_GST_RENDERER_STATE_PAUSED_H + + +#include "mafw-gst-renderer.h" +#include "mafw-gst-renderer-state.h" + +G_BEGIN_DECLS + +/*---------------------------------------------------------------------------- + GObject type conversion macros + ----------------------------------------------------------------------------*/ + +#define MAFW_TYPE_GST_RENDERER_STATE_PAUSED \ + (mafw_gst_renderer_state_paused_get_type()) +#define MAFW_GST_RENDERER_STATE_PAUSED(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER_STATE_PAUSED, \ + MafwGstRendererStatePaused)) +#define MAFW_IS_GST_RENDERER_STATE_PAUSED(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER_STATE_PAUSED)) +#define MAFW_GST_RENDERER_STATE_PAUSED_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER_STATE_PAUSED, \ + MafwGstRendererStatePaused)) +#define MAFW_GST_RENDERER_STATE_PAUSED_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER_STATE_PAUSED, \ + MafwGstRendererStatePausedClass)) +#define MAFW_IS_GST_RENDERER_STATE_PAUSED_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER_STATE_PAUSED)) + +/*---------------------------------------------------------------------------- + Type definitions + ----------------------------------------------------------------------------*/ + + +typedef struct _MafwGstRendererStatePaused MafwGstRendererStatePaused; +typedef struct _MafwGstRendererStatePausedClass MafwGstRendererStatePausedClass; + +struct _MafwGstRendererStatePausedClass { + MafwGstRendererStateClass parent_class; +}; + +struct _MafwGstRendererStatePaused { + MafwGstRendererState parent; +}; + +GType mafw_gst_renderer_state_paused_get_type(void); + +GObject *mafw_gst_renderer_state_paused_new(MafwGstRenderer *renderer); + +G_END_DECLS + +#endif diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c b/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c new file mode 100644 index 0000000..bc7bb8c --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c @@ -0,0 +1,445 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-state-playing" + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _do_play(MafwGstRendererState *self, GError **error); +static void _do_play_object(MafwGstRendererState *self, const gchar *object_id, + GError **error); +static void _do_stop(MafwGstRendererState *self, GError **error); +static void _do_pause(MafwGstRendererState *self, GError **error); +static void _do_set_position(MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error); +static void _do_get_position(MafwGstRendererState *self, + gint *seconds, + GError **error); + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void _do_next(MafwGstRendererState *self, GError **error); +static void _do_previous(MafwGstRendererState *self, GError **error); +static void _do_goto_index(MafwGstRendererState *self, guint index, + GError **error); + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error); + +/*---------------------------------------------------------------------------- + Notification worker + ----------------------------------------------------------------------------*/ + +static void _notify_play(MafwGstRendererState *self, GError **error); +static void _notify_pause(MafwGstRendererState *self, GError **error); +static void _notify_seek(MafwGstRendererState *self, GError **error); +static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent, + GError **error); +static void _notify_eos(MafwGstRendererState *self, GError **error); + +/*---------------------------------------------------------------------------- + Playlist editing signals + ----------------------------------------------------------------------------*/ + +static void _playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error); + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +static GValue* _get_property_value(MafwGstRendererState *self, + const gchar *name); + +/*---------------------------------------------------------------------------- + Memory card event handlers + ----------------------------------------------------------------------------*/ + +static void _handle_pre_unmount(MafwGstRendererState *self, + const gchar *mount_point); + +/*---------------------------------------------------------------------------- + GObject initialization + ----------------------------------------------------------------------------*/ + +G_DEFINE_TYPE(MafwGstRendererStatePlaying, mafw_gst_renderer_state_playing, + MAFW_TYPE_GST_RENDERER_STATE); + +static void mafw_gst_renderer_state_playing_init(MafwGstRendererStatePlaying *self) +{ +} + +static void mafw_gst_renderer_state_playing_class_init( + MafwGstRendererStatePlayingClass *klass) +{ + MafwGstRendererStateClass *state_class; + + state_class = MAFW_GST_RENDERER_STATE_CLASS(klass); + g_return_if_fail(state_class != NULL); + + state_class->name = g_strdup("Playing"); + + /* Playback */ + + state_class->play = _do_play; + state_class->play_object = _do_play_object; + state_class->stop = _do_stop; + state_class->pause = _do_pause; + /* state_class->resume is not allowed */ + state_class->set_position = _do_set_position; + state_class->get_position = _do_get_position; + + /* Playlist */ + + state_class->next = _do_next; + state_class->previous = _do_previous; + state_class->goto_index = _do_goto_index; + + /* Notification metadata */ + + state_class->notify_metadata = _notify_metadata; + + /* Notification worker */ + + state_class->notify_play = _notify_play; + state_class->notify_pause = _notify_pause; + state_class->notify_seek = _notify_seek; + state_class->notify_buffer_status = _notify_buffer_status; + state_class->notify_eos = _notify_eos; + + /* Playlist editing signals */ + + state_class->playlist_contents_changed = + _playlist_contents_changed; + + /* Property methods */ + + state_class->get_property_value = _get_property_value; + + /* Memory card event handlers */ + + state_class->handle_pre_unmount = _handle_pre_unmount; +} + +GObject *mafw_gst_renderer_state_playing_new(MafwGstRenderer *renderer) +{ + MafwGstRendererState *state; + + state = MAFW_GST_RENDERER_STATE( + g_object_new(MAFW_TYPE_GST_RENDERER_STATE_PLAYING, NULL)); + state->renderer = renderer; + + return G_OBJECT(state); +} + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _do_play(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + mafw_gst_renderer_state_do_play(self, error); +} + +static void _do_play_object(MafwGstRendererState *self, const gchar *object_id, + GError **error) +{ + MafwGstRendererPlaybackMode cur_mode, prev_mode; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + + prev_mode = mafw_gst_renderer_get_playback_mode(self->renderer); + mafw_gst_renderer_state_do_play_object(self, object_id, error); + cur_mode = mafw_gst_renderer_get_playback_mode(self->renderer); + + /* If this happens it means that we interrupted playlist playback + so let's resume it when play_object is finished */ + if (cur_mode != prev_mode) { + self->renderer->resume_playlist = TRUE; + } +} + +static void _do_stop(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + + /* Stop playback */ + mafw_gst_renderer_state_do_stop(self, error); +} + +static void _do_pause(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + + MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + mafw_gst_renderer_worker_pause(renderer->worker); + + /* Transition will be done when receiving pause + * notification */ +} + +static void _do_set_position(MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + mafw_gst_renderer_state_do_set_position(self, mode, seconds, error); +} + +static void _do_get_position(MafwGstRendererState *self, + gint *seconds, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + mafw_gst_renderer_state_do_get_position(self, seconds, error); +} + + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void _do_next(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + mafw_gst_renderer_state_do_next(self, error); +} + +static void _do_previous(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + mafw_gst_renderer_state_do_prev(self, error); +} + +static void _do_goto_index(MafwGstRendererState *self, guint index, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + mafw_gst_renderer_state_do_goto_index(self, index, error); +} + + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error) +{ + g_debug("running _notify_metadata..."); + /* Kindly Ignore this notification: + probably a delayed (now useless) metadata resolution */ +} + + +/*---------------------------------------------------------------------------- + Notification worker + ----------------------------------------------------------------------------*/ + +static void _notify_play(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + /* Kindly ignore this notification: it's received when seeking + * in a stream */ +} + +static void _notify_pause(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + + MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* Change status to pause */ + mafw_gst_renderer_set_state(renderer, Paused); +} + +static void _notify_seek(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + mafw_gst_renderer_state_do_notify_seek(self, error); +} + +static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent, + GError **error) +{ + mafw_gst_renderer_state_do_notify_buffer_status (self, percent, error); +} + +static void _notify_eos(MafwGstRendererState *self, GError **error) +{ + MafwGstRenderer *renderer; + MafwGstRendererMovementResult move_type; + MafwGstRendererPlaybackMode mode; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* Update playcount */ + if (renderer->update_playcount_id > 0) { + g_source_remove(renderer->update_playcount_id); + mafw_gst_renderer_update_stats(renderer); + } + + /* Notice: playback has already stopped, so calling + * mafw_gst_renderer_stop or mafw_gst_renderer_state_stop + * here is an error. + * To set the renderer state to Stopped use this instead: + * mafw_gst_renderer_set_state(self->renderer, Stopped); + */ + + /* If we are not in playlist mode, switch to it, + otherwise move to the next in the playlist */ + mode = mafw_gst_renderer_get_playback_mode(renderer); + if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) { + mafw_gst_renderer_worker_stop(self->renderer->worker); + mafw_gst_renderer_set_state(self->renderer, Stopped); + mafw_gst_renderer_set_playback_mode( + renderer, MAFW_GST_RENDERER_MODE_PLAYLIST); + mafw_gst_renderer_set_media_playlist(renderer); + + /* Do we have to resume playlist playback? */ + if (renderer->resume_playlist) { + mafw_gst_renderer_state_play(self, error); + } + } else { + /* Move to next in playlist */ + move_type = + mafw_gst_renderer_move(renderer, + MAFW_GST_RENDERER_MOVE_TYPE_NEXT, + 0, error); + + switch (move_type) { + case MAFW_GST_RENDERER_MOVE_RESULT_OK: + mafw_gst_renderer_state_play(self, error); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT: + case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST: + mafw_gst_renderer_worker_stop(self->renderer->worker); + mafw_gst_renderer_set_state(self->renderer, Stopped); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_ERROR: + break; + default: + g_critical("Movement not controlled"); + } + } +} + +/*---------------------------------------------------------------------------- + Playlist editing signals + ----------------------------------------------------------------------------*/ + +static void _playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error) +{ + MafwGstRendererPlaybackMode mode; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self)); + + /* Play the new index only if we are not in standalone mode. + Otherwise, when play_object finishes the new item will be + played if that's been suggested with renderer->resume_playlist */ + mode = mafw_gst_renderer_get_playback_mode(self->renderer); + if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST) { + mafw_gst_renderer_state_do_play(self, error); + } +} + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +GValue* _get_property_value(MafwGstRendererState *self, const gchar *name) +{ + GValue *value = NULL; + + g_return_val_if_fail(MAFW_IS_GST_RENDERER_STATE_PLAYING(self), value); + + if (!g_strcmp0(name, MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)) { + gboolean is_seekable = + mafw_gst_renderer_worker_get_seekable( + self->renderer->worker); + + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_STRING); + if (is_seekable) { + g_value_set_string(value, "seek"); + } else { + g_value_set_string(value, ""); + } + } + + return value; +} + +/*---------------------------------------------------------------------------- + Memory card event handlers + ----------------------------------------------------------------------------*/ + +static void _handle_pre_unmount(MafwGstRendererState *self, + const gchar *mount_point) +{ + gchar *mount_uri; + + /* If not playing anything, bail out */ + if (!self->renderer->media->uri) { + return; + } + + /* Check if mount point is URI or path, we need URI */ + if (!g_str_has_prefix(mount_point, "file://")) { + mount_uri = g_filename_to_uri(mount_point, NULL, NULL); + } else { + mount_uri = g_strdup(mount_point); + } + + /* Stop if playing from unmounted location */ + if (g_str_has_prefix(self->renderer->media->uri, mount_uri)) { + mafw_gst_renderer_stop(MAFW_RENDERER(self->renderer), + NULL, + NULL); + } + + g_free(mount_uri); +} diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h b/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h new file mode 100644 index 0000000..0127edb --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h @@ -0,0 +1,76 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MAFW_GST_RENDERER_STATE_PLAYING_H +#define MAFW_GST_RENDERER_STATE_PLAYING_H + + +#include "mafw-gst-renderer.h" +#include "mafw-gst-renderer-state.h" + +G_BEGIN_DECLS + +/*---------------------------------------------------------------------------- + GObject type conversion macros + ----------------------------------------------------------------------------*/ + +#define MAFW_TYPE_GST_RENDERER_STATE_PLAYING \ + (mafw_gst_renderer_state_playing_get_type()) +#define MAFW_GST_RENDERER_STATE_PLAYING(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER_STATE_PLAYING, \ + MafwGstRendererStatePlaying)) +#define MAFW_IS_GST_RENDERER_STATE_PLAYING(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER_STATE_PLAYING)) +#define MAFW_GST_RENDERER_STATE_PLAYING_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER_STATE_PLAYING, \ + MafwGstRendererStatePlaying)) +#define MAFW_GST_RENDERER_STATE_PLAYING_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER_STATE_PLAYING, \ + MafwGstRendererStatePlayingClass)) +#define MAFW_IS_GST_RENDERER_STATE_PLAYING_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER_STATE_PLAYING)) + +/*---------------------------------------------------------------------------- + Type definitions + ----------------------------------------------------------------------------*/ + + +typedef struct _MafwGstRendererStatePlaying MafwGstRendererStatePlaying; +typedef struct _MafwGstRendererStatePlayingClass MafwGstRendererStatePlayingClass; + +struct _MafwGstRendererStatePlayingClass { + MafwGstRendererStateClass parent_class; +}; + +struct _MafwGstRendererStatePlaying { + MafwGstRendererState parent; +}; + +GType mafw_gst_renderer_state_playing_get_type(void); + +GObject *mafw_gst_renderer_state_playing_new(MafwGstRenderer *renderer); + +G_END_DECLS + +#endif diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c b/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c new file mode 100644 index 0000000..3b46057 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c @@ -0,0 +1,319 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include "mafw-gst-renderer-state-stopped.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-state-stopped" + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _do_play(MafwGstRendererState *self, GError **error); +static void _do_play_object(MafwGstRendererState *self, const gchar *object_id, + GError **error); +static void _do_stop(MafwGstRendererState *self, GError **error); + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void _do_next(MafwGstRendererState *self, GError **error); +static void _do_previous(MafwGstRendererState *self, GError **error); +static void _do_goto_index(MafwGstRendererState *self, guint index, + GError **error); + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error); + +/*---------------------------------------------------------------------------- + Playlist editing signals + ----------------------------------------------------------------------------*/ + +static void _playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error); + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +static GValue* _get_property_value(MafwGstRendererState *self, + const gchar *name); + +/*---------------------------------------------------------------------------- + GObject initialization + ----------------------------------------------------------------------------*/ + +G_DEFINE_TYPE(MafwGstRendererStateStopped, mafw_gst_renderer_state_stopped, + MAFW_TYPE_GST_RENDERER_STATE); + +static void mafw_gst_renderer_state_stopped_init(MafwGstRendererStateStopped *self) +{ +} + +static void mafw_gst_renderer_state_stopped_class_init( + MafwGstRendererStateStoppedClass *klass) +{ + MafwGstRendererStateClass *state_klass; + + state_klass = MAFW_GST_RENDERER_STATE_CLASS(klass); + g_return_if_fail(state_klass != NULL); + + state_klass->name = g_strdup("Stopped"); + + /* Playback */ + + state_klass->play = _do_play; + state_klass->play_object = _do_play_object; + state_klass->stop = _do_stop; + + /* Playlist */ + + state_klass->next = _do_next; + state_klass->previous = _do_previous; + state_klass->goto_index = _do_goto_index; + + /* Metadata */ + + state_klass->notify_metadata = _notify_metadata; + + /* Playlist editing signals */ + + state_klass->playlist_contents_changed = + _playlist_contents_changed; + + /* Property methods */ + + state_klass->get_property_value = _get_property_value; +} + +GObject *mafw_gst_renderer_state_stopped_new(MafwGstRenderer *renderer) +{ + MafwGstRendererState *state; + + state = MAFW_GST_RENDERER_STATE( + g_object_new(MAFW_TYPE_GST_RENDERER_STATE_STOPPED, NULL)); + state->renderer = renderer; + + return G_OBJECT(state); +} + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _do_play(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self)); + mafw_gst_renderer_state_do_play(self, error); +} + +static void _do_play_object(MafwGstRendererState *self, const gchar *object_id, + GError **error) +{ + MafwGstRendererPlaybackMode cur_mode, prev_mode; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self)); + + prev_mode = mafw_gst_renderer_get_playback_mode(self->renderer); + mafw_gst_renderer_state_do_play_object(self, object_id, error); + cur_mode = mafw_gst_renderer_get_playback_mode(self->renderer); + + /* If this happens it means that we interrupted playlist mode + but we did so in Stopped state, so when play_object finishes + we want to stay Stopped */ + if (cur_mode != prev_mode) { + self->renderer->resume_playlist = FALSE; + } +} + +static void _do_stop(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self)); + /* We are already in Stopped state, so do nothing */ +} + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void _do_next(MafwGstRendererState *self, GError **error) +{ + MafwGstRenderer *renderer = NULL; + MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self)); + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + value = mafw_gst_renderer_move(renderer, + MAFW_GST_RENDERER_MOVE_TYPE_NEXT, + 0, error); + + switch (value) { + case MAFW_GST_RENDERER_MOVE_RESULT_ERROR: + case MAFW_GST_RENDERER_MOVE_RESULT_OK: + break; + case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST: + g_set_error (error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "There is no playlist or media to play"); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT: + mafw_playlist_iterator_reset(renderer->iterator, NULL); + mafw_gst_renderer_set_media_playlist(renderer); + break; + default: + g_critical("Movement not controlled"); + } +} + +static void _do_previous(MafwGstRendererState *self, GError **error) +{ + MafwGstRenderer *renderer = NULL; + MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK; + + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self)); + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + value = mafw_gst_renderer_move(renderer, + MAFW_GST_RENDERER_MOVE_TYPE_PREV, + 0, error); + + switch (value) { + case MAFW_GST_RENDERER_MOVE_RESULT_ERROR: + case MAFW_GST_RENDERER_MOVE_RESULT_OK: + break; + case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST: + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "There is no playlist or media to play"); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT: + + mafw_playlist_iterator_move_to_last(renderer->iterator, NULL); + mafw_gst_renderer_set_media_playlist(renderer); + break; + default: + g_critical("Movement not controlled"); + } +} + +static void _do_goto_index(MafwGstRendererState *self, guint index, + GError **error) +{ + MafwGstRenderer *renderer = NULL; + MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self)); + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + value = mafw_gst_renderer_move(renderer, + MAFW_GST_RENDERER_MOVE_TYPE_INDEX, + index, error); + + switch (value) { + case MAFW_GST_RENDERER_MOVE_RESULT_ERROR: + case MAFW_GST_RENDERER_MOVE_RESULT_OK: + break; + case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST: + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "There is no playlist or media to play"); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT: + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_INDEX_OUT_OF_BOUNDS, + "Index is out of bounds"); + break; + default: + g_critical("Movement not controlled"); + } +} + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error) +{ + g_debug("running _notify_metadata..."); + /* This happens because we issued a play() command, this moved us to + Transitioning state, waiting for the URL of the objectid to play, + but before we got the URL and moved to Playing state, a stop() + command was issued. Now we got the results of the stopped play() + command, so we just ignore the result and stay in Stopped state. */ + +} + +/*---------------------------------------------------------------------------- + Playlist editing signals + ----------------------------------------------------------------------------*/ + +static void _playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self)); + + /* Do nothing, we just stay in Stopped state in any case */ +} + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +GValue* _get_property_value(MafwGstRendererState *self, const gchar *name) +{ + GValue *value = NULL; + + g_return_val_if_fail(MAFW_IS_GST_RENDERER_STATE_STOPPED(self), value); + + if (!g_strcmp0(name, MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)) { + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, ""); + } + + return value; +} diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h b/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h new file mode 100644 index 0000000..106ff33 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h @@ -0,0 +1,76 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MAFW_GST_RENDERER_STATE_STOPPED_H +#define MAFW_GST_RENDERER_STATE_STOPPED_H + + +#include "mafw-gst-renderer.h" +#include "mafw-gst-renderer-state.h" + +G_BEGIN_DECLS + +/*---------------------------------------------------------------------------- + GObject type conversion macros + ----------------------------------------------------------------------------*/ + +#define MAFW_TYPE_GST_RENDERER_STATE_STOPPED \ + (mafw_gst_renderer_state_stopped_get_type()) +#define MAFW_GST_RENDERER_STATE_STOPPED(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER_STATE_STOPPED, \ + MafwGstRendererStateStopped)) +#define MAFW_IS_GST_RENDERER_STATE_STOPPED(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER_STATE_STOPPED)) +#define MAFW_GST_RENDERER_STATE_STOPPED_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER_STATE_STOPPED, \ + MafwGstRendererStateStopped)) +#define MAFW_GST_RENDERER_STATE_STOPPED_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER_STATE_STOPPED, \ + MafwGstRendererStateStoppedClass)) +#define MAFW_IS_GST_RENDERER_STATE_STOPPED_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER_STATE_STOPPED)) + +/*---------------------------------------------------------------------------- + Type definitions + ----------------------------------------------------------------------------*/ + + +typedef struct _MafwGstRendererStateStopped MafwGstRendererStateStopped; +typedef struct _MafwGstRendererStateStoppedClass MafwGstRendererStateStoppedClass; + +struct _MafwGstRendererStateStoppedClass { + MafwGstRendererStateClass parent_class; +}; + +struct _MafwGstRendererStateStopped { + MafwGstRendererState parent; +}; + +GType mafw_gst_renderer_state_stopped_get_type(void); + +GObject *mafw_gst_renderer_state_stopped_new(MafwGstRenderer *renderer); + +G_END_DECLS + +#endif diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c b/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c new file mode 100644 index 0000000..fdf8a17 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c @@ -0,0 +1,414 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include "mafw-gst-renderer-state-transitioning.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-state-transitioning" + +#define UPDATE_DELAY 10 + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _do_play(MafwGstRendererState *self, GError **error); +static void _do_play_object(MafwGstRendererState *self, const gchar *object_id, + GError **error); +static void _do_pause(MafwGstRendererState *self, GError **error); +static void _do_stop(MafwGstRendererState *self, GError **error); +static void _do_resume(MafwGstRendererState *self, GError **error); +static void _do_get_position(MafwGstRendererState *self, gint *seconds, + GError **error); + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void _do_next(MafwGstRendererState *self, GError **error); +static void _do_previous(MafwGstRendererState *self,GError **error); +static void _do_goto_index(MafwGstRendererState *self, guint index, + GError **error); + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error); + +/*---------------------------------------------------------------------------- + Notification worker + ----------------------------------------------------------------------------*/ + +static void _notify_play(MafwGstRendererState *self, GError **error); +static void _notify_pause(MafwGstRendererState *self,GError **error); + +static void _notify_buffer_status(MafwGstRendererState *self, + gdouble percent, + GError **error); + +/*---------------------------------------------------------------------------- + Playlist editing signals + ----------------------------------------------------------------------------*/ + +static void _playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error); + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +static GValue* _get_property_value(MafwGstRendererState *self, + const gchar *name); + +/*---------------------------------------------------------------------------- + GObject initialization + ----------------------------------------------------------------------------*/ + +G_DEFINE_TYPE(MafwGstRendererStateTransitioning, + mafw_gst_renderer_state_transitioning, + MAFW_TYPE_GST_RENDERER_STATE); + +static void mafw_gst_renderer_state_transitioning_init( + MafwGstRendererStateTransitioning *self) +{ +} + +static void mafw_gst_renderer_state_transitioning_class_init( + MafwGstRendererStateTransitioningClass *klass) +{ + MafwGstRendererStateClass *state_klass ; + + state_klass = MAFW_GST_RENDERER_STATE_CLASS(klass); + g_return_if_fail(state_klass != NULL); + + state_klass->name = g_strdup("Transitioning"); + + /* Playback */ + + state_klass->play = _do_play; + state_klass->play_object = _do_play_object; + state_klass->stop = _do_stop; + state_klass->pause = _do_pause; + state_klass->resume = _do_resume; + state_klass->get_position = _do_get_position; + + /* Playlist */ + + state_klass->next = _do_next; + state_klass->previous = _do_previous; + state_klass->goto_index = _do_goto_index; + + /* Metadata */ + + state_klass->notify_metadata = _notify_metadata; + + /* Notification worker */ + + state_klass->notify_play = _notify_play; + state_klass->notify_pause = _notify_pause; + state_klass->notify_buffer_status = _notify_buffer_status; + + /* Playlist editing signals */ + + state_klass->playlist_contents_changed = + _playlist_contents_changed; + + /* Property methods */ + + state_klass->get_property_value = _get_property_value; +} + +GObject *mafw_gst_renderer_state_transitioning_new(MafwGstRenderer *renderer) +{ + MafwGstRendererState *state; + + state = MAFW_GST_RENDERER_STATE( + g_object_new(MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING, NULL)); + state->renderer = renderer; + + return G_OBJECT(state); +} + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _do_play(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + mafw_gst_renderer_state_do_play(self, error); +} + +static void _do_play_object(MafwGstRendererState *self, const gchar *object_id, + GError **error) +{ + MafwGstRendererPlaybackMode cur_mode, prev_mode; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + + prev_mode = mafw_gst_renderer_get_playback_mode(self->renderer); + mafw_gst_renderer_state_do_play_object(self, object_id, error); + cur_mode = mafw_gst_renderer_get_playback_mode(self->renderer); + + /* If this happens it means that we interrupted playlist playback + so let's resume it when play_object is finished */ + if (cur_mode != prev_mode) { + self->renderer->resume_playlist = TRUE; + } +} + +static void _do_stop(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + + /* Stop playback */ + mafw_gst_renderer_state_do_stop(self, error); +} + +static void _do_pause(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + g_debug("Got pause while transitioning"); + self->renderer->worker->stay_paused = TRUE; +} + +static void _do_resume(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + if (self->renderer->worker->stay_paused) { + g_debug("Got resume while transitioning/paused"); + self->renderer->worker->stay_paused = FALSE; + } else { + g_set_error(error, MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_CANNOT_PLAY, + "cannot resume in transitioning state without " + "having paused before"); + } +} + +static void _do_get_position(MafwGstRendererState *self, gint *seconds, + GError **error) +{ + *seconds = 0; +} + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void _do_next(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + mafw_gst_renderer_state_do_next(self, error); +} + +static void _do_previous(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + mafw_gst_renderer_state_do_prev(self, error); +} + +static void _do_goto_index(MafwGstRendererState *self, guint index, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + mafw_gst_renderer_state_do_goto_index(self, index, error); +} + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + + MafwGstRenderer *renderer; + GValue *mval; + gpointer value; + gint nuris, i; + gchar **uris; + gchar *uri; + + g_debug("running _notify_metadata..."); + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* If we have received metadata for the item that we are playing + then play it */ + if (object_id && renderer->media->object_id && + !strcmp(object_id, renderer->media->object_id)) { + /* Check how many uris provide the object_id */ + value = g_hash_table_lookup(metadata, MAFW_METADATA_KEY_URI); + nuris = mafw_metadata_nvalues(value); + if (nuris == 1) { + mval = mafw_metadata_first(metadata, + MAFW_METADATA_KEY_URI); + g_assert(mval); + g_free(renderer->media->uri); + renderer->media->uri = + g_strdup(g_value_get_string(mval)); + uri = renderer->media->uri; + } else if (nuris > 1) { + uris = g_new0(gchar *, nuris + 1); + for (i = 0; i < nuris; i++) { + mval = g_value_array_get_nth(value, i); + uris[i] = (gchar *) g_value_get_string(mval); + } + + /* Try the first URI, if that fails to play back another + * one will be selected until we get a successful one or + * all failed. On success, the selected URI will be + * emitted as metadata */ + g_free(renderer->media->uri); + renderer->media->uri = g_strdup(uris[0]); + } else { + g_assert_not_reached(); + } + + /* Set seekability property; currently, if several uris are + * provided it uses the value of the first uri. If later another + * uri is actually played, then this value should be changed. */ + mval = mafw_metadata_first(metadata, + MAFW_METADATA_KEY_IS_SEEKABLE); + if (mval != NULL) { + renderer->media->seekability = + g_value_get_boolean(mval) ? + SEEKABILITY_SEEKABLE : SEEKABILITY_NO_SEEKABLE; + g_debug("_notify_metadata: source seekability %d", + renderer->media->seekability); + } else { + renderer->media->seekability = SEEKABILITY_UNKNOWN; + g_debug("_notify_metadata: source seekability unknown"); + } + + /* Check for source duration to keep it updated if needed */ + mval = mafw_metadata_first(metadata, + MAFW_METADATA_KEY_DURATION); + + if (mval != NULL) { + renderer->media->duration = g_value_get_int(mval); + g_debug("_notify_metadata: source duration %d", + renderer->media->duration); + } else { + renderer->media->duration = -1; + g_debug("_notify_metadata: source duration unknown"); + } + + /* Play the available uri(s) */ + if (nuris == 1) { + mafw_gst_renderer_worker_play(renderer->worker, uri); + } else { + mafw_gst_renderer_worker_play_alternatives( + renderer->worker, uris); + g_free(uris); + } + } +} + +/*---------------------------------------------------------------------------- + Notification worker + ----------------------------------------------------------------------------*/ + +static void _notify_play(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + + MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + if (renderer->media->object_id) + { + renderer->update_playcount_id = g_timeout_add_seconds( + UPDATE_DELAY, + mafw_gst_renderer_update_stats, + renderer); + } + + mafw_gst_renderer_set_state(renderer, Playing); +} + +static void _notify_pause(MafwGstRendererState *self, GError **error) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + + MafwGstRenderer *renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + self->renderer->worker->stay_paused = FALSE; + mafw_gst_renderer_set_state(renderer, Paused); +} + +static void _notify_buffer_status(MafwGstRendererState *self, gdouble percent, + GError **error) +{ + mafw_gst_renderer_state_do_notify_buffer_status (self, percent, error); +} + +/*---------------------------------------------------------------------------- + Playlist editing signals + ----------------------------------------------------------------------------*/ + +static void _playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error) +{ + MafwGstRendererPlaybackMode mode; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self)); + + /* Play the new index only if we are not in standalone mode. + Otherwise, when play_object finishes the new item will be + played if that's been suggested with renderer->resume_playlist */ + mode = mafw_gst_renderer_get_playback_mode(self->renderer); + if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST) { + mafw_gst_renderer_state_do_play(self, error); + } +} + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +GValue* _get_property_value(MafwGstRendererState *self, const gchar *name) +{ + GValue *value = NULL; + + g_return_val_if_fail(MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(self), + value); + + if (!g_strcmp0(name, MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)) { + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, ""); + } + + return value; +} diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h b/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h new file mode 100644 index 0000000..4530a4f --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h @@ -0,0 +1,82 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MAFW_GST_RENDERER_STATE_TRANSITIONING_H +#define MAFW_GST_RENDERER_STATE_TRANSITIONING_H + + +#include "mafw-gst-renderer.h" +#include "mafw-gst-renderer-state.h" +#include "mafw-gst-renderer-utils.h" + +G_BEGIN_DECLS + +/*---------------------------------------------------------------------------- + GObject type conversion macros + ----------------------------------------------------------------------------*/ + +#define MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING \ + (mafw_gst_renderer_state_transitioning_get_type()) +#define MAFW_GST_RENDERER_STATE_(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING, \ + MafwGstRendererStateTransitioning)) +#define MAFW_IS_GST_RENDERER_STATE_TRANSITIONING(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING)) +#define MAFW_GST_RENDERER_STATE_TRANSITIONING_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING, \ + MafwGstRendererStateTransitioning)) +#define MAFW_GST_RENDERER_STATE_TRANSITIONING_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), \ + MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING, \ + MafwGstRendererStateTransitioningClass)) +#define MAFW_IS_GST_RENDERER_STATE_TRANSITIONING_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), \ + MAFW_TYPE_GST_RENDERER_STATE_TRANSITIONING)) + +/*---------------------------------------------------------------------------- + Type definitions + ----------------------------------------------------------------------------*/ + + +typedef struct _MafwGstRendererStateTransitioning MafwGstRendererStateTransitioning; +typedef struct _MafwGstRendererStateTransitioningClass MafwGstRendererStateTransitioningClass; + +struct _MafwGstRendererStateTransitioningClass { + MafwGstRendererStateClass parent_class; +}; + +struct _MafwGstRendererStateTransitioning { + MafwGstRendererState parent; +}; + +GType mafw_gst_renderer_state_transitioning_get_type(void); + +GObject *mafw_gst_renderer_state_transitioning_new(MafwGstRenderer *renderer); + +G_END_DECLS + +#endif diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state.c b/libmafw-gst-renderer/mafw-gst-renderer-state.c new file mode 100644 index 0000000..274fcec --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state.c @@ -0,0 +1,825 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include + +#include "mafw-gst-renderer.h" +#include "mafw-gst-renderer-state.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-state" + +/*---------------------------------------------------------------------------- + Default playback implementations + ----------------------------------------------------------------------------*/ + +static void _default_play(MafwGstRendererState *self, GError **error) +{ + g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PLAY, + "Play: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + + +static void _default_play_object(MafwGstRendererState *self, + const gchar *objectid, + GError **error) +{ + g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PLAY, + "Play object: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_stop(MafwGstRendererState *self, GError **error) +{ + g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_STOP, + "Stop: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_pause(MafwGstRendererState *self, GError **error) +{ + g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PAUSE, + "Pause: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_resume(MafwGstRendererState *self, GError **error) +{ + g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PLAY, + "Resume: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_set_position (MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error) +{ + g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_PLAY, + "Set position: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_get_position (MafwGstRendererState *self, + gint *seconds, + GError **error) +{ + g_set_error(error, MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_CANNOT_GET_POSITION, + "Get position: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +/*---------------------------------------------------------------------------- + Default playlist implementations + ----------------------------------------------------------------------------*/ + +static void _default_next(MafwGstRendererState *self, GError **error) +{ + g_set_error(error, MAFW_EXTENSION_ERROR, MAFW_EXTENSION_ERROR_FAILED, + "Next: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_previous(MafwGstRendererState *self, GError **error) +{ + g_set_error(error, MAFW_EXTENSION_ERROR, MAFW_EXTENSION_ERROR_FAILED, + "Previous: Operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_goto_index(MafwGstRendererState *self, guint index, + GError **error) +{ + g_set_error(error, MAFW_EXTENSION_ERROR, MAFW_EXTENSION_ERROR_FAILED, + "Goto index: operation not allowed in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +/*---------------------------------------------------------------------------- + Default notify metadata implementation + ----------------------------------------------------------------------------*/ + +static void _default_notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error) +{ + + g_critical("Notify metadata: got unexpected metadata in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +/*---------------------------------------------------------------------------- + Default notify worker implementations + ----------------------------------------------------------------------------*/ + +static void _default_notify_play(MafwGstRendererState *self, GError **error) +{ + g_critical("Notify play: unexpected Play notification received in %s " + "state", MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_notify_pause(MafwGstRendererState *self, GError **error) +{ + + g_critical("Notify pause: unexpected Pause notification received %s " + "state", MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_notify_seek(MafwGstRendererState *self, GError **error) +{ + g_critical("Notify seek: incorrect operation in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +static void _default_notify_buffer_status(MafwGstRendererState *self, + gdouble percent, + GError **error) +{ + g_critical("Notify buffer status: incorrect operation in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + + +static void _default_notify_eos(MafwGstRendererState *self, GError **error) +{ + g_critical("Notify eos: incorrect operation in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +/*---------------------------------------------------------------------------- + Default playlist editing signal handlers implementation + ----------------------------------------------------------------------------*/ + +static void _default_playlist_contents_changed(MafwGstRendererState *self, + gboolean clip_changed, + GError **error) +{ + g_warning("playlist::contents-changed not implemented in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +/*---------------------------------------------------------------------------- + Default property methods implementation + ----------------------------------------------------------------------------*/ + +static GValue* _default_get_property_value(MafwGstRendererState *self, + const gchar *name) +{ + g_warning("get_property_value function not implemented in %s state", + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); + return NULL; +} + +/*---------------------------------------------------------------------------- + Default memory card event handlers implementation + ----------------------------------------------------------------------------*/ + +static void _default_handle_pre_unmount(MafwGstRendererState *self, + const gchar *mount_point) +{ + g_debug("pre-unmount signal received: %s in state %s", mount_point, + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->name); +} + +/*---------------------------------------------------------------------------- + GObject initialization + ----------------------------------------------------------------------------*/ + +G_DEFINE_ABSTRACT_TYPE(MafwGstRendererState, mafw_gst_renderer_state, + G_TYPE_OBJECT); + +static void mafw_gst_renderer_state_init(MafwGstRendererState *self) +{ +} + +static void mafw_gst_renderer_state_class_init(MafwGstRendererStateClass *klass) +{ + /* Playback */ + + klass->play = _default_play; + klass->play_object = _default_play_object; + klass->stop = _default_stop; + klass->pause = _default_pause; + klass->resume = _default_resume; + klass->set_position = _default_set_position; + klass->get_position = _default_get_position; + + /* Playlist */ + + klass->next = _default_next; + klass->previous = _default_previous; + klass->goto_index = _default_goto_index; + + /* Notification metadata */ + + klass->notify_metadata = _default_notify_metadata; + + /* Notification worker */ + + klass->notify_play = _default_notify_play; + klass->notify_pause = _default_notify_pause; + klass->notify_seek = _default_notify_seek; + klass->notify_buffer_status = _default_notify_buffer_status; + klass->notify_eos = _default_notify_eos; + + klass->notify_eos = _default_notify_eos; + + /* Playlist editing signals */ + + klass->playlist_contents_changed = + _default_playlist_contents_changed; + + /* Property methods */ + + klass->get_property_value = _default_get_property_value; + + /* Memory card event handlers */ + + klass->handle_pre_unmount = _default_handle_pre_unmount; +} + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_play(MafwGstRendererState *self, GError **error) + +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->play(self, error); +} + +void mafw_gst_renderer_state_play_object(MafwGstRendererState *self, + const gchar *object_id, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->play_object(self, object_id, + error); +} + +void mafw_gst_renderer_state_stop(MafwGstRendererState *self, GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->stop(self, error); +} + +void mafw_gst_renderer_state_pause(MafwGstRendererState *self, GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->pause(self, error); +} + +void mafw_gst_renderer_state_resume(MafwGstRendererState *self, GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->resume(self, error); +} + +void mafw_gst_renderer_state_set_position(MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->set_position(self, mode, seconds, + error); +} + +void mafw_gst_renderer_state_get_position(MafwGstRendererState *self, + gint *seconds, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->get_position(self, seconds, + error); +} + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_next(MafwGstRendererState *self, GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->next(self, error); +} + +void mafw_gst_renderer_state_previous(MafwGstRendererState *self, GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->previous(self, error); +} + +void mafw_gst_renderer_state_goto_index(MafwGstRendererState *self, guint index, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->goto_index(self, index, error); + +} + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_metadata(self, object_id, + metadata, + error); +} + +/*---------------------------------------------------------------------------- + Notification worker + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_notify_play(MafwGstRendererState *self, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_play(self, error); +} + +void mafw_gst_renderer_state_notify_pause(MafwGstRendererState *self, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_pause(self, error); +} + +void mafw_gst_renderer_state_notify_seek(MafwGstRendererState *self, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_seek(self, error); +} + +void mafw_gst_renderer_state_notify_buffer_status(MafwGstRendererState *self, + gdouble percent, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_buffer_status(self, + percent, + error); +} + +void mafw_gst_renderer_state_notify_eos(MafwGstRendererState *self, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->notify_eos(self, error); +} + +/*---------------------------------------------------------------------------- + Playlist editing handlers + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_playlist_contents_changed_handler( + MafwGstRendererState *self, + gboolean clip_changed, + GError **error) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)->playlist_contents_changed( + self, + clip_changed, + error); +} + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +GValue* mafw_gst_renderer_state_get_property_value(MafwGstRendererState *self, + const gchar *name) +{ + return MAFW_GST_RENDERER_STATE_GET_CLASS(self)->get_property_value( + self, + name); +} + +/*---------------------------------------------------------------------------- + Memory card event handlers + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_handle_pre_unmount(MafwGstRendererState *self, + const gchar *mount_point) +{ + MAFW_GST_RENDERER_STATE_GET_CLASS(self)-> + handle_pre_unmount(self, mount_point); +} + +/*---------------------------------------------------------------------------- + Helpers + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_do_play(MafwGstRendererState *self, GError **error) +{ + MafwGstRenderer *renderer; + GError *gm_error = NULL; + MafwGstRendererPlaybackMode mode; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* Stop any on going playback */ + mafw_gst_renderer_worker_stop(renderer->worker); + + /* Play command only affects playlists, so switch to playlist + mode first if necessary */ + mode = mafw_gst_renderer_get_playback_mode(renderer); + if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) { + mafw_gst_renderer_set_playback_mode( + renderer, MAFW_GST_RENDERER_MODE_PLAYLIST); + mafw_gst_renderer_set_media_playlist(renderer); + } + + /* Do we have any objectid to play? Otherwise we cannot do it */ + if (renderer->media->object_id) { + /* If so, resolve URI for this objectid */ + mafw_gst_renderer_get_metadata(renderer, + renderer->media->object_id, + &gm_error); + if (gm_error) { + MafwGstRendererErrorClosure *error_closure; + if (error) { + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "Unable to find media"); + } + + /* This is a playback error: execute error policy */ + error_closure = g_new0(MafwGstRendererErrorClosure, 1); + error_closure->renderer = renderer; + error_closure->error = g_error_copy(gm_error); + g_idle_add(mafw_gst_renderer_manage_error_idle, + error_closure); + + g_error_free(gm_error); + } else { + mafw_gst_renderer_set_state(renderer, Transitioning); + } + } else if (error) { + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "There is no media to play"); + mafw_gst_renderer_set_state(renderer, Stopped); + } +} + +void mafw_gst_renderer_state_do_play_object(MafwGstRendererState *self, + const gchar *object_id, + GError **error) +{ + MafwGstRenderer *renderer; + GError *gm_error = NULL; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* Stop any ongoing playback */ + mafw_gst_renderer_worker_stop(renderer->worker); + + if (object_id) { + /* Switch to standalone mode */ + mafw_gst_renderer_set_playback_mode( + renderer, MAFW_GST_RENDERER_MODE_STANDALONE); + + mafw_gst_renderer_set_object(renderer, object_id); + mafw_gst_renderer_get_metadata(renderer, + renderer->media->object_id, + &gm_error); + if (gm_error) { + MafwGstRendererErrorClosure *error_closure; + if (error) { + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "Unable to find media"); + } + + /* This is a playback error: execute error policy */ + error_closure = g_new0(MafwGstRendererErrorClosure, 1); + error_closure->renderer = renderer; + error_closure->error = g_error_copy(gm_error); + g_idle_add(mafw_gst_renderer_manage_error_idle, + error_closure); + g_error_free(gm_error); + } else { + /* Play object has been successful */ + mafw_gst_renderer_set_state(renderer, Transitioning); + } + } else if (error) { + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "There is no media to play"); + mafw_gst_renderer_set_state(renderer, Stopped); + } +} + +void mafw_gst_renderer_state_do_stop(MafwGstRendererState *self, GError **error) +{ + MafwGstRenderer *renderer; + MafwGstRendererPlaybackMode mode; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* Stop any ongoing playback */ + mafw_gst_renderer_worker_stop(renderer->worker); + + /* Cancel update */ + if (renderer->update_playcount_id > 0) { + g_source_remove(renderer->update_playcount_id); + renderer->update_playcount_id = 0; + } + + /* Set new state */ + mafw_gst_renderer_set_state(renderer, Stopped); + + /* If we were playing a standalone object, then go back + to playlist mode and stay stopped */ + mode = mafw_gst_renderer_get_playback_mode(renderer); + if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) { + mafw_gst_renderer_set_playback_mode( + renderer, MAFW_GST_RENDERER_MODE_PLAYLIST); + mafw_gst_renderer_set_media_playlist(renderer); + } +} + +void mafw_gst_renderer_state_do_next (MafwGstRendererState *self, GError **error) +{ + MafwGstRenderer *renderer; + MafwGstRendererMovementResult move_type; + MafwGstRendererPlaybackMode mode; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* If we are in standalone mode, we switch back to playlist + * mode. Then we resume playback only if renderer->resume_playlist + * was set. + * If we are in playlist mode we just move to the next and + * play. + */ + mode = mafw_gst_renderer_get_playback_mode(renderer); + if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) { + mafw_gst_renderer_set_playback_mode( + renderer, MAFW_GST_RENDERER_MODE_PLAYLIST); + mafw_gst_renderer_set_media_playlist(renderer); + } + + move_type = mafw_gst_renderer_move(renderer, + MAFW_GST_RENDERER_MOVE_TYPE_NEXT, + 0, error); + switch (move_type) { + case MAFW_GST_RENDERER_MOVE_RESULT_OK: + if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST || + renderer->resume_playlist) { + /* We issued the comand in playlist mode, or + in standalone mode but with resume_playlist + set, so let's play the new item */ + mafw_gst_renderer_state_play(self, error); + + } else { + /* We issued the command in standalone mode and we + do not want to resume playlist, so let's + move to Stopped */ + mafw_gst_renderer_state_stop(self, NULL); + } + break; + case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST: + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "There is no playlist or media to play"); + mafw_gst_renderer_state_stop(self, NULL); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT: + /* Normal mode */ + mafw_playlist_iterator_reset(renderer->iterator, NULL); + mafw_gst_renderer_set_media_playlist(renderer); + mafw_gst_renderer_state_play(self, error); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_ERROR: + break; + default: + g_critical("Movement not controlled"); + } +} + +void mafw_gst_renderer_state_do_prev(MafwGstRendererState *self, GError **error) +{ + MafwGstRenderer *renderer; + MafwGstRendererMovementResult move_type; + MafwGstRendererPlaybackMode mode; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + mode = mafw_gst_renderer_get_playback_mode(renderer); + if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) { + mafw_gst_renderer_set_playback_mode( + renderer, MAFW_GST_RENDERER_MODE_PLAYLIST); + mafw_gst_renderer_set_media_playlist(renderer); + } + + move_type = mafw_gst_renderer_move(renderer, + MAFW_GST_RENDERER_MOVE_TYPE_PREV, + 0, error); + switch (move_type) { + case MAFW_GST_RENDERER_MOVE_RESULT_OK: + if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST || + renderer->resume_playlist) { + /* We issued the comand in playlist mode, or + in standalone mode but with resume_playlist + set, so let's play the new item */ + mafw_gst_renderer_state_play(self, error); + + } else { + /* We issued the command in standalone mode and we + do not want to resume playlist, so let's + move to Stopped */ + mafw_gst_renderer_state_stop(self, NULL); + } + break; + case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST: + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "There is no playlist or media to play"); + mafw_gst_renderer_state_stop(self, NULL); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT: + /* Normal mode */ + mafw_playlist_iterator_move_to_last(renderer->iterator, NULL); + mafw_gst_renderer_set_media_playlist(renderer); + mafw_gst_renderer_state_play(self, error); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_ERROR: + break; + default: + g_critical("Movement not controlled"); + } +} + + +void mafw_gst_renderer_state_do_goto_index(MafwGstRendererState *self, + guint index, + GError **error) +{ + MafwGstRenderer *renderer; + MafwGstRendererMovementResult move_type; + MafwGstRendererPlaybackMode mode; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* If we are in standalone mode, we switch back to playlist + * mode. Then we resume playback only if renderer->resume_playlist + * was set. + * If we are in playlist mode we just move to the next and + * play. + */ + mode = mafw_gst_renderer_get_playback_mode(renderer); + if (mode == MAFW_GST_RENDERER_MODE_STANDALONE) { + mafw_gst_renderer_set_playback_mode( + renderer, MAFW_GST_RENDERER_MODE_PLAYLIST); + mafw_gst_renderer_set_media_playlist(renderer); + } + + move_type = mafw_gst_renderer_move(renderer, MAFW_GST_RENDERER_MOVE_TYPE_INDEX, index, error); + + switch (move_type) { + case MAFW_GST_RENDERER_MOVE_RESULT_OK: + if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST || + renderer->resume_playlist) { + /* We issued the comand in playlist mode, or + in standalone mode but with resume_playlist + set, so let's play the new item */ + mafw_gst_renderer_state_play(self, error); + + } else { + /* We issued the command in standalone mode and we + do not want to resume playlist, so let's + move to Stopped */ + mafw_gst_renderer_state_stop(self, NULL); + } + break; + case MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST: + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_NO_MEDIA, + "There is no playlist or media to play"); + mafw_gst_renderer_state_stop(self, NULL); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT: + g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_INDEX_OUT_OF_BOUNDS, + "Index is out of bounds"); + mafw_gst_renderer_state_stop(self, NULL); + break; + case MAFW_GST_RENDERER_MOVE_RESULT_ERROR: + break; + default: + g_critical("Movement not controlled"); + } +} + +void mafw_gst_renderer_state_do_get_position(MafwGstRendererState *self, + gint *seconds, + GError **error) +{ + *seconds = mafw_gst_renderer_worker_get_position(self->renderer->worker); + if (*seconds < 0) { + *seconds = 0; + g_set_error(error, MAFW_EXTENSION_ERROR, + MAFW_RENDERER_ERROR_CANNOT_GET_POSITION, + "Position query failed"); + } +} + +void mafw_gst_renderer_state_do_set_position(MafwGstRendererState *self, + MafwRendererSeekMode mode, + gint seconds, + GError **error) +{ + MafwGstRenderer *renderer; + GstSeekType seektype; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + /* TODO Gst stuff should be moved to worker, not handled here... */ + if (mode == SeekAbsolute) { + if (seconds < 0) { + seektype = GST_SEEK_TYPE_END; + seconds *= -1; + } else { + seektype = GST_SEEK_TYPE_SET; + } + } else if (mode == SeekRelative) { + seektype = GST_SEEK_TYPE_CUR; + } else { + g_critical("Unknown seek mode: %d", mode); + g_set_error(error, MAFW_EXTENSION_ERROR, + MAFW_EXTENSION_ERROR_INVALID_PARAMS, + "Unknown seek mode: %d", mode); + return; + } + if (renderer->seek_pending) { + g_debug("seek pending, storing position %d", seconds); + renderer->seek_type_pending = seektype; + renderer->seeking_to = seconds; + } else { + renderer->seek_pending = TRUE; + mafw_gst_renderer_worker_set_position(renderer->worker, + seektype, + seconds, + error); + } +} + +void mafw_gst_renderer_state_do_notify_seek(MafwGstRendererState *self, + GError **error) +{ + MafwGstRenderer *renderer; + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + if (renderer->seeking_to != -1) { + renderer->seek_pending = TRUE; + mafw_gst_renderer_worker_set_position(renderer->worker, + renderer->seek_type_pending, + renderer->seeking_to, + NULL); + } else { + renderer->seek_pending = FALSE; + } + renderer->seeking_to = -1; +} + +void mafw_gst_renderer_state_do_notify_buffer_status(MafwGstRendererState *self, + gdouble percent, + GError **error) +{ + MafwGstRenderer *renderer = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER_STATE(self)); + + renderer = MAFW_GST_RENDERER_STATE(self)->renderer; + + mafw_renderer_emit_buffering_info(MAFW_RENDERER(renderer), percent / 100.0); +} diff --git a/libmafw-gst-renderer/mafw-gst-renderer-state.h b/libmafw-gst-renderer/mafw-gst-renderer-state.h new file mode 100644 index 0000000..5fd3325 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-state.h @@ -0,0 +1,239 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include "mafw-gst-renderer-worker.h" + +/* Solving the cyclic dependencies */ +typedef struct _MafwGstRendererState MafwGstRendererState; +typedef struct _MafwGstRendererStateClass MafwGstRendererStateClass; +#include "mafw-gst-renderer.h" + +G_BEGIN_DECLS + +/*---------------------------------------------------------------------------- + GObject type conversion macros + ----------------------------------------------------------------------------*/ + +#define MAFW_TYPE_GST_RENDERER_STATE \ + (mafw_gst_renderer_state_get_type()) +#define MAFW_GST_RENDERER_STATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER_STATE, \ + MafwGstRendererState)) +#define MAFW_IS_GST_RENDERER_STATE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER_STATE)) +#define MAFW_GST_RENDERER_STATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER_STATE, \ + MafwGstRendererStateClass)) +#define MAFW_GST_RENDERER_STATE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER_STATE, \ + MafwGstRendererStateClass)) +#define MAFW_IS_GST_RENDERER_STATE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER_STATE)) + +/*---------------------------------------------------------------------------- + Type definitions + ----------------------------------------------------------------------------*/ + + +struct _MafwGstRendererStateClass { + GObjectClass parent_class; + const gchar* name; + + /* Playback */ + + void (*play)(MafwGstRendererState *self, GError **error); + void (*play_object)(MafwGstRendererState *self, const gchar *object_id, + GError **error); + void (*stop)(MafwGstRendererState *self, GError **error); + void (*pause)(MafwGstRendererState *self, GError **error); + void (*resume)(MafwGstRendererState *self, GError **error); + void (*set_position) (MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error); + void (*get_position) (MafwGstRendererState *self, + gint *seconds, + GError **error); + + /* Playlist */ + + void (*next)(MafwGstRendererState *self, GError **error); + void (*previous)(MafwGstRendererState *self, GError **error); + void (*goto_index)(MafwGstRendererState *self, guint index, + GError **error); + + /* Notification metadata */ + + void (*notify_metadata)(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error); + + + /* Notifications */ + + void (*notify_play)(MafwGstRendererState *self, GError **error); + void (*notify_pause)(MafwGstRendererState *self, GError **error); + void (*notify_seek)(MafwGstRendererState *self, GError **error); + void (*notify_buffer_status)(MafwGstRendererState *self, gdouble percent, + GError **error); + void (*notify_eos) (MafwGstRendererState *self, GError **error); + + /* Playlist editing signals */ + + void (*playlist_contents_changed)(MafwGstRendererState *self, + gboolean clip_changed, + GError **error); + /* Property methods */ + + GValue* (*get_property_value)(MafwGstRendererState *self, + const gchar *name); + + /* Memory card event handlers */ + + void (*handle_pre_unmount)(MafwGstRendererState *self, + const gchar *mount_point); +}; + +struct _MafwGstRendererState { + GObject parent; + + MafwGstRenderer *renderer; +}; + +GType mafw_gst_renderer_state_get_type(void); + + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_play(MafwGstRendererState *self, GError **error); +void mafw_gst_renderer_state_play_object(MafwGstRendererState *self, + const gchar *object_id, + GError **error); +void mafw_gst_renderer_state_stop(MafwGstRendererState *self, GError **error); +void mafw_gst_renderer_state_pause(MafwGstRendererState *self, GError **error); +void mafw_gst_renderer_state_resume(MafwGstRendererState *self, GError **error); +void mafw_gst_renderer_state_set_position(MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error); +void mafw_gst_renderer_state_get_position(MafwGstRendererState *self, + gint *seconds, + GError **error); + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_next(MafwGstRendererState *self, GError **error); +void mafw_gst_renderer_state_previous(MafwGstRendererState *self, GError **error); +void mafw_gst_renderer_state_goto_index(MafwGstRendererState *self, guint index, + GError **error); + + +/*---------------------------------------------------------------------------- + Notification metatada + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_notify_metadata(MafwGstRendererState *self, + const gchar *object_id, + GHashTable *metadata, + GError **error); + +/*---------------------------------------------------------------------------- + Notification worker + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_notify_play(MafwGstRendererState *self, + GError **error); +void mafw_gst_renderer_state_notify_pause(MafwGstRendererState *self, + GError **error); +void mafw_gst_renderer_state_notify_seek(MafwGstRendererState *self, + GError **error); +void mafw_gst_renderer_state_notify_buffer_status(MafwGstRendererState *self, + gdouble percent, + GError **error); +void mafw_gst_renderer_state_notify_eos(MafwGstRendererState *self, + GError **error); + +/*---------------------------------------------------------------------------- + Playlist editing handlers + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_playlist_contents_changed_handler( + MafwGstRendererState *self, + gboolean clip_changed, + GError **error); + +/*---------------------------------------------------------------------------- + Property methods + ----------------------------------------------------------------------------*/ + +GValue* mafw_gst_renderer_state_get_property_value(MafwGstRendererState *self, + const gchar *name); + +/*---------------------------------------------------------------------------- + Memory card event handlers + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_handle_pre_unmount(MafwGstRendererState *self, + const gchar *mount_point); + +/*---------------------------------------------------------------------------- + Helpers + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_state_do_play(MafwGstRendererState *self, GError **error); +void mafw_gst_renderer_state_do_play_object(MafwGstRendererState *self, + const gchar *object_id, + GError **error); +void mafw_gst_renderer_state_do_stop(MafwGstRendererState *self, + GError **error); +void mafw_gst_renderer_state_do_next(MafwGstRendererState *self, + GError **error); +void mafw_gst_renderer_state_do_prev(MafwGstRendererState *self, + GError **error); +void mafw_gst_renderer_state_do_goto_index(MafwGstRendererState *self, + guint index, + GError **error); +void mafw_gst_renderer_state_do_set_position(MafwGstRendererState *self, + MafwRendererSeekMode mode, gint seconds, + GError **error); +void mafw_gst_renderer_state_do_get_position(MafwGstRendererState *self, + gint *seconds, + GError **error); +void mafw_gst_renderer_state_do_notify_seek(MafwGstRendererState *self, + GError **error); +void mafw_gst_renderer_state_do_notify_buffer_status(MafwGstRendererState *self, + gdouble percent, + GError **error); + +G_END_DECLS + +#endif diff --git a/libmafw-gst-renderer/mafw-gst-renderer-utils.c b/libmafw-gst-renderer/mafw-gst-renderer-utils.c new file mode 100644 index 0000000..0169862 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-utils.c @@ -0,0 +1,105 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 + +#include "mafw-gst-renderer-utils.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-utils" + +/** + * convert_utf8: + * @src: string. + * @dst: location for utf8 version of @src. + * + * Tries to convert @src into UTF-8, placing it into @dst. + * + * Returns: TRUE on success. + */ +gboolean convert_utf8(const gchar *src, gchar **dst) +{ + GError *error; + + if (!src) + return FALSE; + if (g_utf8_validate(src, -1, NULL)) { + *dst = g_strdup(src); + return TRUE; + } + error = NULL; + *dst = g_locale_to_utf8(src, -1, NULL, NULL, &error); + if (error) { + g_warning("utf8 conversion failed '%s' (%d: %s)", + src, error->code, error->message); + g_error_free(error); + return FALSE; + } + return TRUE; +} + +gboolean uri_is_playlist(const gchar *uri) { + /* TODO: Return if the uri is a playlist or not, using the mime type + instead of the file extension. */ + if ((g_str_has_suffix(uri, ".pls")) || + (g_str_has_suffix(uri, ".m3u")) || + (g_str_has_suffix(uri, ".smil")) || + (g_str_has_suffix(uri, ".smi")) || + (g_str_has_suffix(uri, ".wpl")) || + (g_str_has_suffix(uri, ".wax")) || + (g_str_has_suffix(uri, ".uni")) || + (g_str_has_suffix(uri, ".ram")) || +/* (g_str_has_suffix(uri, ".ra")) || */ + (g_str_has_suffix(uri, ".asx")) || + (g_str_has_suffix(uri, ".rpm"))) + { + return TRUE; + } + return FALSE; +} + +/** + * uri_is_stream: + * @uri: the URI to be checked. + * + * Check if given URI is a stream (not a local resource). To not depend on + * gnomevfs for this, we assume everything that doesn't start with "file://" is + * a stream. + * + * Returns: TRUE if the URI is not local. + */ +gboolean uri_is_stream(const gchar *uri) +{ + if (uri == NULL) { + return FALSE; + } else { + return !g_str_has_prefix(uri, "file://"); + } +} + +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/libmafw-gst-renderer/mafw-gst-renderer-utils.h b/libmafw-gst-renderer/mafw-gst-renderer-utils.h new file mode 100644 index 0000000..57bbe81 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-utils.h @@ -0,0 +1,34 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef MAFW_GST_RENDERER_UTILS_H +#define MAFW_GST_RENDERER_UTILS_H + +G_BEGIN_DECLS + +gboolean convert_utf8(const gchar *src, gchar **dst); +gboolean uri_is_playlist(const gchar *uri); +gboolean uri_is_stream(const gchar *uri); + +G_END_DECLS +#endif +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c b/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c new file mode 100644 index 0000000..4d17ea9 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c @@ -0,0 +1,702 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include +#include + +#include "mafw-gst-renderer-worker-volume.h" +#include "config.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-worker-volume" + +#define MAFW_GST_RENDERER_WORKER_VOLUME_SERVER NULL + +#define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PROPERTY "PULSE_PROP_media.role" +#define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX "sink-input-by-media-role:" +#define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE "x-maemo" + +#define MAFW_GST_RENDERER_WORKER_SET_TIMEOUT 200 + + +struct _MafwGstRendererWorkerVolume { + pa_glib_mainloop *mainloop; + pa_context *context; + gdouble pulse_volume; + gboolean pulse_mute; + MafwGstRendererWorkerVolumeChangedCb cb; + gpointer user_data; + MafwGstRendererWorkerVolumeMuteCb mute_cb; + gpointer mute_user_data; + gdouble current_volume; + gboolean current_mute; + gboolean pending_operation; + gdouble pending_operation_volume; + gboolean pending_operation_mute; + guint change_request_id; + pa_operation *pa_operation; +}; + +typedef struct { + MafwGstRendererWorkerVolume *wvolume; + MafwGstRendererWorkerVolumeInitCb cb; + gpointer user_data; +} InitCbClosure; + +#define _pa_volume_to_per_one(volume) \ + ((guint) ((((gdouble)(volume) / (gdouble) PA_VOLUME_NORM) + \ + (gdouble) 0.005) * (gdouble) 100.0) / (gdouble) 100.0) +#define _pa_volume_from_per_one(volume) \ + ((pa_volume_t)((gdouble)(volume) * (gdouble) PA_VOLUME_NORM)) + +#define _pa_operation_running(wvolume) \ + (wvolume->pa_operation != NULL && \ + pa_operation_get_state(wvolume->pa_operation) == PA_OPERATION_RUNNING) + +static void _state_cb_init(pa_context *c, void *data); + + +static gchar *_get_client_name(void) { + gchar buf[PATH_MAX]; + gchar *name = NULL; + + if (pa_get_binary_name(buf, sizeof(buf))) + name = g_strdup_printf("mafw-gst-renderer[%s]", buf); + else + name = g_strdup("mafw-gst-renderer"); + + return name; +} + +static void _ext_stream_restore_read_cb(pa_context *c, + const pa_ext_stream_restore2_info *i, + int eol, + void *userdata) +{ + MafwGstRendererWorkerVolume *wvolume = userdata; + gdouble volume; + gboolean mute; + + if (eol < 0) { + g_critical("eol parameter should not be < 1. " + "Discarding volume event"); + return; + } + + if (i == NULL || + strcmp(i->name, MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX + MAFW_GST_RENDERER_WORKER_VOLUME_ROLE) != 0) { + return; + } + + volume = _pa_volume_to_per_one(pa_cvolume_max(&i->volume)); + mute = i->mute != 0 ? TRUE : FALSE; + + if (_pa_operation_running(wvolume) || + (wvolume->pending_operation && + (wvolume->pending_operation_volume != volume || + wvolume->pending_operation_mute != mute))) { + g_debug("volume notification, but operation running, ignoring"); + return; + } + + wvolume->pulse_volume = volume; + wvolume->pulse_mute = mute; + + /* EMIT VOLUME */ + g_debug("ext stream volume is %lf (mute: %d) for role %s in device %s", + wvolume->pulse_volume, wvolume->pulse_mute, i->name, i->device); + if (!wvolume->pending_operation && + wvolume->pulse_volume != wvolume->current_volume) { + wvolume->current_volume = wvolume->pulse_volume; + if (wvolume->cb != NULL) { + g_debug("signalling volume"); + wvolume->cb(wvolume, wvolume->current_volume, + wvolume->user_data); + } + } + if (!wvolume->pending_operation && + wvolume->pulse_mute != wvolume->current_mute) { + wvolume->current_mute = wvolume->pulse_mute; + if (wvolume->mute_cb != NULL) { + g_debug("signalling mute"); + wvolume->mute_cb(wvolume, wvolume->current_mute, + wvolume->mute_user_data); + } + } + + wvolume->pending_operation = FALSE; +} + +static void _destroy_context(MafwGstRendererWorkerVolume *wvolume) +{ + if (wvolume->pa_operation != NULL) { + if (pa_operation_get_state(wvolume->pa_operation) == + PA_OPERATION_RUNNING) { + pa_operation_cancel(wvolume->pa_operation); + } + pa_operation_unref(wvolume->pa_operation); + wvolume->pa_operation = NULL; + } + pa_context_unref(wvolume->context); +} + +static InitCbClosure *_init_cb_closure_new(MafwGstRendererWorkerVolume *wvolume, + MafwGstRendererWorkerVolumeInitCb cb, + gpointer user_data) +{ + InitCbClosure *closure; + + closure = g_new(InitCbClosure, 1); + closure->wvolume = wvolume; + closure->cb = cb; + closure->user_data = user_data; + + return closure; +} + +static void _connect(gpointer user_data) +{ + gchar *name = NULL; + pa_mainloop_api *api = NULL; + InitCbClosure *closure = user_data; + MafwGstRendererWorkerVolume *wvolume = closure->wvolume; + + name = _get_client_name(); + + /* get the mainloop api and create a context */ + api = pa_glib_mainloop_get_api(wvolume->mainloop); + wvolume->context = pa_context_new(api, name); + g_assert(wvolume->context != NULL); + + /* register some essential callbacks */ + pa_context_set_state_callback(wvolume->context, _state_cb_init, + closure); + + g_debug("connecting to pulse"); + + g_assert(pa_context_connect(wvolume->context, + MAFW_GST_RENDERER_WORKER_VOLUME_SERVER, + PA_CONTEXT_NOAUTOSPAWN | PA_CONTEXT_NOFAIL, + NULL) >= 0); + g_free(name); +} + +static gboolean _reconnect(gpointer user_data) +{ + InitCbClosure *closure = user_data; + MafwGstRendererWorkerVolume *wvolume = closure->wvolume; + + g_warning("got disconnected from pulse, reconnecting"); + _destroy_context(wvolume); + _connect(user_data); + + return FALSE; +} + +static void +_state_cb(pa_context *c, void *data) +{ + MafwGstRendererWorkerVolume *wvolume = data; + pa_context_state_t state; + + state = pa_context_get_state(c); + + switch (state) { + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + { + InitCbClosure *closure; + + closure = _init_cb_closure_new(wvolume, NULL, NULL); + g_idle_add(_reconnect, closure); + break; + } + case PA_CONTEXT_READY: { + pa_operation *o; + + o = pa_ext_stream_restore2_read(c, _ext_stream_restore_read_cb, + wvolume); + g_assert(o != NULL); + pa_operation_unref(o); + + break; + } + default: + break; + } +} + +static void _ext_stream_restore_read_cb_init(pa_context *c, + const pa_ext_stream_restore2_info *i, + int eol, + void *userdata) +{ + InitCbClosure *closure = userdata; + + if (eol < 0) { + g_critical("eol parameter should not be < 1"); + } + + if (i == NULL || + strcmp(i->name, MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX + MAFW_GST_RENDERER_WORKER_VOLUME_ROLE) != 0) + return; + + closure->wvolume->pulse_volume = + _pa_volume_to_per_one(pa_cvolume_max(&i->volume)); + closure->wvolume->pulse_mute = i->mute != 0 ? TRUE : FALSE; + closure->wvolume->current_volume = closure->wvolume->pulse_volume; + closure->wvolume->current_mute = closure->wvolume->pulse_mute; + + /* NOT EMIT VOLUME, BUT DEBUG */ + g_debug("ext stream volume is %lf (mute: %d) for role %s in device %s", + closure->wvolume->pulse_volume, i->mute, i->name, i->device); + + if (closure->cb != NULL) { + g_debug("initialized: returning volume manager"); + closure->cb(closure->wvolume, closure->user_data); + } else { + if (closure->wvolume->cb != NULL) { + g_debug("signalling volume after reconnection"); + closure->wvolume->cb(closure->wvolume, + closure->wvolume->current_volume, + closure->wvolume->user_data); + } + if (closure->wvolume->mute_cb != NULL) { + g_debug("signalling mute after reconnection"); + closure->wvolume->mute_cb(closure->wvolume, + closure->wvolume-> + current_mute, + closure->wvolume-> + mute_user_data); + } + } + + pa_context_set_state_callback(closure->wvolume->context, _state_cb, + closure->wvolume); + + g_free(closure); +} + +static void _ext_stream_restore_subscribe_cb(pa_context *c, void *userdata) +{ + pa_operation *o; + + o = pa_ext_stream_restore2_read(c, _ext_stream_restore_read_cb, userdata); + g_assert(o != NULL); + pa_operation_unref(o); +} + +static void +_state_cb_init(pa_context *c, void *data) +{ + InitCbClosure *closure = data; + MafwGstRendererWorkerVolume *wvolume = closure->wvolume; + pa_context_state_t state; + + state = pa_context_get_state(c); + + g_debug("state: %d", state); + + switch (state) { + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + g_critical("Connection to pulse failed, reconnection in 1 " + "second"); + g_timeout_add_seconds(1, _reconnect, closure); + break; + case PA_CONTEXT_READY: { + pa_operation *o; + + g_debug("PA_CONTEXT_READY"); + + o = pa_ext_stream_restore2_read(c, + _ext_stream_restore_read_cb_init, + closure); + g_assert(o != NULL); + pa_operation_unref(o); + + pa_ext_stream_restore_set_subscribe_cb( + c, _ext_stream_restore_subscribe_cb, wvolume); + + o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL); + g_assert(o != NULL); + pa_operation_unref(o); + + break; + } + default: + break; + } +} + +static gboolean _destroy_idle(gpointer data) +{ + MafwGstRendererWorkerVolume *wvolume = data; + + g_debug("destroying"); + + _destroy_context(wvolume); + pa_glib_mainloop_free(wvolume->mainloop); + g_free(wvolume); + + return FALSE; +} + +static void +_state_cb_destroy(pa_context *c, void *data) +{ + pa_context_state_t state; + + state = pa_context_get_state(c); + + switch (state) { + case PA_CONTEXT_TERMINATED: + g_idle_add(_destroy_idle, data); + break; + case PA_CONTEXT_FAILED: + g_error("Unexpected problem in volume management"); + break; + default: + break; + } +} + +static void _success_cb(pa_context *c, int success, void *userdata) +{ + if (success == 0) { + g_critical("Setting volume to pulse operation failed"); + } +} + +static void _remove_set_timeout(MafwGstRendererWorkerVolume *wvolume) +{ + if (wvolume->change_request_id != 0) { + g_source_remove(wvolume->change_request_id); + } + wvolume->change_request_id = 0; +} + +static gboolean _set_timeout(gpointer data) +{ + pa_ext_stream_restore2_info info; + pa_ext_stream_restore2_info *infos[1]; + MafwGstRendererWorkerVolume *wvolume = data; + + if (wvolume->pending_operation) { + g_debug("setting volume ignored as there is still a pending " + "operation. Waiting till next iteration"); + } else if (wvolume->pulse_mute != wvolume->current_mute || + wvolume->pulse_volume != wvolume->current_volume) { + + info.name = MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX + MAFW_GST_RENDERER_WORKER_VOLUME_ROLE; + info.channel_map.channels = 1; + info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO; + info.device = NULL; + info.volume_is_absolute = TRUE; + infos[0] = &info; + + info.mute = wvolume->current_mute; + pa_cvolume_init(&info.volume); + pa_cvolume_set(&info.volume, info.channel_map.channels, + _pa_volume_from_per_one(wvolume-> + current_volume)); + + g_debug("setting volume to %lf and mute to %d", + wvolume->current_volume, wvolume->current_mute); + + if (wvolume->pa_operation != NULL) { + pa_operation_unref(wvolume->pa_operation); + } + + wvolume->pending_operation = TRUE; + wvolume->pending_operation_volume = wvolume->current_volume; + wvolume->pending_operation_mute = wvolume->current_mute; + + wvolume->pa_operation = pa_ext_stream_restore2_write( + wvolume->context, + PA_UPDATE_REPLACE, + (const pa_ext_stream_restore2_info* + const *)infos, + 1, TRUE, _success_cb, wvolume); + + if (wvolume->pa_operation == NULL) { + g_critical("NULL operation when writing volume to " + "pulse"); + _remove_set_timeout(wvolume); + } + } else { + g_debug("removing volume timeout"); + _remove_set_timeout(wvolume); + } + + return wvolume->change_request_id != 0; +} + +void mafw_gst_renderer_worker_volume_init(GMainContext *main_context, + MafwGstRendererWorkerVolumeInitCb cb, + gpointer user_data, + MafwGstRendererWorkerVolumeChangedCb + changed_cb, + gpointer changed_user_data, + MafwGstRendererWorkerVolumeMuteCb + mute_cb, gpointer mute_user_data) +{ + MafwGstRendererWorkerVolume *wvolume = NULL; + InitCbClosure *closure; + + g_return_if_fail(cb != NULL); + + g_assert(g_setenv(MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PROPERTY, + MAFW_GST_RENDERER_WORKER_VOLUME_ROLE, FALSE)); + + g_debug("initializing volume manager"); + + wvolume = g_new0(MafwGstRendererWorkerVolume, 1); + + wvolume->pulse_volume = 1.0; + wvolume->pulse_mute = FALSE; + wvolume->cb = changed_cb; + wvolume->user_data = changed_user_data; + wvolume->mute_cb = mute_cb; + wvolume->mute_user_data = mute_user_data; + + wvolume->mainloop = pa_glib_mainloop_new(main_context); + g_assert(wvolume->mainloop != NULL); + + closure = _init_cb_closure_new(wvolume, cb, user_data); + _connect(closure); +} + +void mafw_gst_renderer_worker_volume_set(MafwGstRendererWorkerVolume *wvolume, + gdouble volume, gboolean mute) +{ + gboolean signal_volume, signal_mute; + + g_return_if_fail(wvolume != NULL); + g_return_if_fail(pa_context_get_state(wvolume->context) == + PA_CONTEXT_READY); + +#ifndef MAFW_GST_RENDERER_ENABLE_MUTE + mute = FALSE; +#endif + + signal_volume = wvolume->current_volume != volume && + wvolume->cb != NULL; + signal_mute = wvolume->current_mute != mute && wvolume->mute_cb != NULL; + + wvolume->current_volume = volume; + wvolume->current_mute = mute; + + g_debug("volume set: %lf (mute %d)", volume, mute); + + if (signal_volume) { + g_debug("signalling volume"); + wvolume->cb(wvolume, volume, wvolume->user_data); + } + + if (signal_mute) { + g_debug("signalling mute"); + wvolume->mute_cb(wvolume, mute, wvolume->mute_user_data); + } + + if ((signal_mute || signal_volume) && wvolume->change_request_id == 0) { + wvolume->change_request_id = + g_timeout_add(MAFW_GST_RENDERER_WORKER_SET_TIMEOUT, + _set_timeout, wvolume); + + _set_timeout(wvolume); + } +} + +gdouble mafw_gst_renderer_worker_volume_get( + MafwGstRendererWorkerVolume *wvolume) +{ + g_return_val_if_fail(wvolume != NULL, 0.0); + + g_debug("getting volume; %lf", wvolume->current_volume); + + return wvolume->current_volume; +} + +gboolean mafw_gst_renderer_worker_volume_is_muted( + MafwGstRendererWorkerVolume *wvolume) +{ + g_return_val_if_fail(wvolume != NULL, FALSE); + + g_debug("getting mute; %d", wvolume->current_mute); + + return wvolume->current_mute; +} + +void mafw_gst_renderer_worker_volume_destroy( + MafwGstRendererWorkerVolume *wvolume) +{ + g_return_if_fail(wvolume != NULL); + + g_debug("disconnecting"); + + pa_ext_stream_restore_set_subscribe_cb(wvolume->context, NULL, NULL); + pa_context_set_state_callback(wvolume->context, _state_cb_destroy, + wvolume); + pa_context_disconnect(wvolume->context); +} + + + +#else + + +#include "mafw-gst-renderer-worker-volume.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-worker-volume-fake" + +struct _MafwGstRendererWorkerVolume { + MafwGstRendererWorkerVolumeChangedCb cb; + gpointer user_data; + MafwGstRendererWorkerVolumeMuteCb mute_cb; + gpointer mute_user_data; + gdouble current_volume; + gboolean current_mute; +}; + +typedef struct { + MafwGstRendererWorkerVolume *wvolume; + MafwGstRendererWorkerVolumeInitCb cb; + gpointer user_data; +} InitCbClosure; + +static gboolean _init_cb_closure(gpointer user_data) +{ + InitCbClosure *closure = user_data; + + if (closure->cb != NULL) { + closure->cb(closure->wvolume, closure->user_data); + } + g_free(closure); + + return FALSE; +} + +void mafw_gst_renderer_worker_volume_init(GMainContext *main_context, + MafwGstRendererWorkerVolumeInitCb cb, + gpointer user_data, + MafwGstRendererWorkerVolumeChangedCb + changed_cb, + gpointer changed_user_data, + MafwGstRendererWorkerVolumeMuteCb + mute_cb, gpointer mute_user_data) +{ + MafwGstRendererWorkerVolume *wvolume = NULL; + InitCbClosure *closure; + + g_return_if_fail(cb != NULL); + + g_debug("initializing volume manager"); + + wvolume = g_new0(MafwGstRendererWorkerVolume, 1); + + wvolume->cb = changed_cb; + wvolume->user_data = changed_user_data; + wvolume->mute_cb = mute_cb; + wvolume->mute_user_data = mute_user_data; + wvolume->current_volume = 0.485; + + closure = g_new0(InitCbClosure, 1); + closure->wvolume = wvolume; + closure->cb = cb; + closure->user_data = user_data; + g_idle_add(_init_cb_closure, closure); +} + +void mafw_gst_renderer_worker_volume_set(MafwGstRendererWorkerVolume *wvolume, + gdouble volume, gboolean mute) +{ + gboolean signal_volume, signal_mute; + + g_return_if_fail(wvolume != NULL); + +#ifndef MAFW_GST_RENDERER_ENABLE_MUTE + mute = FALSE; +#endif + + signal_volume = wvolume->current_volume != volume && + wvolume->cb != NULL; + signal_mute = wvolume->current_mute != mute && wvolume->mute_cb != NULL; + + wvolume->current_volume = volume; + wvolume->current_mute = mute; + + g_debug("volume set: %lf (mute %d)", volume, mute); + + if (signal_volume) { + g_debug("signalling volume"); + wvolume->cb(wvolume, volume, wvolume->user_data); + } + + if (signal_mute) { + g_debug("signalling mute"); + wvolume->mute_cb(wvolume, mute, wvolume->mute_user_data); + } +} + +gdouble mafw_gst_renderer_worker_volume_get( + MafwGstRendererWorkerVolume *wvolume) +{ + g_return_val_if_fail(wvolume != NULL, 0.0); + + g_debug("getting volume; %lf", wvolume->current_volume); + + return wvolume->current_volume; +} + +gboolean mafw_gst_renderer_worker_volume_is_muted( + MafwGstRendererWorkerVolume *wvolume) +{ + g_return_val_if_fail(wvolume != NULL, FALSE); + + g_debug("getting mute; %d", wvolume->current_mute); + + return wvolume->current_mute; +} + +void mafw_gst_renderer_worker_volume_destroy( + MafwGstRendererWorkerVolume *wvolume) +{ + g_return_if_fail(wvolume != NULL); + + g_free(wvolume); +} + +#endif diff --git a/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h b/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h new file mode 100644 index 0000000..c1e860e --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h @@ -0,0 +1,64 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 + +typedef struct _MafwGstRendererWorkerVolume MafwGstRendererWorkerVolume; + +typedef void(*MafwGstRendererWorkerVolumeChangedCb)( + MafwGstRendererWorkerVolume *wvolume, gdouble volume, gpointer data); + +typedef void(*MafwGstRendererWorkerVolumeMuteCb)( + MafwGstRendererWorkerVolume *wvolume, gboolean mute, gpointer data); + +typedef void(*MafwGstRendererWorkerVolumeInitCb)( + MafwGstRendererWorkerVolume *volume, gpointer data); + +G_BEGIN_DECLS + +void mafw_gst_renderer_worker_volume_init(GMainContext *main_context, + MafwGstRendererWorkerVolumeInitCb cb, + gpointer user_data, + MafwGstRendererWorkerVolumeChangedCb + changed_cb, + gpointer changed_user_data, + MafwGstRendererWorkerVolumeMuteCb + mute_cb, gpointer mute_user_data); + +void mafw_gst_renderer_worker_volume_set(MafwGstRendererWorkerVolume *wvolume, + gdouble volume, gboolean mute); + +gdouble mafw_gst_renderer_worker_volume_get( + MafwGstRendererWorkerVolume *wvolume); +gboolean mafw_gst_renderer_worker_volume_is_muted( + MafwGstRendererWorkerVolume *wvolume); + +void mafw_gst_renderer_worker_volume_destroy( + MafwGstRendererWorkerVolume *wvolume); + +G_END_DECLS +#endif diff --git a/libmafw-gst-renderer/mafw-gst-renderer-worker.c b/libmafw-gst-renderer/mafw-gst-renderer-worker.c new file mode 100644 index 0000000..0cd1ec4 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-worker.c @@ -0,0 +1,2373 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_GDKPIXBUF +#include +#include +#include +#include "gstscreenshot.h" +#endif + +#include +#include "mafw-gst-renderer.h" +#include "mafw-gst-renderer-worker.h" +#include "mafw-gst-renderer-utils.h" +#include "blanking.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-worker" + +#define MAFW_GST_RENDERER_WORKER_SECONDS_READY 60 +#define MAFW_GST_RENDERER_WORKER_SECONDS_DURATION_AND_SEEKABILITY 4 + +#define MAFW_GST_MISSING_TYPE_DECODER "decoder" +#define MAFW_GST_MISSING_TYPE_ENCODER "encoder" + +#define MAFW_GST_BUFFER_TIME 600000L +#define MAFW_GST_LATENCY_TIME (MAFW_GST_BUFFER_TIME / 2) + +#define NSECONDS_TO_SECONDS(ns) ((ns)%1000000000 < 500000000?\ + GST_TIME_AS_SECONDS((ns)):\ + GST_TIME_AS_SECONDS((ns))+1) + +/* Private variables. */ +/* Global reference to worker instance, needed for Xerror handler */ +static MafwGstRendererWorker *Global_worker = NULL; + +/* Forward declarations. */ +static void _do_play(MafwGstRendererWorker *worker); +static void _do_seek(MafwGstRendererWorker *worker, GstSeekType seek_type, + gint position, GError **error); +static void _play_pl_next(MafwGstRendererWorker *worker); + +static void _emit_metadatas(MafwGstRendererWorker *worker); + +static void _current_metadata_add(MafwGstRendererWorker *worker, + const gchar *key, GType type, + const gpointer value); + +/* + * Sends @error to MafwGstRenderer. Only call this from the glib main thread, or + * face the consequences. @err is free'd. + */ +static void _send_error(MafwGstRendererWorker *worker, GError *err) +{ + worker->is_error = TRUE; + if (worker->notify_error_handler) + worker->notify_error_handler(worker, worker->owner, err); + g_error_free(err); +} + +/* + * Posts an @error on the gst bus. _async_bus_handler will then pick it up and + * forward to MafwGstRenderer. @err is free'd. + */ +static void _post_error(MafwGstRendererWorker *worker, GError *err) +{ + gst_bus_post(worker->bus, + gst_message_new_error(GST_OBJECT(worker->pipeline), + err, NULL)); + g_error_free(err); +} + +#ifdef HAVE_GDKPIXBUF +typedef struct { + MafwGstRendererWorker *worker; + gchar *metadata_key; + GdkPixbuf *pixbuf; +} SaveGraphicData; + +static gchar *_init_tmp_file(void) +{ + gint fd; + gchar *path = NULL; + + fd = g_file_open_tmp("mafw-gst-renderer-XXXXXX.jpeg", &path, NULL); + close(fd); + + return path; +} + +static void _init_tmp_files_pool(MafwGstRendererWorker *worker) +{ + guint8 i; + + worker->tmp_files_pool_index = 0; + + for (i = 0; i < MAFW_GST_RENDERER_MAX_TMP_FILES; i++) { + worker->tmp_files_pool[i] = NULL; + } +} + +static void _destroy_tmp_files_pool(MafwGstRendererWorker *worker) +{ + guint8 i; + + for (i = 0; (i < MAFW_GST_RENDERER_MAX_TMP_FILES) && + (worker->tmp_files_pool[i] != NULL); i++) { + g_unlink(worker->tmp_files_pool[i]); + g_free(worker->tmp_files_pool[i]); + } +} + +static const gchar *_get_tmp_file_from_pool( + MafwGstRendererWorker *worker) +{ + gchar *path = worker->tmp_files_pool[worker->tmp_files_pool_index]; + + if (path == NULL) { + path = _init_tmp_file(); + worker->tmp_files_pool[worker->tmp_files_pool_index] = path; + } + + if (++(worker->tmp_files_pool_index) >= + MAFW_GST_RENDERER_MAX_TMP_FILES) { + worker->tmp_files_pool_index = 0; + } + + return path; +} + +static void _destroy_pixbuf (guchar *pixbuf, gpointer data) +{ + gst_buffer_unref(GST_BUFFER(data)); +} + +static void _emit_gst_buffer_as_graphic_file_cb(GstBuffer *new_buffer, + gpointer user_data) +{ + SaveGraphicData *sgd = user_data; + GdkPixbuf *pixbuf = NULL; + + if (new_buffer != NULL) { + gint width, height; + GstStructure *structure; + + structure = + gst_caps_get_structure(GST_BUFFER_CAPS(new_buffer), 0); + + gst_structure_get_int(structure, "width", &width); + gst_structure_get_int(structure, "height", &height); + + pixbuf = gdk_pixbuf_new_from_data( + GST_BUFFER_DATA(new_buffer), GDK_COLORSPACE_RGB, + FALSE, 8, width, height, + GST_ROUND_UP_4(3 * width), _destroy_pixbuf, + new_buffer); + + if (sgd->pixbuf != NULL) { + g_object_unref(sgd->pixbuf); + sgd->pixbuf = NULL; + } + } else { + pixbuf = sgd->pixbuf; + } + + if (pixbuf != NULL) { + gboolean save_ok; + GError *error = NULL; + const gchar *filename; + + filename = _get_tmp_file_from_pool(sgd->worker); + + save_ok = gdk_pixbuf_save (pixbuf, filename, "jpeg", &error, + NULL); + + g_object_unref (pixbuf); + + if (save_ok) { + /* Add the info to the current metadata. */ + _current_metadata_add(sgd->worker, sgd->metadata_key, + G_TYPE_STRING, + (const gpointer) filename); + + /* Emit the metadata. */ + mafw_renderer_emit_metadata_string(sgd->worker->owner, + sgd->metadata_key, + (gchar *) filename); + } else { + if (error != NULL) { + g_warning ("%s\n", error->message); + g_error_free (error); + } else { + g_critical("Unknown error when saving pixbuf " + "with GStreamer data"); + } + } + } else { + g_warning("Could not create pixbuf from GstBuffer"); + } + + g_free(sgd->metadata_key); + g_free(sgd); +} + +static void _pixbuf_size_prepared_cb (GdkPixbufLoader *loader, + gint width, gint height, + gpointer user_data) +{ + /* Be sure the image size is reasonable */ + if (width > 512 || height > 512) { + g_debug ("pixbuf: image is too big: %dx%d", width, height); + gdouble ar; + ar = (gdouble) width / height; + if (width > height) { + width = 512; + height = width / ar; + } else { + height = 512; + width = height * ar; + } + g_debug ("pixbuf: scaled image to %dx%d", width, height); + gdk_pixbuf_loader_set_size (loader, width, height); + } +} + +static void _emit_gst_buffer_as_graphic_file(MafwGstRendererWorker *worker, + GstBuffer *buffer, + const gchar *metadata_key) +{ + GdkPixbufLoader *loader; + GstStructure *structure; + const gchar *mime = NULL; + GError *error = NULL; + + g_return_if_fail((buffer != NULL) && GST_IS_BUFFER(buffer)); + + structure = gst_caps_get_structure(GST_BUFFER_CAPS(buffer), 0); + mime = gst_structure_get_name(structure); + + if (g_str_has_prefix(mime, "video/x-raw")) { + gint framerate_d, framerate_n; + GstCaps *to_caps; + SaveGraphicData *sgd; + + gst_structure_get_fraction (structure, "framerate", + &framerate_n, &framerate_d); + + to_caps = gst_caps_new_simple ("video/x-raw-rgb", + "bpp", G_TYPE_INT, 24, + "depth", G_TYPE_INT, 24, + "framerate", GST_TYPE_FRACTION, + framerate_n, framerate_d, + "pixel-aspect-ratio", + GST_TYPE_FRACTION, 1, 1, + "endianness", + G_TYPE_INT, G_BIG_ENDIAN, + "red_mask", G_TYPE_INT, + 0xff0000, + "green_mask", + G_TYPE_INT, 0x00ff00, + "blue_mask", + G_TYPE_INT, 0x0000ff, + NULL); + + sgd = g_new0(SaveGraphicData, 1); + sgd->worker = worker; + sgd->metadata_key = g_strdup(metadata_key); + + g_debug("pixbuf: using bvw to convert image format"); + bvw_frame_conv_convert (buffer, to_caps, + _emit_gst_buffer_as_graphic_file_cb, + sgd); + } else { + GdkPixbuf *pixbuf = NULL; + loader = gdk_pixbuf_loader_new_with_mime_type(mime, &error); + g_signal_connect (G_OBJECT (loader), "size-prepared", + (GCallback)_pixbuf_size_prepared_cb, NULL); + + if (loader == NULL) { + g_warning ("%s\n", error->message); + g_error_free (error); + } else { + if (!gdk_pixbuf_loader_write (loader, + GST_BUFFER_DATA(buffer), + GST_BUFFER_SIZE(buffer), + &error)) { + g_warning ("%s\n", error->message); + g_error_free (error); + + gdk_pixbuf_loader_close (loader, NULL); + } else { + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + if (!gdk_pixbuf_loader_close (loader, &error)) { + g_warning ("%s\n", error->message); + g_error_free (error); + + g_object_unref(pixbuf); + } else { + SaveGraphicData *sgd; + + sgd = g_new0(SaveGraphicData, 1); + + sgd->worker = worker; + sgd->metadata_key = + g_strdup(metadata_key); + sgd->pixbuf = pixbuf; + + _emit_gst_buffer_as_graphic_file_cb( + NULL, sgd); + } + } + } + } +} +#endif + +static gboolean _go_to_gst_ready(gpointer user_data) +{ + MafwGstRendererWorker *worker = user_data; + + g_return_val_if_fail(worker->state == GST_STATE_PAUSED || + worker->prerolling, FALSE); + + worker->seek_position = + mafw_gst_renderer_worker_get_position(worker); + + g_debug("going to GST_STATE_READY"); + gst_element_set_state(worker->pipeline, GST_STATE_READY); + worker->in_ready = TRUE; + worker->ready_timeout = 0; + + return FALSE; +} + +static void _add_ready_timeout(MafwGstRendererWorker *worker) +{ + if (worker->media.seekable) { + if (!worker->ready_timeout) + { + g_debug("Adding timeout to go to GST_STATE_READY"); + worker->ready_timeout = + g_timeout_add_seconds( + MAFW_GST_RENDERER_WORKER_SECONDS_READY, + _go_to_gst_ready, + worker); + } + } else { + g_debug("Not adding timeout to go to GST_STATE_READY as media " + "is not seekable"); + worker->ready_timeout = 0; + } +} + +static void _remove_ready_timeout(MafwGstRendererWorker *worker) +{ + g_debug("removing timeout for READY"); + if (worker->ready_timeout != 0) { + g_source_remove(worker->ready_timeout); + worker->ready_timeout = 0; + } + worker->in_ready = FALSE; +} + +static gboolean _emit_video_info(MafwGstRendererWorker *worker) +{ + mafw_renderer_emit_metadata_int(worker->owner, + MAFW_METADATA_KEY_RES_X, + worker->media.video_width); + mafw_renderer_emit_metadata_int(worker->owner, + MAFW_METADATA_KEY_RES_Y, + worker->media.video_height); + mafw_renderer_emit_metadata_double(worker->owner, + MAFW_METADATA_KEY_VIDEO_FRAMERATE, + worker->media.fps); + return FALSE; +} + +/* + * Checks if the video details are supported. It also extracts other useful + * information (such as PAR and framerate) from the caps, if available. NOTE: + * this will be called from a different thread than glib's mainloop (when + * invoked via _stream_info_cb); don't call MafwGstRenderer directly. + * + * Returns: TRUE if video details are acceptable. + */ +static gboolean _handle_video_info(MafwGstRendererWorker *worker, + const GstStructure *structure) +{ + gint width, height; + gdouble fps; + + width = height = 0; + gst_structure_get_int(structure, "width", &width); + gst_structure_get_int(structure, "height", &height); + g_debug("video size: %d x %d", width, height); + if (gst_structure_has_field(structure, "pixel-aspect-ratio")) + { + gst_structure_get_fraction(structure, "pixel-aspect-ratio", + &worker->media.par_n, + &worker->media.par_d); + g_debug("video PAR: %d:%d", worker->media.par_n, + worker->media.par_d); + width = width * worker->media.par_n / worker->media.par_d; + } + + fps = 1.0; + if (gst_structure_has_field(structure, "framerate")) + { + gint fps_n, fps_d; + + gst_structure_get_fraction(structure, "framerate", + &fps_n, &fps_d); + if (fps_d > 0) + fps = (gdouble)fps_n / (gdouble)fps_d; + g_debug("video fps: %f", fps); + } + + worker->media.video_width = width; + worker->media.video_height = height; + worker->media.fps = fps; + + /* Add the info to the current metadata. */ + gint *p_width = g_new0(gint, 1); + gint *p_height = g_new0(gint, 1); + gdouble *p_fps = g_new0(gdouble, 1); + + *p_width = width;* p_height = height; *p_fps = fps; + + _current_metadata_add(worker, MAFW_METADATA_KEY_RES_X, G_TYPE_INT, + (const gpointer) p_width); + _current_metadata_add(worker, MAFW_METADATA_KEY_RES_Y, G_TYPE_INT, + (const gpointer) p_height); + _current_metadata_add(worker, MAFW_METADATA_KEY_VIDEO_FRAMERATE, + G_TYPE_DOUBLE, + (const gpointer) p_fps); + + g_free(p_width); g_free(p_height); g_free(p_fps); + + /* Emit the metadata.*/ + g_idle_add((GSourceFunc)_emit_video_info, worker); + + return TRUE; +} + +static void _parse_stream_info_item(MafwGstRendererWorker *worker, GObject *obj) +{ + GParamSpec *pspec; + GEnumValue *val; + gint type; + + g_object_get(obj, "type", &type, NULL); + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), "type"); + val = g_enum_get_value(G_PARAM_SPEC_ENUM(pspec)->enum_class, type); + if (!val) + return; + if (!g_ascii_strcasecmp(val->value_nick, "video") || + !g_ascii_strcasecmp(val->value_name, "video")) + { + GstCaps *vcaps; + GstObject *object; + + object = NULL; + g_object_get(obj, "object", &object, NULL); + vcaps = NULL; + if (object) { + vcaps = gst_pad_get_caps(GST_PAD_CAST(object)); + } else { + g_object_get(obj, "caps", &vcaps, NULL); + gst_caps_ref(vcaps); + } + if (vcaps) { + if (gst_caps_is_fixed(vcaps)) + { + _handle_video_info( + worker, + gst_caps_get_structure(vcaps, 0)); + } + gst_caps_unref(vcaps); + } + } +} + +/* It always returns FALSE, because it is used as an idle callback as well. */ +static gboolean _parse_stream_info(MafwGstRendererWorker *worker) +{ + GList *stream_info, *s; + + stream_info = NULL; + if (g_object_class_find_property(G_OBJECT_GET_CLASS(worker->pipeline), + "stream-info")) + { + g_object_get(worker->pipeline, + "stream-info", &stream_info, NULL); + } + for (s = stream_info; s; s = g_list_next(s)) + _parse_stream_info_item(worker, G_OBJECT(s->data)); + return FALSE; +} + +static void mafw_gst_renderer_worker_apply_xid(MafwGstRendererWorker *worker) +{ + /* Set sink to render on the provided XID if we have do have + a XID a valid video sink and we are rendeing video content */ + if (worker->xid && + worker->vsink && + worker->media.has_visual_content) + { + g_debug ("Setting overlay, window id: %x", + (gint) worker->xid); + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(worker->vsink), + worker->xid); + /* Ask the gst to redraw the frame if we are paused */ + /* TODO: in MTG this works only in non-fs -> fs way. */ + if (worker->state == GST_STATE_PAUSED) + { + gst_x_overlay_expose(GST_X_OVERLAY(worker->vsink)); + } + } else { + g_debug("Not setting overlay for window id: %x", + (gint) worker->xid); + } +} + +/* + * GstBus synchronous message handler. NOTE that this handler is NOT invoked + * from the glib thread, so be careful what you do here. + */ +static GstBusSyncReply _sync_bus_handler(GstBus *bus, GstMessage *msg, + MafwGstRendererWorker *worker) +{ + if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT && + gst_structure_has_name(msg->structure, "prepare-xwindow-id")) + { + g_debug("got prepare-xwindow-id"); + worker->media.has_visual_content = TRUE; + /* The user has to preset the XID, we don't create windows by + * ourselves. */ + if (!worker->xid) { + /* We must post an error message to the bus that will + * be picked up by _async_bus_handler. Calling the + * notification function directly from here (different + * thread) is not healthy. */ + g_warning("No video window set!"); + _post_error(worker, + g_error_new_literal( + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_PLAYBACK, + "No video window XID set")); + return GST_BUS_DROP; + } else { + g_debug ("Video window to use is: %x", + (gint) worker->xid); + } + + /* Instruct vsink to use the client-provided window */ + mafw_gst_renderer_worker_apply_xid(worker); + + /* Handle colorkey and autopaint */ + mafw_gst_renderer_worker_set_autopaint( + worker, + worker->autopaint); + g_object_get(worker->vsink, + "colorkey", &worker->colorkey, NULL); + /* Defer the signal emission to the thread running the + * mainloop. */ + if (worker->colorkey != -1) { + gst_bus_post(worker->bus, + gst_message_new_application( + GST_OBJECT(worker->vsink), + gst_structure_empty_new("ckey"))); + } + return GST_BUS_DROP; + } + return GST_BUS_PASS; +} + +static void _free_taglist_item(GstMessage *msg, gpointer data) +{ + gst_message_unref(msg); +} + +static void _free_taglist(MafwGstRendererWorker *worker) +{ + if (worker->tag_list != NULL) + { + g_ptr_array_foreach(worker->tag_list, (GFunc)_free_taglist_item, + NULL); + g_ptr_array_free(worker->tag_list, TRUE); + worker->tag_list = NULL; + } +} + +static gboolean _seconds_duration_equal(gint64 duration1, gint64 duration2) +{ + gint64 duration1_seconds, duration2_seconds; + + duration1_seconds = NSECONDS_TO_SECONDS(duration1); + duration2_seconds = NSECONDS_TO_SECONDS(duration2); + + return duration1_seconds == duration2_seconds; +} + +static void _check_duration(MafwGstRendererWorker *worker, gint64 value) +{ + MafwGstRenderer *renderer = worker->owner; + gboolean right_query = TRUE; + + if (value == -1) { + GstFormat format = GST_FORMAT_TIME; + right_query = + gst_element_query_duration(worker->pipeline, &format, + &value); + } + + if (right_query && value > 0) { + gint duration_seconds = NSECONDS_TO_SECONDS(value); + + if (!_seconds_duration_equal(worker->media.length_nanos, + value)) { + gint64 *duration = g_new0(gint64, 1); + *duration = duration_seconds; + + /* Add the duration to the current metadata. */ + _current_metadata_add(worker, + MAFW_METADATA_KEY_DURATION, + G_TYPE_INT64, + (const gpointer) duration); + + /* Emit the duration. */ + mafw_renderer_emit_metadata_int64( + worker->owner, MAFW_METADATA_KEY_DURATION, + *duration); + g_free(duration); + } + + /* We compare this duration we just got with the + * source one and update it in the source if needed */ + if (duration_seconds != renderer->media->duration) { + mafw_gst_renderer_update_source_duration( + renderer, + duration_seconds); + } + } + + worker->media.length_nanos = value; + g_debug("media duration: %lld", worker->media.length_nanos); +} + +static void _check_seekability(MafwGstRendererWorker *worker) +{ + MafwGstRenderer *renderer = worker->owner; + SeekabilityType seekable = SEEKABILITY_NO_SEEKABLE; + + if (worker->media.length_nanos != -1) + { + g_debug("source seekability %d", renderer->media->seekability); + + if (renderer->media->seekability != SEEKABILITY_NO_SEEKABLE) { + g_debug("Quering GStreamer for seekability"); + GstQuery *seek_query; + GstFormat format = GST_FORMAT_TIME; + /* Query the seekability of the stream */ + seek_query = gst_query_new_seeking(format); + if (gst_element_query(worker->pipeline, seek_query)) { + gboolean renderer_seekable = FALSE; + gst_query_parse_seeking(seek_query, NULL, + &renderer_seekable, + NULL, NULL); + g_debug("GStreamer seekability %d", + renderer_seekable); + seekable = renderer_seekable ? + SEEKABILITY_SEEKABLE : + SEEKABILITY_NO_SEEKABLE; + } + gst_query_unref(seek_query); + } + } + + if (worker->media.seekable != seekable) { + gboolean *is_seekable = g_new0(gboolean, 1); + *is_seekable = (seekable == SEEKABILITY_SEEKABLE) ? TRUE : FALSE; + + /* Add the seekability to the current metadata. */ + _current_metadata_add(worker, MAFW_METADATA_KEY_IS_SEEKABLE, + G_TYPE_BOOLEAN, (const gpointer) is_seekable); + + /* Emit. */ + mafw_renderer_emit_metadata_boolean( + worker->owner, MAFW_METADATA_KEY_IS_SEEKABLE, + *is_seekable); + + g_free(is_seekable); + } + + g_debug("media seekable: %d", seekable); + worker->media.seekable = seekable; +} + +static gboolean _query_duration_and_seekability_timeout(gpointer data) +{ + MafwGstRendererWorker *worker = data; + + _check_duration(worker, -1); + _check_seekability(worker); + + worker->duration_seek_timeout = 0; + + return FALSE; +} + +/* + * Called when the pipeline transitions into PAUSED state. It extracts more + * information from Gst. + */ +static void _finalize_startup(MafwGstRendererWorker *worker) +{ + /* Check video caps */ + if (worker->media.has_visual_content) { + GstPad *pad = GST_BASE_SINK_PAD(worker->vsink); + GstCaps *caps = GST_PAD_CAPS(pad); + if (caps && gst_caps_is_fixed(caps)) { + GstStructure *structure; + structure = gst_caps_get_structure(caps, 0); + if (!_handle_video_info(worker, structure)) + return; + } + } + + /* Something might have gone wrong at this point already. */ + if (worker->is_error) { + g_debug("Error occured during preroll"); + return; + } + + /* Streaminfo might reveal the media to be unsupported. Therefore we + * need to check the error again. */ + _parse_stream_info(worker); + if (worker->is_error) { + g_debug("Error occured. Leaving"); + return; + } + + /* Check duration and seekability */ + if (worker->duration_seek_timeout != 0) { + g_source_remove(worker->duration_seek_timeout); + worker->duration_seek_timeout = 0; + } + _check_duration(worker, -1); + _check_seekability(worker); +} + +static void _add_duration_seek_query_timeout(MafwGstRendererWorker *worker) +{ + if (worker->duration_seek_timeout != 0) { + g_source_remove(worker->duration_seek_timeout); + } + worker->duration_seek_timeout = g_timeout_add_seconds( + MAFW_GST_RENDERER_WORKER_SECONDS_DURATION_AND_SEEKABILITY, + _query_duration_and_seekability_timeout, + worker); +} + +static void _do_pause_postprocessing(MafwGstRendererWorker *worker) +{ + if (worker->notify_pause_handler) { + worker->notify_pause_handler(worker, worker->owner); + } + +#ifdef HAVE_GDKPIXBUF + if (worker->media.has_visual_content && + worker->current_frame_on_pause) { + GstBuffer *buffer = NULL; + + g_object_get(worker->pipeline, "frame", &buffer, NULL); + + if (buffer != NULL) { + _emit_gst_buffer_as_graphic_file( + worker, buffer, + MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI); + } + } +#endif + + _add_ready_timeout(worker); +} + +static void _report_playing_state(MafwGstRendererWorker * worker) +{ + if (worker->report_statechanges) { + switch (worker->mode) { + case WORKER_MODE_SINGLE_PLAY: + /* Notify play if we are playing in + * single mode */ + if (worker->notify_play_handler) + worker->notify_play_handler( + worker, + worker->owner); + break; + case WORKER_MODE_PLAYLIST: + case WORKER_MODE_REDUNDANT: + /* Only notify play when the "playlist" + playback starts, don't notify play for each + individual element of the playlist. */ + if (worker->pl.notify_play_pending) { + if (worker->notify_play_handler) + worker->notify_play_handler( + worker, + worker->owner); + worker->pl.notify_play_pending = FALSE; + } + break; + default: break; + } + } +} + +static void _handle_state_changed(GstMessage *msg, MafwGstRendererWorker *worker) +{ + GstState newstate, oldstate; + GstStateChange statetrans; + MafwGstRenderer *renderer = (MafwGstRenderer*)worker->owner; + + gst_message_parse_state_changed(msg, &oldstate, &newstate, NULL); + statetrans = GST_STATE_TRANSITION(oldstate, newstate); + g_debug ("State changed: %d: %d -> %d", worker->state, oldstate, newstate); + + /* If the state is the same we do nothing, otherwise, we keep + * it */ + if (worker->state == newstate) { + return; + } else { + worker->state = newstate; + } + + if (statetrans == GST_STATE_CHANGE_READY_TO_PAUSED && + worker->in_ready) { + /* Woken up from READY, resume stream position and playback */ + g_debug("State changed to pause after ready"); + if (worker->seek_position > 0) { + _check_seekability(worker); + if (worker->media.seekable) { + g_debug("performing a seek"); + _do_seek(worker, GST_SEEK_TYPE_SET, + worker->seek_position, NULL); + } else { + g_critical("media is not seekable (and should)"); + } + } + + /* If playing a stream wait for buffering to finish before + starting to play */ + if (!worker->is_stream || worker->is_live) { + _do_play(worker); + } + return; + } + + /* While buffering, we have to wait in PAUSED + until we reach 100% before doing anything */ + if (worker->buffering) { + if (statetrans == GST_STATE_CHANGE_PAUSED_TO_PLAYING) { + /* Mmm... probably the client issued a seek on the + * stream and then a play/resume command right away, + * so the stream got into PLAYING state while + * buffering. When the next buffering signal arrives, + * the stream will be PAUSED silently and resumed when + * buffering is done (silently too), so let's signal + * the state change to PLAYING here. */ + _report_playing_state(worker); + } + return; + } + + switch (statetrans) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (worker->prerolling && worker->report_statechanges) { + /* PAUSED after pipeline has been + * constructed. We check caps, seek and + * duration and if staying in pause is needed, + * we perform operations for pausing, such as + * current frame on pause and signalling state + * change and adding the timeout to go to ready */ + g_debug ("Prerolling done, finalizaing startup"); + _finalize_startup(worker); + _do_play(worker); + renderer->play_failed_count = 0; + + if (worker->stay_paused) { + _do_pause_postprocessing(worker); + } + worker->prerolling = FALSE; + } + break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + /* When pausing we do the stuff, like signalling + * state, current frame on pause and timeout to go to + * ready */ + if (worker->report_statechanges) { + _do_pause_postprocessing(worker); + } + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + /* if seek was called, at this point it is really ended */ + worker->seek_position = -1; + worker->eos = FALSE; + + /* Signal state change if needed */ + _report_playing_state(worker); + + /* Prevent blanking if we are playing video */ + if (worker->media.has_visual_content) { + blanking_prohibit(); + } + /* Remove the ready timeout if we are playing [again] */ + _remove_ready_timeout(worker); + /* If mode is redundant we are trying to play one of several + * candidates, so when we get a successful playback, we notify + * the real URI that we are playing */ + if (worker->mode == WORKER_MODE_REDUNDANT) { + mafw_renderer_emit_metadata_string( + worker->owner, + MAFW_METADATA_KEY_URI, + worker->media.location); + } + + /* Emit metadata. We wait until we reach the playing + state because this speeds up playback start time */ + _emit_metadatas(worker); + /* Query duration and seekability. Useful for vbr + * clips or streams. */ + _add_duration_seek_query_timeout(worker); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + /* If we went to READY, we free the taglist and + * deassign the timout it */ + if (worker->in_ready) { + g_debug("changed to GST_STATE_READY"); + _free_taglist(worker); + } + break; + default: + break; + } +} + +static void _handle_duration(MafwGstRendererWorker *worker, GstMessage *msg) +{ + GstFormat fmt; + gint64 duration; + + gst_message_parse_duration(msg, &fmt, &duration); + + if (worker->duration_seek_timeout != 0) { + g_source_remove(worker->duration_seek_timeout); + worker->duration_seek_timeout = 0; + } + + _check_duration(worker, + duration != GST_CLOCK_TIME_NONE ? duration : -1); + _check_seekability(worker); +} + +#ifdef HAVE_GDKPIXBUF +static void _emit_renderer_art(MafwGstRendererWorker *worker, + const GstTagList *list) +{ + GstBuffer *buffer = NULL; + const GValue *value = NULL; + + g_return_if_fail(gst_tag_list_get_tag_size(list, GST_TAG_IMAGE) > 0); + + value = gst_tag_list_get_value_index(list, GST_TAG_IMAGE, 0); + + g_return_if_fail((value != NULL) && G_VALUE_HOLDS(value, GST_TYPE_BUFFER)); + + buffer = g_value_peek_pointer(value); + + g_return_if_fail((buffer != NULL) && GST_IS_BUFFER(buffer)); + + _emit_gst_buffer_as_graphic_file(worker, buffer, + MAFW_METADATA_KEY_RENDERER_ART_URI); +} +#endif + + + +static void _current_metadata_add(MafwGstRendererWorker *worker, + const gchar *key, GType type, + const gpointer value) +{ + g_return_if_fail(value != NULL); + + if (!worker->current_metadata) + worker->current_metadata = mafw_metadata_new(); + + mafw_metadata_add_something(worker->current_metadata, key, type, 1, value); +} + +static GHashTable* _build_tagmap(void) +{ + GHashTable *hash_table = NULL; + + hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + g_free); + + g_hash_table_insert(hash_table, g_strdup(GST_TAG_TITLE), + g_strdup(MAFW_METADATA_KEY_TITLE)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_ARTIST), + g_strdup(MAFW_METADATA_KEY_ARTIST)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_AUDIO_CODEC), + g_strdup(MAFW_METADATA_KEY_AUDIO_CODEC)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_VIDEO_CODEC), + g_strdup(MAFW_METADATA_KEY_VIDEO_CODEC)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_BITRATE), + g_strdup(MAFW_METADATA_KEY_BITRATE)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_LANGUAGE_CODE), + g_strdup(MAFW_METADATA_KEY_ENCODING)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_ALBUM), + g_strdup(MAFW_METADATA_KEY_ALBUM)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_GENRE), + g_strdup(MAFW_METADATA_KEY_GENRE)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_TRACK_NUMBER), + g_strdup(MAFW_METADATA_KEY_TRACK)); + g_hash_table_insert(hash_table, g_strdup(GST_TAG_ORGANIZATION), + g_strdup(MAFW_METADATA_KEY_ORGANIZATION)); +#ifdef HAVE_GDKPIXBUF + g_hash_table_insert(hash_table, g_strdup(GST_TAG_IMAGE), + g_strdup(MAFW_METADATA_KEY_RENDERER_ART_URI)); +#endif + + return hash_table; +} + +/* + * Emits metadata-changed signals for gst tags. + */ +static void _emit_tag(const GstTagList *list, const gchar *tag, + MafwGstRendererWorker *worker) +{ + /* Mapping between Gst <-> MAFW metadata tags + * NOTE: This assumes that GTypes matches between GST and MAFW. */ + static GHashTable *tagmap = NULL; + gint i, count; + const gchar *mafwtag; + GType type; + GValueArray *values; + + if (tagmap == NULL) { + tagmap = _build_tagmap(); + } + + g_debug("tag: '%s' (type: %s)", tag, + g_type_name(gst_tag_get_type(tag))); + /* Is there a mapping for this tag? */ + mafwtag = g_hash_table_lookup(tagmap, tag); + if (!mafwtag) + return; + +#ifdef HAVE_GDKPIXBUF + if (strcmp (mafwtag, MAFW_METADATA_KEY_RENDERER_ART_URI) == 0) { + _emit_renderer_art(worker, list); + return; + } +#endif + + /* Build a value array of this tag. We need to make sure that strings + * are UTF-8. GstTagList API says that the value is always UTF8, but it + * looks like the ID3 demuxer still might sometimes produce non-UTF-8 + * strings. */ + count = gst_tag_list_get_tag_size(list, tag); + type = gst_tag_get_type(tag); + values = g_value_array_new(count); + for (i = 0; i < count; ++i) { + GValue *v = (GValue *) + gst_tag_list_get_value_index(list, tag, i); + if (type == G_TYPE_STRING) { + gchar *orig, *utf8; + + gst_tag_list_get_string_index(list, tag, i, &orig); + if (convert_utf8(orig, &utf8)) { + GValue utf8gval = {0}; + + g_value_init(&utf8gval, G_TYPE_STRING); + g_value_take_string(&utf8gval, utf8); + _current_metadata_add(worker, mafwtag, G_TYPE_VALUE, + (const gpointer) &utf8gval); + g_value_array_append(values, &utf8gval); + g_value_unset(&utf8gval); + } + g_free(orig); + } else if (type == G_TYPE_UINT) { + GValue intgval = {0}; + + g_value_init(&intgval, G_TYPE_INT); + g_value_transform(v, &intgval); + _current_metadata_add(worker, mafwtag, G_TYPE_VALUE, + (const gpointer) &intgval); + g_value_array_append(values, &intgval); + g_value_unset(&intgval); + } else { + _current_metadata_add(worker, mafwtag, G_TYPE_VALUE, + (const gpointer) v); + g_value_array_append(values, v); + } + } + + /* Emit the metadata. */ + g_signal_emit_by_name(worker->owner, "metadata-changed", mafwtag, + values); + + g_value_array_free(values); +} + +/** + * Collect tag-messages, parse it later, when playing is ongoing + */ +static void _handle_tag(MafwGstRendererWorker *worker, GstMessage *msg) +{ + /* Do not emit metadata until we get to PLAYING state to speed up + playback start */ + if (worker->tag_list == NULL) + worker->tag_list = g_ptr_array_new(); + g_ptr_array_add(worker->tag_list, gst_message_ref(msg)); + + /* Some tags come in playing state, so in this case we have + to emit them right away (example: radio stations) */ + if (worker->state == GST_STATE_PLAYING) { + _emit_metadatas(worker); + } +} + +/** + * Parses the list of tag-messages + */ +static void _parse_tagmsg(GstMessage *msg, MafwGstRendererWorker *worker) +{ + GstTagList *new_tags; + + gst_message_parse_tag(msg, &new_tags); + gst_tag_list_foreach(new_tags, (gpointer)_emit_tag, worker); + gst_tag_list_free(new_tags); + gst_message_unref(msg); +} + +/** + * Parses the collected tag messages, and emits the metadatas + */ +static void _emit_metadatas(MafwGstRendererWorker *worker) +{ + if (worker->tag_list != NULL) + { + g_ptr_array_foreach(worker->tag_list, (GFunc)_parse_tagmsg, + worker); + g_ptr_array_free(worker->tag_list, TRUE); + worker->tag_list = NULL; + } +} + +static void _reset_volume_and_mute_to_pipeline(MafwGstRendererWorker *worker) +{ +#ifdef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME + g_debug("resetting volume and mute to pipeline"); + + if (worker->pipeline != NULL) { + g_object_set( + G_OBJECT(worker->pipeline), "volume", + mafw_gst_renderer_worker_volume_get(worker->wvolume), + "mute", + mafw_gst_renderer_worker_volume_is_muted(worker->wvolume), + NULL); + } +#endif +} + +static void _handle_buffering(MafwGstRendererWorker *worker, GstMessage *msg) +{ + gint percent; + MafwGstRenderer *renderer = (MafwGstRenderer*)worker->owner; + + gst_message_parse_buffering(msg, &percent); + g_debug("buffering: %d", percent); + + /* No state management needed for live pipelines */ + if (!worker->is_live) { + worker->buffering = TRUE; + if (worker->state == GST_STATE_PLAYING) { + g_debug("setting pipeline to PAUSED not to wolf the " + "buffer down"); + worker->report_statechanges = FALSE; + /* We can't call _pause() here, since it sets + * the "report_statechanges" to TRUE. We don't + * want that, application doesn't need to know + * that internally the state changed to + * PAUSED. */ + if (gst_element_set_state(worker->pipeline, + GST_STATE_PAUSED) == + GST_STATE_CHANGE_ASYNC) + { + /* XXX this blocks at most 2 seconds. */ + gst_element_get_state(worker->pipeline, NULL, + NULL, + 2 * GST_SECOND); + } + } + + if (percent >= 100) { + /* On buffering we go to PAUSED, so here we move back to + PLAYING */ + worker->buffering = FALSE; + if (worker->state == GST_STATE_PAUSED) { + /* If buffering more than once, do this only the + first time we are done with buffering */ + if (worker->prerolling) { + g_debug("buffering concluded during " + "prerolling"); + _finalize_startup(worker); + _do_play(worker); + renderer->play_failed_count = 0; + /* Send the paused notification */ + if (worker->stay_paused && + worker->notify_pause_handler) { + worker->notify_pause_handler( + worker, + worker->owner); + } + worker->prerolling = FALSE; + } else if (worker->in_ready) { + /* If we had been woken up from READY + and we have finish our buffering, + check if we have to play or stay + paused and if we have to play, + signal the state change. */ + g_debug("buffering concluded, " + "continuing playing"); + _do_play(worker); + } else if (!worker->stay_paused) { + /* This means, that we were playing but + ran out of buffer, so we silently + paused waited for buffering to + finish and now we continue silently + (silently meaning we do not expose + state changes) */ + g_debug("buffering concluded, setting " + "pipeline to PLAYING again"); + _reset_volume_and_mute_to_pipeline( + worker); + if (gst_element_set_state( + worker->pipeline, + GST_STATE_PLAYING) == + GST_STATE_CHANGE_ASYNC) + { + /* XXX this blocks at most 2 seconds. */ + gst_element_get_state( + worker->pipeline, NULL, NULL, + 2 * GST_SECOND); + } + } + } else if (worker->state == GST_STATE_PLAYING) { + g_debug("buffering concluded, signalling " + "state change"); + /* In this case we got a PLAY command while + buffering, likely because it was issued + before we got the first buffering signal. + The UI should not do this, but if it does, + we have to signal that we have executed + the state change, since in + _handle_state_changed we do not do anything + if we are buffering */ + if (worker->report_statechanges && + worker->notify_play_handler) { + worker->notify_play_handler( + worker, + worker->owner); + } + _add_duration_seek_query_timeout(worker); + } + } + } + + /* Send buffer percentage */ + if (worker->notify_buffer_status_handler) + worker->notify_buffer_status_handler(worker, worker->owner, + percent); +} + +static void _handle_element_msg(MafwGstRendererWorker *worker, GstMessage *msg) +{ + /* Only HelixBin sends "resolution" messages. */ + if (gst_structure_has_name(msg->structure, "resolution") && + _handle_video_info(worker, msg->structure)) + { + worker->media.has_visual_content = TRUE; + } +} + +static void _reset_pl_info(MafwGstRendererWorker *worker) +{ + if (worker->pl.items) { + g_slist_foreach(worker->pl.items, (GFunc) g_free, NULL); + g_slist_free(worker->pl.items); + worker->pl.items = NULL; + } + + worker->pl.current = 0; + worker->pl.notify_play_pending = TRUE; +} + +static GError * _get_specific_missing_plugin_error(GstMessage *msg) +{ + const GstStructure *gst_struct; + const gchar *type; + + GError *error; + gchar *desc; + + desc = gst_missing_plugin_message_get_description(msg); + + gst_struct = gst_message_get_structure(msg); + type = gst_structure_get_string(gst_struct, "type"); + + if ((type) && ((strcmp(type, MAFW_GST_MISSING_TYPE_DECODER) == 0) || + (strcmp(type, MAFW_GST_MISSING_TYPE_ENCODER) == 0))) { + + /* Missing codec error. */ + const GValue *val; + const GstCaps *caps; + GstStructure *caps_struct; + const gchar *mime; + + val = gst_structure_get_value(gst_struct, "detail"); + caps = gst_value_get_caps(val); + caps_struct = gst_caps_get_structure(caps, 0); + mime = gst_structure_get_name(caps_struct); + + if (g_strrstr(mime, "video")) { + error = g_error_new_literal( + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_VIDEO_CODEC_NOT_FOUND, + desc); + } else if (g_strrstr(mime, "audio")) { + error = g_error_new_literal( + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_AUDIO_CODEC_NOT_FOUND, + desc); + } else { + error = g_error_new_literal( + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_CODEC_NOT_FOUND, + desc); + } + } else { + /* Unsupported type error. */ + error = g_error_new( + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE, + "missing plugin: %s", desc); + } + + g_free(desc); + + return error; +} + +/* + * Asynchronous message handler. It gets removed from if it returns FALSE. + */ +static gboolean _async_bus_handler(GstBus *bus, GstMessage *msg, + MafwGstRendererWorker *worker) +{ + /* No need to handle message if error has already occured. */ + if (worker->is_error) + return TRUE; + + /* Handle missing-plugin (element) messages separately, relaying more + * details. */ + if (gst_is_missing_plugin_message(msg)) { + GError *err = _get_specific_missing_plugin_error(msg); + /* FIXME?: for some reason, calling the error handler directly + * (_send_error) causes problems. On the other hand, turning + * the error into a new GstMessage and letting the next + * iteration handle it seems to work. */ + _post_error(worker, err); + return TRUE; + } + + switch (GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_ERROR: + if (!worker->is_error) { + gchar *debug; + GError *err; + + debug = NULL; + gst_message_parse_error(msg, &err, &debug); + g_debug("gst error: domain = %d, code = %d, " + "message = '%s', debug = '%s'", + err->domain, err->code, err->message, debug); + if (debug) + g_free(debug); + + /* If we are in playlist/radio mode, we silently + ignore the error and continue with the next + item until we end the playlist. If no + playable elements we raise the error and + after finishing we go to normal mode */ + + if (worker->mode == WORKER_MODE_PLAYLIST || + worker->mode == WORKER_MODE_REDUNDANT) { + if (worker->pl.current < + (g_slist_length(worker->pl.items) - 1)) { + /* If the error is "no space left" + notify, otherwise try to play the + next item */ + if (err->code == + GST_RESOURCE_ERROR_NO_SPACE_LEFT) { + _send_error(worker, err); + + } else { + _play_pl_next(worker); + } + } else { + /* Playlist EOS. We cannot try another + * URI, so we have to go back to normal + * mode and signal the error (done + * below) */ + worker->mode = WORKER_MODE_SINGLE_PLAY; + _reset_pl_info(worker); + } + } + + if (worker->mode == WORKER_MODE_SINGLE_PLAY) { + _send_error(worker, err); + } + } + break; + case GST_MESSAGE_EOS: + if (!worker->is_error) { + worker->eos = TRUE; + + if (worker->mode == WORKER_MODE_PLAYLIST) { + if (worker->pl.current < + (g_slist_length(worker->pl.items) - 1)) { + /* If the playlist EOS is not reached + continue playing */ + _play_pl_next(worker); + } else { + /* Playlist EOS, go back to normal + mode */ + worker->mode = WORKER_MODE_SINGLE_PLAY; + _reset_pl_info(worker); + } + } + + if (worker->mode == WORKER_MODE_SINGLE_PLAY || + worker->mode == WORKER_MODE_REDUNDANT) { + if (worker->notify_eos_handler) + worker->notify_eos_handler( + worker, + worker->owner); + + /* We can remove the message handlers now, we + are not interested in bus messages + anymore. */ + if (worker->bus) { + gst_bus_set_sync_handler(worker->bus, + NULL, + NULL); + } + if (worker->async_bus_id) { + g_source_remove(worker->async_bus_id); + worker->async_bus_id = 0; + } + + if (worker->mode == WORKER_MODE_REDUNDANT) { + /* Go to normal mode */ + worker->mode = WORKER_MODE_SINGLE_PLAY; + _reset_pl_info(worker); + } + } + } + break; + case GST_MESSAGE_TAG: + _handle_tag(worker, msg); + break; + case GST_MESSAGE_BUFFERING: + _handle_buffering(worker, msg); + break; + case GST_MESSAGE_DURATION: + _handle_duration(worker, msg); + break; + case GST_MESSAGE_ELEMENT: + _handle_element_msg(worker, msg); + break; + case GST_MESSAGE_STATE_CHANGED: + if ((GstElement *)GST_MESSAGE_SRC(msg) == worker->pipeline) + _handle_state_changed(msg, worker); + break; + case GST_MESSAGE_APPLICATION: + if (gst_structure_has_name(gst_message_get_structure(msg), + "ckey")) + { + GValue v = {0}; + g_value_init(&v, G_TYPE_INT); + g_value_set_int(&v, worker->colorkey); + mafw_extension_emit_property_changed( + MAFW_EXTENSION(worker->owner), + MAFW_PROPERTY_RENDERER_COLORKEY, + &v); + } + default: break; + } + return TRUE; +} + +/* NOTE this function will possibly be called from a different thread than the + * glib main thread. */ +static void _stream_info_cb(GstObject *pipeline, GParamSpec *unused, + MafwGstRendererWorker *worker) +{ + g_debug("stream-info changed"); + _parse_stream_info(worker); +} + +static void _volume_cb(MafwGstRendererWorkerVolume *wvolume, gdouble volume, + gpointer data) +{ + MafwGstRendererWorker *worker = data; + GValue value = {0, }; + + _reset_volume_and_mute_to_pipeline(worker); + + g_value_init(&value, G_TYPE_UINT); + g_value_set_uint(&value, (guint) (volume * 100.0)); + mafw_extension_emit_property_changed(MAFW_EXTENSION(worker->owner), + MAFW_PROPERTY_RENDERER_VOLUME, + &value); +} + +static void _mute_cb(MafwGstRendererWorkerVolume *wvolume, gboolean mute, + gpointer data) +{ + MafwGstRendererWorker *worker = data; + GValue value = {0, }; + + _reset_volume_and_mute_to_pipeline(worker); + + g_value_init(&value, G_TYPE_BOOLEAN); + g_value_set_boolean(&value, mute); + mafw_extension_emit_property_changed(MAFW_EXTENSION(worker->owner), + MAFW_PROPERTY_RENDERER_MUTE, + &value); +} + +/* TODO: I think it's not enought to act on error, we need to handle + * DestroyNotify on the given window ourselves, because for example helixbin + * does it and silently stops the decoder thread. But it doesn't notify + * us... */ +static int xerror(Display *dpy, XErrorEvent *xev) +{ + MafwGstRendererWorker *worker; + + if (Global_worker == NULL) { + return -1; + } else { + worker = Global_worker; + } + + /* Swallow BadWindow and stop pipeline when the error is about the + * currently set xid. */ + if (worker->xid && + xev->resourceid == worker->xid && + xev->error_code == BadWindow) + { + g_warning("BadWindow received for current xid (%x).", + (gint)xev->resourceid); + worker->xid = 0; + /* We must post a message to the bus, because this function is + * invoked from a different thread (xvimagerenderer's queue). */ + _post_error(worker, g_error_new_literal( + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_PLAYBACK, + "Video window gone")); + } + return 0; +} + +/* + * Resets the media information. + */ +static void _reset_media_info(MafwGstRendererWorker *worker) +{ + if (worker->media.location) { + g_free(worker->media.location); + worker->media.location = NULL; + } + worker->media.length_nanos = -1; + worker->media.has_visual_content = FALSE; + worker->media.seekable = SEEKABILITY_UNKNOWN; + worker->media.video_width = 0; + worker->media.video_height = 0; + worker->media.fps = 0.0; +} + +static void _set_volume_and_mute(MafwGstRendererWorker *worker, gdouble vol, + gboolean mute) +{ + g_return_if_fail(worker->wvolume != NULL); + + mafw_gst_renderer_worker_volume_set(worker->wvolume, vol, mute); +} + +static void _set_volume(MafwGstRendererWorker *worker, gdouble new_vol) +{ + g_return_if_fail(worker->wvolume != NULL); + + _set_volume_and_mute( + worker, new_vol, + mafw_gst_renderer_worker_volume_is_muted(worker->wvolume)); +} + +static void _set_mute(MafwGstRendererWorker *worker, gboolean mute) +{ + g_return_if_fail(worker->wvolume != NULL); + + _set_volume_and_mute( + worker, mafw_gst_renderer_worker_volume_get(worker->wvolume), + mute); +} + +/* + * Start to play the media + */ +static void _start_play(MafwGstRendererWorker *worker) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) worker->owner; + GstStateChangeReturn state_change_info; + + g_assert(worker->pipeline); + g_object_set(G_OBJECT(worker->pipeline), + "uri", worker->media.location, NULL); + + g_debug("URI: %s", worker->media.location); + g_debug("setting pipeline to PAUSED"); + + worker->report_statechanges = TRUE; + state_change_info = gst_element_set_state(worker->pipeline, + GST_STATE_PAUSED); + if (state_change_info == GST_STATE_CHANGE_NO_PREROLL) { + /* FIXME: for live sources we may have to handle + buffering and prerolling differently */ + g_debug ("Source is live!"); + worker->is_live = TRUE; + } + worker->prerolling = TRUE; + + worker->is_stream = uri_is_stream(worker->media.location); + + if (renderer->update_playcount_id > 0) { + g_source_remove(renderer->update_playcount_id); + renderer->update_playcount_id = 0; + } + +} + +/* + * Constructs gst pipeline + * + * FIXME: Could the same pipeline be used for playing all media instead of + * constantly deleting and reconstructing it again? + */ +static void _construct_pipeline(MafwGstRendererWorker *worker) +{ + g_debug("constructing pipeline"); + g_assert(worker != NULL); + + /* Return if we have already one */ + if (worker->pipeline) + return; + + _free_taglist(worker); + + g_debug("Creating a new instance of playbin2"); + worker->pipeline = gst_element_factory_make("playbin2", + "playbin"); + if (worker->pipeline == NULL) + { + /* Let's try with playbin */ + g_warning ("playbin2 failed, falling back to playbin"); + worker->pipeline = gst_element_factory_make("playbin", + "playbin"); + + if (worker->pipeline) { + /* Use nwqueue only for non-rtsp and non-mms(h) + streams. */ + gboolean use_nw; + use_nw = worker->media.location && + !g_str_has_prefix(worker->media.location, + "rtsp://") && + !g_str_has_prefix(worker->media.location, + "mms://") && + !g_str_has_prefix(worker->media.location, + "mmsh://"); + + g_debug("playbin using network queue: %d", use_nw); + + /* These need a modified version of playbin. */ + g_object_set(G_OBJECT(worker->pipeline), + "nw-queue", use_nw, NULL); + g_object_set(G_OBJECT(worker->pipeline), + "no-video-transform", TRUE, NULL); + } + } + + if (!worker->pipeline) { + g_critical("failed to create playback pipeline"); + g_signal_emit_by_name(MAFW_EXTENSION (worker->owner), + "error", + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM, + "Could not create pipeline"); + g_assert_not_reached(); + } + + + worker->bus = gst_pipeline_get_bus(GST_PIPELINE(worker->pipeline)); + gst_bus_set_sync_handler(worker->bus, + (GstBusSyncHandler)_sync_bus_handler, worker); + worker->async_bus_id = gst_bus_add_watch_full(worker->bus,G_PRIORITY_HIGH, + (GstBusFunc)_async_bus_handler, + worker, NULL); + + /* Listen for changes in stream-info object to find out whether the + * media contains video and throw error if application has not provided + * video window. */ + g_signal_connect(worker->pipeline, "notify::stream-info", + G_CALLBACK(_stream_info_cb), worker); + +#ifndef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME + g_object_set(worker->pipeline, "flags", 99, NULL); + + /* Set audio and video sinks ourselves. We create and configure + them only once. */ + if (!worker->asink) { + worker->asink = gst_element_factory_make("pulsesink", NULL); + if (!worker->asink) { + g_critical("Failed to create pipeline audio sink"); + g_signal_emit_by_name(MAFW_EXTENSION (worker->owner), + "error", + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM, + "Could not create audio sink"); + g_assert_not_reached(); + } + gst_object_ref(worker->asink); + g_object_set(worker->asink, "buffer-time", + (gint64) MAFW_GST_BUFFER_TIME, NULL); + g_object_set(worker->asink, "latency-time", + (gint64) MAFW_GST_LATENCY_TIME, NULL); + } + g_object_set(worker->pipeline, "audio-sink", worker->asink, NULL); +#endif + + if (!worker->vsink) { + worker->vsink = gst_element_factory_make("xvimagesink", NULL); + if (!worker->vsink) { + g_critical("Failed to create pipeline video sink"); + g_signal_emit_by_name(MAFW_EXTENSION (worker->owner), + "error", + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM, + "Could not create video sink"); + g_assert_not_reached(); + } + gst_object_ref(worker->vsink); + g_object_set(G_OBJECT(worker->vsink), "handle-events", + TRUE, NULL); + g_object_set(worker->vsink, "force-aspect-ratio", + TRUE, NULL); + } + g_object_set(worker->pipeline, "video-sink", worker->vsink, NULL); +} + +/* + * @seek_type: GstSeekType + * @position: Time in seconds where to seek + */ +static void _do_seek(MafwGstRendererWorker *worker, GstSeekType seek_type, + gint position, GError **error) +{ + gboolean ret; + gint64 spos; + + g_assert(worker != NULL); + + if (worker->eos || !worker->media.seekable) + goto err; + + /* According to the docs, relative seeking is not so easy: + GST_SEEK_TYPE_CUR - change relative to currently configured segment. + This can't be used to seek relative to the current playback position - + do a position query, calculate the desired position and then do an + absolute position seek instead if that's what you want to do. */ + if (seek_type == GST_SEEK_TYPE_CUR) + { + gint curpos = mafw_gst_renderer_worker_get_position(worker); + position = curpos + position; + seek_type = GST_SEEK_TYPE_SET; + } + + if (position < 0) { + position = 0; + } + + worker->seek_position = position; + worker->report_statechanges = FALSE; + spos = (gint64)position * GST_SECOND; + g_debug("seek: type = %d, offset = %lld", seek_type, spos); + + /* If the pipeline has been set to READY by us, then wake it up by + setting it to PAUSED (when we get the READY->PAUSED transition + we will execute the seek). This way when we seek we disable the + READY state (logical, since the player is not idle anymore) + allowing the sink to render the destination frame in case of + video playback */ + if (worker->in_ready && worker->state == GST_STATE_READY) { + gst_element_set_state(worker->pipeline, GST_STATE_PAUSED); + } else { + ret = gst_element_seek(worker->pipeline, 1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH|GST_SEEK_FLAG_KEY_UNIT, + seek_type, spos, + GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); + if (!ret) { + /* Seeking is async, so seek_position should not be + invalidated here */ + goto err; + } + } + return; + +err: g_set_error(error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_CANNOT_SET_POSITION, + "Seeking to %d failed", position); +} + +/* @vol should be between [0 .. 100], higher values (up to 1000) are allowed, + * but probably cause distortion. */ +void mafw_gst_renderer_worker_set_volume( + MafwGstRendererWorker *worker, guint volume) +{ + _set_volume(worker, CLAMP((gdouble)volume / 100.0, 0.0, 1.0)); +} + +guint mafw_gst_renderer_worker_get_volume( + MafwGstRendererWorker *worker) +{ + return (guint) + (mafw_gst_renderer_worker_volume_get(worker->wvolume) * 100); +} + +void mafw_gst_renderer_worker_set_mute(MafwGstRendererWorker *worker, + gboolean mute) +{ + _set_mute(worker, mute); +} + +gboolean mafw_gst_renderer_worker_get_mute(MafwGstRendererWorker *worker) +{ + return mafw_gst_renderer_worker_volume_is_muted(worker->wvolume); +} + +#ifdef HAVE_GDKPIXBUF +void mafw_gst_renderer_worker_set_current_frame_on_pause(MafwGstRendererWorker *worker, + gboolean current_frame_on_pause) +{ + worker->current_frame_on_pause = current_frame_on_pause; +} + +gboolean mafw_gst_renderer_worker_get_current_frame_on_pause(MafwGstRendererWorker *worker) +{ + return worker->current_frame_on_pause; +} +#endif + +void mafw_gst_renderer_worker_set_position(MafwGstRendererWorker *worker, + GstSeekType seek_type, + gint position, GError **error) +{ + /* If player is paused and we have a timeout for going to ready + * restart it. This is logical, since the user is seeking and + * thus, the player is not idle anymore. Also this prevents that + * when seeking streams we enter buffering and in the middle of + * the buffering process we set the pipeline to ready (which stops + * the buffering before it reaches 100%, making the client think + * buffering is still going on). + */ + if (worker->ready_timeout) { + _remove_ready_timeout(worker); + _add_ready_timeout(worker); + } + + _do_seek(worker, seek_type, position, error); + if (worker->notify_seek_handler) + worker->notify_seek_handler(worker, worker->owner); +} + +/* + * Gets current position, rounded down into precision of one second. If a seek + * is pending, returns the position we are going to seek. Returns -1 on + * failure. + */ +gint mafw_gst_renderer_worker_get_position(MafwGstRendererWorker *worker) +{ + GstFormat format; + gint64 time = 0; + g_assert(worker != NULL); + + /* If seek is ongoing, return the position where we are seeking. */ + if (worker->seek_position != -1) + { + return worker->seek_position; + } + /* Otherwise query position from pipeline. */ + format = GST_FORMAT_TIME; + if (worker->pipeline && + gst_element_query_position(worker->pipeline, &format, &time)) + { + return (gint)(NSECONDS_TO_SECONDS(time)); + } + return -1; +} + +GHashTable *mafw_gst_renderer_worker_get_current_metadata( + MafwGstRendererWorker *worker) +{ + return worker->current_metadata; +} + +void mafw_gst_renderer_worker_set_xid(MafwGstRendererWorker *worker, XID xid) +{ + /* Check for errors on the target window */ + XSetErrorHandler(xerror); + + /* Store the target window id */ + g_debug("Setting xid: %x", (guint)xid); + worker->xid = xid; + + /* Check if we should use it right away */ + mafw_gst_renderer_worker_apply_xid(worker); +} + +XID mafw_gst_renderer_worker_get_xid(MafwGstRendererWorker *worker) +{ + return worker->xid; +} + +gboolean mafw_gst_renderer_worker_get_autopaint( + MafwGstRendererWorker *worker) +{ + return worker->autopaint; +} +void mafw_gst_renderer_worker_set_autopaint( + MafwGstRendererWorker *worker, gboolean autopaint) +{ + worker->autopaint = autopaint; + if (worker->vsink) + g_object_set(worker->vsink, "autopaint-colorkey", + autopaint, NULL); +} + +gint mafw_gst_renderer_worker_get_colorkey( + MafwGstRendererWorker *worker) +{ + return worker->colorkey; +} + +gboolean mafw_gst_renderer_worker_get_seekable(MafwGstRendererWorker *worker) +{ + return worker->media.seekable; +} + +static void _play_pl_next(MafwGstRendererWorker *worker) { + gchar *next; + + g_assert(worker != NULL); + g_return_if_fail(worker->pl.items != NULL); + + next = (gchar *) g_slist_nth_data(worker->pl.items, + ++worker->pl.current); + mafw_gst_renderer_worker_stop(worker); + _reset_media_info(worker); + + worker->media.location = g_strdup(next); + _construct_pipeline(worker); + _start_play(worker); +} + +static void _on_pl_entry_parsed(TotemPlParser *parser, gchar *uri, + gpointer metadata, gpointer user_data) +{ + MafwGstRendererWorker *worker = user_data; + + if (uri != NULL) { + worker->pl.items = + g_slist_append(worker->pl.items, g_strdup(uri)); + } +} + +static void _do_play(MafwGstRendererWorker *worker) +{ + g_assert(worker != NULL); + + if (worker->pipeline == NULL) { + g_debug("play without a pipeline!"); + return; + } + worker->report_statechanges = TRUE; + + /* If we have to stay paused, we do and add the ready + * timeout. Otherwise, we move the pipeline */ + if (!worker->stay_paused) { + /* If pipeline is READY, we move it to PAUSED, + * otherwise, to PLAYING */ + if (worker->state == GST_STATE_READY) { + gst_element_set_state(worker->pipeline, + GST_STATE_PAUSED); + g_debug("setting pipeline to PAUSED"); + } else { + _reset_volume_and_mute_to_pipeline(worker); + gst_element_set_state(worker->pipeline, + GST_STATE_PLAYING); + g_debug("setting pipeline to PLAYING"); + } + } + else { + g_debug("staying in PAUSED state"); + _add_ready_timeout(worker); + } +} + +void mafw_gst_renderer_worker_play(MafwGstRendererWorker *worker, + const gchar *uri) +{ + g_assert(uri); + + mafw_gst_renderer_worker_stop(worker); + _reset_media_info(worker); + _reset_pl_info(worker); + /* Check if the item to play is a single item or a playlist. */ + if (uri_is_playlist(uri)){ + /* In case of a playlist we parse it and start playing the first + item of the playlist. */ + TotemPlParser *pl_parser; + gchar *item; + + /* Initialize the playlist parser */ + pl_parser = totem_pl_parser_new (); + g_object_set(pl_parser, "recurse", TRUE, "disable-unsafe", + TRUE, NULL); + g_signal_connect(G_OBJECT(pl_parser), "entry-parsed", + G_CALLBACK(_on_pl_entry_parsed), worker); + + /* Parsing */ + if (totem_pl_parser_parse(pl_parser, uri, FALSE) != + TOTEM_PL_PARSER_RESULT_SUCCESS) { + /* An error happens while parsing */ + _send_error(worker, + g_error_new(MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_PLAYLIST_PARSING, + "Playlist parsing failed: %s", + uri)); + return; + } + + if (!worker->pl.items) { + /* The playlist is empty */ + _send_error(worker, + g_error_new(MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_PLAYLIST_PARSING, + "The playlist %s is empty.", + uri)); + return; + } + + /* Set the playback mode */ + worker->mode = WORKER_MODE_PLAYLIST; + worker->pl.notify_play_pending = TRUE; + + /* Set the item to be played */ + worker->pl.current = 0; + item = (gchar *) g_slist_nth_data(worker->pl.items, 0); + worker->media.location = g_strdup(item); + + /* Free the playlist parser */ + g_object_unref(pl_parser); + } else { + /* Single item. Set the playback mode according to that */ + worker->mode = WORKER_MODE_SINGLE_PLAY; + + /* Set the item to be played */ + worker->media.location = g_strdup(uri); + } + _construct_pipeline(worker); + _start_play(worker); +} + +void mafw_gst_renderer_worker_play_alternatives(MafwGstRendererWorker *worker, + gchar **uris) +{ + gint i; + gchar *item; + + g_assert(uris && uris[0]); + + mafw_gst_renderer_worker_stop(worker); + _reset_media_info(worker); + _reset_pl_info(worker); + + /* Add the uris to playlist */ + i = 0; + while (uris[i]) { + worker->pl.items = + g_slist_append(worker->pl.items, g_strdup(uris[i])); + i++; + } + + /* Set the playback mode */ + worker->mode = WORKER_MODE_REDUNDANT; + worker->pl.notify_play_pending = TRUE; + + /* Set the item to be played */ + worker->pl.current = 0; + item = (gchar *) g_slist_nth_data(worker->pl.items, 0); + worker->media.location = g_strdup(item); + + /* Start playing */ + _construct_pipeline(worker); + _start_play(worker); +} + +/* + * Currently, stop destroys the Gst pipeline and resets the worker into + * default startup configuration. + */ +void mafw_gst_renderer_worker_stop(MafwGstRendererWorker *worker) +{ + g_debug("worker stop"); + g_assert(worker != NULL); + + /* If location is NULL, this is a pre-created pipeline */ + if (worker->async_bus_id && worker->pipeline && !worker->media.location) + return; + + if (worker->pipeline) { + g_debug("destroying pipeline"); + if (worker->async_bus_id) { + g_source_remove(worker->async_bus_id); + worker->async_bus_id = 0; + } + gst_bus_set_sync_handler(worker->bus, NULL, NULL); + gst_element_set_state(worker->pipeline, GST_STATE_NULL); + if (worker->bus) { + gst_object_unref(GST_OBJECT_CAST(worker->bus)); + worker->bus = NULL; + } + gst_object_unref(GST_OBJECT(worker->pipeline)); + worker->pipeline = NULL; + } + + /* Reset worker */ + worker->report_statechanges = TRUE; + worker->state = GST_STATE_NULL; + worker->prerolling = FALSE; + worker->is_live = FALSE; + worker->buffering = FALSE; + worker->is_stream = FALSE; + worker->is_error = FALSE; + worker->eos = FALSE; + worker->seek_position = -1; + _remove_ready_timeout(worker); + _free_taglist(worker); + if (worker->current_metadata) { + g_hash_table_destroy(worker->current_metadata); + worker->current_metadata = NULL; + } + + if (worker->duration_seek_timeout != 0) { + g_source_remove(worker->duration_seek_timeout); + worker->duration_seek_timeout = 0; + } + + /* Reset media iformation */ + _reset_media_info(worker); + + /* We are not playing, so we can let the screen blank */ + blanking_allow(); + + /* And now get a fresh pipeline ready */ + _construct_pipeline(worker); +} + +void mafw_gst_renderer_worker_pause(MafwGstRendererWorker *worker) +{ + g_assert(worker != NULL); + + if (worker->buffering && worker->state == GST_STATE_PAUSED && + !worker->prerolling) { + /* If we are buffering and get a pause, we have to + * signal state change and stay_paused */ + g_debug("Pausing while buffering, signalling state change"); + worker->stay_paused = TRUE; + if (worker->notify_pause_handler) { + worker->notify_pause_handler( + worker, + worker->owner); + } + } else { + worker->report_statechanges = TRUE; + + if (gst_element_set_state(worker->pipeline, GST_STATE_PAUSED) == + GST_STATE_CHANGE_ASYNC) + { + /* XXX this blocks at most 2 seconds. */ + gst_element_get_state(worker->pipeline, NULL, NULL, + 2 * GST_SECOND); + } + blanking_allow(); + } +} + +void mafw_gst_renderer_worker_resume(MafwGstRendererWorker *worker) +{ + if (worker->mode == WORKER_MODE_PLAYLIST || + worker->mode == WORKER_MODE_REDUNDANT) { + /* We must notify play if the "playlist" playback + is resumed */ + worker->pl.notify_play_pending = TRUE; + } + if (worker->buffering && worker->state == GST_STATE_PAUSED && + !worker->prerolling) { + /* If we are buffering we cannot resume, but we know + * that the pipeline will be moved to PLAYING as + * stay_paused is FALSE, so we just activate the state + * change report, this way as soon as buffering is finished + * the pipeline will be set to PLAYING and the state + * change will be reported */ + worker->report_statechanges = TRUE; + g_debug("Resumed while buffering, activating pipeline state " + "changes"); + /* Notice though that we can receive the Resume before + we get any buffering information. In that case + we go with the "else" branch and set the pipeline to + to PLAYING. However, it is possible that in this case + we get the fist buffering signal before the + PAUSED -> PLAYING state change. In that case, since we + ignore state changes while buffering we never signal + the state change to PLAYING. We can only fix this by + checking, when we receive a PAUSED -> PLAYING transition + if we are buffering, and in that case signal the state + change (if we get that transition while buffering + is on, it can only mean that the client resumed playback + while buffering, and we must notify the state change) */ + } else { + _do_play(worker); + } +} + +static void _volume_init_cb(MafwGstRendererWorkerVolume *wvolume, + gpointer data) +{ + MafwGstRendererWorker *worker = data; + gdouble volume; + gboolean mute; + + worker->wvolume = wvolume; + + g_debug("volume manager initialized"); + + volume = mafw_gst_renderer_worker_volume_get(wvolume); + mute = mafw_gst_renderer_worker_volume_is_muted(wvolume); + _volume_cb(wvolume, volume, worker); + _mute_cb(wvolume, mute, worker); +} + +MafwGstRendererWorker *mafw_gst_renderer_worker_new(gpointer owner) +{ + MafwGstRendererWorker *worker; + GMainContext *main_context; + + worker = g_new0(MafwGstRendererWorker, 1); + worker->mode = WORKER_MODE_SINGLE_PLAY; + worker->pl.items = NULL; + worker->pl.current = 0; + worker->pl.notify_play_pending = TRUE; + worker->owner = owner; + worker->report_statechanges = TRUE; + worker->state = GST_STATE_NULL; + worker->seek_position = -1; + worker->ready_timeout = 0; + worker->in_ready = FALSE; + worker->xid = 0; + worker->autopaint = TRUE; + worker->colorkey = -1; + worker->vsink = NULL; + worker->asink = NULL; + worker->tag_list = NULL; + worker->current_metadata = NULL; + +#ifdef HAVE_GDKPIXBUF + worker->current_frame_on_pause = FALSE; + _init_tmp_files_pool(worker); +#endif + worker->notify_seek_handler = NULL; + worker->notify_pause_handler = NULL; + worker->notify_play_handler = NULL; + worker->notify_buffer_status_handler = NULL; + worker->notify_eos_handler = NULL; + worker->notify_error_handler = NULL; + Global_worker = worker; + main_context = g_main_context_default(); + worker->wvolume = NULL; + mafw_gst_renderer_worker_volume_init(main_context, + _volume_init_cb, worker, + _volume_cb, worker, + _mute_cb, worker); + blanking_init(); + _construct_pipeline(worker); + + return worker; +} + +void mafw_gst_renderer_worker_exit(MafwGstRendererWorker *worker) +{ + blanking_deinit(); +#ifdef HAVE_GDKPIXBUF + _destroy_tmp_files_pool(worker); +#endif + mafw_gst_renderer_worker_volume_destroy(worker->wvolume); + mafw_gst_renderer_worker_stop(worker); +} +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/libmafw-gst-renderer/mafw-gst-renderer-worker.h b/libmafw-gst-renderer/mafw-gst-renderer-worker.h new file mode 100644 index 0000000..d59c09a --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer-worker.h @@ -0,0 +1,210 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include +#include "mafw-gst-renderer-worker-volume.h" + +#define MAFW_GST_RENDERER_MAX_TMP_FILES 5 + +typedef struct _MafwGstRendererWorker MafwGstRendererWorker; + +typedef void (*MafwGstRendererWorkerNotifySeekCb)(MafwGstRendererWorker *worker, gpointer owner); +typedef void (*MafwGstRendererWorkerNotifyPauseCb)(MafwGstRendererWorker *worker, gpointer owner); +typedef void (*MafwGstRendererWorkerNotifyPlayCb)(MafwGstRendererWorker *worker, gpointer owner); +typedef void (*MafwGstRendererWorkerNotifyBufferStatusCb)(MafwGstRendererWorker *worker, gpointer owner, gdouble percent); +typedef void (*MafwGstRendererWorkerNotifyEOSCb)(MafwGstRendererWorker *worker, gpointer owner); +typedef void (*MafwGstRendererWorkerNotifyErrorCb)(MafwGstRendererWorker *worker, + gpointer owner, + const GError *error); + +typedef enum { + WORKER_MODE_SINGLE_PLAY, + WORKER_MODE_PLAYLIST, + WORKER_MODE_REDUNDANT, +} PlaybackMode; + +typedef enum { + SEEKABILITY_UNKNOWN = -1, + SEEKABILITY_NO_SEEKABLE, + SEEKABILITY_SEEKABLE, +} SeekabilityType; + +/* + * media: Information about currently selected media. + * location: Current media location + * length_nanos: Length of the media, in nanoseconds + * has_visual_content: the clip contains some visual content (video) + * video_width: If media contains video, this tells the video width + * video_height: If media contains video, this tells the video height + * seekable: Tells whether the media can be seeked + * par_n: Video pixel aspect ratio numerator + * par_d: Video pixel aspect ratio denominator + * owner: Owner of the worker; usually a MafwGstRenderer (FIXME USUALLY?) + * pipeline: Playback pipeline + * bus: Message bus + * state: Current playback pipeline state + * is_stream: Is currently playing media a stream + * muted: Is the audio muted + * eos: Has playback reached EOS already + * is_error: Has there been an error situation + * buffering: Indicates the buffering state + * prerolling: Indicates the prerolling state (NULL -> PAUSED) + * report_statechanges: Report state change bus messages + * current_volume: Current audio volume [0.0 .. 1.0], see playbin:volume + * async_bus_id: ID handle for GstBus + * buffer_probe_id: ID of the video renderer buffer probe + * seek_position: Indicates the pos where to seek, in seconds + * vsink: Video sink element of the pipeline + * asink: Audio sink element of the pipeline + * xid: XID for video playback + * current_frame_on_pause: whether to emit current frame when pausing + */ +struct _MafwGstRendererWorker { + struct { + gchar *location; + gint64 length_nanos; + gboolean has_visual_content; + gint video_width; + gint video_height; + gdouble fps; + SeekabilityType seekable; + gint par_n; + gint par_d; + } media; + PlaybackMode mode; + struct { + GSList *items; + gint current; + gboolean notify_play_pending; + } pl; + gpointer owner; + GstElement *pipeline; + GstBus *bus; + /* GStreamer state we are considering right now */ + GstState state; + MafwGstRendererWorkerVolume *wvolume; + gboolean is_stream; + gboolean muted; + /* we are handing eos or we did */ + gboolean eos; + /* if we are handling (or handled) and error */ + gboolean is_error; + /* pipeline is buffering */ + gboolean buffering; + /* pipeline is prerolling */ + gboolean prerolling; + /* stream is live and doesn't need prerolling */ + gboolean is_live; + /* if we have to stay in paused though a do_play was + * requested. Usually used when pausing in transitioning */ + gboolean stay_paused; + /* this variable should be FALSE while we are hiding state + * changed to the UI. This is that GStreamer can perform + * state_changes without us requiring it, for example, then + * seeking, buffering and so on and we have to hide those + * changes */ + gboolean report_statechanges; + guint async_bus_id; + gint seek_position; + guint ready_timeout; + guint duration_seek_timeout; + /* After some time PAUSED, we set the pipeline to READY in order to + * save resources. This field states if we are in this special + * situation. + * It is set to TRUE when the state change to READY is requested + * and stays like that until we reach again PLAYING state (not PAUSED). + * The reason for this is that when resuming streams, we have to + * move from READY to PAUSED, then seek to the position where the + * stream had been paused, then wait for buffering to finish, and then + * play (and notify the state change to PLAYING), and we have to + * differentiate this case from the one in which we have entered PAUSED + * silently (when we ran out of buffer while playing, because in that + * case, when we are done buffering we want to resume playback silently + * again. + */ + gboolean in_ready; + GstElement *vsink; + GstElement *asink; + XID xid; + gboolean autopaint; + gint colorkey; + GPtrArray *tag_list; + GHashTable *current_metadata; + +#ifdef HAVE_GDKPIXBUF + gboolean current_frame_on_pause; + gchar *tmp_files_pool[MAFW_GST_RENDERER_MAX_TMP_FILES]; + guint8 tmp_files_pool_index; +#endif + + /* Handlers for notifications */ + MafwGstRendererWorkerNotifySeekCb notify_seek_handler; + MafwGstRendererWorkerNotifyPauseCb notify_pause_handler; + MafwGstRendererWorkerNotifyPlayCb notify_play_handler; + MafwGstRendererWorkerNotifyBufferStatusCb notify_buffer_status_handler; + MafwGstRendererWorkerNotifyEOSCb notify_eos_handler; + MafwGstRendererWorkerNotifyErrorCb notify_error_handler; +}; + +G_BEGIN_DECLS + +MafwGstRendererWorker *mafw_gst_renderer_worker_new(gpointer owner); +void mafw_gst_renderer_worker_exit(MafwGstRendererWorker *worker); + +void mafw_gst_renderer_worker_set_volume(MafwGstRendererWorker *worker, + guint vol); +guint mafw_gst_renderer_worker_get_volume(MafwGstRendererWorker *worker); +void mafw_gst_renderer_worker_set_mute(MafwGstRendererWorker *worker, + gboolean mute); +gboolean mafw_gst_renderer_worker_get_mute(MafwGstRendererWorker *worker); +#ifdef HAVE_GDKPIXBUF +void mafw_gst_renderer_worker_set_current_frame_on_pause(MafwGstRendererWorker *worker, + gboolean current_frame_on_pause); +gboolean mafw_gst_renderer_worker_get_current_frame_on_pause(MafwGstRendererWorker *worker); +#endif +void mafw_gst_renderer_worker_set_position(MafwGstRendererWorker *worker, + GstSeekType seek_type, + gint position, + GError **error); +gint mafw_gst_renderer_worker_get_position(MafwGstRendererWorker *worker); +void mafw_gst_renderer_worker_set_xid(MafwGstRendererWorker *worker, XID xid); +XID mafw_gst_renderer_worker_get_xid(MafwGstRendererWorker *worker); +gboolean mafw_gst_renderer_worker_get_autopaint(MafwGstRendererWorker *worker); +void mafw_gst_renderer_worker_set_autopaint(MafwGstRendererWorker *worker, gboolean autopaint); +gint mafw_gst_renderer_worker_get_colorkey(MafwGstRendererWorker *worker); +gboolean mafw_gst_renderer_worker_get_seekable(MafwGstRendererWorker *worker); +GHashTable *mafw_gst_renderer_worker_get_current_metadata(MafwGstRendererWorker *worker); +void mafw_gst_renderer_worker_play(MafwGstRendererWorker *worker, const gchar *uri); +void mafw_gst_renderer_worker_play_alternatives(MafwGstRendererWorker *worker, gchar **uris); +void mafw_gst_renderer_worker_stop(MafwGstRendererWorker *worker); +void mafw_gst_renderer_worker_pause(MafwGstRendererWorker *worker); +void mafw_gst_renderer_worker_resume(MafwGstRendererWorker *worker); + +G_END_DECLS +#endif +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/libmafw-gst-renderer/mafw-gst-renderer.c b/libmafw-gst-renderer/mafw-gst-renderer.c new file mode 100644 index 0000000..6412b13 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer.c @@ -0,0 +1,2191 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include +#include +#include + +#include +#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 +#endif + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer" + +#define is_current_uri_stream(self) \ + (((self)->media != NULL) && ((self)->media->uri != NULL) && \ + uri_is_stream((self)->media->uri)) + +#define GCONF_OSSO_AF "/system/osso/af" +#define GCONF_BATTERY_COVER_OPEN "/system/osso/af/mmc-cover-open" +#define HAL_VIDEOOUT_UDI "/org/freedesktop/Hal/devices" \ + "/platform_soc_audio_logicaldev_input" + +/*---------------------------------------------------------------------------- + Static variable definitions + ----------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + Plugin initialization + ----------------------------------------------------------------------------*/ + +static gboolean mafw_gst_renderer_initialize(MafwRegistry *registry, + GError **error); +static void mafw_gst_renderer_deinitialize(GError **error); + +/*---------------------------------------------------------------------------- + GObject initialization + ----------------------------------------------------------------------------*/ + +static void mafw_gst_renderer_dispose(GObject *object); +static void mafw_gst_renderer_finalize(GObject *object); + +/*---------------------------------------------------------------------------- + Hal callbacks + ----------------------------------------------------------------------------*/ +static void _property_modified(LibHalContext *ctx, const char *udi, + const char *key, dbus_bool_t is_removed, + dbus_bool_t is_added); +static gboolean _tv_out_is_connected(LibHalContext *ctx, const char *udi); + +/*---------------------------------------------------------------------------- + GConf notifications + ----------------------------------------------------------------------------*/ + +static void _battery_cover_open_cb(GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + MafwGstRenderer *renderer); + +/*---------------------------------------------------------------------------- + Gnome VFS notifications + ----------------------------------------------------------------------------*/ + +static void _volume_pre_unmount_cb(GnomeVFSVolumeMonitor *monitor, + GnomeVFSVolume *volume, + MafwGstRenderer *renderer); + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +static void _signal_state_changed(MafwGstRenderer * self); +static void _signal_media_changed(MafwGstRenderer * self); +static void _signal_playlist_changed(MafwGstRenderer * self); +static void _signal_transport_actions_property_changed(MafwGstRenderer * self); + +/*---------------------------------------------------------------------------- + Properties + ----------------------------------------------------------------------------*/ + +static void _set_error_policy(MafwGstRenderer *renderer, MafwRendererErrorPolicy policy); +static MafwRendererErrorPolicy _get_error_policy(MafwGstRenderer *renderer); + +static void mafw_gst_renderer_set_property(MafwExtension *self, const gchar *key, + const GValue *value); +static void mafw_gst_renderer_get_property(MafwExtension *self, const gchar *key, + MafwExtensionPropertyCallback callback, + gpointer user_data); + +/*---------------------------------------------------------------------------- + Metadata + ----------------------------------------------------------------------------*/ + +static void _notify_metadata(MafwSource *cb_source, + const gchar *cb_object_id, + GHashTable *cb_metadata, + gpointer cb_user_data, + const GError *cb_error); + +/*---------------------------------------------------------------------------- + Notification operations + ----------------------------------------------------------------------------*/ + +static void _notify_play(MafwGstRendererWorker *worker, gpointer owner); +static void _notify_pause(MafwGstRendererWorker *worker, gpointer owner); +static void _notify_seek(MafwGstRendererWorker *worker, gpointer owner); +static void _notify_buffer_status(MafwGstRendererWorker *worker, gpointer owner, + gdouble percent); +static void _notify_eos(MafwGstRendererWorker *worker, gpointer owner); +static void _error_handler(MafwGstRendererWorker *worker, gpointer owner, + const GError *error); + +#ifdef HAVE_CONIC +/*---------------------------------------------------------------------------- + Connection + ----------------------------------------------------------------------------*/ + +static void _connection_init(MafwGstRenderer *renderer); +#endif + +/*---------------------------------------------------------------------------- + Plugin initialization + ----------------------------------------------------------------------------*/ + +/* + * Registers the plugin descriptor making this plugin available to the + * framework and applications + */ +G_MODULE_EXPORT MafwPluginDescriptor mafw_gst_renderer_plugin_description = { + { .name = MAFW_GST_RENDERER_PLUGIN_NAME }, + .initialize = mafw_gst_renderer_initialize, + .deinitialize = mafw_gst_renderer_deinitialize, +}; + +static gboolean mafw_gst_renderer_initialize(MafwRegistry *registry, + GError **error) +{ + MafwGstRenderer *self; + + g_assert(registry != NULL); + self = MAFW_GST_RENDERER(mafw_gst_renderer_new(registry)); + mafw_registry_add_extension(registry, MAFW_EXTENSION(self)); + + return TRUE; +} + +static void mafw_gst_renderer_deinitialize(GError **error) +{ +} + +/*---------------------------------------------------------------------------- + GObject initialization + ----------------------------------------------------------------------------*/ + +G_DEFINE_TYPE(MafwGstRenderer, mafw_gst_renderer, MAFW_TYPE_RENDERER); + +static void mafw_gst_renderer_class_init(MafwGstRendererClass *klass) +{ + GObjectClass *gclass = NULL; + MafwRendererClass *renderer_class = NULL; + const gchar *preloaded_plugins[] = {"playback", "uridecodebin", + "coreelements", "typefindfunctions", "omx", "selector", + "autodetect", "pulseaudio", "audioconvert", "audioresample", + "xvimagesink", "ffmpegcolorspace", "videoscale", NULL}; + gint i = 0; + GObject *plugin; + + gclass = G_OBJECT_CLASS(klass); + g_return_if_fail(gclass != NULL); + + renderer_class = MAFW_RENDERER_CLASS(klass); + g_return_if_fail(renderer_class != NULL); + + /* GObject */ + + gclass->dispose = mafw_gst_renderer_dispose; + gclass->finalize = mafw_gst_renderer_finalize; + + /* Playback */ + + renderer_class->play = mafw_gst_renderer_play; + renderer_class->play_object = mafw_gst_renderer_play_object; + renderer_class->stop = mafw_gst_renderer_stop; + renderer_class->pause = mafw_gst_renderer_pause; + renderer_class->resume = mafw_gst_renderer_resume; + renderer_class->get_status = mafw_gst_renderer_get_status; + + /* Playlist operations */ + + renderer_class->assign_playlist = mafw_gst_renderer_assign_playlist; + renderer_class->next = mafw_gst_renderer_next; + renderer_class->previous = mafw_gst_renderer_previous; + renderer_class->goto_index = mafw_gst_renderer_goto_index; + + /* Playback position */ + + renderer_class->set_position = mafw_gst_renderer_set_position; + renderer_class->get_position = mafw_gst_renderer_get_position; + + /* Metadata */ + + renderer_class->get_current_metadata = + mafw_gst_renderer_get_current_metadata; + + /* Properties */ + + MAFW_EXTENSION_CLASS(klass)->get_extension_property = + (gpointer) mafw_gst_renderer_get_property; + MAFW_EXTENSION_CLASS(klass)->set_extension_property = + (gpointer) mafw_gst_renderer_set_property; + + gst_init(NULL, NULL); + gst_pb_utils_init(); + + /* Pre-load some common plugins */ + while (preloaded_plugins[i]) + { + plugin = G_OBJECT(gst_plugin_load_by_name(preloaded_plugins[i])); + if (plugin) + g_object_unref(plugin); + else + g_debug("Can not load plugin: %s", preloaded_plugins[i]); + i++; + } +} + +static void mafw_gst_renderer_init(MafwGstRenderer *self) +{ + MafwGstRenderer *renderer = NULL; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + renderer = MAFW_GST_RENDERER(self); + g_return_if_fail(renderer != NULL); + + mafw_extension_add_property(MAFW_EXTENSION(self), "volume", G_TYPE_UINT); + mafw_extension_add_property(MAFW_EXTENSION(self), "mute", G_TYPE_BOOLEAN); + mafw_extension_add_property(MAFW_EXTENSION(self), "xid", G_TYPE_UINT); + mafw_extension_add_property(MAFW_EXTENSION(self), "error-policy", G_TYPE_UINT); + MAFW_EXTENSION_SUPPORTS_AUTOPAINT(self); + MAFW_EXTENSION_SUPPORTS_COLORKEY(self); +#ifdef HAVE_GDKPIXBUF + mafw_extension_add_property(MAFW_EXTENSION(self), + "current-frame-on-pause", + G_TYPE_BOOLEAN); +#endif + mafw_extension_add_property(MAFW_EXTENSION(self), + MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED, + G_TYPE_BOOLEAN); + MAFW_EXTENSION_SUPPORTS_TRANSPORT_ACTIONS(self); + renderer->media = g_new0(MafwGstRendererMedia, 1); + renderer->media->seekability = SEEKABILITY_UNKNOWN; + renderer->current_state = Stopped; + + renderer->playlist = NULL; + renderer->iterator = NULL; + renderer->seeking_to = -1; + renderer->update_playcount_id = 0; + + self->worker = mafw_gst_renderer_worker_new(self); + + /* Set notification handlers for worker */ + renderer->worker->notify_play_handler = _notify_play; + renderer->worker->notify_pause_handler = _notify_pause; + renderer->worker->notify_seek_handler = _notify_seek; + renderer->worker->notify_error_handler = _error_handler; + renderer->worker->notify_eos_handler = _notify_eos; + renderer->worker->notify_buffer_status_handler = _notify_buffer_status; + + renderer->states = g_new0 (MafwGstRendererState*, _LastMafwPlayState); + renderer->states[Stopped] = + MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_stopped_new(self)); + renderer->states[Transitioning] = + MAFW_GST_RENDERER_STATE( + mafw_gst_renderer_state_transitioning_new(self)); + renderer->states[Playing] = + MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_playing_new(self)); + renderer->states[Paused] = + MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_paused_new(self)); + + renderer->current_state = Stopped; + renderer->resume_playlist = FALSE; + renderer->playback_mode = MAFW_GST_RENDERER_MODE_PLAYLIST; + +#ifdef HAVE_CONIC + renderer->connected = FALSE; + renderer->connection = NULL; + + _connection_init(renderer); +#endif + renderer->gconf_client = gconf_client_get_default(); + gconf_client_add_dir(renderer->gconf_client, GCONF_OSSO_AF, + GCONF_CLIENT_PRELOAD_ONELEVEL, &error); + if (error) { + g_warning("%s", error->message); + g_error_free(error); + error = NULL; + } + + gconf_client_notify_add(renderer->gconf_client, + GCONF_BATTERY_COVER_OPEN, + (GConfClientNotifyFunc) _battery_cover_open_cb, + renderer, + NULL, &error); + + if (error) { + g_warning("%s", error->message); + g_error_free(error); + } + + if (gnome_vfs_init()) { + GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor(); + g_signal_connect(monitor, "volume-pre-unmount", + G_CALLBACK(_volume_pre_unmount_cb), renderer); + } else { + g_warning("Failed to initialize gnome-vfs"); + } +} + +static void mafw_gst_renderer_dispose(GObject *object) +{ + MafwGstRenderer *renderer; + + g_return_if_fail(MAFW_IS_GST_RENDERER(object)); + + renderer = MAFW_GST_RENDERER(object); + + if (renderer->worker != NULL) { + mafw_gst_renderer_worker_exit(renderer->worker); + renderer->seek_pending = FALSE; + g_free(renderer->worker); + renderer->worker = NULL; + } + + if (renderer->registry != NULL) { + g_object_unref(renderer->registry); + renderer->registry = NULL; + } + + if (renderer->states != NULL) { + guint i = 0; + + for (i = 0; i < _LastMafwPlayState; i++) { + if (renderer->states[i] != NULL) + g_object_unref(renderer->states[i]); + } + g_free(renderer->states); + renderer->states = NULL; + } + + if (renderer->hal_ctx != NULL) { + libhal_device_remove_property_watch(renderer->hal_ctx, + HAL_VIDEOOUT_UDI, + NULL); + libhal_ctx_shutdown(renderer->hal_ctx, NULL); + libhal_ctx_free(renderer->hal_ctx); + } + +#ifdef HAVE_CONIC + if (renderer->connection != NULL) { + g_object_unref(renderer->connection); + renderer->connection = NULL; + } +#endif + + if (renderer->gconf_client != NULL) { + g_object_unref(renderer->gconf_client); + renderer->gconf_client = NULL; + } + + G_OBJECT_CLASS(mafw_gst_renderer_parent_class)->dispose(object); +} + +static void mafw_gst_renderer_finalize(GObject *object) +{ + MafwGstRenderer *self = (MafwGstRenderer*) object; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + mafw_gst_renderer_clear_media(self); + + if (self->media) + { + g_free(self->media); + self->media = NULL; + } + + G_OBJECT_CLASS(mafw_gst_renderer_parent_class)->finalize(object); +} + +/** + * mafw_gst_renderer_new: + * @registry: The registry that owns this renderer. + * + * Creates a new MafwGstRenderer object + */ +GObject *mafw_gst_renderer_new(MafwRegistry* registry) +{ + GObject* object; + LibHalContext *ctx; + DBusConnection *conn; + DBusError err; + char **jackets; + char **jack; + gint num_jacks; + + object = g_object_new(MAFW_TYPE_GST_RENDERER, + "uuid", MAFW_GST_RENDERER_UUID, + "name", MAFW_GST_RENDERER_NAME, + "plugin", MAFW_GST_RENDERER_PLUGIN_NAME, + NULL); + g_assert(object != NULL); + MAFW_GST_RENDERER(object)->registry = g_object_ref(registry); + + /* Set default error policy */ + MAFW_GST_RENDERER(object)->error_policy = + MAFW_RENDERER_ERROR_POLICY_CONTINUE; + + MAFW_GST_RENDERER(object)->tv_connected = FALSE; + + /* Setup hal connection for reacting usb cable connected event */ + dbus_error_init(&err); + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + + if (dbus_error_is_set(&err)) { + g_warning("Couldn't setup HAL connection: %s", err.message); + dbus_error_free(&err); + + goto err1; + } + ctx = libhal_ctx_new(); + libhal_ctx_set_dbus_connection(ctx, conn); + libhal_ctx_set_user_data(ctx, object); + + if (libhal_ctx_init(ctx, &err) == FALSE) { + if (dbus_error_is_set(&err)) { + g_warning("Could not initialize hal: %s", err.message); + dbus_error_free(&err); + } else { + g_warning("Could not initialize hal"); + } + goto err2; + } + + libhal_device_add_property_watch(ctx, HAL_VIDEOOUT_UDI, &err); + + if (dbus_error_is_set(&err)) { + g_warning("Could not start watching usb device: %s", + err.message); + dbus_error_free(&err); + + goto err3; + } + libhal_ctx_set_device_property_modified(ctx, _property_modified); + + /* Initializes blanking policy */ + jackets = libhal_find_device_by_capability(ctx, + "input.jack.video-out", + &num_jacks, NULL); + if (jackets != NULL) { + jack = jackets; + while (*jack) { + if (_tv_out_is_connected(ctx, *jack)) { + MAFW_GST_RENDERER(object)->tv_connected = TRUE; + break; + } + jack++; + } + + blanking_control(*jack == NULL); + libhal_free_string_array(jackets); + } + + MAFW_GST_RENDERER(object)->hal_ctx = ctx; + + return object; +err3: + libhal_ctx_shutdown(ctx, NULL); +err2: + libhal_ctx_free(ctx); +err1: + return object; +} + +/** + * mafw_gst_renderer_error_quark: + * + * Fetches the quark representing the domain of the errors in the + * gst renderer + * + * Return value: a quark identifying the error domain of the + * #MafwGstRenderer objects. + * + **/ +GQuark mafw_gst_renderer_error_quark(void) +{ + return g_quark_from_static_string("mafw-gst-renderer-error-quark"); +} + +void mafw_gst_renderer_set_playback_mode(MafwGstRenderer *self, + MafwGstRendererPlaybackMode mode) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + self->playback_mode = mode; +} + +MafwGstRendererPlaybackMode mafw_gst_renderer_get_playback_mode( + MafwGstRenderer *self) +{ + g_return_val_if_fail(MAFW_IS_GST_RENDERER(self), + MAFW_GST_RENDERER_MODE_STANDALONE); + return self->playback_mode; +} + +/*---------------------------------------------------------------------------- + Set Media + ----------------------------------------------------------------------------*/ + +static MafwSource* _get_source(MafwGstRenderer *renderer, + const gchar *object_id) +{ + MafwSource* source; + gchar* sourceid = NULL; + + g_assert(object_id != NULL); + + /* Attempt to find a source that provided the object ID */ + mafw_source_split_objectid(object_id, &sourceid, NULL); + source = MAFW_SOURCE(mafw_registry_get_extension_by_uuid( + renderer->registry, sourceid)); + g_free(sourceid); + + return source; +} + +void mafw_gst_renderer_get_metadata(MafwGstRenderer* self, + const gchar* objectid, + GError **error) +{ + MafwSource* source; + + g_assert(self != NULL); + + /* + * Any error here is an error when trying to Play, so + * it must be handled by error policy. + * Problem: if we get an error here and we are not in + * Transitioning yet (maybe we are still in Stopped state) + * then the policy may move to next and stay Stopped (instead of + * trying to play), so errors need to be handled by the policy + * in an idle callback, so that any error that may happen here + * is not processed until we have moved to Transitioning state + */ + + source = _get_source(self, objectid); + if (source != NULL) + { + /* List of metadata keys that we are interested in when going to + Transitioning state */ + static const gchar * const keys[] = + { MAFW_METADATA_KEY_URI, + MAFW_METADATA_KEY_IS_SEEKABLE, + MAFW_METADATA_KEY_DURATION, + NULL }; + + /* Source found, get metadata */ + mafw_source_get_metadata(source, objectid, + keys, + _notify_metadata, + self); + + } + else + { + /* This is a playback error: execute error policy */ + MafwGstRendererErrorClosure *error_closure; + error_closure = g_new0(MafwGstRendererErrorClosure, 1); + error_closure->renderer = self; + g_set_error (&(error_closure->error), + MAFW_EXTENSION_ERROR, + MAFW_EXTENSION_ERROR_EXTENSION_NOT_AVAILABLE, + "Unable to find source for current object ID"); + g_idle_add(mafw_gst_renderer_manage_error_idle, error_closure); + } +} + +void mafw_gst_renderer_set_object(MafwGstRenderer *self, const gchar *object_id) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) self; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + g_return_if_fail(object_id != NULL); + + /* This is intended to be called only when using play_object(), + * as for playlists we use set_media_playlist() + */ + + /* Stop any ongoing playback */ + mafw_gst_renderer_clear_media(renderer); + + /* Set new object */ + renderer->media->object_id = g_strdup(object_id); + + /* Signal media changed */ + _signal_media_changed(renderer); +} + + +/** + * mafw_gst_renderer_clear_media: + * + * @renderer A #MafwGstRenderer whose media to clear + * + * Clears & frees the renderer's current media details + **/ +void mafw_gst_renderer_clear_media(MafwGstRenderer *self) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + g_return_if_fail(self->media != NULL); + + g_free(self->media->object_id); + self->media->object_id = NULL; + + g_free(self->media->uri); + self->media->uri = NULL; + + g_free(self->media->title); + self->media->title = NULL; + + g_free(self->media->artist); + self->media->artist = NULL; + + g_free(self->media->album); + self->media->album = NULL; + + self->media->duration = 0; + self->media->position = 0; +} + + +/** + * mafw_gst_renderer_set_media_playlist: + * + * @self A #MafwGstRenderer, whose media to set + * + * Set current media from the renderer's playlist, using the current playlist index. + **/ +void mafw_gst_renderer_set_media_playlist(MafwGstRenderer* self) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + /* Get rid of old media details */ + mafw_gst_renderer_clear_media(self); + + if (self->playlist != NULL && + mafw_playlist_iterator_get_size(self->iterator, NULL) > 0) { + /* Get the current item from playlist */ + self->media->object_id = + g_strdup(mafw_playlist_iterator_get_current_objectid(self->iterator)); + } else { + self->media->object_id = NULL; + } + + _signal_media_changed(self); +} + +#ifdef HAVE_CONIC +/*---------------------------------------------------------------------------- + Connection + ----------------------------------------------------------------------------*/ + +static void +_con_ic_status_handler(ConIcConnection *conn, ConIcConnectionEvent *event, + gpointer data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) data; + + g_assert(MAFW_IS_GST_RENDERER(renderer)); + + renderer->connected = + con_ic_connection_event_get_status(event) == + CON_IC_STATUS_CONNECTED; +} + +static void +_connection_init(MafwGstRenderer *renderer) +{ + g_assert (MAFW_IS_GST_RENDERER(renderer)); + + if (renderer->connection == NULL) { + renderer->connection = con_ic_connection_new(); + renderer->connected = FALSE; + + g_assert(renderer->connection != NULL); + } + + g_object_set(renderer->connection, "automatic-connection-events", + TRUE, NULL); + g_signal_connect(renderer->connection, "connection-event", + G_CALLBACK (_con_ic_status_handler), renderer); + + con_ic_connection_connect(renderer->connection, + CON_IC_CONNECT_FLAG_AUTOMATICALLY_TRIGGERED); +} +#endif + +/*---------------------------------------------------------------------------- + Hal callbacks + ----------------------------------------------------------------------------*/ + +static gboolean _tv_out_is_connected(LibHalContext *ctx, const char *udi) +{ + gboolean is_tv_out_jack = FALSE; + char **jack_types; + char **jack; + + if (udi == NULL) { + return FALSE; + } + + jack_types = libhal_device_get_property_strlist(ctx, udi, + "input.jack.type", + NULL); + if (jack_types == NULL) { + return FALSE; + } + + jack = jack_types; + while (*jack) { + if (strcmp(*jack, "video-out") == 0) { + is_tv_out_jack = TRUE; + break; + } else { + jack++; + } + } + + libhal_free_string_array(jack_types); + + return is_tv_out_jack; +} + +static void _property_modified(LibHalContext *ctx, const char *udi, + const char *key, dbus_bool_t is_removed, + dbus_bool_t is_added) +{ + MafwGstRenderer *renderer; + gboolean connected; + GValue value = { 0 }; + + g_debug("HAL property modified! jack changed\n"); + connected = _tv_out_is_connected(ctx, udi); + renderer = MAFW_GST_RENDERER(libhal_ctx_get_user_data(ctx)); + if (renderer->tv_connected != connected) { + /* Notify the change */ + renderer->tv_connected = connected; + g_value_init(&value, G_TYPE_BOOLEAN); + g_value_set_boolean(&value, renderer->tv_connected); + mafw_extension_emit_property_changed( + MAFW_EXTENSION(renderer), + MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED, + &value); + g_value_unset(&value); + } + blanking_control(connected == FALSE); +} + +/*---------------------------------------------------------------------------- + GConf notifications + ----------------------------------------------------------------------------*/ + +static void _battery_cover_open_cb(GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + MafwGstRenderer *renderer) +{ + GConfValue *value = NULL; + gboolean is_cover_open; + + value = gconf_entry_get_value(entry); + is_cover_open = gconf_value_get_bool(value); + + if (is_cover_open) { + /* External mmc could be removed!. */ + const gchar *emmc_path = g_getenv("MMC_MOUNTPOINT"); + + mafw_gst_renderer_state_handle_pre_unmount( + MAFW_GST_RENDERER_STATE( + renderer->states[renderer->current_state]), + emmc_path); + } +} + +/*---------------------------------------------------------------------------- + Gnome VFS notifications + ----------------------------------------------------------------------------*/ + +static void _volume_pre_unmount_cb(GnomeVFSVolumeMonitor *monitor, + GnomeVFSVolume *volume, + MafwGstRenderer *renderer) +{ + gchar *location = gnome_vfs_volume_get_activation_uri(volume); + if (!location) { + return; + } + + mafw_gst_renderer_state_handle_pre_unmount( + MAFW_GST_RENDERER_STATE( + renderer->states[renderer->current_state]), + location); + + g_free(location); +} + +/*---------------------------------------------------------------------------- + Signals + ----------------------------------------------------------------------------*/ + + +/** + * _signal_state_changed: + * @self: A #MafwGstRenderer + * + * Signals state_changed to all UIs + **/ +static void _signal_state_changed(MafwGstRenderer * self) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_signal_emit_by_name(MAFW_RENDERER(self), + "state-changed", self->current_state); +} + +/** + * _signal_playlist_changed: + * @self: A #MafwGstRenderer + * + * Signals playlist update to all UIs + **/ +static void _signal_playlist_changed(MafwGstRenderer * self) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_signal_emit_by_name(MAFW_RENDERER(self), + "playlist-changed", self->playlist); +} + +/** + * _signal_media_changed: + * @self: A #MafwGstRenderer + * + * Signals media_changed to all UIs + **/ +static void _signal_media_changed(MafwGstRenderer *self) +{ + + MafwGstRendererPlaybackMode mode; + gint index; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + mode = mafw_gst_renderer_get_playback_mode(MAFW_GST_RENDERER(self)); + if ((mode == MAFW_GST_RENDERER_MODE_STANDALONE) || + (self->iterator == NULL)) { + index = -1; + } else { + index = mafw_playlist_iterator_get_current_index(self->iterator); + } + + g_signal_emit_by_name(MAFW_RENDERER(self), + "media-changed", + index, + self->media->object_id); +} + +/** + * _signal_transport_actions_property_changed: + * @self: A #MafwGstRenderer + * + * Signals transport_actions property_changed to all UIs + **/ +static void _signal_transport_actions_property_changed(MafwGstRenderer * self) +{ + GValue *value; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + value = mafw_gst_renderer_state_get_property_value( + MAFW_GST_RENDERER_STATE( + self->states[self->current_state]), + MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS); + + if (value) { + mafw_extension_emit_property_changed( + MAFW_EXTENSION(self), + MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS, + value); + g_value_unset(value); + g_free(value); + } +} + + +/*---------------------------------------------------------------------------- + State pattern support + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_set_state(MafwGstRenderer *self, MafwPlayState state) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + self->current_state = state; + _signal_state_changed(self); + _signal_transport_actions_property_changed(self); +} + +void mafw_gst_renderer_play(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_play( + MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]), + &error); + + if (callback != NULL) + callback(self, user_data, error); + if (error) + g_error_free(error); +} + +void mafw_gst_renderer_play_object(MafwRenderer *self, + const gchar *object_id, + MafwRendererPlaybackCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + g_return_if_fail(object_id != NULL); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_play_object( + MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]), + object_id, + &error); + + if (callback != NULL) + callback(self, user_data, error); + if (error) + g_error_free(error); +} + +void mafw_gst_renderer_stop(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + renderer->play_failed_count = 0; + mafw_gst_renderer_state_stop( + MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]), + &error); + + if (callback != NULL) + callback(self, user_data, error); + if (error) + g_error_free(error); +} + + +void mafw_gst_renderer_pause(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_pause( + MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]), + &error); + + if (callback != NULL) + callback(self, user_data, error); + if (error) + g_error_free(error); +} + +void mafw_gst_renderer_resume(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_resume( + MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]), + &error); + + if (callback != NULL) + callback(self, user_data, error); + if (error) + g_error_free(error); +} + +void mafw_gst_renderer_next(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + renderer->play_failed_count = 0; + mafw_gst_renderer_state_next( + MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]), + &error); + + if (callback != NULL) + callback(self, user_data, error); + if (error) + g_error_free(error); +} + +void mafw_gst_renderer_previous(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + renderer->play_failed_count = 0; + mafw_gst_renderer_state_previous( + MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]), + &error); + + if (callback != NULL) + callback(self, user_data, error); + if (error) + g_error_free(error); +} + +void mafw_gst_renderer_goto_index(MafwRenderer *self, guint index, + MafwRendererPlaybackCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + renderer->play_failed_count = 0; + mafw_gst_renderer_state_goto_index( + MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]), + index, + &error); + + if (callback != NULL) + callback(self, user_data, error); + if (error) + g_error_free(error); +} + +void mafw_gst_renderer_get_position(MafwRenderer *self, MafwRendererPositionCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer; + gint pos; + GError *error = NULL; + + g_return_if_fail(callback != NULL); + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + renderer = MAFW_GST_RENDERER(self); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_get_position( + MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]), + &pos, + &error); + + callback(self, pos, user_data, error); + if (error) + g_error_free(error); +} + +void mafw_gst_renderer_set_position(MafwRenderer *self, MafwRendererSeekMode mode, + gint seconds, MafwRendererPositionCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) self; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_set_position( + MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]), + mode, + seconds, + &error); + + if (callback != NULL) + callback(self, seconds, user_data, error); + if (error) + g_error_free(error); +} + +gboolean mafw_gst_renderer_manage_error_idle(gpointer data) +{ + MafwGstRendererErrorClosure *mec = (MafwGstRendererErrorClosure *) data; + + mafw_gst_renderer_manage_error(mec->renderer, mec->error); + if (mec->error) + g_error_free(mec->error); + g_free(mec); + + return FALSE; +} + +static void _run_error_policy(MafwGstRenderer *self, const GError *in_err, + GError **out_err) +{ + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + gboolean play_next = FALSE; + + /* Check what to do on error */ + if (in_err->code == MAFW_EXTENSION_ERROR_OUT_OF_MEMORY) { + play_next = FALSE; + } else { + MafwGstRendererPlaybackMode mode; + + mode = mafw_gst_renderer_get_playback_mode(self); + + if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST) { + /* In playlist mode we try to play next if + error policy suggests so */ + play_next = + (_get_error_policy(self) == + MAFW_RENDERER_ERROR_POLICY_CONTINUE); + } else { + /* In standalone mode, then switch back to playlist + mode and resume if necessary or move to Stopped + otherwise */ + mafw_gst_renderer_set_playback_mode( + self, MAFW_GST_RENDERER_MODE_PLAYLIST); + mafw_gst_renderer_set_media_playlist(self); + if (self->resume_playlist) { + mafw_gst_renderer_play(MAFW_RENDERER(self), + NULL, NULL); + } else { + mafw_gst_renderer_worker_stop(self->worker); + mafw_gst_renderer_set_state(self, Stopped); + } + if (out_err) *out_err = g_error_copy(in_err); + + /* Bail out, he have already managed the error + for the case of standalone mode */ + return; + } + } + + if (play_next) { + if (self->playlist){ + MafwPlaylistIteratorMovementResult result; + + result = mafw_playlist_iterator_move_to_next(self->iterator, + NULL); + self->play_failed_count++; + + if (mafw_playlist_iterator_get_size(self->iterator, + NULL) <= + self->play_failed_count) + { + mafw_gst_renderer_state_stop( + MAFW_GST_RENDERER_STATE(self->states[self->current_state]), + NULL); + self->play_failed_count = 0; + mafw_gst_renderer_set_media_playlist(self); + } else if (result != + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK) { + mafw_playlist_iterator_reset(self->iterator, NULL); + mafw_gst_renderer_set_media_playlist(self); + mafw_gst_renderer_stop(MAFW_RENDERER(self), NULL, NULL); + } else { + mafw_gst_renderer_set_media_playlist(self); + mafw_gst_renderer_play(MAFW_RENDERER(self), NULL, NULL); + } + + if (out_err) *out_err = g_error_copy(in_err); + } + } else { + /* We cannot move to next in the playlist or decided + we do not want to do it, just stop on error */ + mafw_gst_renderer_stop(MAFW_RENDERER(self), NULL, NULL); + if (out_err) *out_err = g_error_copy(in_err); + } +} + +static void _metadata_set_cb(MafwSource *self, const gchar *object_id, + const gchar **failed_keys, gpointer user_data, + const GError *error) +{ + if (error != NULL) { + g_debug("Ignoring error received when setting metadata: " + "%s (%d): %s", g_quark_to_string(error->domain), + error->code, error->message); + } else { + g_debug("Metadata set correctly"); + } +} + +/** + * _update_playcount_metadata_cb: + * @cb_source: The #MafwSource that sent the metadata results + * @cb_object_id: The object ID, whose metadata results were received + * @cb_metadata: GHashTable containing metadata key-value pairs + * @cb_user_data: Optional user data pointer (self) + * @cb_error: Set if any errors occurred during metadata browsing + * + * Receives the results of a metadata request about the playcount. It increases + * it, or sets to 1, and sets the metadata to that. + */ +static void _update_playcount_metadata_cb (MafwSource *cb_source, + const gchar *cb_object_id, + GHashTable *cb_metadata, + gpointer cb_user_data, + const GError *cb_error) +{ + GValue *curval = NULL; + gint curplaycount = -1; + GHashTable *mdata = cb_user_data; + + if (cb_error == NULL) { + if (cb_metadata) + curval = mafw_metadata_first(cb_metadata, + MAFW_METADATA_KEY_PLAY_COUNT); + if (curval && !G_VALUE_HOLDS(curval, G_TYPE_INT)) + goto set_data; + if (curval) + { + curplaycount = g_value_get_int(curval); + curplaycount++; + } + else + { /* Playing at first time, or not supported... */ + curplaycount = 1; + } + if (!mdata) + mdata = mafw_metadata_new(); + mafw_metadata_add_int(mdata, + MAFW_METADATA_KEY_PLAY_COUNT, + curplaycount); + + } else { + g_warning("_playcount_metadata received an error: " + "%s (%d): %s", g_quark_to_string(cb_error->domain), + cb_error->code, cb_error->message); + if (mdata) + g_hash_table_unref(mdata); + return; + } +set_data: + + if (mdata) + { + mafw_source_set_metadata(cb_source, cb_object_id, mdata, + _metadata_set_cb, NULL); + g_hash_table_unref(mdata); + } +} + +/** + * mafw_gst_renderer_add_lastplayed: + * @mdata: Exisiting mdata, or NULL + * + * Sets the MAFW_METADATA_KEY_LAST_PLAYED metadata in the given metadata-table, + * or creates a new metadata-table, and sets the current time there. + */ +static GHashTable *mafw_gst_renderer_add_lastplayed(GHashTable *mdata) +{ + GHashTable *metadata; + GTimeVal timeval; + + + if (!mdata) + metadata = mafw_metadata_new(); + else + metadata = mdata; + + + + g_get_current_time(&timeval); + + mafw_metadata_add_long(metadata, + MAFW_METADATA_KEY_LAST_PLAYED, + timeval.tv_sec); + return metadata; +} + +/** + * mafw_gst_renderer_increase_playcount: + * @self: Gst renderer + * @object_id: The object ID of the touched object + * @mdat: Existing metadatas to add the playcount to, or NULL + * + * Increases the playcount of the given object. + */ +static void mafw_gst_renderer_increase_playcount(MafwGstRenderer* self, + const gchar *object_id, GHashTable *mdat) +{ + MafwSource* source; + + g_assert(self != NULL); + source = _get_source(self, object_id); + if (source != NULL) + { + static const gchar * const keys[] = + { MAFW_METADATA_KEY_PLAY_COUNT, NULL }; + + mafw_source_get_metadata(source, object_id, + keys, + _update_playcount_metadata_cb, + mdat); + + } +} + +/** + * mafw_gst_renderer_update_stats: + * @data: user data + * + * Updates both playcount and lastplayed after a while. + **/ +gboolean mafw_gst_renderer_update_stats(gpointer data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer *) data; + + /* Update stats only for audio content */ + if (renderer->media->object_id && + !renderer->worker->media.has_visual_content) { + GHashTable *mdata = mafw_gst_renderer_add_lastplayed(NULL); + mafw_gst_renderer_increase_playcount(renderer, + renderer->media->object_id, + mdata); + } + renderer->update_playcount_id = 0; + return FALSE; +} + +void mafw_gst_renderer_update_source_duration(MafwGstRenderer *renderer, + gint duration) +{ + GHashTable *metadata; + MafwSource* source; + + source = _get_source(renderer, renderer->media->object_id); + g_return_if_fail(source != NULL); + + renderer->media->duration = duration; + + g_debug("updated source duration to %d", duration); + + metadata = mafw_metadata_new(); + mafw_metadata_add_int(metadata, MAFW_METADATA_KEY_DURATION, duration); + + mafw_source_set_metadata(source, renderer->media->object_id, metadata, + _metadata_set_cb, NULL); + g_hash_table_unref(metadata); +} + +/** + * _notify_metadata: + * @source: The #MafwSource that sent the metadata results + * @objectid: The object ID, whose metadata results were received + * @metadata: GHashTable containing metadata key-value pairs + * @userdata: Optional user data pointer (self) + * @error: Set if any errors occurred during metadata browsing + * + * Receives the results of a metadata request. + */ +static void _notify_metadata (MafwSource *cb_source, + const gchar *cb_object_id, + GHashTable *cb_metadata, + gpointer cb_user_data, + const GError *cb_error) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) cb_user_data; + GError *mafw_error = NULL; + GError *error = NULL; + GValue *mval; + + g_return_if_fail(MAFW_IS_GST_RENDERER(renderer)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + g_debug("running _notify_metadata..."); + + mval = mafw_metadata_first(cb_metadata, MAFW_METADATA_KEY_URI); + + if (cb_error == NULL && mval != NULL) { + mafw_gst_renderer_state_notify_metadata( + MAFW_GST_RENDERER_STATE( + renderer->states[renderer->current_state]), + cb_object_id, + cb_metadata, + &error); + } + else { + g_set_error(&mafw_error, + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_URI_NOT_AVAILABLE, "%s", + cb_error ? cb_error->message : "URI not available"); + mafw_gst_renderer_manage_error(renderer, mafw_error); + g_error_free(mafw_error); + } +} + +static void _notify_play(MafwGstRendererWorker *worker, gpointer owner) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) owner; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(renderer)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + g_debug("running _notify_play..."); + + mafw_gst_renderer_state_notify_play(renderer->states[renderer->current_state], + &error); + + if (error != NULL) { + g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error", + error->domain, + error->code, + error->message); + g_error_free (error); + } +} + +static void _notify_pause(MafwGstRendererWorker *worker, gpointer owner) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) owner; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER (renderer)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_notify_pause(renderer->states[renderer->current_state], + &error); + + if (error != NULL) { + g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error", + error->domain, error->code, + error->message); + g_error_free(error); + } +} + +static void _notify_buffer_status (MafwGstRendererWorker *worker, + gpointer owner, + gdouble percent) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) owner; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(renderer)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_notify_buffer_status( + renderer->states[renderer->current_state], + percent, + &error); + + if (error != NULL) { + g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error", + error->domain, error->code, + error->message); + g_error_free(error); + } +} + +static void _notify_seek(MafwGstRendererWorker *worker, gpointer owner) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) owner; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(renderer)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_notify_seek(renderer->states[renderer->current_state], + &error); + + if (error != NULL) { + g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error", + error->domain, error->code, + error->message); + g_error_free(error); + } +} + +static void _playlist_changed_handler(MafwPlaylistIterator *iterator, + gboolean clip_changed, GQuark domain, + gint code, const gchar *message, + gpointer user_data) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) user_data; + + g_return_if_fail(MAFW_IS_GST_RENDERER(renderer)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + /* We update the current index and media here, for this is + the same for all the states. Then we delegate in the state + to finish the task (for example, start playback if needed) */ + + if (renderer->playlist == NULL) { + g_critical("Got iterator:contents-changed but renderer has no" \ + "playlist assigned!. Skipping..."); + return; + } + + if (domain != 0) { + g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error", + domain, code, message); + } else { + GError *error = NULL; + MafwGstRendererPlaybackMode mode; + + mode = mafw_gst_renderer_get_playback_mode(renderer); + + /* Only in non-playobject mode */ + if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST) + mafw_gst_renderer_set_media_playlist(renderer); + + /* We let the state know if the current clip has changed as + result of this operation, so it can do its work */ + mafw_gst_renderer_state_playlist_contents_changed_handler( + renderer->states[renderer->current_state], + clip_changed, + &error); + + if (error != NULL) { + g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error", + error->domain, error->code, + error->message); + g_error_free(error); + } + } +} + +static void _error_handler(MafwGstRendererWorker *worker, gpointer owner, + const GError *error) +{ + MafwGstRenderer *renderer = MAFW_GST_RENDERER(owner); + + mafw_gst_renderer_manage_error(renderer, error); +} + +void mafw_gst_renderer_manage_error(MafwGstRenderer *self, const GError *error) +{ + GError *new_err = NULL; + GError *raise_error = NULL; + GQuark new_err_domain = MAFW_RENDERER_ERROR; + gint new_err_code = 0; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + + g_return_if_fail((self->states != 0) && + (self->current_state != _LastMafwPlayState) && + (self->states[self->current_state] != NULL)); + + g_warning("Got error in renderer:\n\tdomain: %d, code: %d, message: %s", + error->domain, error->code, error->message); + + /* Get a MAFW error */ + if (error->domain == GST_RESOURCE_ERROR) { + /* handle RESOURCE errors */ + switch (error->code) { + case GST_RESOURCE_ERROR_READ: + if (is_current_uri_stream(self)) { +#ifdef HAVE_CONIC + if (self->connected) { + new_err_code = MAFW_RENDERER_ERROR_STREAM_DISCONNECTED; + } else { + new_err_domain = MAFW_EXTENSION_ERROR; + new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN; + } +#else + /* Stream + cannot read resource -> + disconnected */ + new_err_code = MAFW_RENDERER_ERROR_STREAM_DISCONNECTED; +#endif + } else { + /* This shouldn't happen */ + /* Unknown RESOURCE error */ + new_err_domain = MAFW_EXTENSION_ERROR; + new_err_code = MAFW_EXTENSION_ERROR_FAILED; + } + break; + case GST_RESOURCE_ERROR_NOT_FOUND: +#ifdef HAVE_CONIC + if (!is_current_uri_stream(self) || self->connected) { + new_err_code = + MAFW_RENDERER_ERROR_INVALID_URI; + } else { + new_err_domain = MAFW_EXTENSION_ERROR; + new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN; + } +#else + new_err_code = + MAFW_RENDERER_ERROR_INVALID_URI; +#endif + break; + case GST_RESOURCE_ERROR_OPEN_READ_WRITE: + case GST_RESOURCE_ERROR_OPEN_READ: +#ifdef HAVE_CONIC + if (!is_current_uri_stream(self) || self->connected) { + new_err_code = + MAFW_RENDERER_ERROR_MEDIA_NOT_FOUND; + } else { + new_err_domain = MAFW_EXTENSION_ERROR; + new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN; + } +#else + new_err_code = + MAFW_RENDERER_ERROR_MEDIA_NOT_FOUND; +#endif + break; + case GST_RESOURCE_ERROR_NO_SPACE_LEFT: + new_err_domain = MAFW_EXTENSION_ERROR; + new_err_code = MAFW_EXTENSION_ERROR_OUT_OF_MEMORY; + break; + case GST_RESOURCE_ERROR_WRITE: + /* DSP renderers send ERROR_WRITE when they find + corrupted data */ + new_err_code = MAFW_RENDERER_ERROR_CORRUPTED_FILE; + break; + case GST_RESOURCE_ERROR_SEEK: + new_err_code = MAFW_RENDERER_ERROR_CANNOT_SET_POSITION; + break; + default: + /* Unknown RESOURCE error */ + new_err_domain = MAFW_EXTENSION_ERROR; + new_err_code = MAFW_EXTENSION_ERROR_FAILED; + } + + } else if (error->domain == GST_STREAM_ERROR) { + /* handle STREAM errors */ + switch (error->code) { + case GST_STREAM_ERROR_TYPE_NOT_FOUND: + new_err_code = MAFW_RENDERER_ERROR_TYPE_NOT_AVAILABLE; + break; + case GST_STREAM_ERROR_FORMAT: + case GST_STREAM_ERROR_WRONG_TYPE: + case GST_STREAM_ERROR_FAILED: + new_err_code = MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE; + break; + case GST_STREAM_ERROR_DECODE: + case GST_STREAM_ERROR_DEMUX: + new_err_code = MAFW_RENDERER_ERROR_CORRUPTED_FILE; + break; + case GST_STREAM_ERROR_CODEC_NOT_FOUND: + new_err_code = MAFW_RENDERER_ERROR_CODEC_NOT_FOUND; + break; + case GST_STREAM_ERROR_DECRYPT: + case GST_STREAM_ERROR_DECRYPT_NOKEY: + new_err_code = MAFW_RENDERER_ERROR_DRM; + break; + default: + /* Unknown STREAM error */ + new_err_domain = MAFW_EXTENSION_ERROR; + new_err_code = MAFW_EXTENSION_ERROR_FAILED; + } + } else if (error->domain == MAFW_GST_RENDERER_ERROR) { + /* Handle own errors. Errors that belong to this domain: + - MAFW_GST_RENDERER_ERROR_PLUGIN_NOT_FOUND, + - MAFW_GST_RENDERER_ERROR_VIDEO_CODEC_NOT_SUPPORTED, + - MAFW_GST_RENDERER_ERROR_AUDIO_CODEC_NOT_SUPPORTED */ + new_err_code = MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE; + } else if (error->domain == MAFW_RENDERER_ERROR) { + /* Worker may have sent MAFW_RENDERER_ERROR as well. + No processing needed */ + new_err_code = error->code; + } else { + /* default */ + /* Unknown error */ + new_err_domain = MAFW_EXTENSION_ERROR; + new_err_code = MAFW_EXTENSION_ERROR_FAILED; + } + + g_set_error(&new_err, new_err_domain, new_err_code, "%s", error->message); + + _run_error_policy(self, new_err, &raise_error); + g_error_free(new_err); + + if (raise_error) { + g_signal_emit_by_name(MAFW_EXTENSION (self), "error", + raise_error->domain, + raise_error->code, + raise_error->message); + g_error_free(raise_error); + } +} + +static void _notify_eos(MafwGstRendererWorker *worker, gpointer owner) +{ + MafwGstRenderer *renderer = (MafwGstRenderer*) owner; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER (renderer)); + + g_return_if_fail((renderer->states != 0) && + (renderer->current_state != _LastMafwPlayState) && + (renderer->states[renderer->current_state] != NULL)); + + mafw_gst_renderer_state_notify_eos(renderer->states[renderer->current_state], + &error); + + if (error != NULL) { + g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error", + error->domain, error->code, + error->message); + g_error_free(error); + } +} + +/*---------------------------------------------------------------------------- + Status + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_get_status(MafwRenderer *self, MafwRendererStatusCB callback, + gpointer user_data) +{ + MafwGstRenderer* renderer; + gint index; + MafwGstRendererPlaybackMode mode; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + g_return_if_fail(callback != NULL); + renderer = MAFW_GST_RENDERER(self); + + mode = mafw_gst_renderer_get_playback_mode(MAFW_GST_RENDERER(self)); + if ((mode == MAFW_GST_RENDERER_MODE_STANDALONE) || (renderer->iterator == NULL)) { + index = -1; + } else { + index = + mafw_playlist_iterator_get_current_index(renderer->iterator); + } + + /* TODO: Set error parameter */ + callback(self, renderer->playlist, index, renderer->current_state, + (const gchar*) renderer->media->object_id, user_data, NULL); +} + +void mafw_gst_renderer_get_current_metadata(MafwRenderer *self, + MafwRendererMetadataResultCB callback, + gpointer user_data) +{ + MafwGstRenderer *renderer; + GHashTable *metadata; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + renderer = MAFW_GST_RENDERER(self); + + metadata = mafw_gst_renderer_worker_get_current_metadata( + renderer->worker); + + callback(self, + (const gchar*) renderer->media->object_id, + metadata, + user_data, + NULL); +} + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +static void +_playlist_contents_changed_handler(MafwPlaylist *playlist, + guint from, guint nremove, + guint nreplace, + MafwGstRenderer *renderer) +{ + /* Item(s) added to playlist, so new playable items could come */ + if (nreplace) + renderer->play_failed_count = 0; +} + +gboolean mafw_gst_renderer_assign_playlist(MafwRenderer *self, + MafwPlaylist *playlist, + GError **error) +{ + MafwGstRenderer* renderer = (MafwGstRenderer*) self; + + g_return_val_if_fail(MAFW_IS_GST_RENDERER(self), FALSE); + + /* Get rid of previously assigned playlist */ + if (renderer->playlist != NULL) { + g_signal_handlers_disconnect_matched(renderer->iterator, + (GSignalMatchType) G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, + _playlist_changed_handler, + NULL); + g_signal_handlers_disconnect_matched(renderer->playlist, + (GSignalMatchType) G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, + G_CALLBACK(_playlist_contents_changed_handler), + NULL); + /* Decrement the use count of the previous playlist because the + renderer isn't going to use it more */ + mafw_playlist_decrement_use_count(renderer->playlist, NULL); + + g_object_unref(renderer->iterator); + g_object_unref(renderer->playlist); + } + + /* Assign the new playlist */ + if (playlist == NULL) { + renderer->playlist = NULL; + renderer->iterator = NULL; + } else { + GError *new_error = NULL; + MafwPlaylistIterator *iterator = NULL; + + iterator = mafw_playlist_iterator_new(); + mafw_playlist_iterator_initialize(iterator, playlist, + &new_error); + + g_object_ref(playlist); + + if (new_error == NULL) { + + renderer->playlist = playlist; + renderer->iterator = iterator; + + /* Increment the use_count to avoid the playlist destruction + while the playlist is assigned to some renderer */ + mafw_playlist_increment_use_count(renderer->playlist, NULL); + + g_signal_connect(iterator, + "playlist-changed", + G_CALLBACK(_playlist_changed_handler), + renderer); + g_signal_connect(renderer->playlist, + "contents-changed", + G_CALLBACK(_playlist_contents_changed_handler), + renderer); + } + else { + g_propagate_error (error, new_error); + } + } + + /* Set the new media and signal playlist changed signal */ + _signal_playlist_changed(renderer); + mafw_gst_renderer_set_media_playlist(renderer); + + + /* Stop playback */ + mafw_gst_renderer_stop(MAFW_RENDERER(renderer), NULL , NULL); + + return TRUE; +} + +MafwGstRendererMovementResult mafw_gst_renderer_move(MafwGstRenderer *renderer, + MafwGstRendererMovementType type, + guint index, + GError **error) +{ + MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK; + + if (renderer->playlist == NULL) { + value = MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST; + } else { + MafwPlaylistIteratorMovementResult result; + + switch (type) { + case MAFW_GST_RENDERER_MOVE_TYPE_INDEX: + result = + mafw_playlist_iterator_move_to_index(renderer->iterator, + index, + error); + break; + case MAFW_GST_RENDERER_MOVE_TYPE_PREV: + result = + mafw_playlist_iterator_move_to_prev(renderer->iterator, + error); + break; + case MAFW_GST_RENDERER_MOVE_TYPE_NEXT: + result = + mafw_playlist_iterator_move_to_next(renderer->iterator, + error); + break; + } + + switch (result) { + case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK: + value = MAFW_GST_RENDERER_MOVE_RESULT_OK; + mafw_gst_renderer_set_media_playlist(renderer); + break; + case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID: + g_critical("Iterator is invalid!"); + value = MAFW_GST_RENDERER_MOVE_RESULT_ERROR; + break; + case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR: + value = MAFW_GST_RENDERER_MOVE_RESULT_ERROR; + break; + case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT: + value = MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT; + break; + } + } + + return value; +} + +/*---------------------------------------------------------------------------- + Properties + ----------------------------------------------------------------------------*/ + +static void _set_error_policy(MafwGstRenderer *renderer, MafwRendererErrorPolicy policy) +{ + renderer->error_policy = policy; +} + +static MafwRendererErrorPolicy _get_error_policy(MafwGstRenderer *renderer) +{ + return renderer->error_policy; +} + +static void mafw_gst_renderer_get_property(MafwExtension *self, + const gchar *key, + MafwExtensionPropertyCallback callback, + gpointer user_data) +{ + MafwGstRenderer *renderer; + GValue *value = NULL; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + g_return_if_fail(callback != NULL); + g_return_if_fail(key != NULL); + + renderer = MAFW_GST_RENDERER(self); + if (!strcmp(key, MAFW_PROPERTY_RENDERER_VOLUME)) { + guint volume; + + volume = mafw_gst_renderer_worker_get_volume( + renderer->worker); + + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_UINT); + g_value_set_uint(value, volume); + } + else if (!strcmp(key, MAFW_PROPERTY_RENDERER_MUTE)) { + gboolean mute; + mute = mafw_gst_renderer_worker_get_mute(renderer->worker); + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_BOOLEAN); + g_value_set_boolean(value, mute); + } + else if (!strcmp (key, MAFW_PROPERTY_RENDERER_XID)) { + guint xid; + xid = mafw_gst_renderer_worker_get_xid(renderer->worker); + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_UINT); + g_value_set_uint(value, xid); + } + else if (!strcmp(key, MAFW_PROPERTY_RENDERER_ERROR_POLICY)) { + guint policy; + policy = _get_error_policy(renderer); + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_UINT); + g_value_set_uint(value, policy); + } + else if (!strcmp(key, MAFW_PROPERTY_RENDERER_AUTOPAINT)) { + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_BOOLEAN); + g_value_set_boolean( + value, + mafw_gst_renderer_worker_get_autopaint( + renderer->worker)); + } else if (!strcmp(key, MAFW_PROPERTY_RENDERER_COLORKEY)) { + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_INT); + g_value_set_int( + value, + mafw_gst_renderer_worker_get_colorkey( + renderer->worker)); + } +#ifdef HAVE_GDKPIXBUF + else if (!strcmp(key, + MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE)) { + gboolean current_frame_on_pause; + current_frame_on_pause = + mafw_gst_renderer_worker_get_current_frame_on_pause(renderer->worker); + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_BOOLEAN); + g_value_set_boolean(value, current_frame_on_pause); + } +#endif + else if (!strcmp(key, + MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED)) { + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_BOOLEAN); + g_value_set_boolean(value, renderer->tv_connected); + } + else if (!strcmp(key, + MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)){ + /* Delegate in the state. */ + value = mafw_gst_renderer_state_get_property_value( + MAFW_GST_RENDERER_STATE( + renderer->states[renderer->current_state]), + MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS); + + if (!value) { + /* Something goes wrong. */ + error = g_error_new( + MAFW_GST_RENDERER_ERROR, + MAFW_EXTENSION_ERROR_GET_PROPERTY, + "Error while getting the property value"); + } + } + else { + /* Unsupported property */ + error = g_error_new(MAFW_GST_RENDERER_ERROR, + MAFW_EXTENSION_ERROR_GET_PROPERTY, + "Unsupported property"); + } + + callback(self, key, value, user_data, error); +} + +static void mafw_gst_renderer_set_property(MafwExtension *self, + const gchar *key, + const GValue *value) +{ + MafwGstRenderer *renderer; + + g_return_if_fail(MAFW_IS_GST_RENDERER(self)); + g_return_if_fail(key != NULL); + + renderer = MAFW_GST_RENDERER(self); + + if (!strcmp(key, MAFW_PROPERTY_RENDERER_VOLUME)) { + guint volume = g_value_get_uint(value); + if (volume > 100) + volume = 100; + mafw_gst_renderer_worker_set_volume(renderer->worker, + volume); + /* Property-changed emision is done by worker */ + return; + } + else if (!strcmp(key, MAFW_PROPERTY_RENDERER_MUTE)) { + gboolean mute = g_value_get_boolean(value); + mafw_gst_renderer_worker_set_mute(renderer->worker, mute); + } + else if (!strcmp(key, MAFW_PROPERTY_RENDERER_XID)) { + XID xid = g_value_get_uint(value); + mafw_gst_renderer_worker_set_xid(renderer->worker, xid); + } + else if (!strcmp(key, MAFW_PROPERTY_RENDERER_ERROR_POLICY)) { + MafwRendererErrorPolicy policy = g_value_get_uint(value); + _set_error_policy(renderer, policy); + } + else if (!strcmp(key, MAFW_PROPERTY_RENDERER_AUTOPAINT)) { + mafw_gst_renderer_worker_set_autopaint( + renderer->worker, + g_value_get_boolean(value)); + } +#ifdef HAVE_GDKPIXBUF + else if (!strcmp(key, + MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE)) { + gboolean current_frame_on_pause = g_value_get_boolean(value); + mafw_gst_renderer_worker_set_current_frame_on_pause(renderer->worker, + current_frame_on_pause); + } +#endif + else return; + + /* FIXME I'm not sure when to emit property-changed signals. + * Maybe we should let the worker do it, when the change + * reached the hardware... */ + mafw_extension_emit_property_changed(self, key, value); +} + +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/libmafw-gst-renderer/mafw-gst-renderer.h b/libmafw-gst-renderer/mafw-gst-renderer.h new file mode 100644 index 0000000..0350e34 --- /dev/null +++ b/libmafw-gst-renderer/mafw-gst-renderer.h @@ -0,0 +1,293 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 +#endif + +typedef enum { + MAFW_GST_RENDERER_ERROR_PLUGIN_NOT_FOUND, + MAFW_GST_RENDERER_ERROR_VIDEO_CODEC_NOT_SUPPORTED, + MAFW_GST_RENDERER_ERROR_AUDIO_CODEC_NOT_SUPPORTED, +} MafwGstRendererError; + +typedef enum { + MAFW_GST_RENDERER_MODE_PLAYLIST, + MAFW_GST_RENDERER_MODE_STANDALONE, +} MafwGstRendererPlaybackMode; + +typedef enum { + MAFW_GST_RENDERER_MOVE_RESULT_OK, + MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST, + MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT, + MAFW_GST_RENDERER_MOVE_RESULT_ERROR, +} MafwGstRendererMovementResult; + +typedef enum { + MAFW_GST_RENDERER_MOVE_TYPE_INDEX, + MAFW_GST_RENDERER_MOVE_TYPE_PREV, + MAFW_GST_RENDERER_MOVE_TYPE_NEXT, +} MafwGstRendererMovementType; + +#ifdef HAVE_GDKPIXBUF +#define MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE \ + "current-frame-on-pause" +#endif + +#define MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED "tv-connected" + +/*---------------------------------------------------------------------------- + GObject type conversion macros + ----------------------------------------------------------------------------*/ + +#define MAFW_TYPE_GST_RENDERER \ + (mafw_gst_renderer_get_type()) +#define MAFW_GST_RENDERER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_GST_RENDERER, MafwGstRenderer)) +#define MAFW_IS_GST_RENDERER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_GST_RENDERER)) +#define MAFW_GST_RENDERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_GST_RENDERER, MafwGstRenderer)) +#define MAFW_GST_RENDERER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_GST_RENDERER, \ + MafwGstRendererClass)) +#define MAFW_IS_GST_RENDERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_GST_RENDERER)) + +#define MAFW_GST_RENDERER_ERROR (mafw_gst_renderer_error_quark ()) + +/* Gst renderer plugin name for the plugin descriptor */ +#define MAFW_GST_RENDERER_PLUGIN_NAME "Mafw-Gst-Renderer-Plugin" +/* Gst renderer name */ +#define MAFW_GST_RENDERER_NAME "Mafw-Gst-Renderer" +/* Gst renderer UUID */ +#define MAFW_GST_RENDERER_UUID "gstrenderer" + +/*---------------------------------------------------------------------------- + Type definitions + ----------------------------------------------------------------------------*/ + +typedef struct { + gchar *object_id; + gchar *uri; + gchar *title; + gchar *artist; + gchar *album; + + gint duration; + gint position; + + /* Seekability coming from source */ + SeekabilityType seekability; +} MafwGstRendererMedia; + +struct _MafwGstRendererClass { + MafwRendererClass parent; +}; + +/* + * media: Current media details + * worker: Worker + * registry: The registry that owns this renderer + * media_timer: Stream timer data + * current_state: The renderer's current state + * playlist: The renderer's playlist + * play_index: A playlist index that is currently playing + * seek_pending: Seek is pending or ongoing + * seek_type_pending: Type of the pending seek + * seeking_to: The position of pending seek (milliseconds) + * is_stream: is the URI a stream? + * play_failed_count: The number of unably played items from the playlist. + * playback_mode: Playback mode + * resume_playlist: Do we want to resume playlist playback when play_object + * is finished + * states: State array + * error_policy: error policy + * tv_connected: if TV-out cable is connected + */ +struct _MafwGstRenderer{ + MafwRenderer parent; + + MafwGstRendererMedia *media; + MafwGstRendererWorker *worker; + MafwRegistry *registry; + LibHalContext *hal_ctx; + MafwPlayState current_state; + MafwPlaylist *playlist; + MafwPlaylistIterator *iterator; + gboolean seek_pending; + GstSeekType seek_type_pending; + gint seeking_to; + gboolean is_stream; + gint update_playcount_id; + guint play_failed_count; + + MafwGstRendererPlaybackMode playback_mode; + gboolean resume_playlist; + MafwGstRendererState **states; + MafwRendererErrorPolicy error_policy; + gboolean tv_connected; + +#ifdef HAVE_CONIC + gboolean connected; + ConIcConnection *connection; +#endif + GConfClient *gconf_client; +}; + +typedef struct { + MafwGstRenderer *renderer; + GError *error; +} MafwGstRendererErrorClosure; + +G_BEGIN_DECLS + +GType mafw_gst_renderer_get_type(void); +GObject *mafw_gst_renderer_new(MafwRegistry *registry); +GQuark mafw_gst_renderer_error_quark(void); + +/*---------------------------------------------------------------------------- + Playback + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_play(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data); +void mafw_gst_renderer_play_object(MafwRenderer *self, const gchar *object_id, + MafwRendererPlaybackCB callback, + gpointer user_data); +void mafw_gst_renderer_stop(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data); +void mafw_gst_renderer_pause(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data); +void mafw_gst_renderer_resume(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data); + +/*---------------------------------------------------------------------------- + Status + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_get_status(MafwRenderer *self, MafwRendererStatusCB callback, + gpointer user_data); + +/*---------------------------------------------------------------------------- + Set Media + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_set_object(MafwGstRenderer *self, const gchar *object_id); +void mafw_gst_renderer_clear_media(MafwGstRenderer *self); + +/*---------------------------------------------------------------------------- + Metadata + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_get_metadata(MafwGstRenderer* self, const gchar* objectid, + GError **error); +gboolean mafw_gst_renderer_update_stats(gpointer data); + +/*---------------------------------------------------------------------------- + Playlist + ----------------------------------------------------------------------------*/ + +gboolean mafw_gst_renderer_assign_playlist(MafwRenderer *self, + MafwPlaylist *playlist, + GError **error); +void mafw_gst_renderer_next(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data); +void mafw_gst_renderer_previous(MafwRenderer *self, MafwRendererPlaybackCB callback, + gpointer user_data); +void mafw_gst_renderer_goto_index(MafwRenderer *self, guint index, + MafwRendererPlaybackCB callback, + gpointer user_data); +MafwGstRendererMovementResult mafw_gst_renderer_move(MafwGstRenderer *renderer, + MafwGstRendererMovementType type, + guint index, + GError **error); + +/*---------------------------------------------------------------------------- + Set media + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_set_media_playlist(MafwGstRenderer* self); + +/*---------------------------------------------------------------------------- + Position + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_set_position(MafwRenderer *self, + MafwRendererSeekMode mode, gint seconds, + MafwRendererPositionCB callback, + gpointer user_data); +void mafw_gst_renderer_get_position(MafwRenderer *self, MafwRendererPositionCB callback, + gpointer user_data); + +/*---------------------------------------------------------------------------- + Metadata + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_get_current_metadata(MafwRenderer *self, + MafwRendererMetadataResultCB callback, + gpointer user_data); + +/*---------------------------------------------------------------------------- + Local API + ----------------------------------------------------------------------------*/ + +void mafw_gst_renderer_set_state(MafwGstRenderer *self, MafwPlayState state); + +gboolean mafw_gst_renderer_manage_error_idle(gpointer data); + +void mafw_gst_renderer_manage_error(MafwGstRenderer *self, const GError *error); + +void mafw_gst_renderer_set_playback_mode(MafwGstRenderer *self, + MafwGstRendererPlaybackMode mode); + +MafwGstRendererPlaybackMode mafw_gst_renderer_get_playback_mode( + MafwGstRenderer *self); + +void mafw_gst_renderer_update_source_duration(MafwGstRenderer *renderer, + gint duration); + +G_END_DECLS + +#endif + +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/libmafw-gst-renderer/mafw-playlist-iterator.c b/libmafw-gst-renderer/mafw-playlist-iterator.c new file mode 100644 index 0000000..4a8b869 --- /dev/null +++ b/libmafw-gst-renderer/mafw-playlist-iterator.c @@ -0,0 +1,521 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "mafw-playlist-iterator.h" +#include "mafw-gst-renderer-marshal.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "mafw-gst-renderer-playlist-iterator" + +struct _MafwPlaylistIteratorPrivate { + MafwPlaylist *playlist; + gint current_index; + gchar *current_objectid; + gint size; +}; + +typedef gboolean (*movement_function) (MafwPlaylist *playlist, + guint *index, + gchar **objectid, + GError **error); + +enum { + PLAYLIST_CHANGED = 0, + LAST_SIGNAL, +}; + +static guint mafw_playlist_iterator_signals[LAST_SIGNAL]; + +G_DEFINE_TYPE(MafwPlaylistIterator, mafw_playlist_iterator, G_TYPE_OBJECT); + +static void +mafw_playlist_iterator_dispose(GObject *object) +{ + MafwPlaylistIterator *iterator = (MafwPlaylistIterator *) object; + + g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator)); + + mafw_playlist_iterator_invalidate(iterator); + + G_OBJECT_CLASS(mafw_playlist_iterator_parent_class)->dispose(object); +} + +static void +mafw_playlist_iterator_class_init(MafwPlaylistIteratorClass *klass) +{ + GObjectClass *gclass = NULL; + + gclass = G_OBJECT_CLASS(klass); + g_return_if_fail(gclass != NULL); + + g_type_class_add_private(klass, sizeof(MafwPlaylistIteratorPrivate)); + + gclass->dispose = mafw_playlist_iterator_dispose; + + mafw_playlist_iterator_signals[PLAYLIST_CHANGED] = + g_signal_new("playlist-changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(MafwPlaylistIteratorClass, playlist_changed), + NULL, + NULL, + mafw_gst_renderer_marshal_VOID__BOOLEAN_UINT_INT_STRING, + G_TYPE_NONE, + 4, + G_TYPE_BOOLEAN, + G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING); +} + +static void +mafw_playlist_iterator_init(MafwPlaylistIterator *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + MAFW_TYPE_PLAYLIST_ITERATOR, + MafwPlaylistIteratorPrivate); +} + +static void +mafw_playlist_iterator_set_data(MafwPlaylistIterator *iterator, gint index, + gchar *objectid) +{ + g_assert(mafw_playlist_iterator_is_valid(iterator)); + + g_free(iterator->priv->current_objectid); + iterator->priv->current_index = index; + iterator->priv->current_objectid = objectid; +} + +static MafwPlaylistIteratorMovementResult +mafw_playlist_iterator_move_to_next_in_direction(MafwPlaylistIterator *iterator, + movement_function get_next_in_direction, + GError **error) +{ + gint index; + gchar *objectid = NULL; + GError *new_error = NULL; + gboolean playlist_movement_result = FALSE; + MafwPlaylistIteratorMovementResult iterator_movement_result = + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK; + + g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator), + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID); + + index = iterator->priv->current_index; + + playlist_movement_result = + get_next_in_direction (iterator->priv->playlist, + (guint *) &index, + &objectid, &new_error); + + if (new_error != NULL) { + g_propagate_error(error, new_error); + iterator_movement_result = + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR; + } else if (playlist_movement_result) { + mafw_playlist_iterator_set_data(iterator, index, objectid); + } else { + iterator_movement_result = + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT; + } + + return iterator_movement_result; +} + +static void +mafw_playlist_iterator_playlist_contents_changed_handler(MafwPlaylist *playlist, + guint from, + guint nremove, + guint nreplace, + gpointer user_data) +{ + gint play_index; + gboolean clip_changed = FALSE; + GError *error = NULL; + MafwPlaylistIterator *iterator = (MafwPlaylistIterator*) user_data; + + g_return_if_fail(MAFW_IS_PLAYLIST(playlist)); + g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator)); + + if (iterator->priv->playlist == NULL) { + g_critical("Got playlist:contents-changed but renderer has no" \ + "playlist assigned!. Skipping..."); + return; + } + + play_index = iterator->priv->current_index; + iterator->priv->size += nreplace; + + if (nremove > 0) { + /* Items have been removed from the playlist */ + iterator->priv->size -= nremove; + if ((play_index >= from) && + (play_index < from + nremove)) { + /* The current index has been removed */ + guint pls_size = + mafw_playlist_iterator_get_size(iterator, + &error); + if (error == NULL) { + /* Is the current index invalid now? If not, + set current item to the last in the playlist, + otherwise the keep the index and update the + media */ + if (pls_size == 0) { + mafw_playlist_iterator_set_data(iterator, -1, NULL); + } else if (play_index >= pls_size) { + mafw_playlist_iterator_move_to_index(iterator, + pls_size - 1, + &error); + } else { + mafw_playlist_iterator_update(iterator, + &error); + } + + clip_changed = TRUE; + } + } else if (from < play_index) { + /* The current index has been moved towards + the head of the playlist */ + play_index -= nremove; + if (play_index < 0) { + play_index = 0; + } + mafw_playlist_iterator_move_to_index(iterator, + play_index, + &error); + } + } else if (nremove == 0) { + /* Items have been inserted in the playlist */ + if (play_index == -1) { + /* First item has been added to an empty playlist */ + mafw_playlist_iterator_reset(iterator, + &error); + clip_changed = TRUE; + } else if (play_index >= from) { + /* The current item has been moved towards the + tail of the playlist */ + mafw_playlist_iterator_move_to_index(iterator, + play_index + nreplace, + &error); + } + } + + if (error != NULL) { + g_critical("playlist::contents-changed handler failed " + "with \"%s\"", error->message); + g_signal_emit(iterator, + mafw_playlist_iterator_signals[PLAYLIST_CHANGED], + 0, FALSE, error->domain, error->code, error->message); + g_error_free (error); + } else { + g_signal_emit(iterator, + mafw_playlist_iterator_signals[PLAYLIST_CHANGED], + 0, clip_changed, 0, 0, NULL); + } +} + +static void +mafw_playlist_iterator_playlist_item_moved_handler(MafwPlaylist *playlist, + guint from, + guint to, + gpointer user_data) +{ + MafwPlaylistIterator *iterator = (MafwPlaylistIterator *) user_data; + gint play_index; + GError *error = NULL; + + g_return_if_fail(MAFW_IS_PLAYLIST(playlist)); + g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator)); + + if (iterator->priv->playlist == NULL) { + g_critical("Got playlist:item-moved but renderer has not a " \ + "playlist assigned! Skipping..."); + return; + } + + play_index = iterator->priv->current_index; + + if (play_index == from) { + /* So the current item has been moved, let's update the + the current index to the new location */ + mafw_playlist_iterator_move_to_index(iterator, to, &error); + } else if (play_index > from && play_index <= to) { + /* So we current item has been pushed one position towards + the head, let's update the current index */ + mafw_playlist_iterator_move_to_prev(iterator, &error); + } else if (play_index >= to && play_index < from) { + /* So we current item has been pushed one position towards + the head, let's update the current index */ + mafw_playlist_iterator_move_to_next(iterator, &error); + } + + if (error != NULL) { + g_critical("playlist::item-moved handler failed " + "with \"%s\"", error->message); + g_error_free (error); + } +} + +MafwPlaylistIterator * +mafw_playlist_iterator_new(void) +{ + MafwPlaylistIterator *iterator = (MafwPlaylistIterator *) + g_object_new(MAFW_TYPE_PLAYLIST_ITERATOR, NULL); + + g_assert(iterator != NULL); + + iterator->priv->playlist = NULL; + iterator->priv->current_index = -1; + iterator->priv->current_objectid = NULL; + iterator->priv->size = -1; + + return iterator; +} + +void +mafw_playlist_iterator_initialize(MafwPlaylistIterator *iterator, + MafwPlaylist *playlist, GError **error) +{ + guint size; + gint index = -1; + gchar *objectid = NULL; + GError *new_error = NULL; + + g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator)); + g_return_if_fail(iterator->priv->playlist == NULL); + + iterator->priv->size = -1; + + mafw_playlist_get_starting_index(playlist, (guint *) &index, &objectid, + &new_error); + + if (new_error == NULL) { + size = mafw_playlist_get_size(playlist, &new_error); + } + + if (new_error == NULL) { + iterator->priv->playlist = g_object_ref(playlist); + iterator->priv->current_index = index; + iterator->priv->current_objectid = objectid; + iterator->priv->size = size; + + g_signal_connect(playlist, + "item-moved", + G_CALLBACK(mafw_playlist_iterator_playlist_item_moved_handler), + iterator); + g_signal_connect(playlist, + "contents-changed", + G_CALLBACK(mafw_playlist_iterator_playlist_contents_changed_handler), + iterator); + } + else { + g_propagate_error (error, new_error); + } +} + +void +mafw_playlist_iterator_invalidate(MafwPlaylistIterator *iterator) +{ + g_return_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator)); + + if (iterator->priv->playlist != NULL) { + g_signal_handlers_disconnect_matched(iterator->priv->playlist, + (GSignalMatchType) G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, + mafw_playlist_iterator_playlist_item_moved_handler, + NULL); + + g_signal_handlers_disconnect_matched(iterator->priv->playlist, + (GSignalMatchType) G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, + mafw_playlist_iterator_playlist_contents_changed_handler, + NULL); + + g_object_unref(iterator->priv->playlist); + g_free(iterator->priv->current_objectid); + iterator->priv->playlist = NULL; + iterator->priv->current_index = -1; + iterator->priv->current_objectid = NULL; + iterator->priv->size = -1; + } +} + +gboolean +mafw_playlist_iterator_is_valid(MafwPlaylistIterator *iterator) +{ + g_return_val_if_fail(MAFW_IS_PLAYLIST_ITERATOR(iterator), FALSE); + + return iterator->priv->playlist != NULL; +} + +void +mafw_playlist_iterator_reset(MafwPlaylistIterator *iterator, GError **error) +{ + gint index = -1; + gchar *objectid = NULL; + GError *new_error = NULL; + + g_return_if_fail(mafw_playlist_iterator_is_valid(iterator)); + + mafw_playlist_get_starting_index(iterator->priv->playlist, + (guint *) &index, + &objectid, &new_error); + + if (new_error == NULL) { + mafw_playlist_iterator_set_data(iterator, index, objectid); + } + else { + g_propagate_error (error, new_error); + } +} + +void +mafw_playlist_iterator_move_to_last(MafwPlaylistIterator *iterator, + GError **error) +{ + GError *new_error = NULL; + gint index = -1; + gchar *objectid = NULL; + + g_return_if_fail(mafw_playlist_iterator_is_valid(iterator)); + + mafw_playlist_get_last_index(iterator->priv->playlist, + (guint *) &index, + &objectid, &new_error); + + if (new_error == NULL) { + mafw_playlist_iterator_set_data(iterator, index, objectid); + } + else { + g_propagate_error (error, new_error); + } +} + +MafwPlaylistIteratorMovementResult +mafw_playlist_iterator_move_to_next(MafwPlaylistIterator *iterator, + GError **error) +{ + return mafw_playlist_iterator_move_to_next_in_direction(iterator, + mafw_playlist_get_next, + error); +} + +MafwPlaylistIteratorMovementResult +mafw_playlist_iterator_move_to_prev(MafwPlaylistIterator *iterator, + GError **error) +{ + return mafw_playlist_iterator_move_to_next_in_direction(iterator, + mafw_playlist_get_prev, + error); +} + +MafwPlaylistIteratorMovementResult +mafw_playlist_iterator_move_to_index(MafwPlaylistIterator *iterator, + gint index, + GError **error) +{ + GError *new_error = NULL; + MafwPlaylistIteratorMovementResult iterator_movement_result = + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK; + gint playlist_size; + + g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator), + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID); + + playlist_size = mafw_playlist_iterator_get_size(iterator, &new_error); + + if (new_error != NULL) { + g_propagate_error(error, new_error); + iterator_movement_result = + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR; + } else if ((index < 0) || (index >= playlist_size)) { + iterator_movement_result = + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT; + } else { + gchar *objectid = + mafw_playlist_get_item(iterator->priv->playlist, + index, + &new_error); + + if (new_error != NULL) { + g_propagate_error(error, new_error); + iterator_movement_result = + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR; + } else { + mafw_playlist_iterator_set_data(iterator, index, objectid); + } + } + + return iterator_movement_result; +} + +void +mafw_playlist_iterator_update(MafwPlaylistIterator *iterator, GError **error) +{ + GError *new_error = NULL; + gchar *objectid = NULL; + + objectid = + mafw_playlist_get_item(iterator->priv->playlist, + iterator->priv->current_index, + &new_error); + + if (new_error != NULL) { + g_propagate_error(error, new_error); + } else { + mafw_playlist_iterator_set_data(iterator, + iterator->priv->current_index, + objectid); + } +} + +const gchar * +mafw_playlist_iterator_get_current_objectid(MafwPlaylistIterator *iterator) +{ + g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator), NULL); + + return iterator->priv->current_objectid; +} + +gint +mafw_playlist_iterator_get_current_index(MafwPlaylistIterator *iterator) +{ + g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator), 0); + + return iterator->priv->current_index; +} + +gint +mafw_playlist_iterator_get_size(MafwPlaylistIterator *iterator, + GError **error) +{ + g_return_val_if_fail(mafw_playlist_iterator_is_valid(iterator), -1); + + if (iterator->priv->size == -1) { + iterator->priv->size = + mafw_playlist_get_size(iterator->priv->playlist, + error); + } + + return iterator->priv->size; +} diff --git a/libmafw-gst-renderer/mafw-playlist-iterator.h b/libmafw-gst-renderer/mafw-playlist-iterator.h new file mode 100644 index 0000000..6e88360 --- /dev/null +++ b/libmafw-gst-renderer/mafw-playlist-iterator.h @@ -0,0 +1,95 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include + +G_BEGIN_DECLS + +typedef struct _MafwPlaylistIteratorPrivate MafwPlaylistIteratorPrivate; + +typedef struct { + GObject g_object; + + MafwPlaylistIteratorPrivate *priv; +} MafwPlaylistIterator; + +typedef struct { + GObjectClass g_object_class; + + /* Signals */ + void (*playlist_changed)(MafwPlaylistIterator *iterator, + gboolean current_item_changed, + GQuark domain, gint code, const gchar *message); +} MafwPlaylistIteratorClass; + +typedef enum { + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK, + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT, + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID, + MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR, +} MafwPlaylistIteratorMovementResult; + +#define MAFW_TYPE_PLAYLIST_ITERATOR \ + (mafw_playlist_iterator_get_type()) +#define MAFW_PLAYLIST_ITERATOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MAFW_TYPE_PLAYLIST_ITERATOR, MafwPlaylistIterator)) +#define MAFW_IS_PLAYLIST_ITERATOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAFW_TYPE_PLAYLIST_ITERATOR)) +#define MAFW_PLAYLIST_ITERATOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MAFW_TYPE_PLAYLIST_ITERATOR, MafwPlaylistIterator)) +#define MAFW_PLAYLIST_ITERATOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_PLAYLIST_ITERATOR, \ + MafwPlaylistIteratorClass)) +#define MAFW_IS_PLAYLIST_ITERATOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_PLAYLIST_ITERATOR)) + +G_END_DECLS + +GType mafw_playlist_iterator_get_type(void); +MafwPlaylistIterator *mafw_playlist_iterator_new(void); +void mafw_playlist_iterator_initialize(MafwPlaylistIterator *iterator, + MafwPlaylist *playlist, + GError **error); +void mafw_playlist_iterator_invalidate(MafwPlaylistIterator *iterator); +gboolean mafw_playlist_iterator_is_valid(MafwPlaylistIterator *iterator); +void mafw_playlist_iterator_reset(MafwPlaylistIterator *iterator, GError **error); +void mafw_playlist_iterator_move_to_last(MafwPlaylistIterator *iterator, GError **error); +MafwPlaylistIteratorMovementResult mafw_playlist_iterator_move_to_next(MafwPlaylistIterator *iterator, + GError **error); +MafwPlaylistIteratorMovementResult mafw_playlist_iterator_move_to_prev(MafwPlaylistIterator *iterator, + GError **error); +MafwPlaylistIteratorMovementResult mafw_playlist_iterator_move_to_index(MafwPlaylistIterator *iterator, + gint index, + GError **error); +void mafw_playlist_iterator_update(MafwPlaylistIterator *iterator, GError **error); +const gchar *mafw_playlist_iterator_get_current_objectid(MafwPlaylistIterator *iterator); +gint mafw_playlist_iterator_get_current_index(MafwPlaylistIterator *iterator); +gint mafw_playlist_iterator_get_size(MafwPlaylistIterator *iterator, + GError **error); + +#endif diff --git a/mafw-gst-renderer-uninstalled.pc.in b/mafw-gst-renderer-uninstalled.pc.in new file mode 100644 index 0000000..8496267 --- /dev/null +++ b/mafw-gst-renderer-uninstalled.pc.in @@ -0,0 +1,11 @@ +prefix= +exec_prefix= +libdir=${pcfiledir}/libmafw-gst-renderer +includedir=${pcfiledir}/ + +Name: mafw-gst-renderer +Description: MAFW local renderer +Version: @VERSION@ +Libs: ${libdir}/mafw-gst-renderer.la +Cflags: -I${includedir} +Requires: gobject-2.0 gstreamer-0.10 mafw diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..a894fbf --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,60 @@ +# +# Makefile.am for MAFW gst renderer library. +# +# Author: Visa Smolander +# +# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved. + +TESTS = check-mafw-gst-renderer +TESTS_ENVIRONMENT = CK_FORK=yes \ + TESTS_DIR=@abs_srcdir@ + +noinst_PROGRAMS = $(TESTS) + +AM_CFLAGS = $(_CFLAGS) +AM_LDFLAGS = $(_LDFLAGS) + +INCLUDES = -I$(top_srcdir)/libmafw-gst-renderer \ + $(DEPS_CFLAGS) \ + $(DEPS_TESTS_CFLAGS) \ + $(CHECKMORE_CFLAGS) + +LDADD = $(CHECKMORE_LIBS) \ + $(DEPS_LIBS) \ + $(DEPS_TESTS_LIBS) \ + $(top_builddir)/libmafw-gst-renderer/mafw-gst-renderer.la \ + -lgstinterfaces-0.10 -lgsttag-0.10 + +if HAVE_GDKPIXBUF +INCLUDES += $(GDKPIXBUF_CFLAGS) +LDADD += $(GDKPIXBUF_LIBS) +endif + +if HAVE_CONIC +INCLUDES += $(CONIC_CFLAGS) +LDADD += $(CONIC_LIBS) +endif + +EXTRA_DIST = media/test.wav media/test.avi media/testframe.png + +# ----------------------------------------------- +# Test programs build specs +# ----------------------------------------------- + +check_mafw_gst_renderer_SOURCES = check-main.c \ + check-mafw-gst-renderer.c \ + mafw-mock-playlist.c mafw-mock-playlist.h \ + mafw-mock-pulseaudio.c mafw-mock-pulseaudio.h + +CLEANFILES = $(TESTS) mafw.db *.gcno *.gcda +MAINTAINERCLEANFILES = Makefile.in + +# Run valgrind on tests. +VG_OPTS := --suppressions=test.suppressions --tool=memcheck \ + --leak-check=full --show-reachable=yes +vg: $(TESTS) + for p in $^; do \ + G_SLICE=always-malloc G_DEBUG=gc-friendly WAIT_TIMEOUT=25000 \ + libtool --mode=execute valgrind $(VG_OPTS) $$p 2>vglog.$$p; \ + done; + -rm -f vgcore.* diff --git a/tests/check-mafw-gst-renderer.c b/tests/check-mafw-gst-renderer.c new file mode 100644 index 0000000..ae4982b --- /dev/null +++ b/tests/check-mafw-gst-renderer.c @@ -0,0 +1,4174 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 + +#include +#include +#include +#include +#include + +#include +#include + +#include "config.h" + +#include "mafw-gst-renderer.h" +#include "mafw-mock-playlist.h" +#include "mafw-mock-pulseaudio.h" + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "check-mafw-gstreamer-renderer" + +#define SAMPLE_AUDIO_CLIP "test.wav" +#define SAMPLE_VIDEO_CLIP "test.avi" +#define SAMPLE_IMAGE "testframe.png" + +/* Base timeout used when waiting for state transitions or execution of + user function callbacks associated to each mafw-renderer function */ +#define DEFAULT_WAIT_TOUT 2000 + +/* EOS timeout must be longer than the clip duration */ +#define EOS_TIMEOUT 7000 + +SRunner *configure_tests(void); + +typedef struct { + gint index; + MafwPlayState state; +} RendererInfo; + +typedef struct { + gboolean called; + gboolean error; + gint err_code; + gchar *err_msg; + gint seek_position; + gboolean error_signal_expected; + GError *error_signal_received; + const gchar *property_expected; + GValue *property_received; +} CallbackInfo; + +typedef struct { + const gchar *expected_key; + GValue *value; +} MetadataChangedInfo; + +typedef struct { + const gchar *expected; + GValue *received; +} PropertyChangedInfo; + +typedef struct { + gboolean requested; + gboolean received; + gfloat value; +} BufferingInfo; + +static gint wait_tout_val; + +/* Globals. */ + +static MafwRenderer *g_gst_renderer = NULL; + +/* Error messages. */ + +static const gchar *callback_err_msg = "Error received when %s: (%d) %s"; +static const gchar *callback_no_err_msg = "No error received when %s: (%d) %s"; +static const gchar *no_callback_msg = "We forgot to call the user callback"; +static const gchar *state_err_msg = "Call %s didn't change state to %s. " \ +"Current state is: %d"; +static const gchar *index_err_msg = "Actual index is (%d) instead of the " \ +"expected index (%d)"; + + +/*---------------------------------------------------------------------------- + Signal handlers + ----------------------------------------------------------------------------*/ + + +static void error_cb(MafwRenderer *s, GQuark domain, gint code, gchar *msg, + gpointer user_data) +{ + CallbackInfo* c = (CallbackInfo*) user_data; + + /* "MafwExtension::error" signal handler */ + if (user_data == NULL || !c->error_signal_expected) { + fail("Signal error received: (%d) %s", code, msg); + } else { + if (c->error_signal_received != NULL) { + fail("Error received already initialized"); + } else { + c->error_signal_received = + g_error_new_literal(domain, code, msg); + } + } +} + +static void state_changed_cb(MafwRenderer *s, MafwPlayState state, + gpointer user_data) +{ + /* "MafwRenderer::state-changed" signal handler */ + RendererInfo *si = (RendererInfo *) user_data; + gchar *states[] = {"Stopped","Playing","Paused","Transitioning"}; + + si->state = state; + g_debug("state changed (%s) ---", states[state]); +} + +static gboolean media_changed_called; + +static void media_changed_cb(MafwRenderer *s, gint index, gchar *objectid, + gpointer user_data) +{ + /* "MafwRenderer::media-changed" signal handler */ + RendererInfo *si = (RendererInfo *) user_data; + + si->index = index; + g_debug("media changed (%d) ---", index); + media_changed_called = TRUE; +} +static void playlist_changed_cb (MafwRenderer *self, + GObject *playlist, + gpointer user_data) +{ + g_debug("playlist changed"); + fail_if(media_changed_called, "At first playlist-changed should be called"); +} + +static void metadata_changed_cb(MafwRenderer *self, const gchar *key, + GValueArray *value, gpointer user_data) +{ + MetadataChangedInfo *m = user_data; + + if (m->expected_key != NULL && strcmp(key, m->expected_key) == 0) + { + GValue *original; + + original = g_value_array_get_nth(value, 0); + + m->value = g_new0(GValue, 1); + g_value_init(m->value, G_VALUE_TYPE(original)); + g_value_copy(original, m->value); + } +} + +static void property_changed_cb(MafwExtension *extension, const gchar *name, + const GValue *value, gpointer user_data) +{ + PropertyChangedInfo* p = (PropertyChangedInfo*) user_data; + gchar *value_string; + + value_string = g_strdup_value_contents(value); + + g_debug("property_changed_cb: %s (%s)", name, value_string); + g_free(value_string); + + if (p->expected != NULL && + strcmp(p->expected, name) == 0) { + p->received = g_new0(GValue, 1); + g_value_init(p->received, G_VALUE_TYPE(value)); + g_value_copy(value, p->received); + } +} + +static void buffering_info_cb(MafwRenderer *self, gfloat status, + gpointer user_data) +{ + BufferingInfo *b = user_data; + + if (b->requested) { + b->received = TRUE; + b->value = status; + } +} + +/*---------------------------------------------------------------------------- + Function callbacks + ----------------------------------------------------------------------------*/ + + +static void status_cb(MafwRenderer* renderer, MafwPlaylist* playlist, guint index, + MafwPlayState state, + const gchar* object_id, + gpointer user_data, + const GError *error) +{ + /* MafwRendererStatusCB */ + RendererInfo* s = (RendererInfo*) user_data; + g_assert(s != NULL); + + if (error != NULL) { + fail("Error received while trying to get renderer status: (%d) %s", + error->code, error->message); + } + s->state = state; + +} + +static void playback_cb(MafwRenderer* renderer, gpointer user_data, const GError* error) +{ + /* MafwRendererPlaybackCB: + + Called after mafw_renderer_play(), mafw_renderer_play_uri(), + mafw_renderer_play_object(), mafw_renderer_stop(), mafw_renderer_pause(), + mafw_renderer_resume(), mafw_renderer_next(), mafw_renderer_previous() or + mafw_renderer_goto_index() has been called. */ + CallbackInfo* c = (CallbackInfo*) user_data; + g_assert(c != NULL); + + c->called = TRUE; + if (error != NULL) { + c->error = TRUE; + c->err_code = error->code; + c->err_msg = g_strdup(error->message); + } +} + +static void seek_cb (MafwRenderer *self, gint position, gpointer user_data, + const GError *error) +{ + /* Called when seeking */ + + CallbackInfo* c = (CallbackInfo*) user_data; + g_assert(c != NULL); + + c->called = TRUE; + c->seek_position = position; + if (error != NULL) { + c->error = TRUE; + c->err_code = error->code; + c->err_msg = g_strdup(error->message); + } +} + +static void get_position_cb(MafwRenderer *self, gint position, + gpointer user_data, const GError *error) +{ + CallbackInfo* c = (CallbackInfo*) user_data; + + g_debug("get position cb: %d", position); + + if (error != NULL) { + c->error = TRUE; + c->err_code = error->code; + c->err_msg = g_strdup(error->message); + } + c->called = TRUE; +} + +static void get_property_cb(MafwExtension *self, + const gchar *name, + GValue *value, + gpointer user_data, + const GError *error) +{ + CallbackInfo* c = (CallbackInfo*) user_data; + gchar *value_string; + + value_string = g_strdup_value_contents(value); + + g_debug("get property cb: %s (%s)", name, value_string); + g_free(value_string); + + if (error != NULL) { + c->error = TRUE; + c->err_code = error->code; + c->err_msg = g_strdup(error->message); + } + + if (c->property_expected != NULL && + strcmp(c->property_expected, name) == 0) { + c->property_received = g_new0(GValue, 1); + g_value_init(c->property_received, G_VALUE_TYPE(value)); + g_value_copy(value, c->property_received); + + c->called = TRUE; + } +} + +/*---------------------------------------------------------------------------- + Helpers + ----------------------------------------------------------------------------*/ + +static gchar *get_sample_clip_path(const gchar *clip) +{ + gchar *my_dir, *media; + + /* Makefile.am sets TESTS_DIR, required for VPATH builds (like make + * distcheck). Otherwise assume we are running in-place. */ + my_dir = g_strdup(g_getenv("TESTS_DIR")); + if (!my_dir) + my_dir = g_get_current_dir(); + media = g_strconcat("file://", my_dir, G_DIR_SEPARATOR_S, + "media" G_DIR_SEPARATOR_S, clip, + NULL); + g_free(my_dir); + return media; +} + +static gchar *get_sample_clip_objectid(const gchar *clip) +{ + gchar *path = NULL; + gchar *objectid = NULL; + + path = get_sample_clip_path(clip); + objectid = mafw_source_create_objectid(path); + g_free(path); + + return objectid; +} + +static gboolean stop_wait_timeout(gpointer user_data) +{ + gboolean *do_stop = (gboolean *) user_data; + g_debug("stop wait timeout"); + *do_stop = TRUE; + + return FALSE; +} + +static gboolean wait_until_timeout_finishes(guint millis) +{ + guint timeout = 0; + gboolean stop_wait = FALSE; + gboolean result = FALSE; + + g_debug("Init wait_"); + /* We'll wait a limitted ammount of time */ + timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait); + while(!stop_wait) { + result= g_main_context_iteration(NULL, TRUE); + } + + g_debug("End wait_"); + return TRUE; +} + +static gboolean wait_for_state(RendererInfo *renderer_info, + MafwPlayState expected_state, guint millis) +{ + guint timeout = 0; + gboolean stop_wait = FALSE; + + g_debug("Init wait for state"); + /* We'll wait a limitted ammount of time */ + timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait); + + while(renderer_info->state != expected_state && !stop_wait) { + g_main_context_iteration(NULL, TRUE); + } + + if (!stop_wait) { + g_source_remove(timeout); + } + + g_debug("End wait for state"); + return (renderer_info->state == expected_state); +} + +static gboolean wait_for_callback(CallbackInfo *callback, guint millis) +{ + guint timeout = 0; + gboolean stop_wait = FALSE; + + g_debug("Init wait for callback"); + /* We'll wait a limitted ammount of time */ + timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait); + + while (callback->called == FALSE && !stop_wait) { + g_main_context_iteration(NULL, TRUE); + } + if (!stop_wait) { + g_source_remove(timeout); + } + g_debug("End wait for callback"); + return callback->called; +} + +static gboolean wait_for_metadata(MetadataChangedInfo *callback, guint millis) +{ + guint timeout = 0; + gboolean stop_wait = FALSE; + + g_debug("Init wait for metadata"); + /* We'll wait a limitted ammount of time */ + timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait); + + while (callback->value == NULL && !stop_wait) { + g_main_context_iteration(NULL, TRUE); + } + if (!stop_wait) { + g_source_remove(timeout); + } + g_debug("End wait for metadata"); + return callback->value != NULL; +} + +static gboolean wait_for_property(PropertyChangedInfo *callback, guint millis) +{ + guint timeout = 0; + gboolean stop_wait = FALSE; + + g_debug("Init wait for property changed"); + /* We'll wait a limitted ammount of time */ + timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait); + + while (callback->received == NULL && !stop_wait) { + g_main_context_iteration(NULL, TRUE); + } + if (!stop_wait) { + g_source_remove(timeout); + } + g_debug("End wait for callback"); + return callback->received != NULL; +} + +static gboolean wait_for_buffering(BufferingInfo *callback, guint millis) +{ + guint timeout = 0; + gboolean stop_wait = FALSE; + + g_debug("Init wait for buffering info"); + /* We'll wait a limitted ammount of time */ + timeout = g_timeout_add(millis, stop_wait_timeout, &stop_wait); + + while (!callback->received && !stop_wait) { + g_main_context_iteration(NULL, TRUE); + } + if (!stop_wait) { + g_source_remove(timeout); + } + g_debug("End wait for buffering info"); + return callback->received; +} + +static void reset_callback_info(CallbackInfo *callback_info) +{ + if (callback_info->err_msg != NULL) + g_free(callback_info->err_msg); + + callback_info->err_msg = NULL; + callback_info->called = FALSE; + callback_info->error = FALSE; + callback_info->seek_position = 0; + callback_info->error_signal_expected = FALSE; + if (callback_info->error_signal_received != NULL) { + g_error_free(callback_info->error_signal_received); + callback_info->error_signal_received = NULL; + } + callback_info->property_expected = NULL; + if (callback_info->property_received != NULL) { + g_value_unset(callback_info->property_received); + callback_info->property_received = NULL; + } +} + +/*---------------------------------------------------------------------------- + Fixtures + ----------------------------------------------------------------------------*/ + +static void fx_setup_dummy_gst_renderer(void) +{ + MafwRegistry *registry; + + /* Setup GLib */ + g_type_init(); + + /* Create a gst renderer instance */ + registry = MAFW_REGISTRY(mafw_registry_get_instance()); + fail_if(registry == NULL, + "Error: cannot get MAFW registry"); + + g_gst_renderer = MAFW_RENDERER(mafw_gst_renderer_new(registry)); + fail_if(!MAFW_IS_GST_RENDERER(g_gst_renderer), + "Could not create gst renderer instance"); +} + +static void fx_teardown_dummy_gst_renderer(void) +{ + g_object_unref(g_gst_renderer); +} + +/*---------------------------------------------------------------------------- + Mockups + ----------------------------------------------------------------------------*/ + +/* GStreamer mock */ + +GstElement * gst_element_factory_make(const gchar * factoryname, + const gchar * name) +{ + GstElementFactory *factory; + GstElement *element; + const gchar *use_factoryname; + + g_return_val_if_fail(factoryname != NULL, NULL); + + /* For testing, use playbin instead of playbin2 */ + if (g_ascii_strcasecmp(factoryname, "playbin2") == 0) + use_factoryname = "playbin"; + else + use_factoryname = factoryname; + + GST_LOG("gstelementfactory: make \"%s\" \"%s\"", + use_factoryname, GST_STR_NULL (name)); + + factory = gst_element_factory_find(use_factoryname); + if (factory == NULL) { + /* No factory */ + GST_INFO("no such element factory \"%s\"!", use_factoryname); + return NULL; + } + + GST_LOG_OBJECT(factory, "found factory %p", factory); + if (g_ascii_strcasecmp(use_factoryname, "pulsesink") == 0) { + element = gst_element_factory_make("fakesink", "pulsesink"); + g_object_set(G_OBJECT(element), "sync", TRUE, NULL); + } else if (g_ascii_strcasecmp(use_factoryname, "xvimagesink") == 0) { + element = gst_element_factory_make("fakesink", "xvimagesink"); + g_object_set(G_OBJECT(element), "sync", TRUE, NULL); + } else { + element = gst_element_factory_create(factory, name); + } + gst_object_unref(factory); + + if (element == NULL) { + /* Create failed */ + GST_INFO_OBJECT(factory, "couldn't create instance!"); + return NULL; + } + + GST_LOG("gstelementfactory: make \"%s\" \"%s\"",use_factoryname, + GST_STR_NULL(name)); + + /* Playbin will use fake renderer */ + if (g_ascii_strcasecmp(use_factoryname, "playbin") == 0) { + GstElement *audiorenderer = gst_element_factory_make("fakesink", + "audiorenderer"); + + g_object_set(G_OBJECT(audiorenderer), "sync", TRUE, NULL); + g_object_set(G_OBJECT(element), + "audio-sink", + audiorenderer, + NULL); + g_object_set(G_OBJECT(element), + "video-sink", + audiorenderer, + NULL); + } + + return element; +} + + +/*---------------------------------------------------------------------------- + Test cases + ----------------------------------------------------------------------------*/ + +START_TEST(test_basic_playback) +{ + RendererInfo s; + CallbackInfo c; + MetadataChangedInfo m; + GstBus *bus = NULL; + GstMessage *message = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + m.expected_key = NULL; + m.value = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "metadata-changed", + G_CALLBACK(metadata_changed_cb), + &m); + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + /* No media item has been set so, we should get an error */ + if (c.error == FALSE) + fail("Play of unset media did not return an error"); + } else { + fail(no_callback_msg); + } + + /* --- Play object --- */ + + reset_callback_info(&c); + + gchar *objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c); + + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Transitioning", + s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + g_free(objectid); + + /* --- Get position --- */ + + reset_callback_info(&c); + + mafw_renderer_get_position(g_gst_renderer, get_position_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "get_position", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* --- Duration emission --- */ + + m.expected_key = MAFW_METADATA_KEY_DURATION; + + bus = MAFW_GST_RENDERER(g_gst_renderer)->worker->bus; + fail_if(bus == NULL, "No GstBus"); + + message = gst_message_new_duration(NULL, GST_FORMAT_TIME, + 5 * GST_SECOND); + gst_bus_post(bus, message); + + if (wait_for_metadata(&m, wait_tout_val) == FALSE) { + fail("Expected " MAFW_METADATA_KEY_DURATION + ", but not received"); + } + + fail_if(m.value == NULL, "Metadata " MAFW_METADATA_KEY_DURATION + " not received"); + + g_value_unset(m.value); + g_free(m.value); + m.value = NULL; + m.expected_key = NULL; + + /* --- Pause --- */ + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_pause", "Paused", s.state); + } + + /* --- Resume --- */ + + reset_callback_info(&c); + + g_debug("resume..."); + mafw_renderer_resume(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "resuming", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_resume", "Playing", s.state); + } + + /* --- Stop --- */ + + reset_callback_info(&c); + + g_debug("stop..."); + mafw_renderer_stop(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "stopping", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state); + } + +} +END_TEST + +START_TEST(test_playlist_playback) +{ + MafwPlaylist *playlist = NULL; + gint i = 0; + RendererInfo s = {0, }; + CallbackInfo c = {0, }; + gchar *cur_item_oid = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + + /* --- Create and assign a playlist --- */ + + g_debug("assign playlist..."); + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + for (i=0; i<10; i++) { + mafw_playlist_insert_item( + playlist, i, cur_item_oid, NULL); + } + g_free(cur_item_oid); + cur_item_oid = get_sample_clip_objectid("unexisting.wav"); + mafw_playlist_insert_item(playlist, 9, cur_item_oid, NULL); + g_free(cur_item_oid); + + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Playing", s.state); + } + + /* --- Stop --- */ + + reset_callback_info(&c); + + g_debug("stop..."); + mafw_renderer_stop(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "stopping", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_stop", "Stopped", s.state); + } + + /* --- Next --- */ + + /* Get actual index */ + + gint initial_index = s.index; + + for (i=0; i<3; i++) { + + reset_callback_info(&c); + + g_debug("move to next..."); + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check if the playlist index is correct */ + fail_if(s.index != initial_index + (i+1), index_err_msg, s.index, + initial_index + (i+1)); + } + + + /* --- Prev --- */ + + /* Get actual index */ + initial_index = s.index; + + for (i=0; i<3; i++) { + + reset_callback_info(&c); + + g_debug("move to prev..."); + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check if the playlist index is correct */ + fail_if(s.index != initial_index - (i+1), index_err_msg, s.index, + initial_index - (i+1)); + } + + /* Check if renderer remains in Stopped state after some Prev operations */ + fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped state " + "after doing prev. The actual state is %s and must be %s", + s.state, "Stopped"); + + /* --- Stop --- */ + + reset_callback_info(&c); + + g_debug("stop..."); + mafw_renderer_stop(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "stopping playback", + c.err_code, c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg,"mafw_renderer_stop","Stopped", s.state); + } + + /* --- Go to index in Stopped state --- */ + + reset_callback_info(&c); + + g_debug("goto index 3..."); + mafw_renderer_goto_index(g_gst_renderer, 3, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index 3", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check if the playlist index is correct */ + fail_if(s.index != 3, index_err_msg, s.index, 3); + + /* Check if renderer remains in Stopped state after running go to index */ + fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped state " + "after running go to index. The actual state is %s and must be" + " %s", s.state, "Stopped"); + + /* --- Play (playlist index is 3) --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Playing", s.state); + } + + /* --- Goto index in Playing state --- */ + + reset_callback_info(&c); + + g_debug("goto index 5..."); + mafw_renderer_goto_index(g_gst_renderer, 5, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_goto_index", "Playing", s.state); + } + + /* Check if the index if correct */ + fail_if(s.index != 5, index_err_msg, s.index, 5); + + /* Check if renderer remains in Playing state after running go to index */ + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail("Gst renderer didn't remain in Playing state after running " + "go to index. The actual state is %s and must be %s", + s.state, "Playing"); + } + + /* --- Goto an invalid index --- */ + + reset_callback_info(&c); + + g_debug("goto the invalid index 20..."); + mafw_renderer_goto_index(g_gst_renderer, 20, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error == FALSE) + fail("Error not received when we go to an incorrect" + "index"); + } else { + fail(no_callback_msg); + } + + /* Check if the previous index (5) remains after an incorrect go to + index request */ + fail_if(s.index != 5, index_err_msg, 5, s.index); + + reset_callback_info(&c); + + /* --- Reassigning playlist --- */ + + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, + g_object_ref(playlist), NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* --- Go to index with invalid media --- */ + + reset_callback_info(&c); + + g_debug("goto index 9..."); + mafw_renderer_goto_index(g_gst_renderer, 9, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index 9", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check if the playlist index is correct */ + fail_if(s.index != 9, index_err_msg, s.index, 9); + + /* Check if renderer remains in Stopped state after running go + * to index */ + fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped " + "state after running go to index. The actual state is %d and " + "must be %s", s.state, "Stopped"); + + /* --- Play (playlist index is 9) --- */ + + reset_callback_info(&c); + + c.error_signal_expected = TRUE; + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Transitioning", + s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Playing", s.state); + } + + fail_if(c.error_signal_received == NULL || + !g_error_matches(c.error_signal_received, MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_INVALID_URI), + "No error received or incorrect one"); + + if (c.error_signal_received != NULL) { + g_error_free(c.error_signal_received); + c.error_signal_received = NULL; + } + c.error_signal_expected = FALSE; + + /* --- Stop --- */ + + reset_callback_info(&c); + + g_debug("stop..."); + mafw_renderer_stop(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "stopping playback", + c.err_code, c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg,"mafw_renderer_stop","Stopped", s.state); + } + + /* --- Remove last media --- */ + + mafw_playlist_remove_item(playlist, 10, NULL); + + /* --- Go to index with invalid media --- */ + + reset_callback_info(&c); + + g_debug("goto index 9..."); + mafw_renderer_goto_index(g_gst_renderer, 9, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index 9", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check if the playlist index is correct */ + fail_if(s.index != 9, index_err_msg, s.index, 9); + + /* Check if renderer remains in Stopped state after running go + * to index */ + fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped " + "state after running go to index. The actual state is %d and " + "must be %s", s.state, "Stopped"); + + /* --- Play (playlist index is 9) --- */ + + reset_callback_info(&c); + + c.error_signal_expected = TRUE; + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Transitioning", + s.state); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Stopped", s.state); + } + + fail_if(c.error_signal_received == NULL || + !g_error_matches(c.error_signal_received, MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_INVALID_URI), + "No error received or incorrect one"); + + if (c.error_signal_received != NULL) { + g_error_free(c.error_signal_received); + c.error_signal_received = NULL; + } + c.error_signal_expected = FALSE; + + /* --- Play incorrect object --- */ + + reset_callback_info(&c); + + c.error_signal_expected = TRUE; + + gchar *objectid = get_sample_clip_objectid("unexisting.wav"); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", + s.state); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Stopped", + s.state); + } + + fail_if(c.error_signal_received == NULL || + !g_error_matches(c.error_signal_received, MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_INVALID_URI), + "No error received or incorrect one"); + + if (c.error_signal_received != NULL) { + g_error_free(c.error_signal_received); + c.error_signal_received = NULL; + } + c.error_signal_expected = FALSE; + + g_free(objectid); + +} +END_TEST + + +START_TEST(test_repeat_mode_playback) +{ + MafwPlaylist *playlist = NULL; + gint i = 0; + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + + /* --- Create playlist --- */ + + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + for (i=0; i<10; i++) { + gchar *cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + mafw_playlist_insert_item( + playlist, i, cur_item_oid, NULL); + g_free(cur_item_oid); + } + + /* Set repeat mode */ + mafw_playlist_set_repeat(playlist, TRUE); + + /* --- Assign playlist --- */ + + g_debug("assign playlist..."); + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Playing", s.state); + } + + /* --- Go to index --- */ + + reset_callback_info(&c); + + g_debug("goto index 9..."); + /* go to the end of the playlist */ + mafw_renderer_goto_index(g_gst_renderer, 9, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index 9", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* check if the movement was successful */ + fail_if(s.index != 9, index_err_msg, 9, s.index); + + /* --- Stop --- */ + + reset_callback_info(&c); + + g_debug("stop..."); + mafw_renderer_stop(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "stopping playback", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_stop", "Stopped", s.state); + } + + /* --- Next --- */ + + reset_callback_info(&c); + + g_debug("next..."); + /* The actual index is 9 */ + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* check if the movement was successful */ + fail_if(s.index != 0, index_err_msg, s.index, 0); + + /* Check if renderer remains in Stopped state after moving to next */ + fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped state " + "after doing next. The actual state is %s and must be %s", + s.state, "Stopped"); + + /* --- Prev --- */ + + reset_callback_info(&c); + + g_debug("prev..."); + /* The actual index is 0 */ + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* check if the movement was successful */ + fail_if(s.index != 9, index_err_msg, s.index, 9); + + /* Check if renderer remains in Stopped state after moving to next */ + fail_if(s.state != Stopped, "Gst renderer didn't remain in Stopped state " + "after doing next. The actual state is %s and must be %s", + s.state, "Stopped"); +} +END_TEST + + +START_TEST(test_gst_renderer_mode) +{ + MafwPlaylist *playlist = NULL; + MafwGstRenderer *renderer = NULL; + MafwGstRendererPlaybackMode play_mode; + gchar *objectid = NULL; + gint i = 0; + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + gchar *modes[] = {"MAFW_GST_RENDERER_MODE_PLAYLIST", + "MAFW_GST_RENDERER_MODE_STANDALONE"}; + + renderer = MAFW_GST_RENDERER(g_gst_renderer); + + /* Initiliaze callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + + /* --- Create playlist --- */ + + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + for (i=0; i<10; i++) { + gchar *cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + mafw_playlist_insert_item( + playlist, i, cur_item_oid, NULL); + g_free(cur_item_oid); + } + + /* --- Assign playlist --- */ + + g_debug("assign playlist..."); + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Transitioning", s.state); + } + + /* Check that renderer is playing a playlist */ + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Playing", s.state); + } + play_mode = mafw_gst_renderer_get_playback_mode(renderer); + fail_if(play_mode != MAFW_GST_RENDERER_MODE_PLAYLIST, + "Incorrect value of playback_mode: %s", modes[play_mode]); + + /* --- Play object --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s",objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c); + g_free(objectid); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Transitioning", + s.state); + } + + /* Check that renderer is playing an object */ + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + play_mode = mafw_gst_renderer_get_playback_mode(renderer); + fail_if(play_mode != MAFW_GST_RENDERER_MODE_STANDALONE, + "Incorrect value of playback_mode: %s", modes[play_mode]); + + /* Wait EOS_TIMEOUT to ensure that the play_object has finished */ + wait_until_timeout_finishes(EOS_TIMEOUT); + + /* Check that after playing the object, renderer returns to the playlist + playback */ + play_mode = mafw_gst_renderer_get_playback_mode(renderer); + fail_if(play_mode != MAFW_GST_RENDERER_MODE_PLAYLIST, + "Incorrect value of playback_mode: %s", modes[play_mode]); + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- Play object --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c); + g_free(objectid); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Transitioning", + s.state); + } + + /* Check that renderer is playing an object */ + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + play_mode = mafw_gst_renderer_get_playback_mode(renderer); + fail_if(play_mode != MAFW_GST_RENDERER_MODE_STANDALONE, + "Incorrect value of playback_mode: %s", modes[play_mode]); + + + /* --- Move to next when renderer is playing an object --- */ + + reset_callback_info(&c); + + g_debug("next..."); + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check that "next" function finishes the object playback and returns + to the playlist playback */ + play_mode = mafw_gst_renderer_get_playback_mode(renderer); + fail_if(play_mode != MAFW_GST_RENDERER_MODE_PLAYLIST, + "Incorrect value of playback_mode: %s", modes[play_mode]); + + /* Check that renderer is still in Playing state */ + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- Stop --- */ + + reset_callback_info(&c); + + g_debug("stop..."); + mafw_renderer_stop(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "stopping", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state); + } + + /* --- Play object --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Transitioning", + s.state); + } + + /* Check that renderer is playing an object */ + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + play_mode = mafw_gst_renderer_get_playback_mode(renderer); + fail_if(play_mode != MAFW_GST_RENDERER_MODE_STANDALONE, + "Incorrect value of playback_mode: %s", modes[play_mode]); + + /* Wait EOS_TIMEOUT to ensure that object playback finishes */ + wait_until_timeout_finishes(EOS_TIMEOUT); + + /* Check if renderer is in playlist mode and the renderer state is the state before + playing the object */ + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state); + } + play_mode = mafw_gst_renderer_get_playback_mode(renderer); + fail_if(play_mode != MAFW_GST_RENDERER_MODE_PLAYLIST, + "Incorrect value of playback_mode: %s", modes[play_mode]); + + g_free(objectid); +} +END_TEST + +#define MOCK_SOURCE(o) \ + (G_TYPE_CHECK_INSTANCE_CAST((o), \ + mock_source_get_type(), \ + MockSource)) + +typedef struct { + MafwSourceClass parent; +} MockSourceClass; + +typedef struct { + MafwSource parent; + + +} MockSource; + +GType mock_source_get_type(void); +GObject* mock_source_new(void); + +G_DEFINE_TYPE(MockSource, mock_source, MAFW_TYPE_SOURCE); + +static GHashTable *get_md_ht; /* Metadata hash-table for the metadata result */ +static GError *get_md_err; /* Error value for the metadata result */ +static gboolean set_mdata_called; /* Whether set_metadata was called or not */ +static gboolean get_mdata_called; /* Whether get_metadata was called or not */ +static gint reference_pcount; /* Reference playcount, what should come in set_metadata */ +static gboolean set_for_playcount; /* TRUE, when the set_metadata is called to modify the playcount */ +static gboolean set_for_lastplayed; /* TRUE, when the set_metadata is called to modify the last-played */ + +static void get_metadata(MafwSource *self, + const gchar *object_id, + const gchar *const *metadata, + MafwSourceMetadataResultCb callback, + gpointer user_data) +{ + get_mdata_called = TRUE; + fail_if(strcmp(object_id, "mocksource::test")); + callback(self, object_id, get_md_ht, user_data, get_md_err); +} + +static void set_metadata(MafwSource *self, const gchar *object_id, + GHashTable *metadata, + MafwSourceMetadataSetCb callback, + gpointer user_data) +{ + GValue *curval; + gint htsize = 0; + + if (set_for_playcount) + htsize++; + if (set_for_lastplayed) + htsize++; + fail_if(strcmp(object_id, "mocksource::test")); + fail_if(!metadata); + fail_if(g_hash_table_size(metadata) != htsize, "Hash table size: %d vs %d", g_hash_table_size(metadata), htsize); + if (set_for_playcount) + { + curval = mafw_metadata_first(metadata, + MAFW_METADATA_KEY_PLAY_COUNT); + fail_if(!curval); + fail_if(g_value_get_int(curval) != reference_pcount); + } + if (set_for_lastplayed) + { + curval = mafw_metadata_first(metadata, + MAFW_METADATA_KEY_LAST_PLAYED); + fail_if(!curval); + fail_if(!G_VALUE_HOLDS(curval, G_TYPE_LONG)); + } + set_mdata_called = TRUE; +} + +static void mock_source_class_init(MockSourceClass *klass) +{ + MafwSourceClass *sclass = MAFW_SOURCE_CLASS(klass); + + sclass->get_metadata = get_metadata; + sclass->set_metadata = set_metadata; + +} + +static void mock_source_init(MockSource *source) +{ + /* NOP */ +} + +GObject* mock_source_new(void) +{ + GObject* object; + object = g_object_new(mock_source_get_type(), + "plugin", "mockland", + "uuid", "mocksource", + "name", "mocksource", + NULL); + return object; +} + + +START_TEST(test_update_stats) +{ + MafwGstRenderer *renderer = NULL; + MafwSource *src; + MafwRegistry *registry; + + registry = MAFW_REGISTRY(mafw_registry_get_instance()); + fail_if(registry == NULL, + "Error: cannot get MAFW registry"); + + + renderer = MAFW_GST_RENDERER(g_gst_renderer); + src = MAFW_SOURCE(mock_source_new()); + + mafw_registry_add_extension(registry, MAFW_EXTENSION(src)); + + /* Error on get_mdata_cb*/ + set_for_playcount = FALSE; + set_for_lastplayed = FALSE; + get_md_err = NULL; + g_set_error(&get_md_err, MAFW_SOURCE_ERROR, + MAFW_SOURCE_ERROR_INVALID_OBJECT_ID, + "Wrong object id mocksource::test"); + renderer->media->object_id = g_strdup("mocksource::test"); + mafw_gst_renderer_update_stats(renderer); + g_error_free(get_md_err); + fail_if(set_mdata_called); + fail_if(!get_mdata_called); + + /* get_mdata ok, but HashTable is NULL */ + reference_pcount = 1; + get_mdata_called = FALSE; + set_for_lastplayed = TRUE; + set_for_playcount = TRUE; + get_md_err = NULL; + mafw_gst_renderer_update_stats(renderer); + fail_if(!set_mdata_called); + fail_if(!get_mdata_called); + + /* get_mdata ok, but HashTable is empty */ + get_mdata_called = FALSE; + set_mdata_called = FALSE; + set_for_lastplayed = TRUE; + set_for_playcount = TRUE; + get_md_ht = mafw_metadata_new(); + mafw_gst_renderer_update_stats(renderer); + fail_if(!set_mdata_called); + fail_if(!get_mdata_called); + + /* get_mdata ok, but HashTable has valid value */ + get_mdata_called = FALSE; + set_mdata_called = FALSE; + set_for_lastplayed = TRUE; + set_for_playcount = TRUE; + mafw_metadata_add_int(get_md_ht, + MAFW_METADATA_KEY_PLAY_COUNT, + 1); + reference_pcount = 2; + mafw_gst_renderer_update_stats(renderer); + fail_if(!set_mdata_called); + fail_if(!get_mdata_called); +} +END_TEST + +START_TEST(test_play_state) +{ + MafwPlaylist *playlist = NULL; + gint i = 0; + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + gchar *objectid = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Play object --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + if (wait_for_state(&s, Stopped, 3000) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Stop", + s.state); + } + + g_free(objectid); + + + /* --- Create and assign a playlist --- */ + + g_debug("assign playlist..."); + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + for (i=0; i<10; i++) { + gchar *cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + mafw_playlist_insert_item( + playlist, i, cur_item_oid, NULL); + g_free(cur_item_oid); + } + mafw_playlist_set_repeat(playlist, FALSE); + + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, + NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail("Play after assigning playlist failed"); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Playing", + s.state); + } + + /* --- Prev --- */ + + reset_callback_info(&c); + + g_debug("move to prev..."); + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 9, index_err_msg, s.index, 9); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", "Playing", + s.state); + } + + /* Removing last element */ + + g_debug("removing last element..."); + fail_if(mafw_playlist_get_size(playlist, NULL) != 10, + "Playlist should have 10 elements"); + mafw_playlist_remove_item(playlist, 9, NULL); + fail_if(mafw_playlist_get_size(playlist, NULL) != 9, + "Playlist should have 9 elements"); + fail_if(s.index != 8, index_err_msg, s.index, 8); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_playlist_remove_element", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_playlist_remove_element", "Playing", + s.state); + } + + /* --- Next --- */ + + reset_callback_info(&c); + + g_debug("move to next..."); + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 0, index_err_msg, s.index, 0); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_next", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_next", "Playing", + s.state); + } + + /* --- Go to index --- */ + + reset_callback_info(&c); + + g_debug("goto index 8..."); + mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index 8", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 8, index_err_msg, s.index, 8); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_goto_index", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_goto_index", "Playing", + s.state); + } + + /* --- Seeking --- */ + + reset_callback_info(&c); + + g_debug("seeking..."); + mafw_renderer_set_position(g_gst_renderer, SeekAbsolute, 1, + seek_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "seeking failed", c.err_code, + c.err_msg); + if (c.seek_position != 1) { + fail("seeking failed"); + } + } else { + fail(no_callback_msg); + } + + /* --- Waiting EOS --- */ + + if (wait_for_state(&s, Stopped, 2000) == FALSE) { + fail(state_err_msg, "EOS", "Stop", + s.state); + } +} +END_TEST + +START_TEST(test_pause_state) +{ + MafwPlaylist *playlist = NULL; + gint i = 0; + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + gchar *objectid = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Create and assign a playlist --- */ + + g_debug("assign playlist..."); + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + for (i=0; i<10; i++) { + gchar *cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + mafw_playlist_insert_item( + playlist, i, cur_item_oid, NULL); + g_free(cur_item_oid); + } + mafw_playlist_set_repeat(playlist, FALSE); + + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, + NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail("Play failed"); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", + "Transitioning", s.state); + } + + /* Testing pause in transitioning */ + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Testing resume in transitioning */ + + reset_callback_info(&c); + + g_debug("resume..."); + mafw_renderer_resume(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "resuming", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + reset_callback_info(&c); + + /* Testing resume without having paused in transitioning */ + + reset_callback_info(&c); + + g_debug("resume..."); + mafw_renderer_resume(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (!c.error) + fail(callback_no_err_msg, "resuming", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_pause", "Paused", + s.state); + } + + /* --- Play object in pause --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_pause", "Paused", + s.state); + } + + g_free(objectid); + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail("Play failed"); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", + "Transitioning", s.state); + } + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_pause", "Paused", + s.state); + } + + /* --- Prev --- */ + + reset_callback_info(&c); + + g_debug("move to prev..."); + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check if the playlist index is correct */ + fail_if(s.index != 9, index_err_msg, s.index, 9); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", + "Transitioning", s.state); + } + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", "Playing", + s.state); + } + + /* Removing last element */ + + g_debug("removing last element..."); + fail_if(mafw_playlist_get_size(playlist, NULL) != 10, + "Playlist should have 10 elements"); + mafw_playlist_remove_item(playlist, 9, NULL); + fail_if(mafw_playlist_get_size(playlist, NULL) != 9, + "Playlist should have 9 elements"); + fail_if(s.index != 8, index_err_msg, s.index, 8); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_playlist_remove_item", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_playlist_remove_item", "Playing", + s.state); + } + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_playlist_remove_item", "Playing", + s.state); + } + + /* --- Next --- */ + + reset_callback_info(&c); + + g_debug("move to next..."); + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check if the playlist index is correct */ + fail_if(s.index != 0, index_err_msg, s.index, 0); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_next", + "Transitioning", s.state); + } + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_next", "Playing", + s.state); + } + + /* --- Go to index --- */ + + reset_callback_info(&c); + + g_debug("goto index 8..."); + mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index 8", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* Check if the playlist index is correct */ + fail_if(s.index != 8, index_err_msg, s.index, 8); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_goto_index", + "Transitioning", s.state); + } + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_goto_index", "Playing", + s.state); + } + + /* --- Seeking --- */ + + reset_callback_info(&c); + + mafw_renderer_set_position(g_gst_renderer, SeekAbsolute, 1, + seek_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "seeking", c.err_code, + c.err_msg); + if (c.seek_position != 1) { + fail("seeking failed"); + } + } else { + fail(no_callback_msg); + } + + /* --- Stop --- */ + + reset_callback_info(&c); + + g_debug("stop..."); + mafw_renderer_stop(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "stopping", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state); + } +} +END_TEST + +START_TEST(test_stop_state) +{ + MafwPlaylist *playlist = NULL; + gint i = 0; + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Prev --- */ + + reset_callback_info(&c); + + g_debug("move to prev..."); + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (!c.error) + fail(callback_no_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* --- Next --- */ + + reset_callback_info(&c); + + g_debug("move to next..."); + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (!c.error) + fail(callback_no_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* --- Go to index --- */ + + reset_callback_info(&c); + + g_debug("goto index 8..."); + mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (!c.error) + fail(callback_no_err_msg, "going to index 8", + c.err_code, c.err_msg); + } else { + fail(no_callback_msg); + } + + /* --- Create and assign a playlist --- */ + + g_debug("assign playlist..."); + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + for (i=0; i<10; i++) { + gchar *cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + mafw_playlist_insert_item( + playlist, i, cur_item_oid, NULL); + g_free(cur_item_oid); + } + mafw_playlist_set_repeat(playlist, FALSE); + + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, + NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* Removing last element */ + + g_debug("removing last element..."); + fail_if(mafw_playlist_get_size(playlist, NULL) != 10, + "Playlist should have 10 elements"); + mafw_playlist_remove_item(playlist, 9, NULL); + fail_if(mafw_playlist_get_size(playlist, NULL) != 9, + "Playlist should have 9 elements"); + + /* --- Go to index --- */ + + reset_callback_info(&c); + + g_debug("goto index 9..."); + mafw_renderer_goto_index(g_gst_renderer, 9, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (!c.error) + fail(callback_no_err_msg, "going to index 9", + c.err_code, c.err_msg); + } else { + fail(no_callback_msg); + } + reset_callback_info(&c); +} +END_TEST + +START_TEST(test_transitioning_state) +{ + MafwPlaylist *playlist = NULL; + gint i = 0; + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + gchar *objectid = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Create and assign a playlist --- */ + + g_debug("assign playlist..."); + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + for (i=0; i<10; i++) { + gchar *cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + mafw_playlist_insert_item( + playlist, i, cur_item_oid, NULL); + g_free(cur_item_oid); + } + mafw_playlist_set_repeat(playlist, FALSE); + + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, + NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail("Play after assigning playlist failed"); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", + "Transitioning", s.state); + } + + /* --- Play object --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + g_free(objectid); + + + /* --- Prev --- */ + + reset_callback_info(&c); + + g_debug("move to prev..."); + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 9, index_err_msg, s.index, 9); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", + "Transitioning", s.state); + } + + /* Removing last element */ + + g_debug("removing last element..."); + fail_if(mafw_playlist_get_size(playlist, NULL) != 10, + "Playlist should have 10 elements"); + mafw_playlist_remove_item(playlist, 9, NULL); + fail_if(mafw_playlist_get_size(playlist, NULL) != 9, + "Playlist should have 9 elements"); + fail_if(s.index != 8, index_err_msg, s.index, 8); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_playlist_remove_element", + "Transitioning", s.state); + } + + /* --- Next --- */ + + reset_callback_info(&c); + + g_debug("move to next..."); + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 0, index_err_msg, s.index, 0); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_next", + "Transitioning", s.state); + } + + /* --- Go to index --- */ + + reset_callback_info(&c); + + g_debug("goto index 8..."); + mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index 8", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 8, index_err_msg, s.index, 8); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_goto_index", + "Transitioning", s.state); + } +} +END_TEST + +START_TEST(test_state_class) +{ + MafwPlaylist *playlist = NULL; + gint i = 0; + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + gchar *objectid = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Play object --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- Prev --- */ + + reset_callback_info(&c); + + g_debug("move to prev..."); + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (!c.error) + fail(callback_no_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* --- Play object --- */ + + reset_callback_info(&c); + + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- Next --- */ + + reset_callback_info(&c); + + g_debug("move to next..."); + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (!c.error) + fail(callback_no_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* --- Play object --- */ + + reset_callback_info(&c); + + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- Go to index --- */ + + reset_callback_info(&c); + + g_debug("goto index 8..."); + mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (!c.error) + fail(callback_err_msg, "going to index 8", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* --- Create and assign a playlist --- */ + + g_debug("assign playlist..."); + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + for (i=0; i<10; i++) { + gchar *cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + mafw_playlist_insert_item( + playlist, i, cur_item_oid, NULL); + g_free(cur_item_oid); + } + mafw_playlist_set_repeat(playlist, FALSE); + + media_changed_called = FALSE; + if (!mafw_renderer_assign_playlist(g_gst_renderer, playlist, + NULL)) + { + fail("Assign playlist failed"); + } + + wait_for_state(&s, Stopped, wait_tout_val); + + /* --- Play object --- */ + + reset_callback_info(&c); + + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- Next --- */ + + reset_callback_info(&c); + + g_debug("move to next..."); + mafw_renderer_next(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to next", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 1, index_err_msg, s.index, 1); + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_next", "Playing", + s.state); + } + + /* --- Play object --- */ + + reset_callback_info(&c); + + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- Go to index --- */ + + reset_callback_info(&c); + + g_debug("goto index 8..."); + mafw_renderer_goto_index(g_gst_renderer, 8, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "going to index 8", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 8, index_err_msg, s.index, 8); + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_goto_index", "Playing", + s.state); + } + + /* --- Play object --- */ + + reset_callback_info(&c); + + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- Prev --- */ + + reset_callback_info(&c); + + g_debug("move to prev..."); + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 7, index_err_msg, s.index, 7); + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", "Playing", + s.state); + } + + /* --- Play --- */ + + reset_callback_info(&c); + + g_debug("play..."); + mafw_renderer_play(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail("Play after assigning playlist failed"); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play", "Playing", + s.state); + } + + /* --- Prev --- */ + + reset_callback_info(&c); + + g_debug("move to prev..."); + mafw_renderer_previous(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "moving to prev", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(s.index != 6, index_err_msg, s.index, 6); + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", + "Transitioning", s.state); + } + + /* --- Seeking --- */ + + reset_callback_info(&c); + + g_debug("seeking..."); + mafw_renderer_set_position(g_gst_renderer, SeekRelative, 1, + seek_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "seeking failed", c.err_code, + c.err_msg); + if (c.seek_position != 1) { + fail("seeking failed"); + } + } else { + fail(no_callback_msg); + } + + /* --- Seeking --- */ + + reset_callback_info(&c); + + g_debug("seeking..."); + mafw_renderer_set_position(g_gst_renderer, SeekAbsolute, -1, + seek_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "seeking failed", c.err_code, + c.err_msg); + if (c.seek_position != -1) { + fail("seeking failed"); + } + } else { + fail(no_callback_msg); + } + + /* --- Seeking --- */ + + reset_callback_info(&c); + + g_debug("seeking..."); + mafw_renderer_set_position(g_gst_renderer, SeekAbsolute, 1, + seek_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "seeking failed", c.err_code, + c.err_msg); + if (c.seek_position != 1) { + fail("seeking failed"); + } + } else { + fail(no_callback_msg); + } +} +END_TEST + +START_TEST(test_playlist_iterator) +{ + MafwPlaylist *playlist = NULL; + gint i = 0; + CallbackInfo c = {0, };; + MafwPlaylistIterator *iterator = NULL; + GError *error = NULL; + gint size; + gint index; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error = FALSE; + reset_callback_info(&c); + + /* --- Create and assign a playlist --- */ + + g_debug("assign playlist..."); + playlist = MAFW_PLAYLIST(mafw_mock_playlist_new()); + + iterator = mafw_playlist_iterator_new(); + mafw_playlist_iterator_initialize(iterator, playlist, &error); + if (error != NULL) { + fail("Error found: %s, %d, %s", + g_quark_to_string(error->domain), + error->code, error->message); + } + + for (i = 0; i < 3; i++) { + gchar *cur_item_oid = NULL; + cur_item_oid = + get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + mafw_playlist_insert_item(playlist, 0, cur_item_oid, NULL); + g_free(cur_item_oid); + } + + size = mafw_playlist_iterator_get_size(iterator, NULL); + fail_if(size != 3, "Playlist should have 3 elements and it has %d", + size); + index = mafw_playlist_iterator_get_current_index(iterator); + fail_if(index != 2, "Index should be 2 and it is %d", index); + + mafw_playlist_move_item(playlist, 1, 2, NULL); + index = mafw_playlist_iterator_get_current_index(iterator); + fail_if(index != 1, "Index should be 1 and it is %d", index); + + mafw_playlist_move_item(playlist, 2, 1, NULL); + index = mafw_playlist_iterator_get_current_index(iterator); + fail_if(index != 2, "Index should be 2 and it is %d", index); + + mafw_playlist_move_item(playlist, 2, 1, NULL); + index = mafw_playlist_iterator_get_current_index(iterator); + fail_if(index != 1, "Index should be 1 and it is %d", index); + + mafw_playlist_remove_item(playlist, 0, &error); + if (error != NULL) { + fail("Error found: %s, %d, %s", + g_quark_to_string(error->domain), + error->code, error->message); + } + + size = mafw_playlist_iterator_get_size(iterator, NULL); + fail_if(size != 2, "Playlist should have 2 elements and it has %d", + size); + index = mafw_playlist_iterator_get_current_index(iterator); + fail_if(index != 0, "Index should be 0 and it is %d", index); + + mafw_playlist_iterator_reset(iterator, NULL); + index = mafw_playlist_iterator_get_current_index(iterator); + fail_if(index != 0, "Index should be 0 and it is %d", index); + + mafw_playlist_remove_item(playlist, 0, &error); + if (error != NULL) { + fail("Error found: %s, %d, %s", + g_quark_to_string(error->domain), + error->code, error->message); + } + + size = mafw_playlist_iterator_get_size(iterator, NULL); + fail_if(size != 1, "Playlist should have 1 elements and it has %d", + size); + index = mafw_playlist_iterator_get_current_index(iterator); + fail_if(index != 0, "Index should be 0 and it is %d", index); + + mafw_playlist_remove_item(playlist, 0, &error); + if (error != NULL) { + fail("Error found: %s, %d, %s", + g_quark_to_string(error->domain), + error->code, error->message); + } + + size = mafw_playlist_iterator_get_size(iterator, NULL); + fail_if(size != 0, "Playlist should have 0 elements and it has %d", + size); + index = mafw_playlist_iterator_get_current_index(iterator); + fail_if(index != -1, "Index should be -1 and it is %d", index); + + g_object_unref(iterator); +} +END_TEST + +START_TEST(test_video) +{ + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + MetadataChangedInfo m; + gchar *objectid = NULL; + GstBus *bus = NULL; + GstStructure *structure = NULL; + GstMessage *message = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + m.expected_key = NULL; + m.value = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + g_signal_connect(g_gst_renderer, "metadata-changed", + G_CALLBACK(metadata_changed_cb), + &m); + +#ifdef HAVE_GDKPIXBUF + mafw_extension_set_property_boolean( + MAFW_EXTENSION(g_gst_renderer), + MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE, + TRUE); +#endif + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Play object --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_VIDEO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + MAFW_GST_RENDERER(g_gst_renderer)->worker->xid = 0x1; + bus = MAFW_GST_RENDERER(g_gst_renderer)->worker->bus; + fail_if(bus == NULL, "No GstBus"); + + structure = gst_structure_new("prepare-xwindow-id", "width", + G_TYPE_INT, 64, "height", G_TYPE_INT, 32, + NULL); + message = gst_message_new_element(NULL, structure); + gst_bus_post(bus, message); + + /* --- Pause --- */ + + reset_callback_info(&c); + + m.expected_key = MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI; + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", "Playing", + s.state); + } + + if (wait_for_metadata(&m, wait_tout_val) == FALSE) { + fail("Expected " MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI + ", but not received"); + } + + fail_if(m.value == NULL, "Metadata " + MAFW_METADATA_KEY_PAUSED_THUMBNAIL_URI " not received"); + + g_value_unset(m.value); + g_free(m.value); + m.value = NULL; + m.expected_key = NULL; + + /* --- Resume --- */ + + reset_callback_info(&c); + + g_debug("resume..."); + mafw_renderer_resume(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "resuming", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + /* --- EOS --- */ + + if (wait_for_state(&s, Stopped, 3000) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Stop", + s.state); + } + + g_free(objectid); +} +END_TEST + +START_TEST(test_media_art) +{ + RendererInfo s = {0, };; + CallbackInfo c = {0, };; + MetadataChangedInfo m; + gchar *objectid = NULL; + GstBus *bus = NULL; + GstMessage *message = NULL; + GstTagList *list = NULL; + GstBuffer *buffer = NULL; + guchar *image = NULL; + gchar *image_path = NULL; + gsize image_length; + GstCaps *caps = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + m.expected_key = NULL; + m.value = NULL; + c.property_expected = NULL; + c.property_received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "media-changed", + G_CALLBACK(media_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "playlist-changed", + G_CALLBACK(playlist_changed_cb), + NULL); + g_signal_connect(g_gst_renderer, "metadata-changed", + G_CALLBACK(metadata_changed_cb), + &m); + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Play object --- */ + + reset_callback_info(&c); + + objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, + &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + /* --- Pause --- */ + + reset_callback_info(&c); + + g_debug("pause..."); + mafw_renderer_pause(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "pausing", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Paused, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_prev", "Playing", + s.state); + } + + /* Emit image */ + + bus = MAFW_GST_RENDERER(g_gst_renderer)->worker->bus; + fail_if(bus == NULL, "No GstBus"); + + m.expected_key = MAFW_METADATA_KEY_RENDERER_ART_URI; + + image_path = get_sample_clip_path(SAMPLE_IMAGE); + fail_if(!g_file_get_contents(image_path + 7, (gchar **) &image, + &image_length, NULL), + "Could not load test image"); + g_free(image_path); + + buffer = gst_buffer_new(); + gst_buffer_set_data(buffer, image, image_length); + caps = gst_caps_new_simple("image/png", "image-type", + GST_TYPE_TAG_IMAGE_TYPE, + GST_TAG_IMAGE_TYPE_FRONT_COVER, NULL); + gst_buffer_set_caps(buffer, caps); + gst_caps_unref(caps); + + list = gst_tag_list_new(); + gst_tag_list_add(list, GST_TAG_MERGE_APPEND, GST_TAG_IMAGE, buffer, + NULL); + + message = gst_message_new_tag(NULL, list); + gst_bus_post(bus, message); + + /* --- Resume --- */ + + reset_callback_info(&c); + + g_debug("resume..."); + mafw_renderer_resume(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "resuming", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + if (wait_for_metadata(&m, wait_tout_val) == FALSE) { + fail("Expected " MAFW_METADATA_KEY_RENDERER_ART_URI + ", but not received"); + } + + fail_if(m.value == NULL, "Metadata " + MAFW_METADATA_KEY_RENDERER_ART_URI " not received"); + + g_value_unset(m.value); + g_free(m.value); + m.value = NULL; + m.expected_key = NULL; + + /* --- EOS --- */ + + if (wait_for_state(&s, Stopped, 3000) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Stop", + s.state); + } + + g_free(objectid); +} +END_TEST + +START_TEST(test_properties_management) +{ + RendererInfo s; + CallbackInfo c = {0, };; + PropertyChangedInfo p; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + p.expected = NULL; + p.received = NULL; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "property-changed", + G_CALLBACK(property_changed_cb), + &p); + + /* Wait for the volume manager to be initialized */ + + /* Volume */ + + p.expected = MAFW_PROPERTY_RENDERER_VOLUME; + + if (!wait_for_property(&p, wait_tout_val)) { + fail("No property %s received", p.expected); + } + + fail_if(p.received == NULL, "No property %s received", + p.expected); + fail_if(p.received != NULL && + g_value_get_uint(p.received) != 48, + "Property with value %d and %d expected", + g_value_get_uint(p.received), 48); + + if (p.received != NULL) { + g_value_unset(p.received); + g_free(p.received); + p.received = NULL; + } + p.expected = NULL; + + /* --- mute --- */ + + reset_callback_info(&c); + + c.property_expected = MAFW_PROPERTY_RENDERER_MUTE; + + mafw_extension_set_property_boolean(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, TRUE); + + p.expected = MAFW_PROPERTY_RENDERER_MUTE; + +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + if (!wait_for_property(&p, wait_tout_val)) { + fail("No property %s received", p.expected); + } + + fail_if(p.received == NULL, "No property %s received", + p.expected); + fail_if(p.received != NULL && + g_value_get_boolean(p.received) != TRUE, + "Property with value %d and %d expected", + g_value_get_boolean(p.received), TRUE); +#else + if (wait_for_property(&p, wait_tout_val)) { + fail("Property %s received and it should not have been", + p.expected); + } + + fail_if(p.received != NULL, + "Property %s received and it should not have been", + p.expected); +#endif + + if (p.received != NULL) { + g_value_unset(p.received); + g_free(p.received); + p.received = NULL; + } + p.expected = NULL; + + mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, get_property_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "get_property", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(c.property_received == NULL, + "No property %s received and expected", c.property_expected); +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + fail_if(c.property_received != NULL && + g_value_get_boolean(c.property_received) != TRUE, + "Property with value %d and %d expected", + g_value_get_boolean(c.property_received), TRUE); +#else + fail_if(c.property_received != NULL && + g_value_get_boolean(c.property_received) != FALSE, + "Property with value %d and %d expected", + g_value_get_boolean(c.property_received), FALSE); +#endif + + /* --- xid --- */ + + reset_callback_info(&c); + + c.property_expected = MAFW_PROPERTY_RENDERER_XID; + + mafw_extension_set_property_uint(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, 50); + + mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, get_property_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "get_property", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(c.property_received == NULL, + "No property %s received and expected", c.property_expected); + fail_if(c.property_received != NULL && + g_value_get_uint(c.property_received) != 50, + "Property with value %d and %d expected", + g_value_get_uint(c.property_received), 50); + + /* --- error policy --- */ + + reset_callback_info(&c); + + c.property_expected = MAFW_PROPERTY_RENDERER_ERROR_POLICY; + + mafw_extension_set_property_uint(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, 1); + + mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, get_property_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "get_property", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(c.property_received == NULL, + "No property %s received and expected", c.property_expected); + fail_if(c.property_received != NULL && + g_value_get_uint(c.property_received) != 1, + "Property with value %d and %d expected", + g_value_get_uint(c.property_received), 1); + + /* --- autopaint --- */ + + reset_callback_info(&c); + + c.property_expected = MAFW_PROPERTY_RENDERER_AUTOPAINT; + + mafw_extension_set_property_boolean(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, TRUE); + + mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, get_property_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "get_property", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(c.property_received == NULL, + "No property %s received and expected", c.property_expected); + fail_if(c.property_received != NULL && + g_value_get_boolean(c.property_received) != TRUE, + "Property with value %d and %d expected", + g_value_get_boolean(c.property_received), TRUE); + + /* --- colorkey --- */ + + reset_callback_info(&c); + + c.property_expected = MAFW_PROPERTY_RENDERER_COLORKEY; + + mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, get_property_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "get_property", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(c.property_received == NULL, + "No property %s received and expected", c.property_expected); + fail_if(c.property_received != NULL && + g_value_get_int(c.property_received) != -1, + "Property with value %d and %d expected", + g_value_get_int(c.property_received), -1); + + /* --- current frame on pause --- */ + + reset_callback_info(&c); + + c.property_expected = MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE; + + mafw_extension_set_property_boolean(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, TRUE); + + mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, get_property_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "get_property", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(c.property_received == NULL, + "No property %s received and expected", c.property_expected); + fail_if(c.property_received != NULL && + g_value_get_boolean(c.property_received) != TRUE, + "Property with value %d and %d expected", + g_value_get_boolean(c.property_received), TRUE); + + /* --- volume --- */ + + p.expected = MAFW_PROPERTY_RENDERER_VOLUME; + + mafw_extension_set_property_uint(MAFW_EXTENSION(g_gst_renderer), + p.expected, 50); + + if (!wait_for_property(&p, wait_tout_val)) { + fail("No property %s received", p.expected); + } + + fail_if(p.received == NULL, "No property %s received", + p.expected); + fail_if(p.received != NULL && + g_value_get_uint(p.received) != 50, + "Property with value %d and %d expected", + g_value_get_uint(p.received), 50); + + if (p.received != NULL) { + g_value_unset(p.received); + g_free(p.received); + p.received = NULL; + } + p.expected = NULL; + + c.property_expected = MAFW_PROPERTY_RENDERER_VOLUME; + + mafw_extension_get_property(MAFW_EXTENSION(g_gst_renderer), + c.property_expected, get_property_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "get_property", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + fail_if(c.property_received == NULL, + "No property %s received and expected", c.property_expected); + fail_if(c.property_received != NULL && + g_value_get_uint(c.property_received) != 50, + "Property with value %d and %d expected", + g_value_get_uint(c.property_received), 50); + +#ifndef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME + /* Test reconnection to pulse */ + + pa_context_disconnect(pa_context_get_instance()); + + /* Wait for the volume manager to be reinitialized */ + + /* Volume */ + + p.expected = MAFW_PROPERTY_RENDERER_VOLUME; + + if (!wait_for_property(&p, wait_tout_val)) { + fail("No property %s received", p.expected); + } + + fail_if(p.received == NULL, "No property %s received", + p.expected); + fail_if(p.received != NULL && + g_value_get_uint(p.received) != 48, + "Property with value %d and %d expected", + g_value_get_uint(p.received), 48); + + if (p.received != NULL) { + g_value_unset(p.received); + g_free(p.received); + p.received = NULL; + } + p.expected = NULL; + + reset_callback_info(&c); +#endif +} +END_TEST + +START_TEST(test_buffering) +{ + RendererInfo s; + CallbackInfo c; + BufferingInfo b; + GstBus *bus = NULL; + GstMessage *message = NULL; + + /* Initialize callback info */ + c.err_msg = NULL; + c.error_signal_expected = FALSE; + c.error_signal_received = NULL; + c.property_expected = NULL; + c.property_received = NULL; + b.requested = FALSE; + b.received = FALSE; + b.value = 0.0; + + /* Connect to renderer signals */ + g_signal_connect(g_gst_renderer, "error", + G_CALLBACK(error_cb), + &c); + g_signal_connect(g_gst_renderer, "state-changed", + G_CALLBACK(state_changed_cb), + &s); + g_signal_connect(g_gst_renderer, "buffering-info", + G_CALLBACK(buffering_info_cb), + &b); + + /* --- Get initial status --- */ + + reset_callback_info(&c); + + g_debug("get status..."); + mafw_renderer_get_status(g_gst_renderer, status_cb, &s); + + /* --- Play object --- */ + + reset_callback_info(&c); + + gchar *objectid = get_sample_clip_objectid(SAMPLE_AUDIO_CLIP); + g_debug("play_object... %s", objectid); + mafw_renderer_play_object(g_gst_renderer, objectid, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "playing an object", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Transitioning, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", + "Transitioning", s.state); + } + + if (wait_for_state(&s, Playing, wait_tout_val) == FALSE) { + fail(state_err_msg, "mafw_renderer_play_object", "Playing", + s.state); + } + + g_free(objectid); + + /* --- Buffering info --- */ + + b.requested = TRUE; + + bus = MAFW_GST_RENDERER(g_gst_renderer)->worker->bus; + fail_if(bus == NULL, "No GstBus"); + + message = gst_message_new_buffering(NULL, 50); + gst_bus_post(bus, message); + + if (wait_for_buffering(&b, wait_tout_val) == FALSE) { + fail("Expected buffering message but not received"); + } + + fail_if(b.value != 0.5, "Expected buffering 0.50 and received %1.2f", + b.value); + + b.requested = FALSE; + b.received = FALSE; + b.value = 0; + + /* --- Buffering info --- */ + + b.requested = TRUE; + + message = gst_message_new_buffering(NULL, 100); + gst_bus_post(bus, message); + + if (wait_for_buffering(&b, wait_tout_val) == FALSE) { + fail("Expected buffering message but not received"); + } + + fail_if(b.value != 1.0, "Expected buffering 1.00 and received %1.2f", + b.value); + + b.requested = FALSE; + b.received = FALSE; + b.value = 0; + + /* --- Stop --- */ + + reset_callback_info(&c); + + g_debug("stop..."); + mafw_renderer_stop(g_gst_renderer, playback_cb, &c); + + if (wait_for_callback(&c, wait_tout_val)) { + if (c.error) + fail(callback_err_msg, "stopping", c.err_code, + c.err_msg); + } else { + fail(no_callback_msg); + } + + if (wait_for_state(&s, Stopped, wait_tout_val) == FALSE) { + fail(state_err_msg,"mafw_renderer_stop", "Stopped", s.state); + } +} +END_TEST + +/*---------------------------------------------------------------------------- + Suit creation + ----------------------------------------------------------------------------*/ + +SRunner * configure_tests(void) +{ + SRunner *sr = NULL; + Suite *s = NULL; + const gchar *tout = g_getenv("WAIT_TIMEOUT"); + + if (!tout) + wait_tout_val = DEFAULT_WAIT_TOUT; + else + { + wait_tout_val = (gint)strtol(tout, NULL, 0); + if (wait_tout_val<=0) + wait_tout_val = DEFAULT_WAIT_TOUT; + } + + checkmore_wants_dbus(); + mafw_log_init(":error"); + /* Create the suite */ + s = suite_create("MafwGstRenderer"); + + /* Create test cases */ + TCase *tc1 = tcase_create("Playback"); + + /* Create unit tests for test case "Playback" */ + tcase_add_checked_fixture(tc1, fx_setup_dummy_gst_renderer, + fx_teardown_dummy_gst_renderer); +if (1) tcase_add_test(tc1, test_basic_playback); +if (1) tcase_add_test(tc1, test_playlist_playback); +if (1) tcase_add_test(tc1, test_repeat_mode_playback); +if (1) tcase_add_test(tc1, test_gst_renderer_mode); +if (1) tcase_add_test(tc1, test_update_stats); +if (1) tcase_add_test(tc1, test_play_state); +if (1) tcase_add_test(tc1, test_pause_state); +if (1) tcase_add_test(tc1, test_stop_state); +if (1) tcase_add_test(tc1, test_transitioning_state); +if (1) tcase_add_test(tc1, test_state_class); +if (1) tcase_add_test(tc1, test_playlist_iterator); +if (1) tcase_add_test(tc1, test_video); +if (1) tcase_add_test(tc1, test_media_art); +if (1) tcase_add_test(tc1, test_properties_management); +if (1) tcase_add_test(tc1, test_buffering); + + tcase_set_timeout(tc1, 0); + + suite_add_tcase(s, tc1); + + /* Create srunner object with the test suite */ + sr = srunner_create(s); + + return sr; +} + +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/tests/check-main.c b/tests/check-main.c new file mode 100644 index 0000000..c5c5172 --- /dev/null +++ b/tests/check-main.c @@ -0,0 +1,53 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include + + +/* This must be provided by the test suite implementation */ +SRunner *configure_tests(void); + +int main(void) +{ + int nf = 0; + + /* Configure test suites to be executed */ + SRunner *sr = configure_tests(); + + /* Run tests */ + srunner_run_all(sr, CK_ENV); + + /* Retrieve number of failed tests */ + nf = srunner_ntests_failed(sr); + + /* Free resouces */ + srunner_free(sr); + + /* Return global success or failure */ + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/tests/mafw-mock-playlist.c b/tests/mafw-mock-playlist.c new file mode 100644 index 0000000..0720261 --- /dev/null +++ b/tests/mafw-mock-playlist.c @@ -0,0 +1,321 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include + +#include +#include "mafw-mock-playlist.h" + +static GList *pl_list; +static gchar *pl_name; +static gboolean pl_rep; +static gboolean pl_shuffle; + +/* Item manipulation */ + +static gboolean mafw_mock_playlist_insert_item(MafwPlaylist *playlist, + guint index, + const gchar *objectid, + GError **error); + +static gboolean mafw_mock_playlist_remove_item(MafwPlaylist *playlist, + guint index, + GError **error); + +static gchar *mafw_mock_playlist_get_item(MafwPlaylist *playlist, + guint index, GError **error); + +static gboolean mafw_mock_playlist_move_item(MafwPlaylist *playlist, + guint from, guint to, + GError **error); + +static guint mafw_mock_playlist_get_size(MafwPlaylist *playlist, + GError **error); + +static gboolean mafw_mock_playlist_clear(MafwPlaylist *playlist, + GError **error); + +static gboolean mafw_mock_playlist_increment_use_count(MafwPlaylist *playlist, + GError **error); + +static gboolean mafw_mock_playlist_decrement_use_count(MafwPlaylist *playlist, + GError **error); +gboolean mafw_mock_playlist_get_prev(MafwPlaylist *playlist, guint *index, + gchar **object_id, GError **error); +gboolean mafw_mock_playlist_get_next(MafwPlaylist *playlist, guint *index, + gchar **object_id, GError **error); +static void mafw_mock_playlist_get_starting_index(MafwPlaylist *playlist, guint *index, + gchar **object_id, GError **error); +static void mafw_mock_playlist_get_last_index(MafwPlaylist *playlist, + guint *index, gchar **object_id, + GError **error); + +enum { + PROP_0, + PROP_NAME, + PROP_REPEAT, + PROP_IS_SHUFFLED, +}; + +static void set_prop(MafwMockPlaylist *playlist, guint prop, + const GValue *value, GParamSpec *spec) +{ + if (prop == PROP_NAME) { + pl_name = g_value_dup_string(value); + } else if (prop == PROP_REPEAT) { + pl_rep = g_value_get_boolean(value); + } else + G_OBJECT_WARN_INVALID_PROPERTY_ID(playlist, prop, spec); +} + +static void get_prop(MafwMockPlaylist *playlist, guint prop, + GValue *value, GParamSpec *spec) +{ + if (prop == PROP_NAME) { + g_value_take_string(value, pl_name); + } else if (prop == PROP_REPEAT) { + g_value_set_boolean(value, pl_rep); + } else if (prop == PROP_IS_SHUFFLED) { + g_value_set_boolean(value, pl_shuffle); + } else + G_OBJECT_WARN_INVALID_PROPERTY_ID(playlist, prop, spec); +} + +static void mafw_mock_playlist_get_starting_index(MafwPlaylist *playlist, guint *index, + gchar **object_id, GError **error) +{ + if (g_list_length(pl_list) > 0) { + *index = 0; + *object_id = g_strdup(g_list_nth_data(pl_list, 0)); + } +} + +static void mafw_mock_playlist_get_last_index(MafwPlaylist *playlist, + guint *index, gchar **object_id, + GError **error) +{ + *index = g_list_length(pl_list) - 1; + *object_id = g_strdup(g_list_nth_data(pl_list, *index)); +} + + +gboolean mafw_mock_playlist_get_next(MafwPlaylist *playlist, guint *index, + gchar **object_id, GError **error) +{ + gint size; + gboolean return_value = TRUE; + + size = g_list_length(pl_list); + + g_return_val_if_fail(size != 0, FALSE); + + if (*index == (size - 1)) { + return_value = FALSE; + } else { + *object_id = g_strdup(g_list_nth_data(pl_list, ++(*index))); + } + + return return_value; +} + +gboolean mafw_mock_playlist_get_prev(MafwPlaylist *playlist, guint *index, + gchar **object_id, GError **error) +{ + gint size; + gboolean return_value = TRUE; + + size = g_list_length(pl_list); + + g_return_val_if_fail(size != 0, FALSE); + + if (*index == 0) { + return_value = FALSE; + } else { + *object_id = g_strdup(g_list_nth_data(pl_list, --(*index))); + } + + return return_value; +} + +static void playlist_iface_init(MafwPlaylistIface *iface) +{ + iface->get_item = mafw_mock_playlist_get_item; + iface->insert_item = mafw_mock_playlist_insert_item; + iface->clear = mafw_mock_playlist_clear; + iface->get_size = mafw_mock_playlist_get_size; + iface->remove_item = mafw_mock_playlist_remove_item; + iface->move_item = mafw_mock_playlist_move_item; + iface->get_starting_index = mafw_mock_playlist_get_starting_index; + iface->get_last_index = mafw_mock_playlist_get_last_index; + iface->get_next = mafw_mock_playlist_get_next; + iface->get_prev = mafw_mock_playlist_get_prev; + iface->increment_use_count = mafw_mock_playlist_increment_use_count; + iface->decrement_use_count = mafw_mock_playlist_decrement_use_count; +} + + +static void mafw_mock_playlist_finalize(GObject *object) +{ + g_debug(__FUNCTION__); + + while (pl_list) + { + g_free(pl_list->data); + pl_list = g_list_delete_link(pl_list, pl_list); + } + +} + +static void mafw_mock_playlist_class_init( + MafwMockPlaylistClass *klass) +{ + GObjectClass *oclass = NULL; + + oclass = G_OBJECT_CLASS(klass); + + oclass->set_property = (gpointer)set_prop; + oclass->get_property = (gpointer)get_prop; + g_object_class_override_property(oclass, PROP_NAME, "name"); + g_object_class_override_property(oclass, PROP_REPEAT, "repeat"); + g_object_class_override_property(oclass, + PROP_IS_SHUFFLED, "is-shuffled"); + + oclass -> finalize = mafw_mock_playlist_finalize; +} + +static void mafw_mock_playlist_init(MafwMockPlaylist *self) +{ +} + + + +G_DEFINE_TYPE_WITH_CODE(MafwMockPlaylist, mafw_mock_playlist, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(MAFW_TYPE_PLAYLIST, + playlist_iface_init)); + + +GObject *mafw_mock_playlist_new(void) +{ + MafwMockPlaylist *self; + + self = g_object_new(MAFW_TYPE_MOCK_PLAYLIST, NULL); + + return G_OBJECT(self); +} + +gboolean mafw_mock_playlist_insert_item(MafwPlaylist *self, guint index, + const gchar *objectid, + GError **error) +{ + pl_list = g_list_insert(pl_list, g_strdup(objectid), index); + + g_signal_emit_by_name(self, "contents-changed", index, 0, 1); + + return TRUE; +} + +gboolean mafw_mock_playlist_remove_item(MafwPlaylist *self, guint index, + GError **error) +{ + GList *element; + + g_return_val_if_fail(g_list_length(pl_list) > 0, FALSE); + + element = g_list_nth(pl_list, index); + g_free(element->data); + pl_list = g_list_delete_link(pl_list, element); + + g_signal_emit_by_name(self, "contents-changed", index, 1, 0); + + return TRUE; +} + +gchar *mafw_mock_playlist_get_item(MafwPlaylist *self, guint index, + GError **error) +{ + gchar *oid = g_list_nth_data(pl_list, index); + + if (oid) + oid = g_strdup(oid); + + return oid; +} + +guint mafw_mock_playlist_get_size(MafwPlaylist *self, GError **error) +{ + return g_list_length(pl_list); +} + +static gboolean mafw_mock_playlist_move_item(MafwPlaylist *playlist, + guint from, guint to, + GError **error) +{ + GList *element_from, *element_to; + gpointer data; + gint size; + + size = g_list_length(pl_list); + + g_return_val_if_fail(size > 0, FALSE); + g_return_val_if_fail(from != to, FALSE); + g_return_val_if_fail((from < size) && (to < size), FALSE); + + element_from = g_list_nth(pl_list, from); + element_to = g_list_nth(pl_list, to); + + data = element_from->data; + element_from->data = element_to->data; + element_to->data = data; + + g_signal_emit_by_name(playlist, "item-moved", from, to); + + return TRUE; +} + +static gboolean mafw_mock_playlist_increment_use_count(MafwPlaylist *playlist, + GError **error) +{ + return TRUE; +} + +static gboolean mafw_mock_playlist_decrement_use_count(MafwPlaylist *playlist, + GError **error) +{ + return TRUE; +} + +gboolean mafw_mock_playlist_clear(MafwPlaylist *self, GError **error) +{ + mafw_mock_playlist_finalize(NULL); + + return TRUE; +} + +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/tests/mafw-mock-playlist.h b/tests/mafw-mock-playlist.h new file mode 100644 index 0000000..dcc2da1 --- /dev/null +++ b/tests/mafw-mock-playlist.h @@ -0,0 +1,84 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include + +/*---------------------------------------------------------------------------- + GObject type conversion macros + ----------------------------------------------------------------------------*/ + +#define MAFW_TYPE_MOCK_PLAYLIST \ + (mafw_mock_playlist_get_type()) + +#define MAFW_MOCK_PLAYLIST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST(obj, MAFW_TYPE_MOCK_PLAYLIST, \ + MafwMockPlaylist)) + +#define MAFW_MOCK_PLAYLIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST(klass, MAFW_TYPE_MOCK_PLAYLIST, \ + MafwMockPlaylistClass)) + +#define MAFW_IS_MOCK_PLAYLIST(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE(obj, MAFW_TYPE_MOCK_PLAYLIST)) + +#define MAFW_IS_MOCK_PLAYLIST_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAFW_TYPE_MOCK_PLAYLIST)) + +#define MAFW_MOCK_PLAYLIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAFW_TYPE_MOCK_PLAYLIST, \ + MafwMockPlaylistClass)) + +/*---------------------------------------------------------------------------- + GObject type definitions + ----------------------------------------------------------------------------*/ + +typedef struct _MafwMockPlaylist MafwMockPlaylist; +typedef struct _MafwMockPlaylistClass MafwMockPlaylistClass; + + +struct _MafwMockPlaylist +{ + GObject parent_instance; + +}; + +struct _MafwMockPlaylistClass +{ + GObjectClass parent_class; + +}; + +/*---------------------------------------------------------------------------- + Shared playlist-specific functions + ----------------------------------------------------------------------------*/ + +GType mafw_mock_playlist_get_type(void); +GObject *mafw_mock_playlist_new(void); + +#endif diff --git a/tests/mafw-mock-pulseaudio.c b/tests/mafw-mock-pulseaudio.c new file mode 100644 index 0000000..1675073 --- /dev/null +++ b/tests/mafw-mock-pulseaudio.c @@ -0,0 +1,295 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include "mafw-mock-pulseaudio.h" + +typedef void pa_glib_mainloop; +typedef void pa_mainloop_api; +typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata); +typedef guint pa_context_flags_t; +typedef void pa_spawn_api; +typedef void pa_operation; +typedef guint32 pa_volume_t; +typedef struct { + guint8 channels; + guint map[32]; +} pa_channel_map; +typedef struct { + guint8 channels; + pa_volume_t values[32]; +} pa_cvolume; +typedef struct { + const gchar *name; + pa_channel_map channel_map; + pa_cvolume volume; + const char *device; + gint mute; + gboolean volume_is_absolute; +} pa_ext_stream_restore_info; +typedef void (*pa_ext_stream_restore_read_cb_t)( + pa_context *c, + const pa_ext_stream_restore_info *info, int eol, void *userdata); +typedef void (*pa_ext_stream_restore_subscribe_cb_t)(pa_context *c, + void *userdata); +typedef void (*pa_context_success_cb_t)(pa_context *c, int success, + void *userdata); +enum pa_context_state { + PA_CONTEXT_UNCONNECTED = 0, + PA_CONTEXT_CONNECTING, + PA_CONTEXT_AUTHORIZING, + PA_CONTEXT_SETTING_NAME, + PA_CONTEXT_READY, + PA_CONTEXT_FAILED, + PA_CONTEXT_TERMINATED +}; +struct _pa_context { + pa_context_notify_cb_t state_cb; + gpointer state_cb_userdata; + enum pa_context_state state; + pa_ext_stream_restore_read_cb_t read_cb; + gpointer read_cb_userdata; + pa_ext_stream_restore_subscribe_cb_t subscribe_cb; + gpointer subscribe_cb_userdata; + pa_cvolume volume; + gboolean mute; +}; + +static pa_context *context = NULL; + +pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c); +char *pa_get_binary_name(char *s, size_t l); +pa_mainloop_api *pa_glib_mainloop_get_api(pa_glib_mainloop *g); +pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name); +void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, + void *userdata); +int pa_context_connect(pa_context *c, const char *server, + pa_context_flags_t flags, const pa_spawn_api *api); +gint pa_context_get_state(pa_context *c); +pa_operation *pa_ext_stream_restore2_read(pa_context *c, + pa_ext_stream_restore_read_cb_t cb, + void *userdata); +void pa_operation_unref(pa_operation *o); +pa_volume_t pa_cvolume_max(const pa_cvolume *volume); +void pa_ext_stream_restore_set_subscribe_cb( + pa_context *c, + pa_ext_stream_restore_subscribe_cb_t cb, void *userdata); +gint pa_operation_get_state(pa_operation *o); +void pa_operation_cancel(pa_operation *o); +void pa_glib_mainloop_free(pa_glib_mainloop *g); +pa_cvolume *pa_cvolume_init(pa_cvolume *a); +pa_cvolume *pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v); +pa_operation *pa_ext_stream_restore2_write( + pa_context *c, gint mode, const pa_ext_stream_restore_info *data[], + unsigned n, int apply_immediately, pa_context_success_cb_t cb, + void *userdata); +pa_operation *pa_ext_stream_restore_subscribe(pa_context *c, int enable, + pa_context_success_cb_t cb, + void *userdata); +void pa_context_unref(pa_context *c); + +pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) +{ + return (gpointer) 0x1; +} + +char *pa_get_binary_name(char *s, size_t l) +{ + g_strlcpy(s, "mafw-gst-renderer-tests", l); + + return NULL; +} + +pa_mainloop_api *pa_glib_mainloop_get_api(pa_glib_mainloop *g) +{ + return (gpointer) 0x1; +} + +pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) +{ + pa_context *c = g_new0(pa_context, 1); + + pa_cvolume_set(&c->volume, 1, 32000); + + context = c; + + return c; +} + +void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, + void *userdata) +{ + c->state_cb = cb; + c->state_cb_userdata = userdata; +} + +static gboolean _pa_context_connect_idle(gpointer userdata) +{ + pa_context *c = userdata; + c->state++; + if (c->state_cb != NULL) { + c->state_cb(c, c->state_cb_userdata); + } + return c->state != PA_CONTEXT_READY; +} + +int pa_context_connect(pa_context *c, const char *server, + pa_context_flags_t flags, const pa_spawn_api *api) +{ + g_idle_add(_pa_context_connect_idle, c); + return 1; +} + +gint pa_context_get_state(pa_context *c) +{ + return c->state; +} + +static gboolean _pa_ext_stream_restore2_read_idle(gpointer userdata) +{ + pa_context *c = userdata; + pa_ext_stream_restore_info info = { 0, }; + + info.name = "sink-input-by-media-role:x-maemo"; + pa_cvolume_set(&info.volume, 1, c->volume.values[0]); + info.mute = c->mute; + + c->read_cb(c, &info, 1, c->read_cb_userdata); + + return FALSE; +} + +pa_operation *pa_ext_stream_restore2_read(pa_context *c, + pa_ext_stream_restore_read_cb_t cb, + void *userdata) +{ + c->read_cb = cb; + c->read_cb_userdata = userdata; + g_idle_add(_pa_ext_stream_restore2_read_idle, c); + return (gpointer) 0x1; +} + +void pa_operation_unref(pa_operation *o) +{ +} + +pa_volume_t pa_cvolume_max(const pa_cvolume *volume) +{ + return volume->values[0]; +} + +pa_operation *pa_ext_stream_restore_subscribe(pa_context *c, int enable, + pa_context_success_cb_t cb, + void *userdata) +{ + if (cb != NULL) { + cb(c, TRUE, userdata); + } + return (gpointer) 0x1; +} + +void pa_ext_stream_restore_set_subscribe_cb( + pa_context *c, + pa_ext_stream_restore_subscribe_cb_t cb, void *userdata) +{ + c->subscribe_cb = cb; + c->subscribe_cb_userdata = userdata; +} + +gint pa_operation_get_state(pa_operation *o) +{ + return 1; +} + +void pa_operation_cancel(pa_operation *o) +{ +} + +void pa_context_unref(pa_context *c) +{ + g_free(c); +} + +void pa_glib_mainloop_free(pa_glib_mainloop *g) +{ +} + +pa_cvolume *pa_cvolume_init(pa_cvolume *a) +{ + pa_cvolume_set(a, 1, 0); + return a; +} + +pa_cvolume *pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) +{ + a->channels = 1; + a->values[0] = v; + return a; +} + +static gboolean _pa_ext_stream_restore_write_idle(gpointer userdata) +{ + pa_context *c = userdata; + + if (c->subscribe_cb != NULL) { + c->subscribe_cb(c, c->subscribe_cb_userdata); + } + + return FALSE; +} + +pa_operation *pa_ext_stream_restore2_write( + pa_context *c, gint mode, const pa_ext_stream_restore_info *data[], + unsigned n, int apply_immediately, pa_context_success_cb_t cb, + void *userdata) +{ + const pa_ext_stream_restore_info *info = data[0]; + + pa_cvolume_set(&c->volume, 1, info->volume.values[0]); + c->mute = info->mute; + + g_idle_add(_pa_ext_stream_restore_write_idle, c); + + return (gpointer) 0x1; +} + +static gboolean _pa_context_disconnect_idle(gpointer userdata) +{ + pa_context *c = userdata; + c->state = PA_CONTEXT_TERMINATED; + if (c->state_cb != NULL) { + c->state_cb(c, c->state_cb_userdata); + } + return FALSE; +} + +void pa_context_disconnect(pa_context *c) +{ + g_idle_add(_pa_context_disconnect_idle, c); +} + +pa_context *pa_context_get_instance(void) +{ + return context; +} diff --git a/tests/mafw-mock-pulseaudio.h b/tests/mafw-mock-pulseaudio.h new file mode 100644 index 0000000..bfb30a7 --- /dev/null +++ b/tests/mafw-mock-pulseaudio.h @@ -0,0 +1,33 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef MAFW_MOCK_PULSEAUDIO_H +#define MAFW_MOCK_PULSEAUDIO_H + +typedef struct _pa_context pa_context; + +void pa_context_disconnect(pa_context *c); +pa_context *pa_context_get_instance(void); + +#endif diff --git a/tests/mafw-test-player.c b/tests/mafw-test-player.c new file mode 100644 index 0000000..a340672 --- /dev/null +++ b/tests/mafw-test-player.c @@ -0,0 +1,312 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include +#include +#include +#include + +#include "mafw-gst-renderer.h" +#include +#include + +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 \n"); + exit(1); + } + + raw_kb_enable(); + + registry = MAFW_REGISTRY(mafw_registry_get_instance()); + gst_renderer = MAFW_GST_RENDERER(mafw_gst_renderer_new(registry)); + g_signal_connect (G_OBJECT (gst_renderer), + "metadata_changed", + G_CALLBACK (metadata_changed), + gst_renderer); + + g_signal_connect (G_OBJECT (gst_renderer), + "buffering_info", + G_CALLBACK (buffering_cb), + gst_renderer); + + mafw_renderer_play_uri(MAFW_RENDERER (gst_renderer), argv[1], play_uri_cb, + NULL); + + loop = mafw_gst_renderer_get_loop(gst_renderer); + + g_idle_add (idle_cb, NULL); + g_main_loop_run (loop); + + g_object_unref (G_OBJECT (gst_renderer)); + return 0; +} diff --git a/tests/media/test.avi b/tests/media/test.avi new file mode 100644 index 0000000..f868c62 Binary files /dev/null and b/tests/media/test.avi differ diff --git a/tests/media/test.wav b/tests/media/test.wav new file mode 100644 index 0000000..943b040 Binary files /dev/null and b/tests/media/test.wav differ diff --git a/tests/media/testframe.png b/tests/media/testframe.png new file mode 100644 index 0000000..a0db335 Binary files /dev/null and b/tests/media/testframe.png differ diff --git a/tests/test.suppressions b/tests/test.suppressions new file mode 100644 index 0000000..8d0b36b --- /dev/null +++ b/tests/test.suppressions @@ -0,0 +1,864 @@ +{ + <1> + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_slice_alloc0 + fun:g_type_create_instance + fun:g_object_constructor + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + fun:gst_element_factory_create + fun:gst_element_make_from_uri + obj:* + obj:* + obj:* + fun:gst_element_change_state + fun:gst_element_continue_state + fun:gst_element_change_state + obj:* + fun:gst_element_set_state + fun:_construct_pipeline +} +{ + <2> + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_slice_alloc0 + fun:g_type_create_instance + fun:g_object_constructor + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + fun:gst_pad_new_from_template + obj:* + fun:g_type_create_instance + fun:g_object_constructor + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + fun:gst_element_factory_create + fun:gst_element_make_from_uri + obj:* + obj:* +} +{ + <3> + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:do_dlopen + fun:_dl_catch_error + fun:dlerror_run + fun:__libc_dlopen_mode + fun:__nss_lookup_function + fun:__nss_lookup + fun:__nss_passwd_lookup + fun:getpwnam_r@@GLIBC_2.1.2 +} +{ + <4> + Memcheck:Leak + fun:realloc + fun:g_realloc + obj:* + fun:g_signal_newv + fun:g_signal_new_valist + fun:g_signal_new + obj:* + fun:g_type_class_ref + fun:g_type_class_ref + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new +} + +{ + <5> + Memcheck:Leak + fun:calloc + fun:g_malloc0 + obj:* + fun:g_type_create_instance + obj:* + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + fun:gst_element_factory_create + fun:gst_element_make_from_uri + obj:* + obj:* +} + +{ + <6> + Memcheck:Leak + fun:vasprintf + fun:g_vasprintf + fun:g_strdup_vprintf + fun:g_strdup_printf + fun:gst_uri_construct + fun:g_object_newv + obj:* + obj:* + fun:gst_uri_handler_set_uri + fun:gst_element_make_from_uri + obj:* + obj:* +} +{ + <7> + Memcheck:Leak + fun:malloc + fun:open_path + fun:_dl_map_object + fun:openaux + fun:_dl_catch_error + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run +} +{ + <8> + Memcheck:Leak + fun:malloc + fun:expand_dynamic_string_token + fun:_dl_map_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file +} +{ + <9> + Memcheck:Leak + fun:malloc + fun:_dl_new_object + fun:_dl_map_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} +{ + <10> + Memcheck:Leak + fun:malloc + fun:_dl_map_object_deps + fun:_dl_map_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_plugin_load_by_name +} +{ + <11> + Memcheck:Leak + fun:calloc + fun:_dl_check_map_versions + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_plugin_load_by_name +} +{ + <12> + Memcheck:Leak + fun:malloc + fun:g_malloc0 + obj:* + obj:* + fun:g_type_create_instance + obj:* + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + fun:gst_element_factory_create + fun:gst_element_make_from_uri + obj:* + obj:* +} +{ + <13> + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + obj:* + fun:g_array_sized_new + obj:* + fun:gst_structure_copy + fun:gst_caps_copy + fun:gst_audio_filter_class_add_pad_templates + obj:* + fun:g_type_class_ref + fun:gst_element_register +} +{ + <14> + Memcheck:Cond + fun:strlen + fun:_dl_init_paths + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start +} +{ + <15> + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start +} +{ + <15> + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.1 + obj:* + fun:g_thread_create_full + obj:* + fun:g_thread_pool_push + fun:gst_task_start + fun:gst_pad_start_task + obj:* + fun:gst_pad_activate_pull +} +{ + <16> + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_plugin_load_by_name + fun:gst_plugin_feature_load +} +{ + <17> + Memcheck:Leak + fun:malloc + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_plugin_load_by_name + fun:gst_plugin_feature_load +} +{ + <17> + Memcheck:Leak + fun:realloc + fun:vasprintf + fun:g_vasprintf + fun:g_strdup_vprintf + fun:g_strdup_printf + fun:gst_uri_construct + fun:gst_file_src_set_location + fun:gst_file_src_uri_set_uri + fun:gst_uri_handler_set_uri + fun:gst_element_make_from_uri + obj:* + obj:* +} +{ + <18> + Memcheck:Leak + fun:calloc + fun:parse_bracket_exp + fun:parse_expression + fun:parse_branch + fun:parse_reg_exp + fun:parse_expression + fun:parse_branch + fun:parse_reg_exp + fun:parse_expression + fun:parse_branch + fun:parse_reg_exp + fun:re_compile_internal +} +{ + <19> + Memcheck:Leak + fun:malloc + fun:_dl_map_object_deps + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open + fun:gst_plugin_load_file + fun:gst_plugin_load_by_name +} +{ + <20> + Memcheck:Leak + fun:malloc + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} +{ + <21> + Memcheck:Leak + fun:malloc + fun:g_malloc + obj:* + obj:* + fun:g_type_create_instance + obj:* + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + fun:gst_element_factory_create + fun:gst_element_make_from_uri + obj:* +} +{ + <22> + Memcheck:Leak + fun:calloc + fun:dbus_malloc0 + obj:* + obj:* + obj:* + obj:* + fun:dbus_parse_address + obj:* + fun:dbus_connection_open + obj:* + fun:dbus_bus_get + obj:* +} +{ + <23> + Memcheck:Leak + fun:realloc + fun:g_realloc + obj:* + fun:g_array_append_vals + obj:* + fun:gst_structure_set_valist + fun:gst_caps_set_simple + fun:gst_riff_create_audio_caps + fun:gst_riff_create_audio_template_caps + obj:* + fun:g_type_class_ref + fun:gst_element_register +} +{ + <24> + Memcheck:Leak + fun:realloc + fun:dbus_realloc + obj:* + obj:* + obj:* + obj:* + obj:* + obj:* + obj:* + obj:* + obj:* + obj:* +} +{ + <25> + Memcheck:Leak + fun:calloc + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} +{ + <26> + Memcheck:Leak + fun:realloc + fun:vasprintf + fun:g_vasprintf + fun:g_strdup_vprintf + fun:g_strdup_printf + fun:gst_uri_construct + obj:* + obj:* + fun:gst_uri_handler_set_uri + fun:gst_element_make_from_uri + obj:* + obj:* +} +{ + <27> + Memcheck:Leak + fun:malloc + fun:_dl_new_object + fun:_dl_map_object_from_fd + fun:_dl_map_object + fun:dl_open_worker + fun:_dl_catch_error + fun:_dl_open + fun:dlopen_doit + fun:_dl_catch_error + fun:_dlerror_run + fun:dlopen@@GLIBC_2.1 + fun:g_module_open +} +{ + <28> + Memcheck:Leak + fun:realloc + fun:vasprintf + fun:g_vasprintf + fun:g_strdup_vprintf + fun:g_strdup_printf + fun:gst_uri_construct + obj:* + obj:* + fun:gst_uri_handler_set_uri + fun:gst_element_make_from_uri + obj:* + obj:* +} +{ + <29> + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_strdup + fun:gst_object_set_name + fun:gst_element_factory_create + fun:gst_element_make_from_uri + obj:* + obj:* + obj:* + fun:gst_element_change_state + fun:gst_element_continue_state + fun:gst_element_change_state +} +{ + <30> + Memcheck:Cond + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:* + obj:* + obj:* + obj:* +} +{ + <31> + Memcheck:Cond + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so +} +{ + <32> + Memcheck:Cond + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libc-2.5.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/targets/*/lib/libc-2.5.so + fun:__nss_passwd_lookup + fun:getpwnam_r + obj:/targets/*/usr/lib/libglib-2.0.so.0.1800.1 +} +{ + <33> + Memcheck:Cond + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libc-2.5.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/targets/*/lib/libc-2.5.so + fun:__nss_passwd_lookup + fun:getpwnam_r + obj:/targets/*/usr/lib/libglib-2.0.so.0.1800.1 +} +{ + <34> + Memcheck:Leak + fun:calloc + fun:g_malloc0 + obj:/targets/*/usr/lib/libgobject-2.0.so.0.1800.1 + obj:/targets/*/usr/lib/libgobject-2.0.so.0.1800.1 + fun:g_type_init_with_debug_flags + fun:g_type_init + fun:fx_setup_dummy_gst_renderer + fun:tcase_run_checked_setup + fun:srunner_run_all + fun:main +} +{ + <35> + Memcheck:Leak + fun:calloc + fun:g_malloc0 + obj:/targets/*/usr/lib/libglib-2.0.so.0.1800.1 + fun:g_slice_alloc + fun:g_slist_prepend + fun:g_strsplit + fun:mafw_log_init + fun:configure_tests + fun:main +} +{ + <36> + Memcheck:Addr4 + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libdl-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libdl-2.5.so +} +{ + <37> + Memcheck:Leak + fun:* + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + fun:reg* + fun:mafw_source_create_objectid + fun:get_sample_clip_objectid + fun:* + fun:srunner_run_all + fun:main +} +{ + <38> + Memcheck:Leak + fun:* + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so +} +{ + <39> + Memcheck:Leak + fun:* + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + fun:reg* + fun:mafw_source_create_objectid + fun:get_sample_clip_objectid + fun:* + fun:srunner_run_all + fun:main +} +{ + <40> + Memcheck:Leak + fun:* + obj:/targets/*/lib/libc-2.5.so + fun:reg* + fun:mafw_source_create_objectid + fun:get_sample_clip_objectid + fun:* + fun:srunner_run_all + fun:main +} +{ + <41> + Memcheck:Leak + fun:* + fun:reg* + fun:mafw_source_create_objectid + fun:get_sample_clip_objectid + fun:* + fun:srunner_run_all + fun:main +} +{ + <42> + Memcheck:Leak + fun:realloc + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + fun:regcomp + fun:mafw_source_create_objectid + fun:get_sample_clip_objectid + fun:* + fun:srunner_run_all +} +{ + <43> + Memcheck:Cond + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libc-2.5.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/targets/*/lib/libc-2.5.so + fun:__nss_passwd_lookup + fun:getpwnam_r +} +{ + <44> + Memcheck:Addr4 + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libc-2.5.so + fun:__libc_dlopen_mode + fun:__nss_lookup_function + obj:/targets/*/lib/libc-2.5.so +} +{ + <45> + Memcheck:Leak + fun:malloc + fun:fdopen + fun:tmpfile + fun:setup_pipe + fun:srunner_run_all + fun:main +} +{ + <46> + Memcheck:Leak + fun:realloc + fun:erealloc + fun:maybe_grow + fun:list_add_end + fun:_tcase_add_test + fun:configure_tests + fun:main +} +{ + <47> + Memcheck:Leak + fun:malloc + fun:realloc + fun:g_realloc + obj:/targets/*/usr/lib/libgobject-2.0.so.0.1800.1 + fun:g_type_register_static + fun:g_type_plugin_get_type + fun:g_type_init_with_debug_flags + fun:g_type_init + fun:fx_setup_dummy_gst_renderer + fun:tcase_run_checked_setup + fun:srunner_run_all + fun:main +} +{ + <48> + Memcheck:Leak + fun:* + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + obj:/targets/*/lib/libc-2.5.so + fun:reg* + fun:mafw_source_create_objectid + fun:get_sample_clip_objectid + fun:* + fun:srunner_run_all + fun:main +} +{ + <49> + Memcheck:Leak + fun:malloc + fun:emalloc + fun:suite_create + fun:configure_tests + fun:main +} +{ + <50> + Memcheck:Leak + fun:malloc + fun:dbus_malloc + obj:/targets/*/usr/lib/libdbus-1.so.3.4.0 + obj:/targets/*/usr/lib/libdbus-1.so.3.4.0 + fun:dbus_bus_get + obj:/targets/*/usr/lib/libosso.so.1.3.0 + fun:osso_initialize + fun:blanking_init + fun:mafw_gst_renderer_worker_new + fun:mafw_gst_renderer_init + fun:g_type_create_instance + obj:/targets/*/usr/lib/libgobject-2.0.so.0.1800.1 +} +{ + <51> + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_strdup + obj:/targets/*/usr/lib/libgstreamer-0.10.so.0.18.0 + fun:gst_registry_binary_read_cache + obj:/targets/*/usr/lib/libgstreamer-0.10.so.0.18.0 + obj:/targets/*/usr/lib/libgstreamer-0.10.so.0.18.0 + fun:g_option_context_parse + fun:gst_init_check + fun:gst_init + fun:mafw_gst_renderer_class_init + fun:mafw_gst_renderer_class_intern_init +} +{ + <52> + Memcheck:Leak + fun:* + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libdl-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libdl-2.5.so + fun:dlopen + fun:g_module_open +} +{ + <53> + Memcheck:Leak + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_hash_table_new_full + fun:g_hash_table_new + fun:g_quark_from_static_string + fun:g_type_init_with_debug_flags + fun:g_type_init + fun:fx_setup_dummy_gst_renderer + fun:tcase_run_checked_setup + fun:srunner_run_all + fun:main +} +{ + <54> + Memcheck:Leak + fun:* + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libdl-2.5.so + obj:/targets/*/lib/ld-2.5.so + obj:/targets/*/lib/libdl-2.5.so +} +{ + <55> + Memcheck:Leak + fun:malloc + fun:fdopen + fun:tmpfile + fun:setup_pipe + fun:receive_test_result + fun:srunner_run_all + fun:main +} diff --git a/welcome b/welcome deleted file mode 100644 index e69de29..0000000