From be2c98fb83895d10ac44af7b9a9c3e00ca54bf49 Mon Sep 17 00:00:00 2001 From: Roman Moravcik Date: Thu, 23 Jun 2011 13:47:47 +0200 Subject: [PATCH] Source tree moved to mafw-gst-subtitles-renderer directory. --- AUTHORS | 10 - COPYING | 504 --- Makefile.am | 41 - acinclude.m4 | 179 - applet/Makefile.am | 12 - applet/cpmpsubtitles.c | 775 ---- applet/cpmpsubtitles.desktop | 10 - autogen.sh | 11 - configure.ac | 246 -- debian/changelog | 1258 ------ debian/compat | 1 - debian/control | 70 - debian/copyright | 6 - debian/mafw-gst-renderer.mafw.xsession | 5 - debian/mafw-gst-subtitles-applet.install | 3 - debian/mafw-gst-subtitles-renderer.dirs | 1 - debian/mafw-gst-subtitles-renderer.install.in | 1 - debian/mafw-gst-subtitles-renderer.postinst | 12 - debian/mafw-gst-subtitles-renderer.postrm | 20 - debian/mafw-gst-subtitles-renderer.preinst | 11 - debian/mafw-gst-subtitles-renderer.prerm | 7 - debian/rules | 95 - libmafw-gst-renderer/Makefile.am | 56 - libmafw-gst-renderer/blanking.c | 119 - libmafw-gst-renderer/blanking.h | 37 - libmafw-gst-renderer/gstscreenshot.c | 259 -- libmafw-gst-renderer/gstscreenshot.h | 36 - libmafw-gst-renderer/keypad.c | 79 - libmafw-gst-renderer/keypad.h | 34 - libmafw-gst-renderer/mafw-gst-renderer-marshal.c | 91 - libmafw-gst-renderer/mafw-gst-renderer-marshal.h | 20 - .../mafw-gst-renderer-marshal.list | 2 - .../mafw-gst-renderer-state-paused.c | 380 -- .../mafw-gst-renderer-state-paused.h | 76 - .../mafw-gst-renderer-state-playing.c | 465 --- .../mafw-gst-renderer-state-playing.h | 76 - .../mafw-gst-renderer-state-stopped.c | 319 -- .../mafw-gst-renderer-state-stopped.h | 76 - .../mafw-gst-renderer-state-transitioning.c | 414 -- .../mafw-gst-renderer-state-transitioning.h | 82 - libmafw-gst-renderer/mafw-gst-renderer-state.c | 825 ---- libmafw-gst-renderer/mafw-gst-renderer-state.h | 239 -- libmafw-gst-renderer/mafw-gst-renderer-utils.c | 248 -- libmafw-gst-renderer/mafw-gst-renderer-utils.h | 36 - .../mafw-gst-renderer-worker-volume.c | 710 ---- .../mafw-gst-renderer-worker-volume.h | 64 - libmafw-gst-renderer/mafw-gst-renderer-worker.c | 2464 ------------ libmafw-gst-renderer/mafw-gst-renderer-worker.h | 222 -- libmafw-gst-renderer/mafw-gst-renderer.c | 2320 ----------- libmafw-gst-renderer/mafw-gst-renderer.h | 293 -- libmafw-gst-renderer/mafw-playlist-iterator.c | 521 --- libmafw-gst-renderer/mafw-playlist-iterator.h | 95 - mafw-gst-renderer-uninstalled.pc.in | 11 - mafw-gst-subtitles-renderer/AUTHORS | 10 + mafw-gst-subtitles-renderer/COPYING | 504 +++ mafw-gst-subtitles-renderer/Makefile.am | 41 + mafw-gst-subtitles-renderer/acinclude.m4 | 179 + mafw-gst-subtitles-renderer/applet/Makefile.am | 12 + mafw-gst-subtitles-renderer/applet/cpmpsubtitles.c | 775 ++++ .../applet/cpmpsubtitles.desktop | 10 + mafw-gst-subtitles-renderer/autogen.sh | 11 + mafw-gst-subtitles-renderer/configure.ac | 246 ++ mafw-gst-subtitles-renderer/debian/changelog | 1258 ++++++ mafw-gst-subtitles-renderer/debian/compat | 1 + mafw-gst-subtitles-renderer/debian/control | 70 + mafw-gst-subtitles-renderer/debian/copyright | 6 + .../debian/mafw-gst-renderer.mafw.xsession | 5 + .../debian/mafw-gst-subtitles-applet.install | 3 + .../debian/mafw-gst-subtitles-renderer.dirs | 1 + .../debian/mafw-gst-subtitles-renderer.install.in | 1 + .../debian/mafw-gst-subtitles-renderer.postinst | 12 + .../debian/mafw-gst-subtitles-renderer.postrm | 20 + .../debian/mafw-gst-subtitles-renderer.preinst | 11 + .../debian/mafw-gst-subtitles-renderer.prerm | 7 + mafw-gst-subtitles-renderer/debian/rules | 95 + .../libmafw-gst-renderer/Makefile.am | 56 + .../libmafw-gst-renderer/blanking.c | 119 + .../libmafw-gst-renderer/blanking.h | 37 + .../libmafw-gst-renderer/gstscreenshot.c | 259 ++ .../libmafw-gst-renderer/gstscreenshot.h | 36 + .../libmafw-gst-renderer/keypad.c | 79 + .../libmafw-gst-renderer/keypad.h | 34 + .../mafw-gst-renderer-marshal.c | 91 + .../mafw-gst-renderer-marshal.h | 20 + .../mafw-gst-renderer-marshal.list | 2 + .../mafw-gst-renderer-state-paused.c | 380 ++ .../mafw-gst-renderer-state-paused.h | 76 + .../mafw-gst-renderer-state-playing.c | 465 +++ .../mafw-gst-renderer-state-playing.h | 76 + .../mafw-gst-renderer-state-stopped.c | 319 ++ .../mafw-gst-renderer-state-stopped.h | 76 + .../mafw-gst-renderer-state-transitioning.c | 414 ++ .../mafw-gst-renderer-state-transitioning.h | 82 + .../libmafw-gst-renderer/mafw-gst-renderer-state.c | 825 ++++ .../libmafw-gst-renderer/mafw-gst-renderer-state.h | 239 ++ .../libmafw-gst-renderer/mafw-gst-renderer-utils.c | 248 ++ .../libmafw-gst-renderer/mafw-gst-renderer-utils.h | 36 + .../mafw-gst-renderer-worker-volume.c | 710 ++++ .../mafw-gst-renderer-worker-volume.h | 64 + .../mafw-gst-renderer-worker.c | 2464 ++++++++++++ .../mafw-gst-renderer-worker.h | 222 ++ .../libmafw-gst-renderer/mafw-gst-renderer.c | 2320 +++++++++++ .../libmafw-gst-renderer/mafw-gst-renderer.h | 293 ++ .../libmafw-gst-renderer/mafw-playlist-iterator.c | 521 +++ .../libmafw-gst-renderer/mafw-playlist-iterator.h | 95 + .../mafw-gst-renderer-uninstalled.pc.in | 11 + mafw-gst-subtitles-renderer/po/LINGUAS | 8 + mafw-gst-subtitles-renderer/po/POTFILES.in | 4 + mafw-gst-subtitles-renderer/po/cs.po | 189 + mafw-gst-subtitles-renderer/po/da.po | 190 + mafw-gst-subtitles-renderer/po/de.po | 219 + mafw-gst-subtitles-renderer/po/fi.po | 193 + mafw-gst-subtitles-renderer/po/hu.po | 219 + mafw-gst-subtitles-renderer/po/sk.po | 189 + mafw-gst-subtitles-renderer/tests/Makefile.am | 60 + .../tests/check-mafw-gst-renderer.c | 4174 ++++++++++++++++++++ mafw-gst-subtitles-renderer/tests/check-main.c | 53 + .../tests/mafw-mock-playlist.c | 321 ++ .../tests/mafw-mock-playlist.h | 84 + .../tests/mafw-mock-pulseaudio.c | 295 ++ .../tests/mafw-mock-pulseaudio.h | 33 + .../tests/mafw-test-player.c | 312 ++ mafw-gst-subtitles-renderer/tests/media/test.avi | Bin 0 -> 86540 bytes mafw-gst-subtitles-renderer/tests/media/test.wav | Bin 0 -> 441484 bytes .../tests/media/testframe.png | Bin 0 -> 13945 bytes .../tests/test.suppressions | 864 ++++ po/LINGUAS | 8 - po/POTFILES.in | 4 - po/cs.po | 189 - po/da.po | 190 - po/de.po | 219 - po/fi.po | 193 - po/hu.po | 219 - po/sk.po | 189 - tests/Makefile.am | 60 - tests/check-mafw-gst-renderer.c | 4174 -------------------- tests/check-main.c | 53 - tests/mafw-mock-playlist.c | 321 -- tests/mafw-mock-playlist.h | 84 - tests/mafw-mock-pulseaudio.c | 295 -- tests/mafw-mock-pulseaudio.h | 33 - tests/mafw-test-player.c | 312 -- tests/media/test.avi | Bin 86540 -> 0 bytes tests/media/test.wav | Bin 441484 -> 0 bytes tests/media/testframe.png | Bin 13945 -> 0 bytes tests/test.suppressions | 864 ---- 146 files changed, 21354 insertions(+), 21354 deletions(-) delete mode 100644 AUTHORS delete mode 100644 COPYING delete mode 100644 ChangeLog delete mode 100644 Makefile.am delete mode 100644 acinclude.m4 delete mode 100644 applet/Makefile.am delete mode 100644 applet/cpmpsubtitles.c delete mode 100644 applet/cpmpsubtitles.desktop delete mode 100755 autogen.sh delete mode 100644 configure.ac delete mode 100644 debian/changelog delete mode 100644 debian/compat delete mode 100644 debian/control delete mode 100644 debian/copyright delete mode 100644 debian/mafw-gst-renderer.mafw.xsession delete mode 100644 debian/mafw-gst-subtitles-applet.install delete mode 100644 debian/mafw-gst-subtitles-renderer.dirs delete mode 100644 debian/mafw-gst-subtitles-renderer.install.in delete mode 100644 debian/mafw-gst-subtitles-renderer.postinst delete mode 100644 debian/mafw-gst-subtitles-renderer.postrm delete mode 100644 debian/mafw-gst-subtitles-renderer.preinst delete mode 100644 debian/mafw-gst-subtitles-renderer.prerm delete mode 100755 debian/rules delete mode 100644 libmafw-gst-renderer/Makefile.am delete mode 100644 libmafw-gst-renderer/blanking.c delete mode 100644 libmafw-gst-renderer/blanking.h delete mode 100644 libmafw-gst-renderer/gstscreenshot.c delete mode 100644 libmafw-gst-renderer/gstscreenshot.h delete mode 100644 libmafw-gst-renderer/keypad.c delete mode 100644 libmafw-gst-renderer/keypad.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-marshal.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-marshal.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-marshal.list delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state-paused.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state-paused.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state-playing.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state-playing.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-state.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-utils.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-utils.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-worker.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer-worker.h delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer.c delete mode 100644 libmafw-gst-renderer/mafw-gst-renderer.h delete mode 100644 libmafw-gst-renderer/mafw-playlist-iterator.c delete mode 100644 libmafw-gst-renderer/mafw-playlist-iterator.h delete mode 100644 mafw-gst-renderer-uninstalled.pc.in create mode 100644 mafw-gst-subtitles-renderer/AUTHORS create mode 100644 mafw-gst-subtitles-renderer/COPYING create mode 100644 mafw-gst-subtitles-renderer/ChangeLog create mode 100644 mafw-gst-subtitles-renderer/Makefile.am create mode 100644 mafw-gst-subtitles-renderer/acinclude.m4 create mode 100644 mafw-gst-subtitles-renderer/applet/Makefile.am create mode 100644 mafw-gst-subtitles-renderer/applet/cpmpsubtitles.c create mode 100644 mafw-gst-subtitles-renderer/applet/cpmpsubtitles.desktop create mode 100755 mafw-gst-subtitles-renderer/autogen.sh create mode 100644 mafw-gst-subtitles-renderer/configure.ac create mode 100644 mafw-gst-subtitles-renderer/debian/changelog create mode 100644 mafw-gst-subtitles-renderer/debian/compat create mode 100644 mafw-gst-subtitles-renderer/debian/control create mode 100644 mafw-gst-subtitles-renderer/debian/copyright create mode 100644 mafw-gst-subtitles-renderer/debian/mafw-gst-renderer.mafw.xsession create mode 100644 mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-applet.install create mode 100644 mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.dirs create mode 100644 mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.install.in create mode 100644 mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.postinst create mode 100644 mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.postrm create mode 100644 mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.preinst create mode 100644 mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.prerm create mode 100755 mafw-gst-subtitles-renderer/debian/rules create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/Makefile.am create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/blanking.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/blanking.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/gstscreenshot.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/gstscreenshot.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/keypad.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/keypad.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.list create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-utils.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-utils.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.h create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-playlist-iterator.c create mode 100644 mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-playlist-iterator.h create mode 100644 mafw-gst-subtitles-renderer/mafw-gst-renderer-uninstalled.pc.in create mode 100644 mafw-gst-subtitles-renderer/po/LINGUAS create mode 100644 mafw-gst-subtitles-renderer/po/POTFILES.in create mode 100644 mafw-gst-subtitles-renderer/po/cs.po create mode 100644 mafw-gst-subtitles-renderer/po/da.po create mode 100644 mafw-gst-subtitles-renderer/po/de.po create mode 100644 mafw-gst-subtitles-renderer/po/fi.po create mode 100644 mafw-gst-subtitles-renderer/po/hu.po create mode 100644 mafw-gst-subtitles-renderer/po/sk.po create mode 100644 mafw-gst-subtitles-renderer/tests/Makefile.am create mode 100644 mafw-gst-subtitles-renderer/tests/check-mafw-gst-renderer.c create mode 100644 mafw-gst-subtitles-renderer/tests/check-main.c create mode 100644 mafw-gst-subtitles-renderer/tests/mafw-mock-playlist.c create mode 100644 mafw-gst-subtitles-renderer/tests/mafw-mock-playlist.h create mode 100644 mafw-gst-subtitles-renderer/tests/mafw-mock-pulseaudio.c create mode 100644 mafw-gst-subtitles-renderer/tests/mafw-mock-pulseaudio.h create mode 100644 mafw-gst-subtitles-renderer/tests/mafw-test-player.c create mode 100644 mafw-gst-subtitles-renderer/tests/media/test.avi create mode 100644 mafw-gst-subtitles-renderer/tests/media/test.wav create mode 100644 mafw-gst-subtitles-renderer/tests/media/testframe.png create mode 100644 mafw-gst-subtitles-renderer/tests/test.suppressions delete mode 100644 po/LINGUAS delete mode 100644 po/POTFILES.in delete mode 100644 po/cs.po delete mode 100644 po/da.po delete mode 100644 po/de.po delete mode 100644 po/fi.po delete mode 100644 po/hu.po delete mode 100644 po/sk.po delete mode 100644 tests/Makefile.am delete mode 100644 tests/check-mafw-gst-renderer.c delete mode 100644 tests/check-main.c delete mode 100644 tests/mafw-mock-playlist.c delete mode 100644 tests/mafw-mock-playlist.h delete mode 100644 tests/mafw-mock-pulseaudio.c delete mode 100644 tests/mafw-mock-pulseaudio.h delete mode 100644 tests/mafw-test-player.c delete mode 100644 tests/media/test.avi delete mode 100644 tests/media/test.wav delete mode 100644 tests/media/testframe.png delete mode 100644 tests/test.suppressions diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index a2c188d..0000000 --- a/AUTHORS +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 5ab7695..0000000 --- a/COPYING +++ /dev/null @@ -1,504 +0,0 @@ - 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 deleted file mode 100644 index e69de29..0000000 diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index e23b977..0000000 --- a/Makefile.am +++ /dev/null @@ -1,41 +0,0 @@ -# -# Makefile.am for MAFW gst renderer library. -# -# Author: Visa Smolander -# -# Copyright (C) 2007, 2008, 2009 Nokia. All rights reserved. - - -SUBDIRS = libmafw-gst-renderer applet po - -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 deleted file mode 100644 index 1457d2a..0000000 --- a/acinclude.m4 +++ /dev/null @@ -1,179 +0,0 @@ -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/applet/Makefile.am b/applet/Makefile.am deleted file mode 100644 index 957dc9b..0000000 --- a/applet/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -lib_LTLIBRARIES = libcpmpsubtitles.la - -libcpmpsubtitles_la_SOURCES = cpmpsubtitles.c -libcpmpsubtitles_la_LIBADD = $(MAFW_SUBTITLES_CPA_LIBS) -libcpmpsubtitles_la_CPPFLAGS = $(MAFW_SUBTITLES_CPA_CFLAGS) -libcpmpsubtitles_la_LDFLAGS = module -avoid-version - -libdir = $(CPA_PLUGINDIR) - -desktop_DATA = cpmpsubtitles.desktop -desktopdir = $(CPA_DESKTOPDIR) -EXTRA_DIST = $(desktop_DATA) diff --git a/applet/cpmpsubtitles.c b/applet/cpmpsubtitles.c deleted file mode 100644 index 5bf8b1c..0000000 --- a/applet/cpmpsubtitles.c +++ /dev/null @@ -1,775 +0,0 @@ -/* - * Subtitles control panel applet. - * Copyright (C) 2010 Roman Moravcik - * - * encodings structure imported from totem-subtitle-encoding.c - * Copyright (C) 2001-2006 Bastien Nocera - * - * font family detection imported from hildon-font-selection-dialog.c - * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include -#include - -#include -#include -#include - -#include - -#define GCONF_MAFW_GST_SUBTITLE_RENDERER "/system/mafw/mafw-gst-subtitles-renderer" -#define _HL(str) dgettext("hildon-libs",str) - -typedef enum -{ - FONT_STYLE_REGULAR, - FONT_STYLE_ITALIC, - FONT_STYLE_BOLD, - FONT_STYLE_ITALIC_BOLD, - FONT_STYLE_LAST, -} FontStyleIndex; - -typedef struct { - int index; - const char *name; -} FontStyle; - -static FontStyle font_styles[] = { - {FONT_STYLE_REGULAR, N_("Regular")}, - {FONT_STYLE_ITALIC, N_("Italic")}, - {FONT_STYLE_BOLD, N_("Bold")}, - {FONT_STYLE_ITALIC_BOLD, N_("Italic Bold")} -}; - -static const gint font_sizes[] = -{ - 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, -1 -}; - -typedef enum -{ - SUBTITLE_ENCODING_ISO_8859_6, - SUBTITLE_ENCODING_IBM_864, - SUBTITLE_ENCODING_MAC_ARABIC, - SUBTITLE_ENCODING_WINDOWS_1256, - - SUBTITLE_ENCODING_ARMSCII_8, - - SUBTITLE_ENCODING_ISO_8859_4, - SUBTITLE_ENCODING_ISO_8859_13, - SUBTITLE_ENCODING_WINDOWS_1257, - - SUBTITLE_ENCODING_ISO_8859_14, - - SUBTITLE_ENCODING_ISO_8859_2, - SUBTITLE_ENCODING_IBM_852, - SUBTITLE_ENCODING_MAC_CE, - SUBTITLE_ENCODING_WINDOWS_1250, - - SUBTITLE_ENCODING_GB18030, - SUBTITLE_ENCODING_GB2312, - SUBTITLE_ENCODING_GBK, - SUBTITLE_ENCODING_HZ, - - SUBTITLE_ENCODING_BIG5, - SUBTITLE_ENCODING_BIG5_HKSCS, - SUBTITLE_ENCODING_EUC_TW, - - SUBTITLE_ENCODING_MAC_CROATIAN, - - SUBTITLE_ENCODING_ISO_8859_5, - SUBTITLE_ENCODING_IBM_855, - SUBTITLE_ENCODING_ISO_IR_111, - SUBTITLE_ENCODING_KOI8_R, - SUBTITLE_ENCODING_MAC_CYRILLIC, - SUBTITLE_ENCODING_WINDOWS_1251, - - SUBTITLE_ENCODING_CP_866, - - SUBTITLE_ENCODING_MAC_UKRAINIAN, - SUBTITLE_ENCODING_KOI8_U, - - SUBTITLE_ENCODING_GEOSTD8, - - SUBTITLE_ENCODING_ISO_8859_7, - SUBTITLE_ENCODING_MAC_GREEK, - SUBTITLE_ENCODING_WINDOWS_1253, - - SUBTITLE_ENCODING_MAC_GUJARATI, - - SUBTITLE_ENCODING_MAC_GURMUKHI, - - SUBTITLE_ENCODING_ISO_8859_8_I, - SUBTITLE_ENCODING_IBM_862, - SUBTITLE_ENCODING_MAC_HEBREW, - SUBTITLE_ENCODING_WINDOWS_1255, - - SUBTITLE_ENCODING_ISO_8859_8, - - SUBTITLE_ENCODING_MAC_DEVANAGARI, - - SUBTITLE_ENCODING_MAC_ICELANDIC, - - SUBTITLE_ENCODING_EUC_JP, - SUBTITLE_ENCODING_ISO_2022_JP, - SUBTITLE_ENCODING_SHIFT_JIS, - - SUBTITLE_ENCODING_EUC_KR, - SUBTITLE_ENCODING_ISO_2022_KR, - SUBTITLE_ENCODING_JOHAB, - SUBTITLE_ENCODING_UHC, - - SUBTITLE_ENCODING_ISO_8859_10, - - SUBTITLE_ENCODING_MAC_FARSI, - - SUBTITLE_ENCODING_ISO_8859_16, - SUBTITLE_ENCODING_MAC_ROMANIAN, - - SUBTITLE_ENCODING_ISO_8859_3, - - SUBTITLE_ENCODING_TIS_620, - - SUBTITLE_ENCODING_ISO_8859_9, - SUBTITLE_ENCODING_IBM_857, - SUBTITLE_ENCODING_MAC_TURKISH, - SUBTITLE_ENCODING_WINDOWS_1254, - - SUBTITLE_ENCODING_UTF_7, - SUBTITLE_ENCODING_UTF_8, - SUBTITLE_ENCODING_UTF_16, - SUBTITLE_ENCODING_UCS_2, - SUBTITLE_ENCODING_UCS_4, - - SUBTITLE_ENCODING_ISO_8859_1, - SUBTITLE_ENCODING_ISO_8859_15, - SUBTITLE_ENCODING_IBM_850, - SUBTITLE_ENCODING_MAC_ROMAN, - SUBTITLE_ENCODING_WINDOWS_1252, - - SUBTITLE_ENCODING_TCVN, - SUBTITLE_ENCODING_VISCII, - SUBTITLE_ENCODING_WINDOWS_1258, - - SUBTITLE_ENCODING_CURRENT_LOCALE, - - SUBTITLE_ENCODING_LAST -} SubtitleEncodingIndex; - -typedef struct { - int index; - const char *charset; - const char *name; -} SubtitleEncoding; - -static SubtitleEncoding encodings[] = { - {SUBTITLE_ENCODING_ISO_8859_6, "ISO-8859-6", N_("Arabic")}, - {SUBTITLE_ENCODING_IBM_864, "IBM864", N_("Arabic")}, - {SUBTITLE_ENCODING_MAC_ARABIC, "MAC_ARABIC", N_("Arabic")}, - {SUBTITLE_ENCODING_WINDOWS_1256, "WINDOWS-1256", N_("Arabic")}, - - {SUBTITLE_ENCODING_ARMSCII_8, "ARMSCII-8", N_("Armenian")}, - - {SUBTITLE_ENCODING_ISO_8859_4, "ISO-8859-4", N_("Baltic")}, - {SUBTITLE_ENCODING_ISO_8859_13, "ISO-8859-13", N_("Baltic")}, - {SUBTITLE_ENCODING_WINDOWS_1257, "WINDOWS-1257", N_("Baltic")}, - - {SUBTITLE_ENCODING_ISO_8859_14, "ISO-8859-14", N_("Celtic")}, - - {SUBTITLE_ENCODING_ISO_8859_2, "ISO-8859-2", N_("Central European")}, - {SUBTITLE_ENCODING_IBM_852, "IBM852", N_("Central European")}, - {SUBTITLE_ENCODING_MAC_CE, "MAC_CE", N_("Central European")}, - {SUBTITLE_ENCODING_WINDOWS_1250, "WINDOWS-1250", N_("Central European")}, - - {SUBTITLE_ENCODING_GB18030, "GB18030", N_("Chinese Simplified")}, - {SUBTITLE_ENCODING_GB2312, "GB2312", N_("Chinese Simplified")}, - {SUBTITLE_ENCODING_GBK, "GBK", N_("Chinese Simplified")}, - {SUBTITLE_ENCODING_HZ, "HZ", N_("Chinese Simplified")}, - - {SUBTITLE_ENCODING_BIG5, "BIG5", N_("Chinese Traditional")}, - {SUBTITLE_ENCODING_BIG5_HKSCS, "BIG5-HKSCS", N_("Chinese Traditional")}, - {SUBTITLE_ENCODING_EUC_TW, "EUC-TW", N_("Chinese Traditional")}, - - {SUBTITLE_ENCODING_MAC_CROATIAN, "MAC_CROATIAN", N_("Croatian")}, - - {SUBTITLE_ENCODING_ISO_8859_5, "ISO-8859-5", N_("Cyrillic")}, - {SUBTITLE_ENCODING_IBM_855, "IBM855", N_("Cyrillic")}, - {SUBTITLE_ENCODING_ISO_IR_111, "ISO-IR-111", N_("Cyrillic")}, - {SUBTITLE_ENCODING_KOI8_R, "KOI8-R", N_("Cyrillic")}, - {SUBTITLE_ENCODING_MAC_CYRILLIC, "MAC-CYRILLIC", N_("Cyrillic")}, - {SUBTITLE_ENCODING_WINDOWS_1251, "WINDOWS-1251", N_("Cyrillic")}, - - {SUBTITLE_ENCODING_CP_866, "CP866", N_("Cyrillic/Russian")}, - - {SUBTITLE_ENCODING_MAC_UKRAINIAN, "MAC_UKRAINIAN", N_("Cyrillic/Ukrainian")}, - {SUBTITLE_ENCODING_KOI8_U, "KOI8-U", N_("Cyrillic/Ukrainian")}, - - {SUBTITLE_ENCODING_GEOSTD8, "GEORGIAN-PS", N_("Georgian")}, - - {SUBTITLE_ENCODING_ISO_8859_7, "ISO-8859-7", N_("Greek")}, - {SUBTITLE_ENCODING_MAC_GREEK, "MAC_GREEK", N_("Greek")}, - {SUBTITLE_ENCODING_WINDOWS_1253, "WINDOWS-1253", N_("Greek")}, - - {SUBTITLE_ENCODING_MAC_GUJARATI, "MAC_GUJARATI", N_("Gujarati")}, - - {SUBTITLE_ENCODING_MAC_GURMUKHI, "MAC_GURMUKHI", N_("Gurmukhi")}, - - {SUBTITLE_ENCODING_ISO_8859_8_I, "ISO-8859-8-I", N_("Hebrew")}, - {SUBTITLE_ENCODING_IBM_862, "IBM862", N_("Hebrew")}, - {SUBTITLE_ENCODING_MAC_HEBREW, "MAC_HEBREW", N_("Hebrew")}, - {SUBTITLE_ENCODING_WINDOWS_1255, "WINDOWS-1255", N_("Hebrew")}, - - {SUBTITLE_ENCODING_ISO_8859_8, "ISO-8859-8", N_("Hebrew Visual")}, - - {SUBTITLE_ENCODING_MAC_DEVANAGARI, "MAC_DEVANAGARI", N_("Hindi")}, - - {SUBTITLE_ENCODING_MAC_ICELANDIC, "MAC_ICELANDIC", N_("Icelandic")}, - - {SUBTITLE_ENCODING_EUC_JP, "EUC-JP", N_("Japanese")}, - {SUBTITLE_ENCODING_ISO_2022_JP, "ISO2022JP", N_("Japanese")}, - {SUBTITLE_ENCODING_SHIFT_JIS, "SHIFT-JIS", N_("Japanese")}, - - {SUBTITLE_ENCODING_EUC_KR, "EUC-KR", N_("Korean")}, - {SUBTITLE_ENCODING_ISO_2022_KR, "ISO2022KR", N_("Korean")}, - {SUBTITLE_ENCODING_JOHAB, "JOHAB", N_("Korean")}, - {SUBTITLE_ENCODING_UHC, "UHC", N_("Korean")}, - - {SUBTITLE_ENCODING_ISO_8859_10, "ISO-8859-10", N_("Nordic")}, - - {SUBTITLE_ENCODING_MAC_FARSI, "MAC_FARSI", N_("Persian")}, - - {SUBTITLE_ENCODING_ISO_8859_16, "ISO-8859-16", N_("Romanian")}, - {SUBTITLE_ENCODING_MAC_ROMANIAN, "MAC_ROMANIAN", N_("Romanian")}, - - {SUBTITLE_ENCODING_ISO_8859_3, "ISO-8859-3", N_("South European")}, - - {SUBTITLE_ENCODING_TIS_620, "TIS-620", N_("Thai")}, - - {SUBTITLE_ENCODING_ISO_8859_9, "ISO-8859-9", N_("Turkish")}, - {SUBTITLE_ENCODING_IBM_857, "IBM857", N_("Turkish")}, - {SUBTITLE_ENCODING_MAC_TURKISH, "MAC_TURKISH", N_("Turkish")}, - {SUBTITLE_ENCODING_WINDOWS_1254, "WINDOWS-1254", N_("Turkish")}, - - {SUBTITLE_ENCODING_UTF_7, "UTF-7", N_("Unicode")}, - {SUBTITLE_ENCODING_UTF_8, "UTF-8", N_("Unicode")}, - {SUBTITLE_ENCODING_UTF_16, "UTF-16", N_("Unicode")}, - {SUBTITLE_ENCODING_UCS_2, "UCS-2", N_("Unicode")}, - {SUBTITLE_ENCODING_UCS_4, "UCS-4", N_("Unicode")}, - - {SUBTITLE_ENCODING_ISO_8859_1, "ISO-8859-1", N_("Western")}, - {SUBTITLE_ENCODING_ISO_8859_15, "ISO-8859-15", N_("Western")}, - {SUBTITLE_ENCODING_IBM_850, "IBM850", N_("Western")}, - {SUBTITLE_ENCODING_MAC_ROMAN, "MAC_ROMAN", N_("Western")}, - {SUBTITLE_ENCODING_WINDOWS_1252, "WINDOWS-1252", N_("Western")}, - - {SUBTITLE_ENCODING_TCVN, "TCVN", N_("Vietnamese")}, - {SUBTITLE_ENCODING_VISCII, "VISCII", N_("Vietnamese")}, - {SUBTITLE_ENCODING_WINDOWS_1258, "WINDOWS-1258", N_("Vietnamese")}, - - {SUBTITLE_ENCODING_CURRENT_LOCALE, NULL, N_("Current Locale")} -}; - -static gboolean -gconf_get_bool (GConfClient *client, - const gchar *key) -{ - gboolean value = FALSE; - gchar *tmp = NULL; - - tmp = g_strdup_printf ("%s/%s", GCONF_MAFW_GST_SUBTITLE_RENDERER, key); - - value = gconf_client_get_bool (client, tmp, NULL); - - if (tmp) - g_free (tmp); - - return value; -} - -static void -gconf_set_bool (GConfClient *client, - const gchar *key, - gboolean value) -{ - gchar *tmp = NULL; - - tmp = g_strdup_printf ("%s/%s", GCONF_MAFW_GST_SUBTITLE_RENDERER, key); - - gconf_client_set_bool (client, tmp, value, NULL); - - if (tmp) - g_free (tmp); -} - -static gchar * -gconf_get_string (GConfClient *client, - gchar *key) -{ - gchar *value = FALSE; - gchar *tmp = NULL; - - tmp = g_strdup_printf ("%s/%s", GCONF_MAFW_GST_SUBTITLE_RENDERER, key); - - value = gconf_client_get_string (client, tmp, NULL); - - if (tmp) - g_free (tmp); - - return value; -} - -static void -gconf_set_string (GConfClient *client, - gchar *key, - const gchar *value) -{ - gchar *tmp = NULL; - - tmp = g_strdup_printf ("%s/%s", GCONF_MAFW_GST_SUBTITLE_RENDERER, key); - - if (value) - gconf_client_set_string (client, tmp, value, NULL); - else - gconf_client_unset (client, tmp, NULL); - - if (tmp) - g_free (tmp); -} - -static gboolean -is_internal_font (const gchar * name) -{ - /* FIXME Extremally BAD BAD BAD way of doing things */ - - return strcmp (name, "DeviceSymbols") == 0 - || strcmp(name, "Nokia Smiley") == 0; -} - -static void -filter_out_internal_fonts (PangoFontFamily **families, - int *n_families) -{ - int i; - int n; /* counts valid fonts */ - const gchar * name = NULL; - - for (i = 0, n = 0; i < * n_families; i++) { - name = pango_font_family_get_name (families[i]); - - if(!is_internal_font(name)) { - if (i != n) { /* there are filtered out families */ - families[n] = families[i]; /* shift the current family */ - } - n++; /* count one more valid */ - } - } /* foreach font family */ - - *n_families = n; -} - -static int -cmp_families (const void *a, - const void *b) -{ - const char *a_name = pango_font_family_get_name (* (PangoFontFamily **) a); - const char *b_name = pango_font_family_get_name (* (PangoFontFamily **) b); - - return g_utf8_collate (a_name, b_name); -} - -static int -cmp_encodings (const void *a, - const void *b) -{ - const SubtitleEncoding *a_encoding = (SubtitleEncoding *) a; - const SubtitleEncoding *b_encoding = (SubtitleEncoding *) b; - - return g_utf8_collate (_(a_encoding->name), _(b_encoding->name)); -} - -static void -font_selector_dialog (HildonButton *button, - gpointer user_data) -{ - GtkWidget *dialog, *hbox, *family_selector, *style_selector, *size_selector; - gint index = 0; - const gchar *font = NULL; - PangoFontDescription *font_desc = NULL; - PangoFontFamily **families; - gint n_families = 0; - PangoWeight pango_weight; - PangoStyle pango_style; - - font = hildon_button_get_value (HILDON_BUTTON (button)); - if (font == NULL) - return; - - font_desc = pango_font_description_from_string (font); - - dialog = gtk_dialog_new (); - gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - gtk_window_set_title (GTK_WINDOW (dialog), _("Font")); - gtk_dialog_add_button(GTK_DIALOG (dialog), "OK", GTK_RESPONSE_ACCEPT); - gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 400); - - hbox = gtk_hbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); - - /* font family selector */ - family_selector = hildon_touch_selector_new_text (); - gtk_box_pack_start (GTK_BOX (hbox), family_selector, TRUE, TRUE, 0); - - pango_context_list_families (gtk_widget_get_pango_context (GTK_WIDGET (dialog)), - &families, &n_families); - - filter_out_internal_fonts (families, &n_families); - - qsort (families, n_families, sizeof(PangoFontFamily *), cmp_families); - - for (index = 0; index < n_families; index++) { - const gchar *family = pango_font_family_get_name (families[index]); - hildon_touch_selector_insert_text (HILDON_TOUCH_SELECTOR (family_selector), - index, family); - - if (strcmp (family, pango_font_description_get_family (font_desc)) == 0) { - hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (family_selector), 0, - index); - hildon_touch_selector_center_on_selected (HILDON_TOUCH_SELECTOR (family_selector)); - } - } - g_free (families); - - /* font style selector */ - style_selector = hildon_touch_selector_new_text (); - gtk_widget_set_size_request (style_selector, 200, -1); - gtk_box_pack_start (GTK_BOX (hbox), style_selector, FALSE, TRUE, 0); - - index = 0; - while (index < FONT_STYLE_LAST) { - const gchar *style = g_strdup_printf ("%s", _(font_styles[index].name)); - hildon_touch_selector_insert_text (HILDON_TOUCH_SELECTOR (style_selector), - font_styles[index].index, style); - index++; - } - pango_weight = pango_font_description_get_weight (font_desc); - pango_style = pango_font_description_get_style (font_desc); - - if (pango_weight == PANGO_WEIGHT_NORMAL) { - if (pango_style == PANGO_STYLE_NORMAL) { - hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (style_selector), 0, - FONT_STYLE_REGULAR); - } else { - hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (style_selector), 0, - FONT_STYLE_ITALIC); - } - } else { - if (pango_style == PANGO_STYLE_NORMAL) { - hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (style_selector), 0, - FONT_STYLE_BOLD); - } else { - hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (style_selector), 0, - FONT_STYLE_ITALIC_BOLD); - } - } - hildon_touch_selector_center_on_selected (HILDON_TOUCH_SELECTOR (style_selector)); - - /* font size selector */ - size_selector = hildon_touch_selector_new_text (); - gtk_widget_set_size_request (size_selector, 100, -1); - gtk_box_pack_start (GTK_BOX (hbox), size_selector, FALSE, TRUE, 0); - - index = 0; - while (font_sizes[index] != -1) { - const gchar *size = g_strdup_printf ("%d", font_sizes[index]); - hildon_touch_selector_insert_text (HILDON_TOUCH_SELECTOR (size_selector), - index, size); - - if (font_sizes[index] == (pango_font_description_get_size (font_desc) / PANGO_SCALE)) { - hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (size_selector), 0, - index); - hildon_touch_selector_center_on_selected (HILDON_TOUCH_SELECTOR (size_selector)); - } - - index++; - } - - /* Run the dialog */ - gtk_widget_show_all (GTK_WIDGET (dialog)); - if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { - if (font_desc) - pango_font_description_free (font_desc); - - font_desc = pango_font_description_new (); - - /* set font family */ - pango_font_description_set_family (font_desc, - hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (family_selector))); - - /* set font style */ - switch (hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (style_selector), 0)) { - case FONT_STYLE_REGULAR: - pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL); - pango_font_description_set_weight (font_desc, PANGO_WEIGHT_NORMAL); - break; - - case FONT_STYLE_ITALIC: - pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC); - pango_font_description_set_weight (font_desc, PANGO_WEIGHT_NORMAL); - break; - - case FONT_STYLE_BOLD: - pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL); - pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD); - break; - - case FONT_STYLE_ITALIC_BOLD: - pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC); - pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD); - break; - } - - /* set font size */ - pango_font_description_set_size (font_desc, - font_sizes[hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (size_selector), 0)] * PANGO_SCALE); - - hildon_button_set_value (HILDON_BUTTON (button), pango_font_description_to_string (font_desc)); - } - - if (font_desc) - pango_font_description_free (font_desc); - - gtk_widget_destroy(GTK_WIDGET(dialog)); -} - - -static GtkWidget * -create_encoding_selector (void) -{ - GtkWidget *selector; - gint index = 0; - - selector = hildon_touch_selector_new_text (); - - qsort (encodings, SUBTITLE_ENCODING_LAST - 1, sizeof (SubtitleEncoding), cmp_encodings); - - while (index < SUBTITLE_ENCODING_LAST) { - const gchar *encoding = NULL; - - if (encodings[index].charset) { - encoding = g_strdup_printf ("%s (%s)", _(encodings[index].name), - encodings[index].charset); - } else { - encoding = g_strdup_printf ("%s", _(encodings[index].name)); - } - - hildon_touch_selector_insert_text (HILDON_TOUCH_SELECTOR (selector), - index, - encoding); - index++; - } - - return selector; -} - -static GtkWidget * -create_autoload_subtitles_button (GConfClient *gconf_client) -{ - GtkWidget *button; - gboolean autoload_subtitles = FALSE; - - button = hildon_check_button_new (HILDON_SIZE_FINGER_HEIGHT); - gtk_button_set_label (GTK_BUTTON (button), _("Automatically load subtitle files")); - - autoload_subtitles = gconf_get_bool (gconf_client, "autoload_subtitles"); - if (autoload_subtitles) - hildon_check_button_set_active (HILDON_CHECK_BUTTON (button), TRUE); - else - hildon_check_button_set_active (HILDON_CHECK_BUTTON (button), FALSE); - - return button; -} - -static void -save_autoload_subtitles (GConfClient *gconf_client, - GtkWidget *widget) -{ - if (hildon_check_button_get_active (HILDON_CHECK_BUTTON (widget))) - gconf_set_bool (gconf_client, "autoload_subtitles", TRUE); - else - gconf_set_bool (gconf_client, "autoload_subtitles", FALSE); -} - -static GtkWidget * -create_subtitles_font_button (GConfClient *gconf_client) -{ - GtkWidget *button; - const gchar *font = NULL; - - button = hildon_button_new (HILDON_SIZE_FINGER_HEIGHT, - HILDON_BUTTON_ARRANGEMENT_VERTICAL); - hildon_button_set_title (HILDON_BUTTON (button), _("Font")); - hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 0.0); - hildon_button_set_title_alignment (HILDON_BUTTON(button), 0.0, 0.5); - hildon_button_set_value_alignment (HILDON_BUTTON (button), 0.0, 0.5); - hildon_button_set_style (HILDON_BUTTON (button), HILDON_BUTTON_STYLE_PICKER); - - font = gconf_get_string (gconf_client, "subtitle_font"); - if (font) { - hildon_button_set_value (HILDON_BUTTON (button), font); - } else { - hildon_button_set_value (HILDON_BUTTON (button), "Sans Bold 18"); - } - - g_signal_connect (button, "clicked", G_CALLBACK (font_selector_dialog), - NULL); - - return button; -} - -static void -save_subtitles_font (GConfClient *gconf_client, - GtkWidget *widget) -{ - const gchar *font = NULL; - - font = hildon_button_get_value (HILDON_BUTTON (widget)); - gconf_set_string (gconf_client, "subtitle_font", font); -} - -static GtkWidget * -create_subtitles_encoding_button (GConfClient *gconf_client) -{ - GtkWidget *button, *selector; - const gchar *encoding = NULL; - - button = hildon_picker_button_new (HILDON_SIZE_FINGER_HEIGHT, - HILDON_BUTTON_ARRANGEMENT_VERTICAL); - hildon_button_set_title (HILDON_BUTTON (button), _("Encoding")); - hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 0.0); - hildon_button_set_title_alignment (HILDON_BUTTON(button), 0.0, 0.5); - hildon_button_set_value_alignment (HILDON_BUTTON (button), 0.0, 0.5); - - selector = create_encoding_selector (); - hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (button), - HILDON_TOUCH_SELECTOR (selector)); - - encoding = gconf_get_string (gconf_client, "subtitle_encoding"); - if (encoding) { - gint index = 0; - - while (index < SUBTITLE_ENCODING_LAST) { - if (encodings[index].charset) { - if (strcmp (encodings[index].charset, encoding) == 0) { - hildon_picker_button_set_active (HILDON_PICKER_BUTTON (button), - index); - break; - } - } - index++; - } - } else { - hildon_picker_button_set_active (HILDON_PICKER_BUTTON (button), - SUBTITLE_ENCODING_CURRENT_LOCALE); - } - - return button; -} - -static void -save_subtitles_encoding (GConfClient *gconf_client, - GtkWidget *widget) -{ - gint encoding = 0, index = 0; - - encoding = hildon_picker_button_get_active (HILDON_PICKER_BUTTON (widget)); - - while (index < SUBTITLE_ENCODING_LAST) { - if (encoding == index) { - gconf_set_string (gconf_client, "subtitle_encoding", - encodings[index].charset); - break; - } - index++; - } -} - -osso_return_t -execute (osso_context_t *osso, - gpointer data, - gboolean user_activated) -{ - GConfClient *gconf_client = NULL; - GtkWidget *dialog, *vbox, *autoload_subtitles_button; - GtkWidget *subtitles_font_button, *subtitles_encoding_button; - - gconf_client = gconf_client_get_default (); - if (gconf_client == NULL) { - return OSSO_ERROR; - } - - dialog = gtk_dialog_new (); - gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data)); - gtk_window_set_title (GTK_WINDOW (dialog), _("Subtitles")); - gtk_dialog_add_button(GTK_DIALOG (dialog), _HL("wdgt_bd_save"), GTK_RESPONSE_ACCEPT); - - vbox = gtk_vbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox); - - /* autoload subtitles button */ - autoload_subtitles_button = create_autoload_subtitles_button (gconf_client); - gtk_box_pack_start (GTK_BOX (vbox), autoload_subtitles_button, TRUE, TRUE, 0); - - /* font selector */ - subtitles_font_button = create_subtitles_font_button (gconf_client); - gtk_box_pack_start (GTK_BOX (vbox), subtitles_font_button, TRUE, TRUE, 0); - - /* font encoding */ - subtitles_encoding_button = create_subtitles_encoding_button (gconf_client); - gtk_box_pack_start (GTK_BOX (vbox), subtitles_encoding_button, TRUE, TRUE, 0); - - /* Run the dialog */ - gtk_widget_show_all (GTK_WIDGET (dialog)); - if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { - /* save autoload subtitles option */ - save_autoload_subtitles (gconf_client, autoload_subtitles_button); - - /* save subtitle font option */ - save_subtitles_font (gconf_client, subtitles_font_button); - - /* save subtitle encoding option */ - save_subtitles_encoding (gconf_client, subtitles_encoding_button); - } - - gtk_widget_destroy(GTK_WIDGET(dialog)); - return OSSO_OK; -} - -osso_return_t -save_state (osso_context_t *osso, - gpointer data) -{ - return OSSO_OK; -} - diff --git a/applet/cpmpsubtitles.desktop b/applet/cpmpsubtitles.desktop deleted file mode 100644 index 6bccbf0..0000000 --- a/applet/cpmpsubtitles.desktop +++ /dev/null @@ -1,10 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Version=1.0 -Name=Subtitles -Comment=Control panel to configure subtitles for mafw-gst-subtitles-renderer -Type=HildonControlPanelPlugin -Icon=general_video_file -X-control-panel-plugin=libcpmpsubtitles.so -X-Text-Domain=mafw-gst-subtitles-renderer -Categories=extras diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 53c895a..0000000 --- a/autogen.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# Run this to generate all the initial makefiles, etc. - -export AUTOMAKE="automake-1.9" -export ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/` - -glib-gettextize --copy --force -intltoolize --automake --copy --force -autoreconf -v -f -i || exit 1 -test -n "$NOCONFIGURE" || ./configure \ - --enable-debug --enable-maintainer-mode "$@" diff --git a/configure.ac b/configure.ac deleted file mode 100644 index a73df69..0000000 --- a/configure.ac +++ /dev/null @@ -1,246 +0,0 @@ -# -# 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-subtitles-renderer], [0.2.2010.07-2]) - -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 - -IT_PROG_INTLTOOL([0.35]) -AC_SUBST([GETTEXT_PACKAGE], [mafw-gst-subtitles-renderer]) -AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [The domain to use with gettext.]) -AM_GLIB_GNU_GETTEXT() - -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 - mce - dbus-1 -) - -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 Control Panel -PKG_CHECK_MODULES([MAFW_SUBTITLES_CPA], [libosso >= 2.0 - hildon-1 >= 2.1 - hildon-control-panel - gtk+-2.0 - gconf-2.0]) - -CPA_PLUGINDIR=`pkg-config hildon-control-panel --variable=pluginlibdir` -CPA_DESKTOPDIR=`pkg-config hildon-control-panel --variable=plugindesktopentrydir` - -AC_SUBST(MAFW_SUBTITLES_CPA_CFLAGS) -AC_SUBST(MAFW_SUBTITLES_CPA_LIBS) -AC_SUBST(CPA_DESKTOPDIR) -AC_SUBST(CPA_PLUGINDIR) - -dnl Output files. - -AC_CONFIG_FILES([ - Makefile - mafw-gst-renderer-uninstalled.pc - libmafw-gst-renderer/Makefile - applet/Makefile - tests/Makefile - debian/mafw-gst-subtitles-renderer.install - po/Makefile.in -]) - -AC_OUTPUT diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 59d0c83..0000000 --- a/debian/changelog +++ /dev/null @@ -1,1258 +0,0 @@ -mafw-gst-subtitles-renderer (0.3.2010.24-1+0m5-2) unstable; urgency=low - - * Fixed Bug: 6438: Plugin makes background music stop on lock screen. - The only solution to fix this problem was rename back MAFW GStreamer - renderer from mafw-gst-subtitles-renderer to mafw-gst-renderer. - - -- Roman Moravcik Tue, 26 Oct 2010 14:50:06 +0200 - -mafw-gst-subtitles-renderer (0.3.2010.24-1+0m5-1) unstable; urgency=low - - * Package updated to version mafw-gst-renderer-0.3.2010.24-1+0m5. - - -- Roman Moravcik Mon, 25 Oct 2010 12:58:00 +0200 - -mafw-gst-renderer (0.3.2010.24-1+0m5) unstable; urgency=low - - * This entry has been added by BIFH queue processor - version has been changed to 0.3.2010.24-1+0m5 - - -- Pekka Rönkkö Mon, 21 Jun 2010 14:57:08 +0300 - -mafw-gst-renderer (0.3.2010.24-1) unstable; urgency=low - - * Fixes: NB#161636 - Playbin sends EOS for endless stream - - -- Pekka Rönkkö Thu, 17 Jun 2010 12:05:30 +0200 - -mafw-gst-subtitles-renderer (0.2.2010.07-2+0m5-3) unstable; urgency=low - - * This package should depend on gstreamer0.10-plugins-base-subtitles - version (>= 0.10.25-0maemo14+0m5-1). - - -- Roman Moravcik Tue, 25 May 2010 17:12:33 +0200 - -mafw-gst-subtitles-renderer (0.2.2010.07-2+0m5-2) unstable; urgency=low - - * Updated Finnish translation by mikuu. - * Installation of this package disable mafw-gst-renderer instead of replacing it. - (original mafw-gst-renderer will be re-enabled after uninstallation of package). - - -- Roman Moravcik Tue, 18 May 2010 14:35:42 +0200 - -mafw-gst-subtitles-renderer (0.2.2010.07-2+0m5-1) unstable; urgency=low - - * Added Hungarian translation by Gyorgy Lakatos. - * Package updated to version mafw-gst-renderer-0.2.2010.07-2+0m5-1. - - -- Roman Moravcik Wed, 5 May 2010 10:12:28 +0200 - -mafw-gst-renderer (0.2.2010.07-2) unstable; urgency=low - - * Fixes: NB#156757 - Volume settings can't be changed via Volume key, Media player is playing, Tklock is On. - - -- Mika Tapojärvi Wed, 17 Feb 2010 10:09:38 +0200 - -mafw-gst-renderer (0.2.2010.06-1) unstable; urgency=low - - * Fixes: NB#150064 - NP-Video:Frames not changing while performing seek operation when the video is in paused state - - -- Mika Tapojärvi Wed, 10 Feb 2010 21:43:22 +0200 - -mafw-gst-renderer (0.2.2010.01-1) unstable; urgency=low - - * Version number increased in trunk. - * A forbidden word removed from debian/changelog. - - -- Mika Tapojärvi Thu, 07 Jan 2010 19:36:53 +0200 - -mafw-gst-renderer (0.2.2009.52-2) unstable; urgency=low - - * Version number increased. - - -- Tuomas Kamarainen Wed, 23 Dec 2009 12:40:45 +0200 - -mafw-gst-renderer (0.2.2009.52-1) unstable; urgency=low - - * Fixes: NB#149945 - mafw-gst-renderer leaks some GStreamer messages - * Thanks to Mueller Tim for the patch. - - -- Mika Tapojärvi Sun, 20 Dec 2009 22:43:29 +0200 - -mafw-gst-renderer (0.2.2009.50-2) unstable; urgency=low - - * Fixes: NB#148080 - Device UI becomes very slow after long time usage of Media Player - - -- Mika Tapojärvi Thu, 10 Dec 2009 19:07:45 +0200 - -mafw-gst-renderer (0.2.2009.50-1) unstable; urgency=low - - * Version and changelog updated for pre-release 0.2009.50-1 - - -- Mika Tapojärvi Tue, 08 Dec 2009 09:21:47 +0200 - -mafw-gst-renderer (0.2.2009.49-1) unstable; urgency=low - - * Rebuild for 2009.49-1. - - -- Mika Tapojärvi Tue, 01 Dec 2009 19:27:31 +0200 - -mafw-gst-renderer (0.2.2009.48-2) unstable; urgency=low - - * Pre-release PR 1.2 2009.48-2 tag. - - -- Tuomas Kämäräinen Fri, 27 Nov 2009 11:56:14 +0200 - -mafw-gst-renderer (0.2.2009.48-1) unstable; urgency=low - - * Pre-release PR 1.2 2009.48-1 tag. - - -- Mika Tapojärvi Tue, 24 Nov 2009 21:57:59 +0200 - -mafw-gst-renderer (0.1.2009.47-2) unstable; urgency=low - - * Version increased. - - -- Tuomas Kamarainen Fri, 20 Nov 2009 13:26:19 +0300 - -mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-5) unstable; urgency=low - - * Added Danish translation by Joe Hansen. - * Added Finnish translation by Marko Vertainen. - - -- Roman Moravcik Tue, 16 Feb 2010 15:02:02 +0100 - -mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-4) unstable; urgency=low - - * Added German translation by Philipp Zabel. - * Backup/restore original mafw-gst-renderer on installation/uninstallation - of mafw-gst-subtitles package. - - -- Roman Moravcik Wed, 27 Jan 2010 15:29:27 +0100 - -mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-3) unstable; urgency=low - - * Updating of mafw-gst-subtitles-applet package was not updating - mafw-gst-subtitles-renderer package too. - - -- Roman Moravcik Tue, 19 Jan 2010 15:53:19 +0100 - -mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-2) unstable; urgency=low - - * Added Czech translation. - * List of available encoding was not sorted. - * Removed mafw-gst-subtitles-applet postinst/postrm script, because - mafw is restared during mafw-gst-subtitles-renderer instalation. - * Added more font sizes. - * Nokia fonts removed from the list of filtered fonts. - - -- Roman Moravcik Tue, 19 Jan 2010 15:07:48 +0100 - -mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-1) unstable; urgency=low - - * Added missing build dependency to hildon-control-panel-dev - - -- Roman Moravcik Mon, 18 Jan 2010 22:39:17 +0100 - -mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-0) unstable; urgency=low - - * Added subtitles support. - * Added Subtitles control panel applet. - - -- Roman Moravcik Mon, 18 Jan 2010 22:09:11 +0100 - -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 deleted file mode 100644 index b8626c4..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/debian/control b/debian/control deleted file mode 100644 index 9fb6268..0000000 --- a/debian/control +++ /dev/null @@ -1,70 +0,0 @@ -Source: mafw-gst-subtitles-renderer -Section: misc -Priority: optional -Maintainer: Roman Moravcik -XSBC-Original-Maintainer: Mika Tapojarvi -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, mce-dev, - libdbus-1-dev, hildon-control-panel-dev -Standards-Version: 3.7.2 -Homepage: http://mafwsubrenderer.garage.maemo.org/ -Vcs-Browser: https://garage.maemo.org/plugins/ggit/browse.php/?p=mafwsubrenderer -Vcs-Git: https://vcs.maemo.org/git/mafwsubrenderer - -Package: mafw-gst-subtitles-renderer -Section: libs -Architecture: any -Depends: gconf2, ${shlibs:Depends}, ${misc:Depends}, gstreamer0.10-plugins-base-subtitles (>= 0.10.25-0maemo14+0m5-1) -Replaces: mafw-gst-renderer -Description: MAFW gst renderer plugin with subtitles support - Renderer plugin for MAFW-gst - -Package: mafw-gst-subtitles-renderer-dbg -Section: devel -Architecture: any -Priority: extra -Depends: mafw-gst-subtitles-renderer (= ${binary:Version}) -Description: debug symbols for mafw-gst-subtitles-renderer - MAFW-gst renderer debug symbols - -Package: mafw-gst-subtitles-applet -Section: user/multimedia -Architecture: any -Depends: gconf2, ${shlibs:Depends}, ${misc:Depends}, mafw-gst-subtitles-renderer (= ${binary:Version}) -Description: External subtitles support for Media Player - Subtitles font and encoding can be change via 'Settings: Subtitles' - control panel applet. -XB-Maemo-Display-Name: Subtitles Support -XSBC-Bugtracker: https://garage.maemo.org/tracker/?func=add&group_id=1248&atid=4688 -XB-Maemo-Icon-26: - iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0 - d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA9JJREFUeNrsWblOI0EQbVvm - FodkIWRDQGAMERIgyxI5IcT+ATbYdEnZYGM23WD5CiDjA5AAOUKYQxY35jK2 - AZv72Hmt7Va7d46eY4UtTUkjV09NddfrquqjHPj4+CD1TEFS5+QD8AH4AHwA - PgBXFJJfBAKB8Orq6o9YLPa1lgzNZrO/ksnkd23jvTYFoFH4+fk59P7+XlMz - DZtgm/ZYAiCzs7Okvb2d8vPz8/z90tISWVxc5O2ZmRkyODhI+Z2dHTI3N6ck - m5qaIpOTk7w9PT3NeehAlxH0oH93d6cWQqDNzU3S0NBAedETp6enJJ1O8/bt - 7S2Xg1eVjY2NVfUryuQxYTzkLy8v6gBEent70+2YtZncqUwmHC5FmdVh0xKA - OLjcGWRMrmekkQz9GOWYLLMCEJA/0Fah+PLy8reBgYEvn5m0CJn7+3skL23v - 7e39TqVSPzV7dy09cHBwQFTuCb29vaSlpYXyDw8PNEfc0uvrK+np6eGGb2xs - 0Bw6Pz9XD6FKpWKY9fIsNTY2cl5Fx4iurq7IyckJXf3YCggqFoskn8+TQqGg - DsAsRuUEZ9+JvJ3ZhuGY3aenJ/qutbWVvpfHMOrbEIBKCIlAVXWY4TD67Oys - yli9FYoZbwuAmYLsATaYigcwy8fHxzQcZMNFcKIMvFnfrjwgL5VGOjD86OiI - XF5eKvUpAnDkAa8A3NzcUMPxq0qYbU9yQCWEZACMv7i4oLNtx/BP84B4XEBi - Hh4eksfHR8fLqZ4H/msOYLBSqURDZWtry/VGJi4MYggZ2eM4hGA4wgQ7Jr7F - rHtxh/AkhMwQw1AcNRAuw8PDpKuriwPyAoBnSSwDwDEBWz02H73BZNd76QFH - OcAUcBbZ39+nce52I3OTA7YB4GSJXbNcLpvmAZutmgqhlZUV0tnZaWswrzzg - SRL39/eT7u5uy8Gampr4YODj8bj7Ok8oVAWgr6+Pn1qVAWSzWX5uSSQSVZf6 - XC7H20NDQ/zsjiTf3t5WkkWjUXoZYrS+vs556ECXkXYTo/q41CgDwC2IVSVG - Rkb4e+SEWEHAzYndyJDsa2trSjJUJSBnJMoikQiJxWK8nclk6MrnSVVC71Lv - pCohVx7MxrRdWtRbaYzKKnISq8rkRJXBiTJHZZXx8fGqHdYouTs6Orgc/MTE - hJKsra2tql9RhtAVZaOjozR8sA8tLCyoAYDx4XD4HwDNzc300fNQMBjkOnZk - tBhrIgN42x7QVoycppiupeKutgrllAtbWCi0J05qi1DQSqsUtq7/frxLao9K - lh6oN/L/I/MB+ADqnP4IMABJCQV+48xZ9wAAAABJRU5ErkJggg== - ==== diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index 79772e7..0000000 --- a/debian/copyright +++ /dev/null @@ -1,6 +0,0 @@ -Copyright (C) 2007, 2008, 2009 Nokia Corporation. All rights reserved. - -Contact: Visa Smolander - - - diff --git a/debian/mafw-gst-renderer.mafw.xsession b/debian/mafw-gst-renderer.mafw.xsession deleted file mode 100644 index 88fb03c..0000000 --- a/debian/mafw-gst-renderer.mafw.xsession +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -if test -x /usr/sbin/dsmetool; then - sudo /usr/bin/mafw.sh start mafw-gst-renderer -7 -fi diff --git a/debian/mafw-gst-subtitles-applet.install b/debian/mafw-gst-subtitles-applet.install deleted file mode 100644 index 763d763..0000000 --- a/debian/mafw-gst-subtitles-applet.install +++ /dev/null @@ -1,3 +0,0 @@ -/usr/lib/hildon-control-panel/libcpmpsubtitles.so -/usr/share/applications/hildon-control-panel/cpmpsubtitles.desktop -/usr/share/locale/* \ No newline at end of file diff --git a/debian/mafw-gst-subtitles-renderer.dirs b/debian/mafw-gst-subtitles-renderer.dirs deleted file mode 100644 index db72179..0000000 --- a/debian/mafw-gst-subtitles-renderer.dirs +++ /dev/null @@ -1 +0,0 @@ -usr/lib/mafw-plugin diff --git a/debian/mafw-gst-subtitles-renderer.install.in b/debian/mafw-gst-subtitles-renderer.install.in deleted file mode 100644 index f350be5..0000000 --- a/debian/mafw-gst-subtitles-renderer.install.in +++ /dev/null @@ -1 +0,0 @@ -@plugindir@/*.so diff --git a/debian/mafw-gst-subtitles-renderer.postinst b/debian/mafw-gst-subtitles-renderer.postinst deleted file mode 100644 index 896525f..0000000 --- a/debian/mafw-gst-subtitles-renderer.postinst +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -#DEBHELPER# - -# purge old installed startup script -if [ -f /etc/X11/Xsession.post/31mafw-gst-subtitles-renderer ]; then - rm -f /etc/X11/Xsession.post/31mafw-gst-subtitles-renderer -fi - -/usr/bin/mafw.sh start mafw-gst-renderer -7 - -exit 0 diff --git a/debian/mafw-gst-subtitles-renderer.postrm b/debian/mafw-gst-subtitles-renderer.postrm deleted file mode 100644 index 003e7ec..0000000 --- a/debian/mafw-gst-subtitles-renderer.postrm +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -#DEBHELPER# - -case "$1" in - remove) - # restore original mafw-gst-renderer on package removal - if [ -f /usr/lib/mafw-plugin/mafw-gst-renderer.so.removed ]; then - mv -f /usr/lib/mafw-plugin/mafw-gst-renderer.so.removed /usr/lib/mafw-plugin/mafw-gst-renderer.so - fi - ;; - abort-upgrade|abort-remove|abort-deconfigure) - ;; - *) - exit 0 -esac - -/usr/bin/mafw.sh start mafw-gst-renderer -7 - -exit 0 diff --git a/debian/mafw-gst-subtitles-renderer.preinst b/debian/mafw-gst-subtitles-renderer.preinst deleted file mode 100644 index ede9601..0000000 --- a/debian/mafw-gst-subtitles-renderer.preinst +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -#DEBHELPER# - -# create backup of original mafw-gst-renderer.so if it not exists -if [ ! -f /usr/lib/mafw-plugin/mafw-gst-renderer.so.removed ]; then - /usr/bin/mafw.sh stop mafw-gst-renderer - mv -f /usr/lib/mafw-plugin/mafw-gst-renderer.so /usr/lib/mafw-plugin/mafw-gst-renderer.so.removed -fi - -exit 0 diff --git a/debian/mafw-gst-subtitles-renderer.prerm b/debian/mafw-gst-subtitles-renderer.prerm deleted file mode 100644 index f8abba6..0000000 --- a/debian/mafw-gst-subtitles-renderer.prerm +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -#DEBHELPER# - -/usr/bin/mafw.sh stop mafw-gst-renderer - -exit 0 diff --git a/debian/rules b/debian/rules deleted file mode 100755 index 15c86ec..0000000 --- a/debian/rules +++ /dev/null @@ -1,95 +0,0 @@ -#!/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-subtitles-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 deleted file mode 100644 index adc18a3..0000000 --- a/libmafw-gst-renderer/Makefile.am +++ /dev/null @@ -1,56 +0,0 @@ -# -# 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) \ - keypad.c keypad.h \ - 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 deleted file mode 100644 index 4a12836..0000000 --- a/libmafw-gst-renderer/blanking.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 deleted file mode 100644 index 6a69e61..0000000 --- a/libmafw-gst-renderer/blanking.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 deleted file mode 100644 index f7f177e..0000000 --- a/libmafw-gst-renderer/gstscreenshot.c +++ /dev/null @@ -1,259 +0,0 @@ -/* 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 deleted file mode 100644 index d3cf23c..0000000 --- a/libmafw-gst-renderer/gstscreenshot.h +++ /dev/null @@ -1,36 +0,0 @@ -/* 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/keypad.c b/libmafw-gst-renderer/keypad.c deleted file mode 100644 index 0af13a4..0000000 --- a/libmafw-gst-renderer/keypad.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 -#include "keypad.h" - - -#define KEYPAD_TIMER_INTERVAL 50 - -static guint toutid; - -void keypadlocking_allow(void) -{ - if (toutid) - { - g_source_remove(toutid); - toutid = 0; - } -} - -static gboolean no_keylock_timeout(gpointer udata) -{ - static DBusMessage *msg = NULL; - static DBusConnection *sysbus = NULL; - - if (!sysbus) - { - DBusError err; - - dbus_error_init(&err); - sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &err); - g_assert(sysbus); - g_assert(!dbus_error_is_set(&err)); - } - if (!msg) - { - msg = dbus_message_new_method_call(MCE_SERVICE, - MCE_REQUEST_PATH, MCE_REQUEST_IF, - MCE_PREVENT_KEYPAD_OFF_REQ); - g_assert(msg); - } - g_assert(dbus_connection_send(sysbus, msg,NULL)); - dbus_connection_flush(sysbus); - return TRUE; -} - -void keypadlocking_prohibit(void) -{ - if (!toutid) - { - toutid = g_timeout_add_seconds(KEYPAD_TIMER_INTERVAL, - no_keylock_timeout, - NULL); - no_keylock_timeout(NULL); - } -} diff --git a/libmafw-gst-renderer/keypad.h b/libmafw-gst-renderer/keypad.h deleted file mode 100644 index f434567..0000000 --- a/libmafw-gst-renderer/keypad.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 KEYPAD_H -#define KEYPAD_H - -G_BEGIN_DECLS - -void keypadlocking_prohibit(void); -void keypadlocking_allow(void); - -G_END_DECLS - -#endif diff --git a/libmafw-gst-renderer/mafw-gst-renderer-marshal.c b/libmafw-gst-renderer/mafw-gst-renderer-marshal.c deleted file mode 100644 index 3682dcb..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-marshal.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "mafw-gst-renderer-marshal.h" - -#include - - -#ifdef G_ENABLE_DEBUG -#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) -#define g_marshal_value_peek_char(v) g_value_get_char (v) -#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) -#define g_marshal_value_peek_int(v) g_value_get_int (v) -#define g_marshal_value_peek_uint(v) g_value_get_uint (v) -#define g_marshal_value_peek_long(v) g_value_get_long (v) -#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) -#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) -#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) -#define g_marshal_value_peek_enum(v) g_value_get_enum (v) -#define g_marshal_value_peek_flags(v) g_value_get_flags (v) -#define g_marshal_value_peek_float(v) g_value_get_float (v) -#define g_marshal_value_peek_double(v) g_value_get_double (v) -#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) -#define g_marshal_value_peek_param(v) g_value_get_param (v) -#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) -#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) -#define g_marshal_value_peek_object(v) g_value_get_object (v) -#else /* !G_ENABLE_DEBUG */ -/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. - * Do not access GValues directly in your code. Instead, use the - * g_value_get_*() functions - */ -#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int -#define g_marshal_value_peek_char(v) (v)->data[0].v_int -#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint -#define g_marshal_value_peek_int(v) (v)->data[0].v_int -#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint -#define g_marshal_value_peek_long(v) (v)->data[0].v_long -#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong -#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 -#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 -#define g_marshal_value_peek_enum(v) (v)->data[0].v_long -#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong -#define g_marshal_value_peek_float(v) (v)->data[0].v_float -#define g_marshal_value_peek_double(v) (v)->data[0].v_double -#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer -#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer -#endif /* !G_ENABLE_DEBUG */ - - -/* VOID:BOOLEAN,UINT,INT,STRING (mafw-gst-renderer-marshal.list:2) */ -void -mafw_gst_renderer_marshal_VOID__BOOLEAN_UINT_INT_STRING (GClosure *closure, - GValue *return_value G_GNUC_UNUSED, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint G_GNUC_UNUSED, - gpointer marshal_data) -{ - typedef void (*GMarshalFunc_VOID__BOOLEAN_UINT_INT_STRING) (gpointer data1, - gboolean arg_1, - guint arg_2, - gint arg_3, - gpointer arg_4, - gpointer data2); - register GMarshalFunc_VOID__BOOLEAN_UINT_INT_STRING callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; - - g_return_if_fail (n_param_values == 5); - - if (G_CCLOSURE_SWAP_DATA (closure)) - { - data1 = closure->data; - data2 = g_value_peek_pointer (param_values + 0); - } - else - { - data1 = g_value_peek_pointer (param_values + 0); - data2 = closure->data; - } - callback = (GMarshalFunc_VOID__BOOLEAN_UINT_INT_STRING) (marshal_data ? marshal_data : cc->callback); - - callback (data1, - g_marshal_value_peek_boolean (param_values + 1), - g_marshal_value_peek_uint (param_values + 2), - g_marshal_value_peek_int (param_values + 3), - g_marshal_value_peek_string (param_values + 4), - data2); -} - diff --git a/libmafw-gst-renderer/mafw-gst-renderer-marshal.h b/libmafw-gst-renderer/mafw-gst-renderer-marshal.h deleted file mode 100644 index e04e265..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-marshal.h +++ /dev/null @@ -1,20 +0,0 @@ - -#ifndef __mafw_gst_renderer_marshal_MARSHAL_H__ -#define __mafw_gst_renderer_marshal_MARSHAL_H__ - -#include - -G_BEGIN_DECLS - -/* VOID:BOOLEAN,UINT,INT,STRING (mafw-gst-renderer-marshal.list:2) */ -extern void mafw_gst_renderer_marshal_VOID__BOOLEAN_UINT_INT_STRING (GClosure *closure, - GValue *return_value, - guint n_param_values, - const GValue *param_values, - gpointer invocation_hint, - gpointer marshal_data); - -G_END_DECLS - -#endif /* __mafw_gst_renderer_marshal_MARSHAL_H__ */ - diff --git a/libmafw-gst-renderer/mafw-gst-renderer-marshal.list b/libmafw-gst-renderer/mafw-gst-renderer-marshal.list deleted file mode 100644 index 113502f..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-marshal.list +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index 8c4d1c9..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * 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 = TRUE; - 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) { - self->renderer->worker->stay_paused = TRUE; - 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 deleted file mode 100644 index f0098d8..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 deleted file mode 100644 index ccb9ecc..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * 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 - { - gboolean is_stream = uri_is_stream(self->renderer->worker->media.location); - if (is_stream - && self->renderer->error_policy == MAFW_RENDERER_ERROR_POLICY_STOP - && self->renderer->worker->media.length_nanos == -1 - && self->renderer->worker->media.seekable == SEEKABILITY_NO_SEEKABLE - && self->renderer->worker->media.has_visual_content == FALSE) - { - /* Endless Radio stream. Stream has no length, it is not seekable or it is not video. */ - mafw_gst_renderer_worker_stop(self->renderer->worker); - mafw_gst_renderer_set_state(self->renderer, Stopped); - g_signal_emit_by_name(MAFW_EXTENSION(self->renderer), "error", - MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_STREAM_DISCONNECTED, - "EOS FOR ENDLESS STREAM"); - } - 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 deleted file mode 100644 index 0127edb..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 deleted file mode 100644 index 3b46057..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * 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 deleted file mode 100644 index 106ff33..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 deleted file mode 100644 index 801a9c3..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * 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, NULL); - } 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 deleted file mode 100644 index 4530a4f..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 deleted file mode 100644 index 274fcec..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state.c +++ /dev/null @@ -1,825 +0,0 @@ -/* - * 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 deleted file mode 100644 index 5fd3325..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-state.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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 deleted file mode 100644 index 42dae31..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-utils.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * 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-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://"); - } -} - -/* - * Imported from totem-uri.c - * Copyright (C) 2004 Bastien Nocera - */ - -/* List from xine-lib's demux_sputext.c */ -static const char subtitle_ext[][4] = { - "sub", - "srt", - "smi", - "ssa", - "ass", - "asc" -}; - -static inline gboolean -uri_exists (const char *uri) -{ - GFile *file = g_file_new_for_uri (uri); - if (file != NULL) { - if (g_file_query_exists (file, NULL)) { - g_object_unref (file); - return TRUE; - } - g_object_unref (file); - } - return FALSE; -} - -static char * -uri_get_subtitle_for_uri (const char *uri) -{ - char *subtitle; - guint len, i; - gint suffix; - - /* Find the filename suffix delimiter */ - len = strlen (uri); - for (suffix = len - 1; suffix > 0; suffix--) { - if (uri[suffix] == G_DIR_SEPARATOR || - (uri[suffix] == '/')) { - /* This filename has no extension; we'll need to - * add one */ - suffix = len; - break; - } - if (uri[suffix] == '.') { - /* Found our extension marker */ - break; - } - } - if (suffix < 0) - return NULL; - - /* Generate a subtitle string with room at the end to store the - * 3 character extensions for which we want to search */ - subtitle = g_malloc0 (suffix + 4 + 1); - g_return_val_if_fail (subtitle != NULL, NULL); - g_strlcpy (subtitle, uri, suffix + 4 + 1); - g_strlcpy (subtitle + suffix, ".???", 5); - - /* Search for any files with one of our known subtitle extensions */ - for (i = 0; i < G_N_ELEMENTS (subtitle_ext) ; i++) { - char *subtitle_ext_upper; - memcpy (subtitle + suffix + 1, subtitle_ext[i], 3); - - if (uri_exists (subtitle)) - return subtitle; - - /* Check with upper-cased extension */ - subtitle_ext_upper = g_ascii_strup (subtitle_ext[i], -1); - memcpy (subtitle + suffix + 1, subtitle_ext_upper, 3); - g_free (subtitle_ext_upper); - - if (uri_exists (subtitle)) - return subtitle; - } - g_free (subtitle); - return NULL; -} - -static char * -uri_get_subtitle_in_subdir (GFile *file, const char *subdir) -{ - char *filename, *subtitle, *full_path_str; - GFile *parent, *full_path, *directory; - - /* Get the sibling directory @subdir of the file @file */ - parent = g_file_get_parent (file); - directory = g_file_get_child (parent, subdir); - g_object_unref (parent); - - /* Get the file of the same name as @file in the @subdir directory */ - filename = g_file_get_basename (file); - full_path = g_file_get_child (directory, filename); - g_object_unref (directory); - g_free (filename); - - /* Get the subtitles from that URI */ - full_path_str = g_file_get_uri (full_path); - g_object_unref (full_path); - subtitle = uri_get_subtitle_for_uri (full_path_str); - g_free (full_path_str); - - return subtitle; -} - -char * -uri_get_subtitle_uri (const char *uri) -{ - GFile *file; - char *subtitle; - - if (g_str_has_prefix (uri, "http") != FALSE) - return NULL; - - /* Has the user specified a subtitle file manually? */ - if (strstr (uri, "#subtitle:") != NULL) - return NULL; - - /* Does the file exist? */ - file = g_file_new_for_uri (uri); - if (g_file_query_exists (file, NULL) != TRUE) { - g_object_unref (file); - return NULL; - } - - /* Try in the current directory */ - subtitle = uri_get_subtitle_for_uri (uri); - if (subtitle != NULL) { - g_object_unref (file); - return subtitle; - } - - subtitle = uri_get_subtitle_in_subdir (file, "subtitles"); - g_object_unref (file); - - return subtitle; -} - -/* 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 deleted file mode 100644 index fd3f44c..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-utils.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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); - -char *uri_get_subtitle_uri(const char *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 deleted file mode 100644 index 31aa22b..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c +++ /dev/null @@ -1,710 +0,0 @@ -/* - * 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 -#ifdef MAFW_GST_RENDERER_ENABLE_MUTE - || wvolume->pending_operation_mute != mute -#endif - ))) { - 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); - } - } -#ifdef MAFW_GST_RENDERER_ENABLE_MUTE - 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); - } - } -#endif - - 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_volume != wvolume->current_volume -#ifdef MAFW_GST_RENDERER_ENABLE_MUTE - || wvolume->pulse_mute != wvolume->current_mute -#endif - ) { - - 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 deleted file mode 100644 index c1e860e..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 deleted file mode 100644 index 2404608..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-worker.c +++ /dev/null @@ -1,2464 +0,0 @@ -/* - * 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" -#include "keypad.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) - -#define _current_metadata_add(worker, key, type, value) \ - do { \ - if (!worker->current_metadata) \ - worker->current_metadata = mafw_metadata_new(); \ - /* At first remove old value */ \ - g_hash_table_remove(worker->current_metadata, key); \ - mafw_metadata_add_something(worker->current_metadata, \ - key, type, 1, value); \ - } while (0) - -/* 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); - -/* Playlist parsing */ -static void _on_pl_entry_parsed(TotemPlParser *parser, gchar *uri, - gpointer metadata, GSList **plitems) -{ - if (uri != NULL) { - *plitems = g_slist_append(*plitems, g_strdup(uri)); - } -} -static GSList *_parse_playlist(const gchar *uri) -{ - static TotemPlParser *pl_parser = NULL; - GSList *plitems = NULL; - gulong handler_id; - - /* Initialize the playlist parser */ - if (!pl_parser) - { - pl_parser = totem_pl_parser_new (); - g_object_set(pl_parser, "recurse", TRUE, "disable-unsafe", - TRUE, NULL); - } - handler_id = g_signal_connect(G_OBJECT(pl_parser), "entry-parsed", - G_CALLBACK(_on_pl_entry_parsed), &plitems); - /* Parsing */ - if (totem_pl_parser_parse(pl_parser, uri, FALSE) != - TOTEM_PL_PARSER_RESULT_SUCCESS) { - /* An error happens while parsing */ - - } - g_signal_handler_disconnect(pl_parser, handler_id); - return plitems; -} - -/* - * 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, - (gchar*)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); - } - } - g_object_unref(loader); - } - } -} -#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) -{ - if (worker->ready_timeout != 0) { - g_debug("removing timeout for READY"); - 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, p_height, p_fps; - - p_width = width; - p_height = height; - p_fps = fps; - - _current_metadata_add(worker, MAFW_METADATA_KEY_RES_X, G_TYPE_INT, - p_width); - _current_metadata_add(worker, MAFW_METADATA_KEY_RES_Y, G_TYPE_INT, - p_height); - _current_metadata_add(worker, MAFW_METADATA_KEY_VIDEO_FRAMERATE, - G_TYPE_DOUBLE, - 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")); - gst_message_unref (msg); - 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); - if (worker->colorkey == -1) - g_object_get(worker->vsink, - "colorkey", &worker->colorkey, NULL); - else - mafw_gst_renderer_worker_set_colorkey( - worker, - worker->colorkey); - /* 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"))); - } - gst_message_unref (msg); - return GST_BUS_DROP; - } - /* do not unref message when returning PASS */ - 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)) { - /* Add the duration to the current metadata. */ - _current_metadata_add(worker, MAFW_METADATA_KEY_DURATION, - G_TYPE_INT64, - (gint64)duration_seconds); - /* Emit the duration. */ - mafw_renderer_emit_metadata_int64( - worker->owner, MAFW_METADATA_KEY_DURATION, - (gint64)duration_seconds); - } - - /* We compare this duration we just got with the - * source one and update it in the source if needed */ - if (duration_seconds > 0 && - 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 = (seekable == SEEKABILITY_SEEKABLE); - - /* Add the seekability to the current metadata. */ - _current_metadata_add(worker, MAFW_METADATA_KEY_IS_SEEKABLE, - G_TYPE_BOOLEAN, is_seekable); - - /* Emit. */ - mafw_renderer_emit_metadata_boolean( - worker->owner, MAFW_METADATA_KEY_IS_SEEKABLE, - 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(); - } - keypadlocking_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 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_STRING, - utf8); - g_value_array_append(values, &utf8gval); - g_value_unset(&utf8gval); - } - g_free(orig); - } else if (type == G_TYPE_UINT) { - GValue intgval = {0}; - gint intval; - - g_value_init(&intgval, G_TYPE_INT); - g_value_transform(v, &intgval); - intval = g_value_get_int(&intgval); - _current_metadata_add(worker, mafwtag, G_TYPE_INT, - intval); - g_value_array_append(values, &intgval); - g_value_unset(&intgval); - } else { - _current_metadata_add(worker, mafwtag, G_TYPE_VALUE, - 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 (percent < 100 && 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 */ - - /* Set the pipeline to playing. This is an async - handler, it could be, that the reported state - is not the real-current state */ - 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); - } - 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) { - if (err->domain == GST_STREAM_ERROR && - err->code == GST_STREAM_ERROR_WRONG_TYPE) - {/* Maybe it is a playlist? */ - GSList *plitems = _parse_playlist(worker->media.location); - - if (plitems) - {/* Yes, it is a plitem */ - g_error_free(err); - mafw_gst_renderer_worker_play(worker, NULL, plitems); - break; - } - - - } - _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); -} - -#ifdef MAFW_GST_RENDERER_ENABLE_MUTE - -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); -} - -#endif - -/* 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; - char *autoload_sub = NULL; - - g_assert(worker->pipeline); - g_object_set(G_OBJECT(worker->pipeline), - "uri", worker->media.location, NULL); - - if (worker->subtitles.enabled) { - autoload_sub = uri_get_subtitle_uri(worker->media.location); - if (autoload_sub) { - g_debug("SUBURI: %s", autoload_sub); - g_object_set(G_OBJECT(worker->pipeline), - "suburi", autoload_sub, - "subtitle-font-desc", worker->subtitles.font, - "subtitle-encoding", worker->subtitles.encoding, - NULL); - - gst_element_set_state(worker->pipeline, GST_STATE_READY); - g_free(autoload_sub); - } - } else { - g_object_set(G_OBJECT(worker->pipeline), "suburi", NULL, 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, - "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 - - - /* 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, - "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, - "force-aspect-ratio", TRUE, - NULL); - } - g_object_set(worker->pipeline, - "video-sink", worker->vsink, - "flags", 103, - NULL); - - if (!worker->tsink) { - worker->tsink = gst_element_factory_make("textoverlay", NULL); - if (!worker->tsink) { - g_critical("Failed to create pipeline text sink"); - g_signal_emit_by_name(MAFW_EXTENSION (worker->owner), - "error", - MAFW_RENDERER_ERROR, - MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM, - "Could not create text sink"); - g_assert_not_reached(); - } - gst_object_ref(worker->tsink); - } - g_object_set(worker->pipeline, "text-sink", worker->tsink, 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", - worker->autopaint, NULL); -} - -gint mafw_gst_renderer_worker_get_colorkey( - MafwGstRendererWorker *worker) -{ - return worker->colorkey; -} - -void mafw_gst_renderer_worker_set_colorkey( - MafwGstRendererWorker *worker, gint colorkey) -{ - worker->colorkey = colorkey; - if (worker->vsink) - g_object_set(worker->vsink, "colorkey", - worker->colorkey, NULL); -} - -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 _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, GSList *plitems) -{ - g_assert(uri || plitems); - - 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 (plitems || uri_is_playlist(uri)){ - gchar *item; - /* In case of a playlist we parse it and start playing the first - item of the playlist. */ - if (plitems) - { - worker->pl.items = plitems; - } - else - { - worker->pl.items = _parse_playlist(uri); - } - if (!worker->pl.items) - { - _send_error(worker, - g_error_new(MAFW_RENDERER_ERROR, - MAFW_RENDERER_ERROR_PLAYLIST_PARSING, - "Playlist parsing failed: %s", - 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); - } 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(); - keypadlocking_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(); - keypadlocking_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); -#ifdef MAFW_GST_RENDERER_ENABLE_MUTE - _mute_cb(wvolume, mute, worker); -#endif -} - -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->tsink = NULL; - worker->tag_list = NULL; - worker->current_metadata = NULL; - worker->subtitles.enabled = FALSE; - worker->subtitles.font = NULL; - worker->subtitles.encoding = 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, -#ifdef MAFW_GST_RENDERER_ENABLE_MUTE - _mute_cb, -#else - NULL, -#endif - 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 deleted file mode 100644 index 8168a1b..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer-worker.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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 - * subtitles: Configuration of subtitles. - * enabled: Are subtitles enabled - * font: Subtitles font description - * encoding: Subtitles encoding - * 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 - * tsink: Text 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; - struct { - gboolean enabled; - gchar *font; - gchar *encoding; - } subtitles; - 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; - GstElement *tsink; - 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); -void mafw_gst_renderer_worker_set_colorkey(MafwGstRendererWorker *worker, gint autopaint); -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, GSList *plitems); -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 deleted file mode 100644 index e92ea63..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer.c +++ /dev/null @@ -1,2320 +0,0 @@ -/* - * 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 GCONF_MAFW_GST_SUBTITLES_RENDERER "/system/mafw/mafw-gst-subtitles-renderer" -#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); - -static void _autoload_subtitles_changed_cb(GConfClient *client, - guint cnxn_id, - GConfEntry *entry, - MafwGstRenderer *renderer); - -static void _subtitle_font_changed_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", "dsp", - "pulseaudio", "xvimagesink", 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); -#ifdef MAFW_GST_RENDERER_ENABLE_MUTE - mafw_extension_add_property(MAFW_EXTENSION(self), "mute", G_TYPE_BOOLEAN); -#endif - 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); - } - - gconf_client_add_dir(renderer->gconf_client, - GCONF_MAFW_GST_SUBTITLES_RENDERER, - 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_MAFW_GST_SUBTITLES_RENDERER "/autoload_subtitles", - (GConfClientNotifyFunc) _autoload_subtitles_changed_cb, - renderer, - NULL, &error); - if (error) { - g_warning("%s", error->message); - g_error_free(error); - } - - gconf_client_notify_add(renderer->gconf_client, - GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_encoding", - (GConfClientNotifyFunc) _subtitle_font_changed_cb, - renderer, - NULL, &error); - if (error) { - g_warning("%s", error->message); - g_error_free(error); - } - - gconf_client_notify_add(renderer->gconf_client, - GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_font", - (GConfClientNotifyFunc) _subtitle_font_changed_cb, - renderer, - NULL, &error); - if (error) { - g_warning("%s", error->message); - g_error_free(error); - } - - if (self->worker->pipeline) { - gconf_client_notify(renderer->gconf_client, - GCONF_MAFW_GST_SUBTITLES_RENDERER "/autoload_subtitles"); - - gconf_client_notify(renderer->gconf_client, - GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_encoding"); - - gconf_client_notify(renderer->gconf_client, - GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_font"); - } - - 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); - } -} - -static void _autoload_subtitles_changed_cb(GConfClient *client, - guint cnxn_id, - GConfEntry *entry, - MafwGstRenderer *renderer) -{ - GConfValue *value = NULL; - gboolean enabled = FALSE; - - value = gconf_entry_get_value(entry); - if (value == NULL) - return; - - enabled = gconf_value_get_bool(value); - - if (enabled) - renderer->worker->subtitles.enabled = TRUE; - else - renderer->worker->subtitles.enabled = FALSE; -} - -static void _subtitle_font_changed_cb(GConfClient *client, - guint cnxn_id, - GConfEntry *entry, - MafwGstRenderer *renderer) -{ - const gchar *key = NULL; - GConfValue *value = NULL; - const gchar *str_value = NULL; - - key = gconf_entry_get_key(entry); - - /* Only key without absolute path is required */ - key += strlen(GCONF_MAFW_GST_SUBTITLES_RENDERER) + 1; - - value = gconf_entry_get_value(entry); - if (value) - str_value = gconf_value_get_string(value); - else - str_value = NULL; - - if (strcmp(key, "subtitle_font") == 0) { - if (renderer->worker->subtitles.font) - g_free(renderer->worker->subtitles.font); - - if (str_value) - renderer->worker->subtitles.font = g_strdup(str_value); - else - renderer->worker->subtitles.font = NULL; - } else if (strcmp(key, "subtitle_encoding") == 0) { - if (renderer->worker->subtitles.encoding) - g_free(renderer->worker->subtitles.encoding); - - if (str_value) - renderer->worker->subtitles.encoding = g_strdup(str_value); - else - renderer->worker->subtitles.encoding = NULL; - } else { - g_warning("Wrong %s key, %s", GCONF_MAFW_GST_SUBTITLES_RENDERER, key); - } -} - -/*---------------------------------------------------------------------------- - 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)); - } - else if (!strcmp(key, MAFW_PROPERTY_RENDERER_COLORKEY)) { - mafw_gst_renderer_worker_set_colorkey( - renderer->worker, - g_value_get_int(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 deleted file mode 100644 index 0350e34..0000000 --- a/libmafw-gst-renderer/mafw-gst-renderer.h +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 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 deleted file mode 100644 index 4a8b869..0000000 --- a/libmafw-gst-renderer/mafw-playlist-iterator.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * 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 deleted file mode 100644 index 6e88360..0000000 --- a/libmafw-gst-renderer/mafw-playlist-iterator.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 deleted file mode 100644 index 8496267..0000000 --- a/mafw-gst-renderer-uninstalled.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -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/mafw-gst-subtitles-renderer/AUTHORS b/mafw-gst-subtitles-renderer/AUTHORS new file mode 100644 index 0000000..a2c188d --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/COPYING b/mafw-gst-subtitles-renderer/COPYING new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/ChangeLog b/mafw-gst-subtitles-renderer/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/mafw-gst-subtitles-renderer/Makefile.am b/mafw-gst-subtitles-renderer/Makefile.am new file mode 100644 index 0000000..e23b977 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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 applet po + +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/mafw-gst-subtitles-renderer/acinclude.m4 b/mafw-gst-subtitles-renderer/acinclude.m4 new file mode 100644 index 0000000..1457d2a --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/applet/Makefile.am b/mafw-gst-subtitles-renderer/applet/Makefile.am new file mode 100644 index 0000000..957dc9b --- /dev/null +++ b/mafw-gst-subtitles-renderer/applet/Makefile.am @@ -0,0 +1,12 @@ +lib_LTLIBRARIES = libcpmpsubtitles.la + +libcpmpsubtitles_la_SOURCES = cpmpsubtitles.c +libcpmpsubtitles_la_LIBADD = $(MAFW_SUBTITLES_CPA_LIBS) +libcpmpsubtitles_la_CPPFLAGS = $(MAFW_SUBTITLES_CPA_CFLAGS) +libcpmpsubtitles_la_LDFLAGS = module -avoid-version + +libdir = $(CPA_PLUGINDIR) + +desktop_DATA = cpmpsubtitles.desktop +desktopdir = $(CPA_DESKTOPDIR) +EXTRA_DIST = $(desktop_DATA) diff --git a/mafw-gst-subtitles-renderer/applet/cpmpsubtitles.c b/mafw-gst-subtitles-renderer/applet/cpmpsubtitles.c new file mode 100644 index 0000000..5bf8b1c --- /dev/null +++ b/mafw-gst-subtitles-renderer/applet/cpmpsubtitles.c @@ -0,0 +1,775 @@ +/* + * Subtitles control panel applet. + * Copyright (C) 2010 Roman Moravcik + * + * encodings structure imported from totem-subtitle-encoding.c + * Copyright (C) 2001-2006 Bastien Nocera + * + * font family detection imported from hildon-font-selection-dialog.c + * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +#include + +#define GCONF_MAFW_GST_SUBTITLE_RENDERER "/system/mafw/mafw-gst-subtitles-renderer" +#define _HL(str) dgettext("hildon-libs",str) + +typedef enum +{ + FONT_STYLE_REGULAR, + FONT_STYLE_ITALIC, + FONT_STYLE_BOLD, + FONT_STYLE_ITALIC_BOLD, + FONT_STYLE_LAST, +} FontStyleIndex; + +typedef struct { + int index; + const char *name; +} FontStyle; + +static FontStyle font_styles[] = { + {FONT_STYLE_REGULAR, N_("Regular")}, + {FONT_STYLE_ITALIC, N_("Italic")}, + {FONT_STYLE_BOLD, N_("Bold")}, + {FONT_STYLE_ITALIC_BOLD, N_("Italic Bold")} +}; + +static const gint font_sizes[] = +{ + 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, -1 +}; + +typedef enum +{ + SUBTITLE_ENCODING_ISO_8859_6, + SUBTITLE_ENCODING_IBM_864, + SUBTITLE_ENCODING_MAC_ARABIC, + SUBTITLE_ENCODING_WINDOWS_1256, + + SUBTITLE_ENCODING_ARMSCII_8, + + SUBTITLE_ENCODING_ISO_8859_4, + SUBTITLE_ENCODING_ISO_8859_13, + SUBTITLE_ENCODING_WINDOWS_1257, + + SUBTITLE_ENCODING_ISO_8859_14, + + SUBTITLE_ENCODING_ISO_8859_2, + SUBTITLE_ENCODING_IBM_852, + SUBTITLE_ENCODING_MAC_CE, + SUBTITLE_ENCODING_WINDOWS_1250, + + SUBTITLE_ENCODING_GB18030, + SUBTITLE_ENCODING_GB2312, + SUBTITLE_ENCODING_GBK, + SUBTITLE_ENCODING_HZ, + + SUBTITLE_ENCODING_BIG5, + SUBTITLE_ENCODING_BIG5_HKSCS, + SUBTITLE_ENCODING_EUC_TW, + + SUBTITLE_ENCODING_MAC_CROATIAN, + + SUBTITLE_ENCODING_ISO_8859_5, + SUBTITLE_ENCODING_IBM_855, + SUBTITLE_ENCODING_ISO_IR_111, + SUBTITLE_ENCODING_KOI8_R, + SUBTITLE_ENCODING_MAC_CYRILLIC, + SUBTITLE_ENCODING_WINDOWS_1251, + + SUBTITLE_ENCODING_CP_866, + + SUBTITLE_ENCODING_MAC_UKRAINIAN, + SUBTITLE_ENCODING_KOI8_U, + + SUBTITLE_ENCODING_GEOSTD8, + + SUBTITLE_ENCODING_ISO_8859_7, + SUBTITLE_ENCODING_MAC_GREEK, + SUBTITLE_ENCODING_WINDOWS_1253, + + SUBTITLE_ENCODING_MAC_GUJARATI, + + SUBTITLE_ENCODING_MAC_GURMUKHI, + + SUBTITLE_ENCODING_ISO_8859_8_I, + SUBTITLE_ENCODING_IBM_862, + SUBTITLE_ENCODING_MAC_HEBREW, + SUBTITLE_ENCODING_WINDOWS_1255, + + SUBTITLE_ENCODING_ISO_8859_8, + + SUBTITLE_ENCODING_MAC_DEVANAGARI, + + SUBTITLE_ENCODING_MAC_ICELANDIC, + + SUBTITLE_ENCODING_EUC_JP, + SUBTITLE_ENCODING_ISO_2022_JP, + SUBTITLE_ENCODING_SHIFT_JIS, + + SUBTITLE_ENCODING_EUC_KR, + SUBTITLE_ENCODING_ISO_2022_KR, + SUBTITLE_ENCODING_JOHAB, + SUBTITLE_ENCODING_UHC, + + SUBTITLE_ENCODING_ISO_8859_10, + + SUBTITLE_ENCODING_MAC_FARSI, + + SUBTITLE_ENCODING_ISO_8859_16, + SUBTITLE_ENCODING_MAC_ROMANIAN, + + SUBTITLE_ENCODING_ISO_8859_3, + + SUBTITLE_ENCODING_TIS_620, + + SUBTITLE_ENCODING_ISO_8859_9, + SUBTITLE_ENCODING_IBM_857, + SUBTITLE_ENCODING_MAC_TURKISH, + SUBTITLE_ENCODING_WINDOWS_1254, + + SUBTITLE_ENCODING_UTF_7, + SUBTITLE_ENCODING_UTF_8, + SUBTITLE_ENCODING_UTF_16, + SUBTITLE_ENCODING_UCS_2, + SUBTITLE_ENCODING_UCS_4, + + SUBTITLE_ENCODING_ISO_8859_1, + SUBTITLE_ENCODING_ISO_8859_15, + SUBTITLE_ENCODING_IBM_850, + SUBTITLE_ENCODING_MAC_ROMAN, + SUBTITLE_ENCODING_WINDOWS_1252, + + SUBTITLE_ENCODING_TCVN, + SUBTITLE_ENCODING_VISCII, + SUBTITLE_ENCODING_WINDOWS_1258, + + SUBTITLE_ENCODING_CURRENT_LOCALE, + + SUBTITLE_ENCODING_LAST +} SubtitleEncodingIndex; + +typedef struct { + int index; + const char *charset; + const char *name; +} SubtitleEncoding; + +static SubtitleEncoding encodings[] = { + {SUBTITLE_ENCODING_ISO_8859_6, "ISO-8859-6", N_("Arabic")}, + {SUBTITLE_ENCODING_IBM_864, "IBM864", N_("Arabic")}, + {SUBTITLE_ENCODING_MAC_ARABIC, "MAC_ARABIC", N_("Arabic")}, + {SUBTITLE_ENCODING_WINDOWS_1256, "WINDOWS-1256", N_("Arabic")}, + + {SUBTITLE_ENCODING_ARMSCII_8, "ARMSCII-8", N_("Armenian")}, + + {SUBTITLE_ENCODING_ISO_8859_4, "ISO-8859-4", N_("Baltic")}, + {SUBTITLE_ENCODING_ISO_8859_13, "ISO-8859-13", N_("Baltic")}, + {SUBTITLE_ENCODING_WINDOWS_1257, "WINDOWS-1257", N_("Baltic")}, + + {SUBTITLE_ENCODING_ISO_8859_14, "ISO-8859-14", N_("Celtic")}, + + {SUBTITLE_ENCODING_ISO_8859_2, "ISO-8859-2", N_("Central European")}, + {SUBTITLE_ENCODING_IBM_852, "IBM852", N_("Central European")}, + {SUBTITLE_ENCODING_MAC_CE, "MAC_CE", N_("Central European")}, + {SUBTITLE_ENCODING_WINDOWS_1250, "WINDOWS-1250", N_("Central European")}, + + {SUBTITLE_ENCODING_GB18030, "GB18030", N_("Chinese Simplified")}, + {SUBTITLE_ENCODING_GB2312, "GB2312", N_("Chinese Simplified")}, + {SUBTITLE_ENCODING_GBK, "GBK", N_("Chinese Simplified")}, + {SUBTITLE_ENCODING_HZ, "HZ", N_("Chinese Simplified")}, + + {SUBTITLE_ENCODING_BIG5, "BIG5", N_("Chinese Traditional")}, + {SUBTITLE_ENCODING_BIG5_HKSCS, "BIG5-HKSCS", N_("Chinese Traditional")}, + {SUBTITLE_ENCODING_EUC_TW, "EUC-TW", N_("Chinese Traditional")}, + + {SUBTITLE_ENCODING_MAC_CROATIAN, "MAC_CROATIAN", N_("Croatian")}, + + {SUBTITLE_ENCODING_ISO_8859_5, "ISO-8859-5", N_("Cyrillic")}, + {SUBTITLE_ENCODING_IBM_855, "IBM855", N_("Cyrillic")}, + {SUBTITLE_ENCODING_ISO_IR_111, "ISO-IR-111", N_("Cyrillic")}, + {SUBTITLE_ENCODING_KOI8_R, "KOI8-R", N_("Cyrillic")}, + {SUBTITLE_ENCODING_MAC_CYRILLIC, "MAC-CYRILLIC", N_("Cyrillic")}, + {SUBTITLE_ENCODING_WINDOWS_1251, "WINDOWS-1251", N_("Cyrillic")}, + + {SUBTITLE_ENCODING_CP_866, "CP866", N_("Cyrillic/Russian")}, + + {SUBTITLE_ENCODING_MAC_UKRAINIAN, "MAC_UKRAINIAN", N_("Cyrillic/Ukrainian")}, + {SUBTITLE_ENCODING_KOI8_U, "KOI8-U", N_("Cyrillic/Ukrainian")}, + + {SUBTITLE_ENCODING_GEOSTD8, "GEORGIAN-PS", N_("Georgian")}, + + {SUBTITLE_ENCODING_ISO_8859_7, "ISO-8859-7", N_("Greek")}, + {SUBTITLE_ENCODING_MAC_GREEK, "MAC_GREEK", N_("Greek")}, + {SUBTITLE_ENCODING_WINDOWS_1253, "WINDOWS-1253", N_("Greek")}, + + {SUBTITLE_ENCODING_MAC_GUJARATI, "MAC_GUJARATI", N_("Gujarati")}, + + {SUBTITLE_ENCODING_MAC_GURMUKHI, "MAC_GURMUKHI", N_("Gurmukhi")}, + + {SUBTITLE_ENCODING_ISO_8859_8_I, "ISO-8859-8-I", N_("Hebrew")}, + {SUBTITLE_ENCODING_IBM_862, "IBM862", N_("Hebrew")}, + {SUBTITLE_ENCODING_MAC_HEBREW, "MAC_HEBREW", N_("Hebrew")}, + {SUBTITLE_ENCODING_WINDOWS_1255, "WINDOWS-1255", N_("Hebrew")}, + + {SUBTITLE_ENCODING_ISO_8859_8, "ISO-8859-8", N_("Hebrew Visual")}, + + {SUBTITLE_ENCODING_MAC_DEVANAGARI, "MAC_DEVANAGARI", N_("Hindi")}, + + {SUBTITLE_ENCODING_MAC_ICELANDIC, "MAC_ICELANDIC", N_("Icelandic")}, + + {SUBTITLE_ENCODING_EUC_JP, "EUC-JP", N_("Japanese")}, + {SUBTITLE_ENCODING_ISO_2022_JP, "ISO2022JP", N_("Japanese")}, + {SUBTITLE_ENCODING_SHIFT_JIS, "SHIFT-JIS", N_("Japanese")}, + + {SUBTITLE_ENCODING_EUC_KR, "EUC-KR", N_("Korean")}, + {SUBTITLE_ENCODING_ISO_2022_KR, "ISO2022KR", N_("Korean")}, + {SUBTITLE_ENCODING_JOHAB, "JOHAB", N_("Korean")}, + {SUBTITLE_ENCODING_UHC, "UHC", N_("Korean")}, + + {SUBTITLE_ENCODING_ISO_8859_10, "ISO-8859-10", N_("Nordic")}, + + {SUBTITLE_ENCODING_MAC_FARSI, "MAC_FARSI", N_("Persian")}, + + {SUBTITLE_ENCODING_ISO_8859_16, "ISO-8859-16", N_("Romanian")}, + {SUBTITLE_ENCODING_MAC_ROMANIAN, "MAC_ROMANIAN", N_("Romanian")}, + + {SUBTITLE_ENCODING_ISO_8859_3, "ISO-8859-3", N_("South European")}, + + {SUBTITLE_ENCODING_TIS_620, "TIS-620", N_("Thai")}, + + {SUBTITLE_ENCODING_ISO_8859_9, "ISO-8859-9", N_("Turkish")}, + {SUBTITLE_ENCODING_IBM_857, "IBM857", N_("Turkish")}, + {SUBTITLE_ENCODING_MAC_TURKISH, "MAC_TURKISH", N_("Turkish")}, + {SUBTITLE_ENCODING_WINDOWS_1254, "WINDOWS-1254", N_("Turkish")}, + + {SUBTITLE_ENCODING_UTF_7, "UTF-7", N_("Unicode")}, + {SUBTITLE_ENCODING_UTF_8, "UTF-8", N_("Unicode")}, + {SUBTITLE_ENCODING_UTF_16, "UTF-16", N_("Unicode")}, + {SUBTITLE_ENCODING_UCS_2, "UCS-2", N_("Unicode")}, + {SUBTITLE_ENCODING_UCS_4, "UCS-4", N_("Unicode")}, + + {SUBTITLE_ENCODING_ISO_8859_1, "ISO-8859-1", N_("Western")}, + {SUBTITLE_ENCODING_ISO_8859_15, "ISO-8859-15", N_("Western")}, + {SUBTITLE_ENCODING_IBM_850, "IBM850", N_("Western")}, + {SUBTITLE_ENCODING_MAC_ROMAN, "MAC_ROMAN", N_("Western")}, + {SUBTITLE_ENCODING_WINDOWS_1252, "WINDOWS-1252", N_("Western")}, + + {SUBTITLE_ENCODING_TCVN, "TCVN", N_("Vietnamese")}, + {SUBTITLE_ENCODING_VISCII, "VISCII", N_("Vietnamese")}, + {SUBTITLE_ENCODING_WINDOWS_1258, "WINDOWS-1258", N_("Vietnamese")}, + + {SUBTITLE_ENCODING_CURRENT_LOCALE, NULL, N_("Current Locale")} +}; + +static gboolean +gconf_get_bool (GConfClient *client, + const gchar *key) +{ + gboolean value = FALSE; + gchar *tmp = NULL; + + tmp = g_strdup_printf ("%s/%s", GCONF_MAFW_GST_SUBTITLE_RENDERER, key); + + value = gconf_client_get_bool (client, tmp, NULL); + + if (tmp) + g_free (tmp); + + return value; +} + +static void +gconf_set_bool (GConfClient *client, + const gchar *key, + gboolean value) +{ + gchar *tmp = NULL; + + tmp = g_strdup_printf ("%s/%s", GCONF_MAFW_GST_SUBTITLE_RENDERER, key); + + gconf_client_set_bool (client, tmp, value, NULL); + + if (tmp) + g_free (tmp); +} + +static gchar * +gconf_get_string (GConfClient *client, + gchar *key) +{ + gchar *value = FALSE; + gchar *tmp = NULL; + + tmp = g_strdup_printf ("%s/%s", GCONF_MAFW_GST_SUBTITLE_RENDERER, key); + + value = gconf_client_get_string (client, tmp, NULL); + + if (tmp) + g_free (tmp); + + return value; +} + +static void +gconf_set_string (GConfClient *client, + gchar *key, + const gchar *value) +{ + gchar *tmp = NULL; + + tmp = g_strdup_printf ("%s/%s", GCONF_MAFW_GST_SUBTITLE_RENDERER, key); + + if (value) + gconf_client_set_string (client, tmp, value, NULL); + else + gconf_client_unset (client, tmp, NULL); + + if (tmp) + g_free (tmp); +} + +static gboolean +is_internal_font (const gchar * name) +{ + /* FIXME Extremally BAD BAD BAD way of doing things */ + + return strcmp (name, "DeviceSymbols") == 0 + || strcmp(name, "Nokia Smiley") == 0; +} + +static void +filter_out_internal_fonts (PangoFontFamily **families, + int *n_families) +{ + int i; + int n; /* counts valid fonts */ + const gchar * name = NULL; + + for (i = 0, n = 0; i < * n_families; i++) { + name = pango_font_family_get_name (families[i]); + + if(!is_internal_font(name)) { + if (i != n) { /* there are filtered out families */ + families[n] = families[i]; /* shift the current family */ + } + n++; /* count one more valid */ + } + } /* foreach font family */ + + *n_families = n; +} + +static int +cmp_families (const void *a, + const void *b) +{ + const char *a_name = pango_font_family_get_name (* (PangoFontFamily **) a); + const char *b_name = pango_font_family_get_name (* (PangoFontFamily **) b); + + return g_utf8_collate (a_name, b_name); +} + +static int +cmp_encodings (const void *a, + const void *b) +{ + const SubtitleEncoding *a_encoding = (SubtitleEncoding *) a; + const SubtitleEncoding *b_encoding = (SubtitleEncoding *) b; + + return g_utf8_collate (_(a_encoding->name), _(b_encoding->name)); +} + +static void +font_selector_dialog (HildonButton *button, + gpointer user_data) +{ + GtkWidget *dialog, *hbox, *family_selector, *style_selector, *size_selector; + gint index = 0; + const gchar *font = NULL; + PangoFontDescription *font_desc = NULL; + PangoFontFamily **families; + gint n_families = 0; + PangoWeight pango_weight; + PangoStyle pango_style; + + font = hildon_button_get_value (HILDON_BUTTON (button)); + if (font == NULL) + return; + + font_desc = pango_font_description_from_string (font); + + dialog = gtk_dialog_new (); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_window_set_title (GTK_WINDOW (dialog), _("Font")); + gtk_dialog_add_button(GTK_DIALOG (dialog), "OK", GTK_RESPONSE_ACCEPT); + gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 400); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox); + + /* font family selector */ + family_selector = hildon_touch_selector_new_text (); + gtk_box_pack_start (GTK_BOX (hbox), family_selector, TRUE, TRUE, 0); + + pango_context_list_families (gtk_widget_get_pango_context (GTK_WIDGET (dialog)), + &families, &n_families); + + filter_out_internal_fonts (families, &n_families); + + qsort (families, n_families, sizeof(PangoFontFamily *), cmp_families); + + for (index = 0; index < n_families; index++) { + const gchar *family = pango_font_family_get_name (families[index]); + hildon_touch_selector_insert_text (HILDON_TOUCH_SELECTOR (family_selector), + index, family); + + if (strcmp (family, pango_font_description_get_family (font_desc)) == 0) { + hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (family_selector), 0, + index); + hildon_touch_selector_center_on_selected (HILDON_TOUCH_SELECTOR (family_selector)); + } + } + g_free (families); + + /* font style selector */ + style_selector = hildon_touch_selector_new_text (); + gtk_widget_set_size_request (style_selector, 200, -1); + gtk_box_pack_start (GTK_BOX (hbox), style_selector, FALSE, TRUE, 0); + + index = 0; + while (index < FONT_STYLE_LAST) { + const gchar *style = g_strdup_printf ("%s", _(font_styles[index].name)); + hildon_touch_selector_insert_text (HILDON_TOUCH_SELECTOR (style_selector), + font_styles[index].index, style); + index++; + } + pango_weight = pango_font_description_get_weight (font_desc); + pango_style = pango_font_description_get_style (font_desc); + + if (pango_weight == PANGO_WEIGHT_NORMAL) { + if (pango_style == PANGO_STYLE_NORMAL) { + hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (style_selector), 0, + FONT_STYLE_REGULAR); + } else { + hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (style_selector), 0, + FONT_STYLE_ITALIC); + } + } else { + if (pango_style == PANGO_STYLE_NORMAL) { + hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (style_selector), 0, + FONT_STYLE_BOLD); + } else { + hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (style_selector), 0, + FONT_STYLE_ITALIC_BOLD); + } + } + hildon_touch_selector_center_on_selected (HILDON_TOUCH_SELECTOR (style_selector)); + + /* font size selector */ + size_selector = hildon_touch_selector_new_text (); + gtk_widget_set_size_request (size_selector, 100, -1); + gtk_box_pack_start (GTK_BOX (hbox), size_selector, FALSE, TRUE, 0); + + index = 0; + while (font_sizes[index] != -1) { + const gchar *size = g_strdup_printf ("%d", font_sizes[index]); + hildon_touch_selector_insert_text (HILDON_TOUCH_SELECTOR (size_selector), + index, size); + + if (font_sizes[index] == (pango_font_description_get_size (font_desc) / PANGO_SCALE)) { + hildon_touch_selector_set_active (HILDON_TOUCH_SELECTOR (size_selector), 0, + index); + hildon_touch_selector_center_on_selected (HILDON_TOUCH_SELECTOR (size_selector)); + } + + index++; + } + + /* Run the dialog */ + gtk_widget_show_all (GTK_WIDGET (dialog)); + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + if (font_desc) + pango_font_description_free (font_desc); + + font_desc = pango_font_description_new (); + + /* set font family */ + pango_font_description_set_family (font_desc, + hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (family_selector))); + + /* set font style */ + switch (hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (style_selector), 0)) { + case FONT_STYLE_REGULAR: + pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL); + pango_font_description_set_weight (font_desc, PANGO_WEIGHT_NORMAL); + break; + + case FONT_STYLE_ITALIC: + pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC); + pango_font_description_set_weight (font_desc, PANGO_WEIGHT_NORMAL); + break; + + case FONT_STYLE_BOLD: + pango_font_description_set_style (font_desc, PANGO_STYLE_NORMAL); + pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD); + break; + + case FONT_STYLE_ITALIC_BOLD: + pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC); + pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD); + break; + } + + /* set font size */ + pango_font_description_set_size (font_desc, + font_sizes[hildon_touch_selector_get_active (HILDON_TOUCH_SELECTOR (size_selector), 0)] * PANGO_SCALE); + + hildon_button_set_value (HILDON_BUTTON (button), pango_font_description_to_string (font_desc)); + } + + if (font_desc) + pango_font_description_free (font_desc); + + gtk_widget_destroy(GTK_WIDGET(dialog)); +} + + +static GtkWidget * +create_encoding_selector (void) +{ + GtkWidget *selector; + gint index = 0; + + selector = hildon_touch_selector_new_text (); + + qsort (encodings, SUBTITLE_ENCODING_LAST - 1, sizeof (SubtitleEncoding), cmp_encodings); + + while (index < SUBTITLE_ENCODING_LAST) { + const gchar *encoding = NULL; + + if (encodings[index].charset) { + encoding = g_strdup_printf ("%s (%s)", _(encodings[index].name), + encodings[index].charset); + } else { + encoding = g_strdup_printf ("%s", _(encodings[index].name)); + } + + hildon_touch_selector_insert_text (HILDON_TOUCH_SELECTOR (selector), + index, + encoding); + index++; + } + + return selector; +} + +static GtkWidget * +create_autoload_subtitles_button (GConfClient *gconf_client) +{ + GtkWidget *button; + gboolean autoload_subtitles = FALSE; + + button = hildon_check_button_new (HILDON_SIZE_FINGER_HEIGHT); + gtk_button_set_label (GTK_BUTTON (button), _("Automatically load subtitle files")); + + autoload_subtitles = gconf_get_bool (gconf_client, "autoload_subtitles"); + if (autoload_subtitles) + hildon_check_button_set_active (HILDON_CHECK_BUTTON (button), TRUE); + else + hildon_check_button_set_active (HILDON_CHECK_BUTTON (button), FALSE); + + return button; +} + +static void +save_autoload_subtitles (GConfClient *gconf_client, + GtkWidget *widget) +{ + if (hildon_check_button_get_active (HILDON_CHECK_BUTTON (widget))) + gconf_set_bool (gconf_client, "autoload_subtitles", TRUE); + else + gconf_set_bool (gconf_client, "autoload_subtitles", FALSE); +} + +static GtkWidget * +create_subtitles_font_button (GConfClient *gconf_client) +{ + GtkWidget *button; + const gchar *font = NULL; + + button = hildon_button_new (HILDON_SIZE_FINGER_HEIGHT, + HILDON_BUTTON_ARRANGEMENT_VERTICAL); + hildon_button_set_title (HILDON_BUTTON (button), _("Font")); + hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 0.0); + hildon_button_set_title_alignment (HILDON_BUTTON(button), 0.0, 0.5); + hildon_button_set_value_alignment (HILDON_BUTTON (button), 0.0, 0.5); + hildon_button_set_style (HILDON_BUTTON (button), HILDON_BUTTON_STYLE_PICKER); + + font = gconf_get_string (gconf_client, "subtitle_font"); + if (font) { + hildon_button_set_value (HILDON_BUTTON (button), font); + } else { + hildon_button_set_value (HILDON_BUTTON (button), "Sans Bold 18"); + } + + g_signal_connect (button, "clicked", G_CALLBACK (font_selector_dialog), + NULL); + + return button; +} + +static void +save_subtitles_font (GConfClient *gconf_client, + GtkWidget *widget) +{ + const gchar *font = NULL; + + font = hildon_button_get_value (HILDON_BUTTON (widget)); + gconf_set_string (gconf_client, "subtitle_font", font); +} + +static GtkWidget * +create_subtitles_encoding_button (GConfClient *gconf_client) +{ + GtkWidget *button, *selector; + const gchar *encoding = NULL; + + button = hildon_picker_button_new (HILDON_SIZE_FINGER_HEIGHT, + HILDON_BUTTON_ARRANGEMENT_VERTICAL); + hildon_button_set_title (HILDON_BUTTON (button), _("Encoding")); + hildon_button_set_alignment (HILDON_BUTTON (button), 0.0, 0.5, 1.0, 0.0); + hildon_button_set_title_alignment (HILDON_BUTTON(button), 0.0, 0.5); + hildon_button_set_value_alignment (HILDON_BUTTON (button), 0.0, 0.5); + + selector = create_encoding_selector (); + hildon_picker_button_set_selector (HILDON_PICKER_BUTTON (button), + HILDON_TOUCH_SELECTOR (selector)); + + encoding = gconf_get_string (gconf_client, "subtitle_encoding"); + if (encoding) { + gint index = 0; + + while (index < SUBTITLE_ENCODING_LAST) { + if (encodings[index].charset) { + if (strcmp (encodings[index].charset, encoding) == 0) { + hildon_picker_button_set_active (HILDON_PICKER_BUTTON (button), + index); + break; + } + } + index++; + } + } else { + hildon_picker_button_set_active (HILDON_PICKER_BUTTON (button), + SUBTITLE_ENCODING_CURRENT_LOCALE); + } + + return button; +} + +static void +save_subtitles_encoding (GConfClient *gconf_client, + GtkWidget *widget) +{ + gint encoding = 0, index = 0; + + encoding = hildon_picker_button_get_active (HILDON_PICKER_BUTTON (widget)); + + while (index < SUBTITLE_ENCODING_LAST) { + if (encoding == index) { + gconf_set_string (gconf_client, "subtitle_encoding", + encodings[index].charset); + break; + } + index++; + } +} + +osso_return_t +execute (osso_context_t *osso, + gpointer data, + gboolean user_activated) +{ + GConfClient *gconf_client = NULL; + GtkWidget *dialog, *vbox, *autoload_subtitles_button; + GtkWidget *subtitles_font_button, *subtitles_encoding_button; + + gconf_client = gconf_client_get_default (); + if (gconf_client == NULL) { + return OSSO_ERROR; + } + + dialog = gtk_dialog_new (); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data)); + gtk_window_set_title (GTK_WINDOW (dialog), _("Subtitles")); + gtk_dialog_add_button(GTK_DIALOG (dialog), _HL("wdgt_bd_save"), GTK_RESPONSE_ACCEPT); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox); + + /* autoload subtitles button */ + autoload_subtitles_button = create_autoload_subtitles_button (gconf_client); + gtk_box_pack_start (GTK_BOX (vbox), autoload_subtitles_button, TRUE, TRUE, 0); + + /* font selector */ + subtitles_font_button = create_subtitles_font_button (gconf_client); + gtk_box_pack_start (GTK_BOX (vbox), subtitles_font_button, TRUE, TRUE, 0); + + /* font encoding */ + subtitles_encoding_button = create_subtitles_encoding_button (gconf_client); + gtk_box_pack_start (GTK_BOX (vbox), subtitles_encoding_button, TRUE, TRUE, 0); + + /* Run the dialog */ + gtk_widget_show_all (GTK_WIDGET (dialog)); + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + /* save autoload subtitles option */ + save_autoload_subtitles (gconf_client, autoload_subtitles_button); + + /* save subtitle font option */ + save_subtitles_font (gconf_client, subtitles_font_button); + + /* save subtitle encoding option */ + save_subtitles_encoding (gconf_client, subtitles_encoding_button); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); + return OSSO_OK; +} + +osso_return_t +save_state (osso_context_t *osso, + gpointer data) +{ + return OSSO_OK; +} + diff --git a/mafw-gst-subtitles-renderer/applet/cpmpsubtitles.desktop b/mafw-gst-subtitles-renderer/applet/cpmpsubtitles.desktop new file mode 100644 index 0000000..6bccbf0 --- /dev/null +++ b/mafw-gst-subtitles-renderer/applet/cpmpsubtitles.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Name=Subtitles +Comment=Control panel to configure subtitles for mafw-gst-subtitles-renderer +Type=HildonControlPanelPlugin +Icon=general_video_file +X-control-panel-plugin=libcpmpsubtitles.so +X-Text-Domain=mafw-gst-subtitles-renderer +Categories=extras diff --git a/mafw-gst-subtitles-renderer/autogen.sh b/mafw-gst-subtitles-renderer/autogen.sh new file mode 100755 index 0000000..53c895a --- /dev/null +++ b/mafw-gst-subtitles-renderer/autogen.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +export AUTOMAKE="automake-1.9" +export ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/` + +glib-gettextize --copy --force +intltoolize --automake --copy --force +autoreconf -v -f -i || exit 1 +test -n "$NOCONFIGURE" || ./configure \ + --enable-debug --enable-maintainer-mode "$@" diff --git a/mafw-gst-subtitles-renderer/configure.ac b/mafw-gst-subtitles-renderer/configure.ac new file mode 100644 index 0000000..a73df69 --- /dev/null +++ b/mafw-gst-subtitles-renderer/configure.ac @@ -0,0 +1,246 @@ +# +# 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-subtitles-renderer], [0.2.2010.07-2]) + +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 + +IT_PROG_INTLTOOL([0.35]) +AC_SUBST([GETTEXT_PACKAGE], [mafw-gst-subtitles-renderer]) +AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [The domain to use with gettext.]) +AM_GLIB_GNU_GETTEXT() + +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 + mce + dbus-1 +) + +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 Control Panel +PKG_CHECK_MODULES([MAFW_SUBTITLES_CPA], [libosso >= 2.0 + hildon-1 >= 2.1 + hildon-control-panel + gtk+-2.0 + gconf-2.0]) + +CPA_PLUGINDIR=`pkg-config hildon-control-panel --variable=pluginlibdir` +CPA_DESKTOPDIR=`pkg-config hildon-control-panel --variable=plugindesktopentrydir` + +AC_SUBST(MAFW_SUBTITLES_CPA_CFLAGS) +AC_SUBST(MAFW_SUBTITLES_CPA_LIBS) +AC_SUBST(CPA_DESKTOPDIR) +AC_SUBST(CPA_PLUGINDIR) + +dnl Output files. + +AC_CONFIG_FILES([ + Makefile + mafw-gst-renderer-uninstalled.pc + libmafw-gst-renderer/Makefile + applet/Makefile + tests/Makefile + debian/mafw-gst-subtitles-renderer.install + po/Makefile.in +]) + +AC_OUTPUT diff --git a/mafw-gst-subtitles-renderer/debian/changelog b/mafw-gst-subtitles-renderer/debian/changelog new file mode 100644 index 0000000..59d0c83 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/changelog @@ -0,0 +1,1258 @@ +mafw-gst-subtitles-renderer (0.3.2010.24-1+0m5-2) unstable; urgency=low + + * Fixed Bug: 6438: Plugin makes background music stop on lock screen. + The only solution to fix this problem was rename back MAFW GStreamer + renderer from mafw-gst-subtitles-renderer to mafw-gst-renderer. + + -- Roman Moravcik Tue, 26 Oct 2010 14:50:06 +0200 + +mafw-gst-subtitles-renderer (0.3.2010.24-1+0m5-1) unstable; urgency=low + + * Package updated to version mafw-gst-renderer-0.3.2010.24-1+0m5. + + -- Roman Moravcik Mon, 25 Oct 2010 12:58:00 +0200 + +mafw-gst-renderer (0.3.2010.24-1+0m5) unstable; urgency=low + + * This entry has been added by BIFH queue processor + version has been changed to 0.3.2010.24-1+0m5 + + -- Pekka Rönkkö Mon, 21 Jun 2010 14:57:08 +0300 + +mafw-gst-renderer (0.3.2010.24-1) unstable; urgency=low + + * Fixes: NB#161636 - Playbin sends EOS for endless stream + + -- Pekka Rönkkö Thu, 17 Jun 2010 12:05:30 +0200 + +mafw-gst-subtitles-renderer (0.2.2010.07-2+0m5-3) unstable; urgency=low + + * This package should depend on gstreamer0.10-plugins-base-subtitles + version (>= 0.10.25-0maemo14+0m5-1). + + -- Roman Moravcik Tue, 25 May 2010 17:12:33 +0200 + +mafw-gst-subtitles-renderer (0.2.2010.07-2+0m5-2) unstable; urgency=low + + * Updated Finnish translation by mikuu. + * Installation of this package disable mafw-gst-renderer instead of replacing it. + (original mafw-gst-renderer will be re-enabled after uninstallation of package). + + -- Roman Moravcik Tue, 18 May 2010 14:35:42 +0200 + +mafw-gst-subtitles-renderer (0.2.2010.07-2+0m5-1) unstable; urgency=low + + * Added Hungarian translation by Gyorgy Lakatos. + * Package updated to version mafw-gst-renderer-0.2.2010.07-2+0m5-1. + + -- Roman Moravcik Wed, 5 May 2010 10:12:28 +0200 + +mafw-gst-renderer (0.2.2010.07-2) unstable; urgency=low + + * Fixes: NB#156757 - Volume settings can't be changed via Volume key, Media player is playing, Tklock is On. + + -- Mika Tapojärvi Wed, 17 Feb 2010 10:09:38 +0200 + +mafw-gst-renderer (0.2.2010.06-1) unstable; urgency=low + + * Fixes: NB#150064 - NP-Video:Frames not changing while performing seek operation when the video is in paused state + + -- Mika Tapojärvi Wed, 10 Feb 2010 21:43:22 +0200 + +mafw-gst-renderer (0.2.2010.01-1) unstable; urgency=low + + * Version number increased in trunk. + * A forbidden word removed from debian/changelog. + + -- Mika Tapojärvi Thu, 07 Jan 2010 19:36:53 +0200 + +mafw-gst-renderer (0.2.2009.52-2) unstable; urgency=low + + * Version number increased. + + -- Tuomas Kamarainen Wed, 23 Dec 2009 12:40:45 +0200 + +mafw-gst-renderer (0.2.2009.52-1) unstable; urgency=low + + * Fixes: NB#149945 - mafw-gst-renderer leaks some GStreamer messages + * Thanks to Mueller Tim for the patch. + + -- Mika Tapojärvi Sun, 20 Dec 2009 22:43:29 +0200 + +mafw-gst-renderer (0.2.2009.50-2) unstable; urgency=low + + * Fixes: NB#148080 - Device UI becomes very slow after long time usage of Media Player + + -- Mika Tapojärvi Thu, 10 Dec 2009 19:07:45 +0200 + +mafw-gst-renderer (0.2.2009.50-1) unstable; urgency=low + + * Version and changelog updated for pre-release 0.2009.50-1 + + -- Mika Tapojärvi Tue, 08 Dec 2009 09:21:47 +0200 + +mafw-gst-renderer (0.2.2009.49-1) unstable; urgency=low + + * Rebuild for 2009.49-1. + + -- Mika Tapojärvi Tue, 01 Dec 2009 19:27:31 +0200 + +mafw-gst-renderer (0.2.2009.48-2) unstable; urgency=low + + * Pre-release PR 1.2 2009.48-2 tag. + + -- Tuomas Kämäräinen Fri, 27 Nov 2009 11:56:14 +0200 + +mafw-gst-renderer (0.2.2009.48-1) unstable; urgency=low + + * Pre-release PR 1.2 2009.48-1 tag. + + -- Mika Tapojärvi Tue, 24 Nov 2009 21:57:59 +0200 + +mafw-gst-renderer (0.1.2009.47-2) unstable; urgency=low + + * Version increased. + + -- Tuomas Kamarainen Fri, 20 Nov 2009 13:26:19 +0300 + +mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-5) unstable; urgency=low + + * Added Danish translation by Joe Hansen. + * Added Finnish translation by Marko Vertainen. + + -- Roman Moravcik Tue, 16 Feb 2010 15:02:02 +0100 + +mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-4) unstable; urgency=low + + * Added German translation by Philipp Zabel. + * Backup/restore original mafw-gst-renderer on installation/uninstallation + of mafw-gst-subtitles package. + + -- Roman Moravcik Wed, 27 Jan 2010 15:29:27 +0100 + +mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-3) unstable; urgency=low + + * Updating of mafw-gst-subtitles-applet package was not updating + mafw-gst-subtitles-renderer package too. + + -- Roman Moravcik Tue, 19 Jan 2010 15:53:19 +0100 + +mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-2) unstable; urgency=low + + * Added Czech translation. + * List of available encoding was not sorted. + * Removed mafw-gst-subtitles-applet postinst/postrm script, because + mafw is restared during mafw-gst-subtitles-renderer instalation. + * Added more font sizes. + * Nokia fonts removed from the list of filtered fonts. + + -- Roman Moravcik Tue, 19 Jan 2010 15:07:48 +0100 + +mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-1) unstable; urgency=low + + * Added missing build dependency to hildon-control-panel-dev + + -- Roman Moravcik Mon, 18 Jan 2010 22:39:17 +0100 + +mafw-gst-subtitles-renderer (0.1.2009.47-1+0m5-0) unstable; urgency=low + + * Added subtitles support. + * Added Subtitles control panel applet. + + -- Roman Moravcik Mon, 18 Jan 2010 22:09:11 +0100 + +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/mafw-gst-subtitles-renderer/debian/compat b/mafw-gst-subtitles-renderer/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/compat @@ -0,0 +1 @@ +4 diff --git a/mafw-gst-subtitles-renderer/debian/control b/mafw-gst-subtitles-renderer/debian/control new file mode 100644 index 0000000..9fb6268 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/control @@ -0,0 +1,70 @@ +Source: mafw-gst-subtitles-renderer +Section: misc +Priority: optional +Maintainer: Roman Moravcik +XSBC-Original-Maintainer: Mika Tapojarvi +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, mce-dev, + libdbus-1-dev, hildon-control-panel-dev +Standards-Version: 3.7.2 +Homepage: http://mafwsubrenderer.garage.maemo.org/ +Vcs-Browser: https://garage.maemo.org/plugins/ggit/browse.php/?p=mafwsubrenderer +Vcs-Git: https://vcs.maemo.org/git/mafwsubrenderer + +Package: mafw-gst-subtitles-renderer +Section: libs +Architecture: any +Depends: gconf2, ${shlibs:Depends}, ${misc:Depends}, gstreamer0.10-plugins-base-subtitles (>= 0.10.25-0maemo14+0m5-1) +Replaces: mafw-gst-renderer +Description: MAFW gst renderer plugin with subtitles support + Renderer plugin for MAFW-gst + +Package: mafw-gst-subtitles-renderer-dbg +Section: devel +Architecture: any +Priority: extra +Depends: mafw-gst-subtitles-renderer (= ${binary:Version}) +Description: debug symbols for mafw-gst-subtitles-renderer + MAFW-gst renderer debug symbols + +Package: mafw-gst-subtitles-applet +Section: user/multimedia +Architecture: any +Depends: gconf2, ${shlibs:Depends}, ${misc:Depends}, mafw-gst-subtitles-renderer (= ${binary:Version}) +Description: External subtitles support for Media Player + Subtitles font and encoding can be change via 'Settings: Subtitles' + control panel applet. +XB-Maemo-Display-Name: Subtitles Support +XSBC-Bugtracker: https://garage.maemo.org/tracker/?func=add&group_id=1248&atid=4688 +XB-Maemo-Icon-26: + iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0 + d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA9JJREFUeNrsWblOI0EQbVvm + FodkIWRDQGAMERIgyxI5IcT+ATbYdEnZYGM23WD5CiDjA5AAOUKYQxY35jK2 + AZv72Hmt7Va7d46eY4UtTUkjV09NddfrquqjHPj4+CD1TEFS5+QD8AH4AHwA + PgBXFJJfBAKB8Orq6o9YLPa1lgzNZrO/ksnkd23jvTYFoFH4+fk59P7+XlMz + DZtgm/ZYAiCzs7Okvb2d8vPz8/z90tISWVxc5O2ZmRkyODhI+Z2dHTI3N6ck + m5qaIpOTk7w9PT3NeehAlxH0oH93d6cWQqDNzU3S0NBAedETp6enJJ1O8/bt + 7S2Xg1eVjY2NVfUryuQxYTzkLy8v6gBEent70+2YtZncqUwmHC5FmdVh0xKA + OLjcGWRMrmekkQz9GOWYLLMCEJA/0Fah+PLy8reBgYEvn5m0CJn7+3skL23v + 7e39TqVSPzV7dy09cHBwQFTuCb29vaSlpYXyDw8PNEfc0uvrK+np6eGGb2xs + 0Bw6Pz9XD6FKpWKY9fIsNTY2cl5Fx4iurq7IyckJXf3YCggqFoskn8+TQqGg + DsAsRuUEZ9+JvJ3ZhuGY3aenJ/qutbWVvpfHMOrbEIBKCIlAVXWY4TD67Oys + yli9FYoZbwuAmYLsATaYigcwy8fHxzQcZMNFcKIMvFnfrjwgL5VGOjD86OiI + XF5eKvUpAnDkAa8A3NzcUMPxq0qYbU9yQCWEZACMv7i4oLNtx/BP84B4XEBi + Hh4eksfHR8fLqZ4H/msOYLBSqURDZWtry/VGJi4MYggZ2eM4hGA4wgQ7Jr7F + rHtxh/AkhMwQw1AcNRAuw8PDpKuriwPyAoBnSSwDwDEBWz02H73BZNd76QFH + OcAUcBbZ39+nce52I3OTA7YB4GSJXbNcLpvmAZutmgqhlZUV0tnZaWswrzzg + SRL39/eT7u5uy8Gampr4YODj8bj7Ok8oVAWgr6+Pn1qVAWSzWX5uSSQSVZf6 + XC7H20NDQ/zsjiTf3t5WkkWjUXoZYrS+vs556ECXkXYTo/q41CgDwC2IVSVG + Rkb4e+SEWEHAzYndyJDsa2trSjJUJSBnJMoikQiJxWK8nclk6MrnSVVC71Lv + pCohVx7MxrRdWtRbaYzKKnISq8rkRJXBiTJHZZXx8fGqHdYouTs6Orgc/MTE + hJKsra2tql9RhtAVZaOjozR8sA8tLCyoAYDx4XD4HwDNzc300fNQMBjkOnZk + tBhrIgN42x7QVoycppiupeKutgrllAtbWCi0J05qi1DQSqsUtq7/frxLao9K + lh6oN/L/I/MB+ADqnP4IMABJCQV+48xZ9wAAAABJRU5ErkJggg== + ==== diff --git a/mafw-gst-subtitles-renderer/debian/copyright b/mafw-gst-subtitles-renderer/debian/copyright new file mode 100644 index 0000000..79772e7 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/copyright @@ -0,0 +1,6 @@ +Copyright (C) 2007, 2008, 2009 Nokia Corporation. All rights reserved. + +Contact: Visa Smolander + + + diff --git a/mafw-gst-subtitles-renderer/debian/mafw-gst-renderer.mafw.xsession b/mafw-gst-subtitles-renderer/debian/mafw-gst-renderer.mafw.xsession new file mode 100644 index 0000000..88fb03c --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/mafw-gst-renderer.mafw.xsession @@ -0,0 +1,5 @@ +#!/bin/sh + +if test -x /usr/sbin/dsmetool; then + sudo /usr/bin/mafw.sh start mafw-gst-renderer -7 +fi diff --git a/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-applet.install b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-applet.install new file mode 100644 index 0000000..763d763 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-applet.install @@ -0,0 +1,3 @@ +/usr/lib/hildon-control-panel/libcpmpsubtitles.so +/usr/share/applications/hildon-control-panel/cpmpsubtitles.desktop +/usr/share/locale/* \ No newline at end of file diff --git a/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.dirs b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.dirs new file mode 100644 index 0000000..db72179 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.dirs @@ -0,0 +1 @@ +usr/lib/mafw-plugin diff --git a/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.install.in b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.install.in new file mode 100644 index 0000000..f350be5 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.install.in @@ -0,0 +1 @@ +@plugindir@/*.so diff --git a/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.postinst b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.postinst new file mode 100644 index 0000000..896525f --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.postinst @@ -0,0 +1,12 @@ +#!/bin/sh + +#DEBHELPER# + +# purge old installed startup script +if [ -f /etc/X11/Xsession.post/31mafw-gst-subtitles-renderer ]; then + rm -f /etc/X11/Xsession.post/31mafw-gst-subtitles-renderer +fi + +/usr/bin/mafw.sh start mafw-gst-renderer -7 + +exit 0 diff --git a/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.postrm b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.postrm new file mode 100644 index 0000000..003e7ec --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.postrm @@ -0,0 +1,20 @@ +#!/bin/sh + +#DEBHELPER# + +case "$1" in + remove) + # restore original mafw-gst-renderer on package removal + if [ -f /usr/lib/mafw-plugin/mafw-gst-renderer.so.removed ]; then + mv -f /usr/lib/mafw-plugin/mafw-gst-renderer.so.removed /usr/lib/mafw-plugin/mafw-gst-renderer.so + fi + ;; + abort-upgrade|abort-remove|abort-deconfigure) + ;; + *) + exit 0 +esac + +/usr/bin/mafw.sh start mafw-gst-renderer -7 + +exit 0 diff --git a/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.preinst b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.preinst new file mode 100644 index 0000000..ede9601 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.preinst @@ -0,0 +1,11 @@ +#!/bin/sh + +#DEBHELPER# + +# create backup of original mafw-gst-renderer.so if it not exists +if [ ! -f /usr/lib/mafw-plugin/mafw-gst-renderer.so.removed ]; then + /usr/bin/mafw.sh stop mafw-gst-renderer + mv -f /usr/lib/mafw-plugin/mafw-gst-renderer.so /usr/lib/mafw-plugin/mafw-gst-renderer.so.removed +fi + +exit 0 diff --git a/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.prerm b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.prerm new file mode 100644 index 0000000..f8abba6 --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/mafw-gst-subtitles-renderer.prerm @@ -0,0 +1,7 @@ +#!/bin/sh + +#DEBHELPER# + +/usr/bin/mafw.sh stop mafw-gst-renderer + +exit 0 diff --git a/mafw-gst-subtitles-renderer/debian/rules b/mafw-gst-subtitles-renderer/debian/rules new file mode 100755 index 0000000..15c86ec --- /dev/null +++ b/mafw-gst-subtitles-renderer/debian/rules @@ -0,0 +1,95 @@ +#!/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-subtitles-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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/Makefile.am b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/Makefile.am new file mode 100644 index 0000000..adc18a3 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/Makefile.am @@ -0,0 +1,56 @@ +# +# 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) \ + keypad.c keypad.h \ + 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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/blanking.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/blanking.c new file mode 100644 index 0000000..4a12836 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/blanking.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/blanking.h new file mode 100644 index 0000000..6a69e61 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/gstscreenshot.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/gstscreenshot.c new file mode 100644 index 0000000..f7f177e --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/gstscreenshot.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/gstscreenshot.h new file mode 100644 index 0000000..d3cf23c --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/keypad.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/keypad.c new file mode 100644 index 0000000..0af13a4 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/keypad.c @@ -0,0 +1,79 @@ +/* + * 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 +#include "keypad.h" + + +#define KEYPAD_TIMER_INTERVAL 50 + +static guint toutid; + +void keypadlocking_allow(void) +{ + if (toutid) + { + g_source_remove(toutid); + toutid = 0; + } +} + +static gboolean no_keylock_timeout(gpointer udata) +{ + static DBusMessage *msg = NULL; + static DBusConnection *sysbus = NULL; + + if (!sysbus) + { + DBusError err; + + dbus_error_init(&err); + sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + g_assert(sysbus); + g_assert(!dbus_error_is_set(&err)); + } + if (!msg) + { + msg = dbus_message_new_method_call(MCE_SERVICE, + MCE_REQUEST_PATH, MCE_REQUEST_IF, + MCE_PREVENT_KEYPAD_OFF_REQ); + g_assert(msg); + } + g_assert(dbus_connection_send(sysbus, msg,NULL)); + dbus_connection_flush(sysbus); + return TRUE; +} + +void keypadlocking_prohibit(void) +{ + if (!toutid) + { + toutid = g_timeout_add_seconds(KEYPAD_TIMER_INTERVAL, + no_keylock_timeout, + NULL); + no_keylock_timeout(NULL); + } +} diff --git a/mafw-gst-subtitles-renderer/libmafw-gst-renderer/keypad.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/keypad.h new file mode 100644 index 0000000..f434567 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/keypad.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 KEYPAD_H +#define KEYPAD_H + +G_BEGIN_DECLS + +void keypadlocking_prohibit(void); +void keypadlocking_allow(void); + +G_END_DECLS + +#endif diff --git a/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.c new file mode 100644 index 0000000..3682dcb --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.c @@ -0,0 +1,91 @@ +#include "mafw-gst-renderer-marshal.h" + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:BOOLEAN,UINT,INT,STRING (mafw-gst-renderer-marshal.list:2) */ +void +mafw_gst_renderer_marshal_VOID__BOOLEAN_UINT_INT_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__BOOLEAN_UINT_INT_STRING) (gpointer data1, + gboolean arg_1, + guint arg_2, + gint arg_3, + gpointer arg_4, + gpointer data2); + register GMarshalFunc_VOID__BOOLEAN_UINT_INT_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__BOOLEAN_UINT_INT_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_boolean (param_values + 1), + g_marshal_value_peek_uint (param_values + 2), + g_marshal_value_peek_int (param_values + 3), + g_marshal_value_peek_string (param_values + 4), + data2); +} + diff --git a/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.h new file mode 100644 index 0000000..e04e265 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.h @@ -0,0 +1,20 @@ + +#ifndef __mafw_gst_renderer_marshal_MARSHAL_H__ +#define __mafw_gst_renderer_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* VOID:BOOLEAN,UINT,INT,STRING (mafw-gst-renderer-marshal.list:2) */ +extern void mafw_gst_renderer_marshal_VOID__BOOLEAN_UINT_INT_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __mafw_gst_renderer_marshal_MARSHAL_H__ */ + diff --git a/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.list b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-marshal.list new file mode 100644 index 0000000..113502f --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c new file mode 100644 index 0000000..8c4d1c9 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-paused.c @@ -0,0 +1,380 @@ +/* + * 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 = TRUE; + 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) { + self->renderer->worker->stay_paused = TRUE; + 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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-paused.h new file mode 100644 index 0000000..f0098d8 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c new file mode 100644 index 0000000..ccb9ecc --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-playing.c @@ -0,0 +1,465 @@ +/* + * 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 + { + gboolean is_stream = uri_is_stream(self->renderer->worker->media.location); + if (is_stream + && self->renderer->error_policy == MAFW_RENDERER_ERROR_POLICY_STOP + && self->renderer->worker->media.length_nanos == -1 + && self->renderer->worker->media.seekable == SEEKABILITY_NO_SEEKABLE + && self->renderer->worker->media.has_visual_content == FALSE) + { + /* Endless Radio stream. Stream has no length, it is not seekable or it is not video. */ + mafw_gst_renderer_worker_stop(self->renderer->worker); + mafw_gst_renderer_set_state(self->renderer, Stopped); + g_signal_emit_by_name(MAFW_EXTENSION(self->renderer), "error", + MAFW_RENDERER_ERROR, MAFW_RENDERER_ERROR_STREAM_DISCONNECTED, + "EOS FOR ENDLESS STREAM"); + } + 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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-playing.h new file mode 100644 index 0000000..0127edb --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.c new file mode 100644 index 0000000..3b46057 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-stopped.h new file mode 100644 index 0000000..106ff33 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.c new file mode 100644 index 0000000..801a9c3 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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, NULL); + } 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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state-transitioning.h new file mode 100644 index 0000000..4530a4f --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state.c new file mode 100644 index 0000000..274fcec --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-state.h new file mode 100644 index 0000000..5fd3325 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-utils.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-utils.c new file mode 100644 index 0000000..42dae31 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-utils.c @@ -0,0 +1,248 @@ +/* + * 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-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://"); + } +} + +/* + * Imported from totem-uri.c + * Copyright (C) 2004 Bastien Nocera + */ + +/* List from xine-lib's demux_sputext.c */ +static const char subtitle_ext[][4] = { + "sub", + "srt", + "smi", + "ssa", + "ass", + "asc" +}; + +static inline gboolean +uri_exists (const char *uri) +{ + GFile *file = g_file_new_for_uri (uri); + if (file != NULL) { + if (g_file_query_exists (file, NULL)) { + g_object_unref (file); + return TRUE; + } + g_object_unref (file); + } + return FALSE; +} + +static char * +uri_get_subtitle_for_uri (const char *uri) +{ + char *subtitle; + guint len, i; + gint suffix; + + /* Find the filename suffix delimiter */ + len = strlen (uri); + for (suffix = len - 1; suffix > 0; suffix--) { + if (uri[suffix] == G_DIR_SEPARATOR || + (uri[suffix] == '/')) { + /* This filename has no extension; we'll need to + * add one */ + suffix = len; + break; + } + if (uri[suffix] == '.') { + /* Found our extension marker */ + break; + } + } + if (suffix < 0) + return NULL; + + /* Generate a subtitle string with room at the end to store the + * 3 character extensions for which we want to search */ + subtitle = g_malloc0 (suffix + 4 + 1); + g_return_val_if_fail (subtitle != NULL, NULL); + g_strlcpy (subtitle, uri, suffix + 4 + 1); + g_strlcpy (subtitle + suffix, ".???", 5); + + /* Search for any files with one of our known subtitle extensions */ + for (i = 0; i < G_N_ELEMENTS (subtitle_ext) ; i++) { + char *subtitle_ext_upper; + memcpy (subtitle + suffix + 1, subtitle_ext[i], 3); + + if (uri_exists (subtitle)) + return subtitle; + + /* Check with upper-cased extension */ + subtitle_ext_upper = g_ascii_strup (subtitle_ext[i], -1); + memcpy (subtitle + suffix + 1, subtitle_ext_upper, 3); + g_free (subtitle_ext_upper); + + if (uri_exists (subtitle)) + return subtitle; + } + g_free (subtitle); + return NULL; +} + +static char * +uri_get_subtitle_in_subdir (GFile *file, const char *subdir) +{ + char *filename, *subtitle, *full_path_str; + GFile *parent, *full_path, *directory; + + /* Get the sibling directory @subdir of the file @file */ + parent = g_file_get_parent (file); + directory = g_file_get_child (parent, subdir); + g_object_unref (parent); + + /* Get the file of the same name as @file in the @subdir directory */ + filename = g_file_get_basename (file); + full_path = g_file_get_child (directory, filename); + g_object_unref (directory); + g_free (filename); + + /* Get the subtitles from that URI */ + full_path_str = g_file_get_uri (full_path); + g_object_unref (full_path); + subtitle = uri_get_subtitle_for_uri (full_path_str); + g_free (full_path_str); + + return subtitle; +} + +char * +uri_get_subtitle_uri (const char *uri) +{ + GFile *file; + char *subtitle; + + if (g_str_has_prefix (uri, "http") != FALSE) + return NULL; + + /* Has the user specified a subtitle file manually? */ + if (strstr (uri, "#subtitle:") != NULL) + return NULL; + + /* Does the file exist? */ + file = g_file_new_for_uri (uri); + if (g_file_query_exists (file, NULL) != TRUE) { + g_object_unref (file); + return NULL; + } + + /* Try in the current directory */ + subtitle = uri_get_subtitle_for_uri (uri); + if (subtitle != NULL) { + g_object_unref (file); + return subtitle; + } + + subtitle = uri_get_subtitle_in_subdir (file, "subtitles"); + g_object_unref (file); + + return subtitle; +} + +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-utils.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-utils.h new file mode 100644 index 0000000..fd3f44c --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-utils.h @@ -0,0 +1,36 @@ +/* + * 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); + +char *uri_get_subtitle_uri(const char *uri); + +G_END_DECLS +#endif +/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */ diff --git a/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c new file mode 100644 index 0000000..31aa22b --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.c @@ -0,0 +1,710 @@ +/* + * 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 +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + || wvolume->pending_operation_mute != mute +#endif + ))) { + 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); + } + } +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + 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); + } + } +#endif + + 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_volume != wvolume->current_volume +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + || wvolume->pulse_mute != wvolume->current_mute +#endif + ) { + + 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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker-volume.h new file mode 100644 index 0000000..c1e860e --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker.c new file mode 100644 index 0000000..2404608 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker.c @@ -0,0 +1,2464 @@ +/* + * 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" +#include "keypad.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) + +#define _current_metadata_add(worker, key, type, value) \ + do { \ + if (!worker->current_metadata) \ + worker->current_metadata = mafw_metadata_new(); \ + /* At first remove old value */ \ + g_hash_table_remove(worker->current_metadata, key); \ + mafw_metadata_add_something(worker->current_metadata, \ + key, type, 1, value); \ + } while (0) + +/* 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); + +/* Playlist parsing */ +static void _on_pl_entry_parsed(TotemPlParser *parser, gchar *uri, + gpointer metadata, GSList **plitems) +{ + if (uri != NULL) { + *plitems = g_slist_append(*plitems, g_strdup(uri)); + } +} +static GSList *_parse_playlist(const gchar *uri) +{ + static TotemPlParser *pl_parser = NULL; + GSList *plitems = NULL; + gulong handler_id; + + /* Initialize the playlist parser */ + if (!pl_parser) + { + pl_parser = totem_pl_parser_new (); + g_object_set(pl_parser, "recurse", TRUE, "disable-unsafe", + TRUE, NULL); + } + handler_id = g_signal_connect(G_OBJECT(pl_parser), "entry-parsed", + G_CALLBACK(_on_pl_entry_parsed), &plitems); + /* Parsing */ + if (totem_pl_parser_parse(pl_parser, uri, FALSE) != + TOTEM_PL_PARSER_RESULT_SUCCESS) { + /* An error happens while parsing */ + + } + g_signal_handler_disconnect(pl_parser, handler_id); + return plitems; +} + +/* + * 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, + (gchar*)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); + } + } + g_object_unref(loader); + } + } +} +#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) +{ + if (worker->ready_timeout != 0) { + g_debug("removing timeout for READY"); + 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, p_height, p_fps; + + p_width = width; + p_height = height; + p_fps = fps; + + _current_metadata_add(worker, MAFW_METADATA_KEY_RES_X, G_TYPE_INT, + p_width); + _current_metadata_add(worker, MAFW_METADATA_KEY_RES_Y, G_TYPE_INT, + p_height); + _current_metadata_add(worker, MAFW_METADATA_KEY_VIDEO_FRAMERATE, + G_TYPE_DOUBLE, + 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")); + gst_message_unref (msg); + 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); + if (worker->colorkey == -1) + g_object_get(worker->vsink, + "colorkey", &worker->colorkey, NULL); + else + mafw_gst_renderer_worker_set_colorkey( + worker, + worker->colorkey); + /* 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"))); + } + gst_message_unref (msg); + return GST_BUS_DROP; + } + /* do not unref message when returning PASS */ + 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)) { + /* Add the duration to the current metadata. */ + _current_metadata_add(worker, MAFW_METADATA_KEY_DURATION, + G_TYPE_INT64, + (gint64)duration_seconds); + /* Emit the duration. */ + mafw_renderer_emit_metadata_int64( + worker->owner, MAFW_METADATA_KEY_DURATION, + (gint64)duration_seconds); + } + + /* We compare this duration we just got with the + * source one and update it in the source if needed */ + if (duration_seconds > 0 && + 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 = (seekable == SEEKABILITY_SEEKABLE); + + /* Add the seekability to the current metadata. */ + _current_metadata_add(worker, MAFW_METADATA_KEY_IS_SEEKABLE, + G_TYPE_BOOLEAN, is_seekable); + + /* Emit. */ + mafw_renderer_emit_metadata_boolean( + worker->owner, MAFW_METADATA_KEY_IS_SEEKABLE, + 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(); + } + keypadlocking_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 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_STRING, + utf8); + g_value_array_append(values, &utf8gval); + g_value_unset(&utf8gval); + } + g_free(orig); + } else if (type == G_TYPE_UINT) { + GValue intgval = {0}; + gint intval; + + g_value_init(&intgval, G_TYPE_INT); + g_value_transform(v, &intgval); + intval = g_value_get_int(&intgval); + _current_metadata_add(worker, mafwtag, G_TYPE_INT, + intval); + g_value_array_append(values, &intgval); + g_value_unset(&intgval); + } else { + _current_metadata_add(worker, mafwtag, G_TYPE_VALUE, + 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 (percent < 100 && 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 */ + + /* Set the pipeline to playing. This is an async + handler, it could be, that the reported state + is not the real-current state */ + 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); + } + 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) { + if (err->domain == GST_STREAM_ERROR && + err->code == GST_STREAM_ERROR_WRONG_TYPE) + {/* Maybe it is a playlist? */ + GSList *plitems = _parse_playlist(worker->media.location); + + if (plitems) + {/* Yes, it is a plitem */ + g_error_free(err); + mafw_gst_renderer_worker_play(worker, NULL, plitems); + break; + } + + + } + _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); +} + +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + +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); +} + +#endif + +/* 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; + char *autoload_sub = NULL; + + g_assert(worker->pipeline); + g_object_set(G_OBJECT(worker->pipeline), + "uri", worker->media.location, NULL); + + if (worker->subtitles.enabled) { + autoload_sub = uri_get_subtitle_uri(worker->media.location); + if (autoload_sub) { + g_debug("SUBURI: %s", autoload_sub); + g_object_set(G_OBJECT(worker->pipeline), + "suburi", autoload_sub, + "subtitle-font-desc", worker->subtitles.font, + "subtitle-encoding", worker->subtitles.encoding, + NULL); + + gst_element_set_state(worker->pipeline, GST_STATE_READY); + g_free(autoload_sub); + } + } else { + g_object_set(G_OBJECT(worker->pipeline), "suburi", NULL, 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, + "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 + + + /* 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, + "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, + "force-aspect-ratio", TRUE, + NULL); + } + g_object_set(worker->pipeline, + "video-sink", worker->vsink, + "flags", 103, + NULL); + + if (!worker->tsink) { + worker->tsink = gst_element_factory_make("textoverlay", NULL); + if (!worker->tsink) { + g_critical("Failed to create pipeline text sink"); + g_signal_emit_by_name(MAFW_EXTENSION (worker->owner), + "error", + MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_UNABLE_TO_PERFORM, + "Could not create text sink"); + g_assert_not_reached(); + } + gst_object_ref(worker->tsink); + } + g_object_set(worker->pipeline, "text-sink", worker->tsink, 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", + worker->autopaint, NULL); +} + +gint mafw_gst_renderer_worker_get_colorkey( + MafwGstRendererWorker *worker) +{ + return worker->colorkey; +} + +void mafw_gst_renderer_worker_set_colorkey( + MafwGstRendererWorker *worker, gint colorkey) +{ + worker->colorkey = colorkey; + if (worker->vsink) + g_object_set(worker->vsink, "colorkey", + worker->colorkey, NULL); +} + +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 _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, GSList *plitems) +{ + g_assert(uri || plitems); + + 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 (plitems || uri_is_playlist(uri)){ + gchar *item; + /* In case of a playlist we parse it and start playing the first + item of the playlist. */ + if (plitems) + { + worker->pl.items = plitems; + } + else + { + worker->pl.items = _parse_playlist(uri); + } + if (!worker->pl.items) + { + _send_error(worker, + g_error_new(MAFW_RENDERER_ERROR, + MAFW_RENDERER_ERROR_PLAYLIST_PARSING, + "Playlist parsing failed: %s", + 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); + } 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(); + keypadlocking_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(); + keypadlocking_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); +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + _mute_cb(wvolume, mute, worker); +#endif +} + +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->tsink = NULL; + worker->tag_list = NULL; + worker->current_metadata = NULL; + worker->subtitles.enabled = FALSE; + worker->subtitles.font = NULL; + worker->subtitles.encoding = 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, +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + _mute_cb, +#else + NULL, +#endif + 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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker.h new file mode 100644 index 0000000..8168a1b --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer-worker.h @@ -0,0 +1,222 @@ +/* + * 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 + * subtitles: Configuration of subtitles. + * enabled: Are subtitles enabled + * font: Subtitles font description + * encoding: Subtitles encoding + * 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 + * tsink: Text 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; + struct { + gboolean enabled; + gchar *font; + gchar *encoding; + } subtitles; + 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; + GstElement *tsink; + 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); +void mafw_gst_renderer_worker_set_colorkey(MafwGstRendererWorker *worker, gint autopaint); +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, GSList *plitems); +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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.c new file mode 100644 index 0000000..e92ea63 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.c @@ -0,0 +1,2320 @@ +/* + * 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 GCONF_MAFW_GST_SUBTITLES_RENDERER "/system/mafw/mafw-gst-subtitles-renderer" +#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); + +static void _autoload_subtitles_changed_cb(GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + MafwGstRenderer *renderer); + +static void _subtitle_font_changed_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", "dsp", + "pulseaudio", "xvimagesink", 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); +#ifdef MAFW_GST_RENDERER_ENABLE_MUTE + mafw_extension_add_property(MAFW_EXTENSION(self), "mute", G_TYPE_BOOLEAN); +#endif + 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); + } + + gconf_client_add_dir(renderer->gconf_client, + GCONF_MAFW_GST_SUBTITLES_RENDERER, + 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_MAFW_GST_SUBTITLES_RENDERER "/autoload_subtitles", + (GConfClientNotifyFunc) _autoload_subtitles_changed_cb, + renderer, + NULL, &error); + if (error) { + g_warning("%s", error->message); + g_error_free(error); + } + + gconf_client_notify_add(renderer->gconf_client, + GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_encoding", + (GConfClientNotifyFunc) _subtitle_font_changed_cb, + renderer, + NULL, &error); + if (error) { + g_warning("%s", error->message); + g_error_free(error); + } + + gconf_client_notify_add(renderer->gconf_client, + GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_font", + (GConfClientNotifyFunc) _subtitle_font_changed_cb, + renderer, + NULL, &error); + if (error) { + g_warning("%s", error->message); + g_error_free(error); + } + + if (self->worker->pipeline) { + gconf_client_notify(renderer->gconf_client, + GCONF_MAFW_GST_SUBTITLES_RENDERER "/autoload_subtitles"); + + gconf_client_notify(renderer->gconf_client, + GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_encoding"); + + gconf_client_notify(renderer->gconf_client, + GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_font"); + } + + 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); + } +} + +static void _autoload_subtitles_changed_cb(GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + MafwGstRenderer *renderer) +{ + GConfValue *value = NULL; + gboolean enabled = FALSE; + + value = gconf_entry_get_value(entry); + if (value == NULL) + return; + + enabled = gconf_value_get_bool(value); + + if (enabled) + renderer->worker->subtitles.enabled = TRUE; + else + renderer->worker->subtitles.enabled = FALSE; +} + +static void _subtitle_font_changed_cb(GConfClient *client, + guint cnxn_id, + GConfEntry *entry, + MafwGstRenderer *renderer) +{ + const gchar *key = NULL; + GConfValue *value = NULL; + const gchar *str_value = NULL; + + key = gconf_entry_get_key(entry); + + /* Only key without absolute path is required */ + key += strlen(GCONF_MAFW_GST_SUBTITLES_RENDERER) + 1; + + value = gconf_entry_get_value(entry); + if (value) + str_value = gconf_value_get_string(value); + else + str_value = NULL; + + if (strcmp(key, "subtitle_font") == 0) { + if (renderer->worker->subtitles.font) + g_free(renderer->worker->subtitles.font); + + if (str_value) + renderer->worker->subtitles.font = g_strdup(str_value); + else + renderer->worker->subtitles.font = NULL; + } else if (strcmp(key, "subtitle_encoding") == 0) { + if (renderer->worker->subtitles.encoding) + g_free(renderer->worker->subtitles.encoding); + + if (str_value) + renderer->worker->subtitles.encoding = g_strdup(str_value); + else + renderer->worker->subtitles.encoding = NULL; + } else { + g_warning("Wrong %s key, %s", GCONF_MAFW_GST_SUBTITLES_RENDERER, key); + } +} + +/*---------------------------------------------------------------------------- + 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)); + } + else if (!strcmp(key, MAFW_PROPERTY_RENDERER_COLORKEY)) { + mafw_gst_renderer_worker_set_colorkey( + renderer->worker, + g_value_get_int(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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.h new file mode 100644 index 0000000..0350e34 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-playlist-iterator.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-playlist-iterator.c new file mode 100644 index 0000000..4a8b869 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-playlist-iterator.h b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-playlist-iterator.h new file mode 100644 index 0000000..6e88360 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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-subtitles-renderer/mafw-gst-renderer-uninstalled.pc.in b/mafw-gst-subtitles-renderer/mafw-gst-renderer-uninstalled.pc.in new file mode 100644 index 0000000..8496267 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/po/LINGUAS b/mafw-gst-subtitles-renderer/po/LINGUAS new file mode 100644 index 0000000..461f5aa --- /dev/null +++ b/mafw-gst-subtitles-renderer/po/LINGUAS @@ -0,0 +1,8 @@ +# Please keep this list sorted alphabetically +# +cs +da +de +fi +hu +sk diff --git a/mafw-gst-subtitles-renderer/po/POTFILES.in b/mafw-gst-subtitles-renderer/po/POTFILES.in new file mode 100644 index 0000000..1cb7f5b --- /dev/null +++ b/mafw-gst-subtitles-renderer/po/POTFILES.in @@ -0,0 +1,4 @@ +# List of source files containing translatable strings. + +applet/cpmpsubtitles.c +applet/cpmpsubtitles.desktop diff --git a/mafw-gst-subtitles-renderer/po/cs.po b/mafw-gst-subtitles-renderer/po/cs.po new file mode 100644 index 0000000..323562c --- /dev/null +++ b/mafw-gst-subtitles-renderer/po/cs.po @@ -0,0 +1,189 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Roman Moravčík , 2010. +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: mafw-gst-subtitles-renderer\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-01-18 15:46+0100\n" +"PO-Revision-Date: 2010-01-19 09:56+0100\n" +"Last-Translator: Roman Moravčík \n" +"Language-Team: Czech <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../applet/cpmpsubtitles.c:59 +msgid "Regular" +msgstr "" + +#: ../applet/cpmpsubtitles.c:60 +msgid "Italic" +msgstr "Kurzíva" + +#: ../applet/cpmpsubtitles.c:61 +msgid "Bold" +msgstr "Tučné" + +#: ../applet/cpmpsubtitles.c:62 +msgid "Italic Bold" +msgstr "Tučné kurzíva" + +#: ../applet/cpmpsubtitles.c:187 +msgid "Current Locale" +msgstr "Aktuální lokalizace" + +#: ../applet/cpmpsubtitles.c:189 ../applet/cpmpsubtitles.c:190 +#: ../applet/cpmpsubtitles.c:191 ../applet/cpmpsubtitles.c:192 +msgid "Arabic" +msgstr "Arabské" + +#: ../applet/cpmpsubtitles.c:194 +msgid "Armenian" +msgstr "Arménské" + +#: ../applet/cpmpsubtitles.c:196 ../applet/cpmpsubtitles.c:197 +#: ../applet/cpmpsubtitles.c:198 +msgid "Baltic" +msgstr "Baltské" + +#: ../applet/cpmpsubtitles.c:200 +msgid "Celtic" +msgstr "Keltské" + +#: ../applet/cpmpsubtitles.c:202 ../applet/cpmpsubtitles.c:203 +#: ../applet/cpmpsubtitles.c:204 ../applet/cpmpsubtitles.c:205 +msgid "Central European" +msgstr "Střední Evropa" + +#: ../applet/cpmpsubtitles.c:207 ../applet/cpmpsubtitles.c:208 +#: ../applet/cpmpsubtitles.c:209 ../applet/cpmpsubtitles.c:210 +msgid "Chinese Simplified" +msgstr "ZjednoduÅ¡ené čínské" + +#: ../applet/cpmpsubtitles.c:212 ../applet/cpmpsubtitles.c:213 +#: ../applet/cpmpsubtitles.c:214 +msgid "Chinese Traditional" +msgstr "Tradiční čínské" + +#: ../applet/cpmpsubtitles.c:216 +msgid "Croatian" +msgstr "Chorvatské" + +#: ../applet/cpmpsubtitles.c:218 ../applet/cpmpsubtitles.c:219 +#: ../applet/cpmpsubtitles.c:220 ../applet/cpmpsubtitles.c:221 +#: ../applet/cpmpsubtitles.c:222 ../applet/cpmpsubtitles.c:223 +msgid "Cyrillic" +msgstr "Cyrilice" + +#: ../applet/cpmpsubtitles.c:225 +msgid "Cyrillic/Russian" +msgstr "Cyrilice/ruské" + +#: ../applet/cpmpsubtitles.c:227 ../applet/cpmpsubtitles.c:228 +msgid "Cyrillic/Ukrainian" +msgstr "Cyrilice/ukrajinské" + +#: ../applet/cpmpsubtitles.c:230 +msgid "Georgian" +msgstr "Gruzínské" + +#: ../applet/cpmpsubtitles.c:232 ../applet/cpmpsubtitles.c:233 +#: ../applet/cpmpsubtitles.c:234 +msgid "Greek" +msgstr "Řecké" + +#: ../applet/cpmpsubtitles.c:236 +msgid "Gujarati" +msgstr "Gudžarátské" + +#: ../applet/cpmpsubtitles.c:238 +msgid "Gurmukhi" +msgstr "Gurmuchské" + +#: ../applet/cpmpsubtitles.c:240 ../applet/cpmpsubtitles.c:241 +#: ../applet/cpmpsubtitles.c:242 ../applet/cpmpsubtitles.c:243 +msgid "Hebrew" +msgstr "Hebrejské" + +#: ../applet/cpmpsubtitles.c:245 +msgid "Hebrew Visual" +msgstr "Hebrejské vizuální" + +#: ../applet/cpmpsubtitles.c:247 +msgid "Hindi" +msgstr "Hindské" + +#: ../applet/cpmpsubtitles.c:249 +msgid "Icelandic" +msgstr "Islandské" + +#: ../applet/cpmpsubtitles.c:251 ../applet/cpmpsubtitles.c:252 +#: ../applet/cpmpsubtitles.c:253 +msgid "Japanese" +msgstr "Japonské" + +#: ../applet/cpmpsubtitles.c:255 ../applet/cpmpsubtitles.c:256 +#: ../applet/cpmpsubtitles.c:257 ../applet/cpmpsubtitles.c:258 +msgid "Korean" +msgstr "Korejské" + +#: ../applet/cpmpsubtitles.c:260 +msgid "Nordic" +msgstr "Nordické" + +#: ../applet/cpmpsubtitles.c:262 +msgid "Persian" +msgstr "Perské" + +#: ../applet/cpmpsubtitles.c:264 ../applet/cpmpsubtitles.c:265 +msgid "Romanian" +msgstr "Rumunské" + +#: ../applet/cpmpsubtitles.c:267 +msgid "South European" +msgstr "Jihoevropské" + +#: ../applet/cpmpsubtitles.c:269 +msgid "Thai" +msgstr "Thajské" + +#: ../applet/cpmpsubtitles.c:271 ../applet/cpmpsubtitles.c:272 +#: ../applet/cpmpsubtitles.c:273 ../applet/cpmpsubtitles.c:274 +msgid "Turkish" +msgstr "Turecké" + +#: ../applet/cpmpsubtitles.c:276 ../applet/cpmpsubtitles.c:277 +#: ../applet/cpmpsubtitles.c:278 ../applet/cpmpsubtitles.c:279 +#: ../applet/cpmpsubtitles.c:280 +msgid "Unicode" +msgstr "Unicode" + +#: ../applet/cpmpsubtitles.c:282 ../applet/cpmpsubtitles.c:283 +#: ../applet/cpmpsubtitles.c:284 ../applet/cpmpsubtitles.c:285 +#: ../applet/cpmpsubtitles.c:286 +msgid "Western" +msgstr "Západní" + +#: ../applet/cpmpsubtitles.c:288 ../applet/cpmpsubtitles.c:289 +#: ../applet/cpmpsubtitles.c:290 +msgid "Vietnamese" +msgstr "Vietnamské" + +#: ../applet/cpmpsubtitles.c:427 ../applet/cpmpsubtitles.c:622 +msgid "Font" +msgstr "Písmo" + +#: ../applet/cpmpsubtitles.c:593 +msgid "Automatically load subtitle files" +msgstr "Automaticky nahrát titulky" + +#: ../applet/cpmpsubtitles.c:659 +msgid "Encoding" +msgstr "Kódování" + +#: ../applet/cpmpsubtitles.c:724 +msgid "Subtitles" +msgstr "Titulky" diff --git a/mafw-gst-subtitles-renderer/po/da.po b/mafw-gst-subtitles-renderer/po/da.po new file mode 100644 index 0000000..2fb53e1 --- /dev/null +++ b/mafw-gst-subtitles-renderer/po/da.po @@ -0,0 +1,190 @@ +# Danish translation of MAFW. +# Copyright (C) 2010 MAFW & Joe Hansen. +# This file is distributed under the same license as the MAFW package. +# Joe Hansen , 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: Development\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-02-01 04:03+0000\n" +"PO-Revision-Date: 2010-02-01 17:30+01:00\n" +"Last-Translator: Joe Hansen \n" +"Language-Team: Danish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../applet/cpmpsubtitles.c:59 +msgid "Regular" +msgstr "Normal" + +#: ../applet/cpmpsubtitles.c:60 +msgid "Italic" +msgstr "Kursiv" + +#: ../applet/cpmpsubtitles.c:61 +msgid "Bold" +msgstr "Fed" + +#: ../applet/cpmpsubtitles.c:62 +msgid "Italic Bold" +msgstr "Kursiv og fed" + +#: ../applet/cpmpsubtitles.c:187 ../applet/cpmpsubtitles.c:188 +#: ../applet/cpmpsubtitles.c:189 ../applet/cpmpsubtitles.c:190 +msgid "Arabic" +msgstr "Arabisk" + +#: ../applet/cpmpsubtitles.c:192 +msgid "Armenian" +msgstr "Armensk" + +#: ../applet/cpmpsubtitles.c:194 ../applet/cpmpsubtitles.c:195 +#: ../applet/cpmpsubtitles.c:196 +msgid "Baltic" +msgstr "Baltisk" + +#: ../applet/cpmpsubtitles.c:198 +msgid "Celtic" +msgstr "Keltisk" + +#: ../applet/cpmpsubtitles.c:200 ../applet/cpmpsubtitles.c:201 +#: ../applet/cpmpsubtitles.c:202 ../applet/cpmpsubtitles.c:203 +msgid "Central European" +msgstr "Centraleuropæisk" + +#: ../applet/cpmpsubtitles.c:205 ../applet/cpmpsubtitles.c:206 +#: ../applet/cpmpsubtitles.c:207 ../applet/cpmpsubtitles.c:208 +msgid "Chinese Simplified" +msgstr "Forenklet kinesisk" + +#: ../applet/cpmpsubtitles.c:210 ../applet/cpmpsubtitles.c:211 +#: ../applet/cpmpsubtitles.c:212 +msgid "Chinese Traditional" +msgstr "Traditionel kinesisk" + +#: ../applet/cpmpsubtitles.c:214 +msgid "Croatian" +msgstr "Kroatisk" + +#: ../applet/cpmpsubtitles.c:216 ../applet/cpmpsubtitles.c:217 +#: ../applet/cpmpsubtitles.c:218 ../applet/cpmpsubtitles.c:219 +#: ../applet/cpmpsubtitles.c:220 ../applet/cpmpsubtitles.c:221 +msgid "Cyrillic" +msgstr "Kyrillisk" + +#: ../applet/cpmpsubtitles.c:223 +msgid "Cyrillic/Russian" +msgstr "Kyrillisk/russisk" + +#: ../applet/cpmpsubtitles.c:225 ../applet/cpmpsubtitles.c:226 +msgid "Cyrillic/Ukrainian" +msgstr "Kyrillisk/ukrainsk" + +#: ../applet/cpmpsubtitles.c:228 +msgid "Georgian" +msgstr "Georgisk" + +#: ../applet/cpmpsubtitles.c:230 ../applet/cpmpsubtitles.c:231 +#: ../applet/cpmpsubtitles.c:232 +msgid "Greek" +msgstr "Græsk" + +#: ../applet/cpmpsubtitles.c:234 +msgid "Gujarati" +msgstr "Gujarati" + +#: ../applet/cpmpsubtitles.c:236 +msgid "Gurmukhi" +msgstr "Gurmukhi" + +#: ../applet/cpmpsubtitles.c:238 ../applet/cpmpsubtitles.c:239 +#: ../applet/cpmpsubtitles.c:240 ../applet/cpmpsubtitles.c:241 +msgid "Hebrew" +msgstr "Hebraisk" + +#: ../applet/cpmpsubtitles.c:243 +msgid "Hebrew Visual" +msgstr "Visuelt hebraisk" + +#: ../applet/cpmpsubtitles.c:245 +msgid "Hindi" +msgstr "Hindi" + +#: ../applet/cpmpsubtitles.c:247 +msgid "Icelandic" +msgstr "Islandsk" + +#: ../applet/cpmpsubtitles.c:249 ../applet/cpmpsubtitles.c:250 +#: ../applet/cpmpsubtitles.c:251 +msgid "Japanese" +msgstr "Japansk" + +#: ../applet/cpmpsubtitles.c:253 ../applet/cpmpsubtitles.c:254 +#: ../applet/cpmpsubtitles.c:255 ../applet/cpmpsubtitles.c:256 +msgid "Korean" +msgstr "Koreansk" + +#: ../applet/cpmpsubtitles.c:258 +msgid "Nordic" +msgstr "Nordisk" + +#: ../applet/cpmpsubtitles.c:260 +msgid "Persian" +msgstr "Iransk" + +#: ../applet/cpmpsubtitles.c:262 ../applet/cpmpsubtitles.c:263 +msgid "Romanian" +msgstr "Rumænsk" + +#: ../applet/cpmpsubtitles.c:265 +msgid "South European" +msgstr "Sydeuropæisk" + +#: ../applet/cpmpsubtitles.c:267 +msgid "Thai" +msgstr "Thai" + +#: ../applet/cpmpsubtitles.c:269 ../applet/cpmpsubtitles.c:270 +#: ../applet/cpmpsubtitles.c:271 ../applet/cpmpsubtitles.c:272 +msgid "Turkish" +msgstr "Tyrkisk" + +#: ../applet/cpmpsubtitles.c:274 ../applet/cpmpsubtitles.c:275 +#: ../applet/cpmpsubtitles.c:276 ../applet/cpmpsubtitles.c:277 +#: ../applet/cpmpsubtitles.c:278 +msgid "Unicode" +msgstr "Unicode" + +#: ../applet/cpmpsubtitles.c:280 ../applet/cpmpsubtitles.c:281 +#: ../applet/cpmpsubtitles.c:282 ../applet/cpmpsubtitles.c:283 +#: ../applet/cpmpsubtitles.c:284 +msgid "Western" +msgstr "Vestligt" + +#: ../applet/cpmpsubtitles.c:286 ../applet/cpmpsubtitles.c:287 +#: ../applet/cpmpsubtitles.c:288 +msgid "Vietnamese" +msgstr "Vietnamesisk" + +#: ../applet/cpmpsubtitles.c:290 +msgid "Current Locale" +msgstr "Nuværende sprog" + +#: ../applet/cpmpsubtitles.c:432 ../applet/cpmpsubtitles.c:631 +msgid "Font" +msgstr "Skrifttype" + +#: ../applet/cpmpsubtitles.c:602 +msgid "Automatically load subtitle files" +msgstr "Indlæs automatisk undertekstfiler" + +# Det skal vel ikke være kodning her, men sprog? +#: ../applet/cpmpsubtitles.c:668 +msgid "Encoding" +msgstr "Sprog" + +#: ../applet/cpmpsubtitles.c:733 +msgid "Subtitles" +msgstr "Undertekster" diff --git a/mafw-gst-subtitles-renderer/po/de.po b/mafw-gst-subtitles-renderer/po/de.po new file mode 100644 index 0000000..75ccce0 --- /dev/null +++ b/mafw-gst-subtitles-renderer/po/de.po @@ -0,0 +1,219 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: mafw-gst-subtitles-render\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-01-20 14:17+0000\n" +"PO-Revision-Date: 2010-01-20 19:01+0100\n" +"Last-Translator: Philipp Zabel \n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: German\n" +"X-Poedit-Country: GERMANY\n" + +#: ../applet/cpmpsubtitles.c:59 +msgid "Regular" +msgstr "Normal" + +#: ../applet/cpmpsubtitles.c:60 +msgid "Italic" +msgstr "Kursiv" + +#: ../applet/cpmpsubtitles.c:61 +msgid "Bold" +msgstr "Fett" + +#: ../applet/cpmpsubtitles.c:62 +msgid "Italic Bold" +msgstr "Fett Kursiv" + +#: ../applet/cpmpsubtitles.c:187 +#: ../applet/cpmpsubtitles.c:188 +#: ../applet/cpmpsubtitles.c:189 +#: ../applet/cpmpsubtitles.c:190 +msgid "Arabic" +msgstr "Arabisch" + +#: ../applet/cpmpsubtitles.c:192 +msgid "Armenian" +msgstr "Armenisch" + +#: ../applet/cpmpsubtitles.c:194 +#: ../applet/cpmpsubtitles.c:195 +#: ../applet/cpmpsubtitles.c:196 +msgid "Baltic" +msgstr "Baltisch" + +#: ../applet/cpmpsubtitles.c:198 +msgid "Celtic" +msgstr "Keltisch" + +#: ../applet/cpmpsubtitles.c:200 +#: ../applet/cpmpsubtitles.c:201 +#: ../applet/cpmpsubtitles.c:202 +#: ../applet/cpmpsubtitles.c:203 +msgid "Central European" +msgstr "Zentraleuropäisch" + +#: ../applet/cpmpsubtitles.c:205 +#: ../applet/cpmpsubtitles.c:206 +#: ../applet/cpmpsubtitles.c:207 +#: ../applet/cpmpsubtitles.c:208 +msgid "Chinese Simplified" +msgstr "Vereinfachtes Chinesisch" + +#: ../applet/cpmpsubtitles.c:210 +#: ../applet/cpmpsubtitles.c:211 +#: ../applet/cpmpsubtitles.c:212 +msgid "Chinese Traditional" +msgstr "Traditionelles Chinesisch" + +#: ../applet/cpmpsubtitles.c:214 +msgid "Croatian" +msgstr "Kroatisch" + +#: ../applet/cpmpsubtitles.c:216 +#: ../applet/cpmpsubtitles.c:217 +#: ../applet/cpmpsubtitles.c:218 +#: ../applet/cpmpsubtitles.c:219 +#: ../applet/cpmpsubtitles.c:220 +#: ../applet/cpmpsubtitles.c:221 +msgid "Cyrillic" +msgstr "Kyrillisch" + +#: ../applet/cpmpsubtitles.c:223 +msgid "Cyrillic/Russian" +msgstr "Kyrillisch/Russisch" + +#: ../applet/cpmpsubtitles.c:225 +#: ../applet/cpmpsubtitles.c:226 +msgid "Cyrillic/Ukrainian" +msgstr "Kyrillisch/Ukrainisch" + +#: ../applet/cpmpsubtitles.c:228 +msgid "Georgian" +msgstr "Georgisch" + +#: ../applet/cpmpsubtitles.c:230 +#: ../applet/cpmpsubtitles.c:231 +#: ../applet/cpmpsubtitles.c:232 +msgid "Greek" +msgstr "Griechisch" + +#: ../applet/cpmpsubtitles.c:234 +msgid "Gujarati" +msgstr "Gujarati" + +#: ../applet/cpmpsubtitles.c:236 +msgid "Gurmukhi" +msgstr "Gurmukhi" + +#: ../applet/cpmpsubtitles.c:238 +#: ../applet/cpmpsubtitles.c:239 +#: ../applet/cpmpsubtitles.c:240 +#: ../applet/cpmpsubtitles.c:241 +msgid "Hebrew" +msgstr "Hebräisch" + +#: ../applet/cpmpsubtitles.c:243 +msgid "Hebrew Visual" +msgstr "Visuelles Hebräisch" + +#: ../applet/cpmpsubtitles.c:245 +msgid "Hindi" +msgstr "Hindi" + +#: ../applet/cpmpsubtitles.c:247 +msgid "Icelandic" +msgstr "Isländisch" + +#: ../applet/cpmpsubtitles.c:249 +#: ../applet/cpmpsubtitles.c:250 +#: ../applet/cpmpsubtitles.c:251 +msgid "Japanese" +msgstr "Japanisch" + +#: ../applet/cpmpsubtitles.c:253 +#: ../applet/cpmpsubtitles.c:254 +#: ../applet/cpmpsubtitles.c:255 +#: ../applet/cpmpsubtitles.c:256 +msgid "Korean" +msgstr "Koreanisch" + +#: ../applet/cpmpsubtitles.c:258 +msgid "Nordic" +msgstr "Nordisch" + +#: ../applet/cpmpsubtitles.c:260 +msgid "Persian" +msgstr "Persisch" + +#: ../applet/cpmpsubtitles.c:262 +#: ../applet/cpmpsubtitles.c:263 +msgid "Romanian" +msgstr "Rumänisch" + +#: ../applet/cpmpsubtitles.c:265 +msgid "South European" +msgstr "Südeuropäisch" + +#: ../applet/cpmpsubtitles.c:267 +msgid "Thai" +msgstr "Thai" + +#: ../applet/cpmpsubtitles.c:269 +#: ../applet/cpmpsubtitles.c:270 +#: ../applet/cpmpsubtitles.c:271 +#: ../applet/cpmpsubtitles.c:272 +msgid "Turkish" +msgstr "Türkisch" + +#: ../applet/cpmpsubtitles.c:274 +#: ../applet/cpmpsubtitles.c:275 +#: ../applet/cpmpsubtitles.c:276 +#: ../applet/cpmpsubtitles.c:277 +#: ../applet/cpmpsubtitles.c:278 +msgid "Unicode" +msgstr "Unicode" + +#: ../applet/cpmpsubtitles.c:280 +#: ../applet/cpmpsubtitles.c:281 +#: ../applet/cpmpsubtitles.c:282 +#: ../applet/cpmpsubtitles.c:283 +#: ../applet/cpmpsubtitles.c:284 +msgid "Western" +msgstr "Westlich" + +#: ../applet/cpmpsubtitles.c:286 +#: ../applet/cpmpsubtitles.c:287 +#: ../applet/cpmpsubtitles.c:288 +msgid "Vietnamese" +msgstr "Vietnamesisch" + +#: ../applet/cpmpsubtitles.c:290 +msgid "Current Locale" +msgstr "Aktuelle Sprache/Region" + +#: ../applet/cpmpsubtitles.c:432 +#: ../applet/cpmpsubtitles.c:631 +msgid "Font" +msgstr "Schrift" + +#: ../applet/cpmpsubtitles.c:602 +msgid "Automatically load subtitle files" +msgstr "Untertiteldateien automatisch laden" + +#: ../applet/cpmpsubtitles.c:668 +msgid "Encoding" +msgstr "Codierung" + +#: ../applet/cpmpsubtitles.c:733 +msgid "Subtitles" +msgstr "Untertitel" + diff --git a/mafw-gst-subtitles-renderer/po/fi.po b/mafw-gst-subtitles-renderer/po/fi.po new file mode 100644 index 0000000..29d6309 --- /dev/null +++ b/mafw-gst-subtitles-renderer/po/fi.po @@ -0,0 +1,193 @@ +# This is an fi_FI "translation" file for MAFW Gstreamer renderer with subtitles +# support. To translate this to another language, first copy this file to a +# filename with the appropriate language/country code, then modify the "msgstr" +# strings with the correct translation. +# Copyright (C) 2010, Marko Vertainen +# This file is distributed under the same license as the MAFW Gstreamer renderer +# with subtitles support package. +# FIRST AUTHOR , 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-04-21 04:04+0000\n" +"PO-Revision-Date: 2010-02-02 16:46+0200\n" +"Last-Translator: Marko Vertainen \n" +"Language-Team: FI \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../applet/cpmpsubtitles.c:59 +msgid "Regular" +msgstr "Tavallinen" + +#: ../applet/cpmpsubtitles.c:60 +msgid "Italic" +msgstr "Kursivoitu" + +#: ../applet/cpmpsubtitles.c:61 +msgid "Bold" +msgstr "Lihavoitu" + +#: ../applet/cpmpsubtitles.c:62 +msgid "Italic Bold" +msgstr "Lihavoitu kursiivi" + +#: ../applet/cpmpsubtitles.c:187 ../applet/cpmpsubtitles.c:188 +#: ../applet/cpmpsubtitles.c:189 ../applet/cpmpsubtitles.c:190 +msgid "Arabic" +msgstr "Arabialainen" + +#: ../applet/cpmpsubtitles.c:192 +msgid "Armenian" +msgstr "Armenialainen" + +#: ../applet/cpmpsubtitles.c:194 ../applet/cpmpsubtitles.c:195 +#: ../applet/cpmpsubtitles.c:196 +msgid "Baltic" +msgstr "Balttialainen" + +#: ../applet/cpmpsubtitles.c:198 +msgid "Celtic" +msgstr "Kelttiläinen" + +#: ../applet/cpmpsubtitles.c:200 ../applet/cpmpsubtitles.c:201 +#: ../applet/cpmpsubtitles.c:202 ../applet/cpmpsubtitles.c:203 +msgid "Central European" +msgstr "Keskieurooppalainen" + +#: ../applet/cpmpsubtitles.c:205 ../applet/cpmpsubtitles.c:206 +#: ../applet/cpmpsubtitles.c:207 ../applet/cpmpsubtitles.c:208 +msgid "Chinese Simplified" +msgstr "Kiinalainen, yksinkertaistettu" + +#: ../applet/cpmpsubtitles.c:210 ../applet/cpmpsubtitles.c:211 +#: ../applet/cpmpsubtitles.c:212 +msgid "Chinese Traditional" +msgstr "Kiinalainen, perinteinen" + +#: ../applet/cpmpsubtitles.c:214 +msgid "Croatian" +msgstr "Kroatialainen" + +#: ../applet/cpmpsubtitles.c:216 ../applet/cpmpsubtitles.c:217 +#: ../applet/cpmpsubtitles.c:218 ../applet/cpmpsubtitles.c:219 +#: ../applet/cpmpsubtitles.c:220 ../applet/cpmpsubtitles.c:221 +msgid "Cyrillic" +msgstr "Kyriilinen" + +#: ../applet/cpmpsubtitles.c:223 +msgid "Cyrillic/Russian" +msgstr "Kyriilinen/Venäläinen" + +#: ../applet/cpmpsubtitles.c:225 ../applet/cpmpsubtitles.c:226 +msgid "Cyrillic/Ukrainian" +msgstr "Kyriilinen/Ukrainalainen" + +#: ../applet/cpmpsubtitles.c:228 +msgid "Georgian" +msgstr "Georgialainen" + +#: ../applet/cpmpsubtitles.c:230 ../applet/cpmpsubtitles.c:231 +#: ../applet/cpmpsubtitles.c:232 +msgid "Greek" +msgstr "Kreikkalainen" + +#: ../applet/cpmpsubtitles.c:234 +msgid "Gujarati" +msgstr "Gujarati" + +#: ../applet/cpmpsubtitles.c:236 +msgid "Gurmukhi" +msgstr "Gurmukhi" + +#: ../applet/cpmpsubtitles.c:238 ../applet/cpmpsubtitles.c:239 +#: ../applet/cpmpsubtitles.c:240 ../applet/cpmpsubtitles.c:241 +msgid "Hebrew" +msgstr "Hebrealainen" + +#: ../applet/cpmpsubtitles.c:243 +msgid "Hebrew Visual" +msgstr "Hebrealainen, visuaalinen" + +#: ../applet/cpmpsubtitles.c:245 +msgid "Hindi" +msgstr "Hindilainen" + +#: ../applet/cpmpsubtitles.c:247 +msgid "Icelandic" +msgstr "Islantilainen" + +#: ../applet/cpmpsubtitles.c:249 ../applet/cpmpsubtitles.c:250 +#: ../applet/cpmpsubtitles.c:251 +msgid "Japanese" +msgstr "Japanilainen" + +#: ../applet/cpmpsubtitles.c:253 ../applet/cpmpsubtitles.c:254 +#: ../applet/cpmpsubtitles.c:255 ../applet/cpmpsubtitles.c:256 +msgid "Korean" +msgstr "Korealainen" + +#: ../applet/cpmpsubtitles.c:258 +msgid "Nordic" +msgstr "Pohjoismaalainen" + +#: ../applet/cpmpsubtitles.c:260 +msgid "Persian" +msgstr "Persialainen" + +#: ../applet/cpmpsubtitles.c:262 ../applet/cpmpsubtitles.c:263 +msgid "Romanian" +msgstr "Romanialainen" + +#: ../applet/cpmpsubtitles.c:265 +msgid "South European" +msgstr "Eteläeurooppalainen" + +#: ../applet/cpmpsubtitles.c:267 +msgid "Thai" +msgstr "Thaimaalainen" + +#: ../applet/cpmpsubtitles.c:269 ../applet/cpmpsubtitles.c:270 +#: ../applet/cpmpsubtitles.c:271 ../applet/cpmpsubtitles.c:272 +msgid "Turkish" +msgstr "Turkkilainen" + +#: ../applet/cpmpsubtitles.c:274 ../applet/cpmpsubtitles.c:275 +#: ../applet/cpmpsubtitles.c:276 ../applet/cpmpsubtitles.c:277 +#: ../applet/cpmpsubtitles.c:278 +msgid "Unicode" +msgstr "Unicode" + +#: ../applet/cpmpsubtitles.c:280 ../applet/cpmpsubtitles.c:281 +#: ../applet/cpmpsubtitles.c:282 ../applet/cpmpsubtitles.c:283 +#: ../applet/cpmpsubtitles.c:284 +msgid "Western" +msgstr "Länsimainen" + +#: ../applet/cpmpsubtitles.c:286 ../applet/cpmpsubtitles.c:287 +#: ../applet/cpmpsubtitles.c:288 +msgid "Vietnamese" +msgstr "Vietnamilainen" + +#: ../applet/cpmpsubtitles.c:290 +msgid "Current Locale" +msgstr "Nykyinen maa-asetus" + +#: ../applet/cpmpsubtitles.c:432 ../applet/cpmpsubtitles.c:631 +msgid "Font" +msgstr "Kirjasin" + +#: ../applet/cpmpsubtitles.c:602 +msgid "Automatically load subtitle files" +msgstr "Lataa tekstitystiedosto automaattisesti" + +#: ../applet/cpmpsubtitles.c:668 +msgid "Encoding" +msgstr "Merkistökoodaus" + +#: ../applet/cpmpsubtitles.c:733 +msgid "Subtitles" +msgstr "Tekstitys" diff --git a/mafw-gst-subtitles-renderer/po/hu.po b/mafw-gst-subtitles-renderer/po/hu.po new file mode 100644 index 0000000..7ebdd20 --- /dev/null +++ b/mafw-gst-subtitles-renderer/po/hu.po @@ -0,0 +1,219 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: subtitle hun loc\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-02-20 04:03+0000\n" +"PO-Revision-Date: 2010-02-21 13:47-0000\n" +"Last-Translator: Gyorgy Lakatos \n" +"Language-Team: Hungarian Translater Team \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Hungarian\n" +"X-Poedit-Country: UNITED KINGDOM\n" + +#: ../applet/cpmpsubtitles.c:59 +msgid "Regular" +msgstr "Szabályos" + +#: ../applet/cpmpsubtitles.c:60 +msgid "Italic" +msgstr "Itáliai" + +#: ../applet/cpmpsubtitles.c:61 +msgid "Bold" +msgstr "Félkövér" + +#: ../applet/cpmpsubtitles.c:62 +msgid "Italic Bold" +msgstr "Itáliai félkövér" + +#: ../applet/cpmpsubtitles.c:187 +#: ../applet/cpmpsubtitles.c:188 +#: ../applet/cpmpsubtitles.c:189 +#: ../applet/cpmpsubtitles.c:190 +msgid "Arabic" +msgstr "Arab" + +#: ../applet/cpmpsubtitles.c:192 +msgid "Armenian" +msgstr "Armenian" + +#: ../applet/cpmpsubtitles.c:194 +#: ../applet/cpmpsubtitles.c:195 +#: ../applet/cpmpsubtitles.c:196 +msgid "Baltic" +msgstr "Balti" + +#: ../applet/cpmpsubtitles.c:198 +msgid "Celtic" +msgstr "Kelta" + +#: ../applet/cpmpsubtitles.c:200 +#: ../applet/cpmpsubtitles.c:201 +#: ../applet/cpmpsubtitles.c:202 +#: ../applet/cpmpsubtitles.c:203 +msgid "Central European" +msgstr "Közép Európai" + +#: ../applet/cpmpsubtitles.c:205 +#: ../applet/cpmpsubtitles.c:206 +#: ../applet/cpmpsubtitles.c:207 +#: ../applet/cpmpsubtitles.c:208 +msgid "Chinese Simplified" +msgstr "Kínai (egyszerü)" + +#: ../applet/cpmpsubtitles.c:210 +#: ../applet/cpmpsubtitles.c:211 +#: ../applet/cpmpsubtitles.c:212 +msgid "Chinese Traditional" +msgstr "Kínai (hagyományos)" + +#: ../applet/cpmpsubtitles.c:214 +msgid "Croatian" +msgstr "Horvát" + +#: ../applet/cpmpsubtitles.c:216 +#: ../applet/cpmpsubtitles.c:217 +#: ../applet/cpmpsubtitles.c:218 +#: ../applet/cpmpsubtitles.c:219 +#: ../applet/cpmpsubtitles.c:220 +#: ../applet/cpmpsubtitles.c:221 +msgid "Cyrillic" +msgstr "Ciril" + +#: ../applet/cpmpsubtitles.c:223 +msgid "Cyrillic/Russian" +msgstr "Ciril/Orosz" + +#: ../applet/cpmpsubtitles.c:225 +#: ../applet/cpmpsubtitles.c:226 +msgid "Cyrillic/Ukrainian" +msgstr "Ciril/Ukrán" + +#: ../applet/cpmpsubtitles.c:228 +msgid "Georgian" +msgstr "Georgian" + +#: ../applet/cpmpsubtitles.c:230 +#: ../applet/cpmpsubtitles.c:231 +#: ../applet/cpmpsubtitles.c:232 +msgid "Greek" +msgstr "Görög" + +#: ../applet/cpmpsubtitles.c:234 +msgid "Gujarati" +msgstr "Gujarati" + +#: ../applet/cpmpsubtitles.c:236 +msgid "Gurmukhi" +msgstr "Gurmukhi" + +#: ../applet/cpmpsubtitles.c:238 +#: ../applet/cpmpsubtitles.c:239 +#: ../applet/cpmpsubtitles.c:240 +#: ../applet/cpmpsubtitles.c:241 +msgid "Hebrew" +msgstr "Héber" + +#: ../applet/cpmpsubtitles.c:243 +msgid "Hebrew Visual" +msgstr "Héber (hagyományos)" + +#: ../applet/cpmpsubtitles.c:245 +msgid "Hindi" +msgstr "Hindi" + +#: ../applet/cpmpsubtitles.c:247 +msgid "Icelandic" +msgstr "Izlandi" + +#: ../applet/cpmpsubtitles.c:249 +#: ../applet/cpmpsubtitles.c:250 +#: ../applet/cpmpsubtitles.c:251 +msgid "Japanese" +msgstr "Japán" + +#: ../applet/cpmpsubtitles.c:253 +#: ../applet/cpmpsubtitles.c:254 +#: ../applet/cpmpsubtitles.c:255 +#: ../applet/cpmpsubtitles.c:256 +msgid "Korean" +msgstr "Koreai" + +#: ../applet/cpmpsubtitles.c:258 +msgid "Nordic" +msgstr "Nordic" + +#: ../applet/cpmpsubtitles.c:260 +msgid "Persian" +msgstr "Perzsa" + +#: ../applet/cpmpsubtitles.c:262 +#: ../applet/cpmpsubtitles.c:263 +msgid "Romanian" +msgstr "Román" + +#: ../applet/cpmpsubtitles.c:265 +msgid "South European" +msgstr "Dél Európai" + +#: ../applet/cpmpsubtitles.c:267 +msgid "Thai" +msgstr "Thai" + +#: ../applet/cpmpsubtitles.c:269 +#: ../applet/cpmpsubtitles.c:270 +#: ../applet/cpmpsubtitles.c:271 +#: ../applet/cpmpsubtitles.c:272 +msgid "Turkish" +msgstr "Török" + +#: ../applet/cpmpsubtitles.c:274 +#: ../applet/cpmpsubtitles.c:275 +#: ../applet/cpmpsubtitles.c:276 +#: ../applet/cpmpsubtitles.c:277 +#: ../applet/cpmpsubtitles.c:278 +msgid "Unicode" +msgstr "Unicode" + +#: ../applet/cpmpsubtitles.c:280 +#: ../applet/cpmpsubtitles.c:281 +#: ../applet/cpmpsubtitles.c:282 +#: ../applet/cpmpsubtitles.c:283 +#: ../applet/cpmpsubtitles.c:284 +msgid "Western" +msgstr "Nyugati" + +#: ../applet/cpmpsubtitles.c:286 +#: ../applet/cpmpsubtitles.c:287 +#: ../applet/cpmpsubtitles.c:288 +msgid "Vietnamese" +msgstr "Vietnám" + +#: ../applet/cpmpsubtitles.c:290 +msgid "Current Locale" +msgstr "Helyi" + +#: ../applet/cpmpsubtitles.c:432 +#: ../applet/cpmpsubtitles.c:631 +msgid "Font" +msgstr "Betű méret" + +#: ../applet/cpmpsubtitles.c:602 +msgid "Automatically load subtitle files" +msgstr "Autómatikus felirat file megnyitás" + +#: ../applet/cpmpsubtitles.c:668 +msgid "Encoding" +msgstr "Kódolás" + +#: ../applet/cpmpsubtitles.c:733 +msgid "Subtitles" +msgstr "Felirat" + diff --git a/mafw-gst-subtitles-renderer/po/sk.po b/mafw-gst-subtitles-renderer/po/sk.po new file mode 100644 index 0000000..c6f2605 --- /dev/null +++ b/mafw-gst-subtitles-renderer/po/sk.po @@ -0,0 +1,189 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Roman Moravčík , 2010. +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: mafw-gst-subtitles-renderer\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-01-18 14:36+0100\n" +"PO-Revision-Date: 2010-01-19 09:52+0100\n" +"Last-Translator: Roman Moravčík \n" +"Language-Team: Slovak \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../applet/cpmpsubtitles.c:56 +msgid "Regular" +msgstr "Normálne" + +#: ../applet/cpmpsubtitles.c:57 +msgid "Italic" +msgstr "Kurzíva" + +#: ../applet/cpmpsubtitles.c:58 +msgid "Bold" +msgstr "Tučné" + +#: ../applet/cpmpsubtitles.c:59 +msgid "Italic Bold" +msgstr "Tučné kurzíva" + +#: ../applet/cpmpsubtitles.c:184 +msgid "Current Locale" +msgstr "Súčasné miestne" + +#: ../applet/cpmpsubtitles.c:186 ../applet/cpmpsubtitles.c:187 +#: ../applet/cpmpsubtitles.c:188 ../applet/cpmpsubtitles.c:189 +msgid "Arabic" +msgstr "Arabské" + +#: ../applet/cpmpsubtitles.c:191 +msgid "Armenian" +msgstr "Arménske" + +#: ../applet/cpmpsubtitles.c:193 ../applet/cpmpsubtitles.c:194 +#: ../applet/cpmpsubtitles.c:195 +msgid "Baltic" +msgstr "Baltské" + +#: ../applet/cpmpsubtitles.c:197 +msgid "Celtic" +msgstr "Keltské" + +#: ../applet/cpmpsubtitles.c:199 ../applet/cpmpsubtitles.c:200 +#: ../applet/cpmpsubtitles.c:201 ../applet/cpmpsubtitles.c:202 +msgid "Central European" +msgstr "Stredná Európa" + +#: ../applet/cpmpsubtitles.c:204 ../applet/cpmpsubtitles.c:205 +#: ../applet/cpmpsubtitles.c:206 ../applet/cpmpsubtitles.c:207 +msgid "Chinese Simplified" +msgstr "ZjednoduÅ¡ené čínske" + +#: ../applet/cpmpsubtitles.c:209 ../applet/cpmpsubtitles.c:210 +#: ../applet/cpmpsubtitles.c:211 +msgid "Chinese Traditional" +msgstr "Tradičné čínske" + +#: ../applet/cpmpsubtitles.c:213 +msgid "Croatian" +msgstr "Chorvátske" + +#: ../applet/cpmpsubtitles.c:215 ../applet/cpmpsubtitles.c:216 +#: ../applet/cpmpsubtitles.c:217 ../applet/cpmpsubtitles.c:218 +#: ../applet/cpmpsubtitles.c:219 ../applet/cpmpsubtitles.c:220 +msgid "Cyrillic" +msgstr "Cyrilika" + +#: ../applet/cpmpsubtitles.c:222 +msgid "Cyrillic/Russian" +msgstr "Cyrilika/Ruské" + +#: ../applet/cpmpsubtitles.c:224 ../applet/cpmpsubtitles.c:225 +msgid "Cyrillic/Ukrainian" +msgstr "Cyrilika/Ukrajinské" + +#: ../applet/cpmpsubtitles.c:227 +msgid "Georgian" +msgstr "Gruzínske" + +#: ../applet/cpmpsubtitles.c:229 ../applet/cpmpsubtitles.c:230 +#: ../applet/cpmpsubtitles.c:231 +msgid "Greek" +msgstr "Grécke" + +#: ../applet/cpmpsubtitles.c:233 +msgid "Gujarati" +msgstr "Gudžarátske" + +#: ../applet/cpmpsubtitles.c:235 +msgid "Gurmukhi" +msgstr "Chorvátske" + +#: ../applet/cpmpsubtitles.c:237 ../applet/cpmpsubtitles.c:238 +#: ../applet/cpmpsubtitles.c:239 ../applet/cpmpsubtitles.c:240 +msgid "Hebrew" +msgstr "Chorvátske" + +#: ../applet/cpmpsubtitles.c:242 +msgid "Hebrew Visual" +msgstr "Hebrejské vizuálne" + +#: ../applet/cpmpsubtitles.c:244 +msgid "Hindi" +msgstr "Hindustánske" + +#: ../applet/cpmpsubtitles.c:246 +msgid "Icelandic" +msgstr "Islandské" + +#: ../applet/cpmpsubtitles.c:248 ../applet/cpmpsubtitles.c:249 +#: ../applet/cpmpsubtitles.c:250 +msgid "Japanese" +msgstr "Japonské" + +#: ../applet/cpmpsubtitles.c:252 ../applet/cpmpsubtitles.c:253 +#: ../applet/cpmpsubtitles.c:254 ../applet/cpmpsubtitles.c:255 +msgid "Korean" +msgstr "Kórejské" + +#: ../applet/cpmpsubtitles.c:257 +msgid "Nordic" +msgstr "Severogermánske" + +#: ../applet/cpmpsubtitles.c:259 +msgid "Persian" +msgstr "Perzské" + +#: ../applet/cpmpsubtitles.c:261 ../applet/cpmpsubtitles.c:262 +msgid "Romanian" +msgstr "Rumunské" + +#: ../applet/cpmpsubtitles.c:264 +msgid "South European" +msgstr "Južná Európa" + +#: ../applet/cpmpsubtitles.c:266 +msgid "Thai" +msgstr "Thajské" + +#: ../applet/cpmpsubtitles.c:268 ../applet/cpmpsubtitles.c:269 +#: ../applet/cpmpsubtitles.c:270 ../applet/cpmpsubtitles.c:271 +msgid "Turkish" +msgstr "Turecké" + +#: ../applet/cpmpsubtitles.c:273 ../applet/cpmpsubtitles.c:274 +#: ../applet/cpmpsubtitles.c:275 ../applet/cpmpsubtitles.c:276 +#: ../applet/cpmpsubtitles.c:277 +msgid "Unicode" +msgstr "Unicode" + +#: ../applet/cpmpsubtitles.c:279 ../applet/cpmpsubtitles.c:280 +#: ../applet/cpmpsubtitles.c:281 ../applet/cpmpsubtitles.c:282 +#: ../applet/cpmpsubtitles.c:283 +msgid "Western" +msgstr "Západné" + +#: ../applet/cpmpsubtitles.c:285 ../applet/cpmpsubtitles.c:286 +#: ../applet/cpmpsubtitles.c:287 +msgid "Vietnamese" +msgstr "Vietnamské" + +#: ../applet/cpmpsubtitles.c:424 ../applet/cpmpsubtitles.c:584 +msgid "Font" +msgstr "Písmo" + +#: ../applet/cpmpsubtitles.c:555 +msgid "Automatically load subtitle files" +msgstr "Automaticky načítaÅ¥ súbor s titulkami" + +#: ../applet/cpmpsubtitles.c:620 +msgid "Encoding" +msgstr "Kódovanie" + +#: ../applet/cpmpsubtitles.c:685 +msgid "Subtitles" +msgstr "Titulky" diff --git a/mafw-gst-subtitles-renderer/tests/Makefile.am b/mafw-gst-subtitles-renderer/tests/Makefile.am new file mode 100644 index 0000000..a894fbf --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/tests/check-mafw-gst-renderer.c b/mafw-gst-subtitles-renderer/tests/check-mafw-gst-renderer.c new file mode 100644 index 0000000..ae4982b --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/tests/check-main.c b/mafw-gst-subtitles-renderer/tests/check-main.c new file mode 100644 index 0000000..c5c5172 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/tests/mafw-mock-playlist.c b/mafw-gst-subtitles-renderer/tests/mafw-mock-playlist.c new file mode 100644 index 0000000..0720261 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/tests/mafw-mock-playlist.h b/mafw-gst-subtitles-renderer/tests/mafw-mock-playlist.h new file mode 100644 index 0000000..dcc2da1 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/tests/mafw-mock-pulseaudio.c b/mafw-gst-subtitles-renderer/tests/mafw-mock-pulseaudio.c new file mode 100644 index 0000000..1675073 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/tests/mafw-mock-pulseaudio.h b/mafw-gst-subtitles-renderer/tests/mafw-mock-pulseaudio.h new file mode 100644 index 0000000..bfb30a7 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/tests/mafw-test-player.c b/mafw-gst-subtitles-renderer/tests/mafw-test-player.c new file mode 100644 index 0000000..a340672 --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/mafw-gst-subtitles-renderer/tests/media/test.avi b/mafw-gst-subtitles-renderer/tests/media/test.avi new file mode 100644 index 0000000000000000000000000000000000000000..f868c625c4bd780ab3e33dc5cfb83ee6a2b28049 GIT binary patch literal 86540 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%0Aun=rc|E*dFqHw3+5l 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 U+!+ndl6(7R+YIF5J-HN$3U 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 ZImh{7HFeJ+Z3dZ_$Zv+(pL7z;{{gZ!UmE}b diff --git a/tests/media/testframe.png b/tests/media/testframe.png deleted file mode 100644 index a0db335ffcc6a396393e883b44ab5aacb41c2f36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13945 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 OnWC(!OoNnJ`2Pcz^d~C- diff --git a/tests/test.suppressions b/tests/test.suppressions deleted file mode 100644 index 8d0b36b..0000000 --- a/tests/test.suppressions +++ /dev/null @@ -1,864 +0,0 @@ -{ - <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 -} -- 1.7.9.5

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 U+!+ndl6(7R+YIF5J-HN$3U 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 ZImh{7HFeJ+Z3dZ_$Zv+(pL7z;{{gZ!UmE}b literal 0 HcmV?d00001 diff --git a/mafw-gst-subtitles-renderer/tests/media/testframe.png b/mafw-gst-subtitles-renderer/tests/media/testframe.png new file mode 100644 index 0000000000000000000000000000000000000000..a0db335ffcc6a396393e883b44ab5aacb41c2f36 GIT binary patch literal 13945 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 OnWC(!OoNnJ`2Pcz^d~C- literal 0 HcmV?d00001 diff --git a/mafw-gst-subtitles-renderer/tests/test.suppressions b/mafw-gst-subtitles-renderer/tests/test.suppressions new file mode 100644 index 0000000..8d0b36b --- /dev/null +++ b/mafw-gst-subtitles-renderer/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/po/LINGUAS b/po/LINGUAS deleted file mode 100644 index 461f5aa..0000000 --- a/po/LINGUAS +++ /dev/null @@ -1,8 +0,0 @@ -# Please keep this list sorted alphabetically -# -cs -da -de -fi -hu -sk diff --git a/po/POTFILES.in b/po/POTFILES.in deleted file mode 100644 index 1cb7f5b..0000000 --- a/po/POTFILES.in +++ /dev/null @@ -1,4 +0,0 @@ -# List of source files containing translatable strings. - -applet/cpmpsubtitles.c -applet/cpmpsubtitles.desktop diff --git a/po/cs.po b/po/cs.po deleted file mode 100644 index 323562c..0000000 --- a/po/cs.po +++ /dev/null @@ -1,189 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# Roman Moravčík , 2010. -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: mafw-gst-subtitles-renderer\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-01-18 15:46+0100\n" -"PO-Revision-Date: 2010-01-19 09:56+0100\n" -"Last-Translator: Roman Moravčík \n" -"Language-Team: Czech <>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../applet/cpmpsubtitles.c:59 -msgid "Regular" -msgstr "" - -#: ../applet/cpmpsubtitles.c:60 -msgid "Italic" -msgstr "Kurzíva" - -#: ../applet/cpmpsubtitles.c:61 -msgid "Bold" -msgstr "Tučné" - -#: ../applet/cpmpsubtitles.c:62 -msgid "Italic Bold" -msgstr "Tučné kurzíva" - -#: ../applet/cpmpsubtitles.c:187 -msgid "Current Locale" -msgstr "Aktuální lokalizace" - -#: ../applet/cpmpsubtitles.c:189 ../applet/cpmpsubtitles.c:190 -#: ../applet/cpmpsubtitles.c:191 ../applet/cpmpsubtitles.c:192 -msgid "Arabic" -msgstr "Arabské" - -#: ../applet/cpmpsubtitles.c:194 -msgid "Armenian" -msgstr "Arménské" - -#: ../applet/cpmpsubtitles.c:196 ../applet/cpmpsubtitles.c:197 -#: ../applet/cpmpsubtitles.c:198 -msgid "Baltic" -msgstr "Baltské" - -#: ../applet/cpmpsubtitles.c:200 -msgid "Celtic" -msgstr "Keltské" - -#: ../applet/cpmpsubtitles.c:202 ../applet/cpmpsubtitles.c:203 -#: ../applet/cpmpsubtitles.c:204 ../applet/cpmpsubtitles.c:205 -msgid "Central European" -msgstr "Střední Evropa" - -#: ../applet/cpmpsubtitles.c:207 ../applet/cpmpsubtitles.c:208 -#: ../applet/cpmpsubtitles.c:209 ../applet/cpmpsubtitles.c:210 -msgid "Chinese Simplified" -msgstr "Zjednodušené čínské" - -#: ../applet/cpmpsubtitles.c:212 ../applet/cpmpsubtitles.c:213 -#: ../applet/cpmpsubtitles.c:214 -msgid "Chinese Traditional" -msgstr "Tradiční čínské" - -#: ../applet/cpmpsubtitles.c:216 -msgid "Croatian" -msgstr "Chorvatské" - -#: ../applet/cpmpsubtitles.c:218 ../applet/cpmpsubtitles.c:219 -#: ../applet/cpmpsubtitles.c:220 ../applet/cpmpsubtitles.c:221 -#: ../applet/cpmpsubtitles.c:222 ../applet/cpmpsubtitles.c:223 -msgid "Cyrillic" -msgstr "Cyrilice" - -#: ../applet/cpmpsubtitles.c:225 -msgid "Cyrillic/Russian" -msgstr "Cyrilice/ruské" - -#: ../applet/cpmpsubtitles.c:227 ../applet/cpmpsubtitles.c:228 -msgid "Cyrillic/Ukrainian" -msgstr "Cyrilice/ukrajinské" - -#: ../applet/cpmpsubtitles.c:230 -msgid "Georgian" -msgstr "Gruzínské" - -#: ../applet/cpmpsubtitles.c:232 ../applet/cpmpsubtitles.c:233 -#: ../applet/cpmpsubtitles.c:234 -msgid "Greek" -msgstr "Řecké" - -#: ../applet/cpmpsubtitles.c:236 -msgid "Gujarati" -msgstr "Gudžarátské" - -#: ../applet/cpmpsubtitles.c:238 -msgid "Gurmukhi" -msgstr "Gurmuchské" - -#: ../applet/cpmpsubtitles.c:240 ../applet/cpmpsubtitles.c:241 -#: ../applet/cpmpsubtitles.c:242 ../applet/cpmpsubtitles.c:243 -msgid "Hebrew" -msgstr "Hebrejské" - -#: ../applet/cpmpsubtitles.c:245 -msgid "Hebrew Visual" -msgstr "Hebrejské vizuální" - -#: ../applet/cpmpsubtitles.c:247 -msgid "Hindi" -msgstr "Hindské" - -#: ../applet/cpmpsubtitles.c:249 -msgid "Icelandic" -msgstr "Islandské" - -#: ../applet/cpmpsubtitles.c:251 ../applet/cpmpsubtitles.c:252 -#: ../applet/cpmpsubtitles.c:253 -msgid "Japanese" -msgstr "Japonské" - -#: ../applet/cpmpsubtitles.c:255 ../applet/cpmpsubtitles.c:256 -#: ../applet/cpmpsubtitles.c:257 ../applet/cpmpsubtitles.c:258 -msgid "Korean" -msgstr "Korejské" - -#: ../applet/cpmpsubtitles.c:260 -msgid "Nordic" -msgstr "Nordické" - -#: ../applet/cpmpsubtitles.c:262 -msgid "Persian" -msgstr "Perské" - -#: ../applet/cpmpsubtitles.c:264 ../applet/cpmpsubtitles.c:265 -msgid "Romanian" -msgstr "Rumunské" - -#: ../applet/cpmpsubtitles.c:267 -msgid "South European" -msgstr "Jihoevropské" - -#: ../applet/cpmpsubtitles.c:269 -msgid "Thai" -msgstr "Thajské" - -#: ../applet/cpmpsubtitles.c:271 ../applet/cpmpsubtitles.c:272 -#: ../applet/cpmpsubtitles.c:273 ../applet/cpmpsubtitles.c:274 -msgid "Turkish" -msgstr "Turecké" - -#: ../applet/cpmpsubtitles.c:276 ../applet/cpmpsubtitles.c:277 -#: ../applet/cpmpsubtitles.c:278 ../applet/cpmpsubtitles.c:279 -#: ../applet/cpmpsubtitles.c:280 -msgid "Unicode" -msgstr "Unicode" - -#: ../applet/cpmpsubtitles.c:282 ../applet/cpmpsubtitles.c:283 -#: ../applet/cpmpsubtitles.c:284 ../applet/cpmpsubtitles.c:285 -#: ../applet/cpmpsubtitles.c:286 -msgid "Western" -msgstr "Západní" - -#: ../applet/cpmpsubtitles.c:288 ../applet/cpmpsubtitles.c:289 -#: ../applet/cpmpsubtitles.c:290 -msgid "Vietnamese" -msgstr "Vietnamské" - -#: ../applet/cpmpsubtitles.c:427 ../applet/cpmpsubtitles.c:622 -msgid "Font" -msgstr "Písmo" - -#: ../applet/cpmpsubtitles.c:593 -msgid "Automatically load subtitle files" -msgstr "Automaticky nahrát titulky" - -#: ../applet/cpmpsubtitles.c:659 -msgid "Encoding" -msgstr "Kódování" - -#: ../applet/cpmpsubtitles.c:724 -msgid "Subtitles" -msgstr "Titulky" diff --git a/po/da.po b/po/da.po deleted file mode 100644 index 2fb53e1..0000000 --- a/po/da.po +++ /dev/null @@ -1,190 +0,0 @@ -# Danish translation of MAFW. -# Copyright (C) 2010 MAFW & Joe Hansen. -# This file is distributed under the same license as the MAFW package. -# Joe Hansen , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: Development\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-02-01 04:03+0000\n" -"PO-Revision-Date: 2010-02-01 17:30+01:00\n" -"Last-Translator: Joe Hansen \n" -"Language-Team: Danish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../applet/cpmpsubtitles.c:59 -msgid "Regular" -msgstr "Normal" - -#: ../applet/cpmpsubtitles.c:60 -msgid "Italic" -msgstr "Kursiv" - -#: ../applet/cpmpsubtitles.c:61 -msgid "Bold" -msgstr "Fed" - -#: ../applet/cpmpsubtitles.c:62 -msgid "Italic Bold" -msgstr "Kursiv og fed" - -#: ../applet/cpmpsubtitles.c:187 ../applet/cpmpsubtitles.c:188 -#: ../applet/cpmpsubtitles.c:189 ../applet/cpmpsubtitles.c:190 -msgid "Arabic" -msgstr "Arabisk" - -#: ../applet/cpmpsubtitles.c:192 -msgid "Armenian" -msgstr "Armensk" - -#: ../applet/cpmpsubtitles.c:194 ../applet/cpmpsubtitles.c:195 -#: ../applet/cpmpsubtitles.c:196 -msgid "Baltic" -msgstr "Baltisk" - -#: ../applet/cpmpsubtitles.c:198 -msgid "Celtic" -msgstr "Keltisk" - -#: ../applet/cpmpsubtitles.c:200 ../applet/cpmpsubtitles.c:201 -#: ../applet/cpmpsubtitles.c:202 ../applet/cpmpsubtitles.c:203 -msgid "Central European" -msgstr "Centraleuropæisk" - -#: ../applet/cpmpsubtitles.c:205 ../applet/cpmpsubtitles.c:206 -#: ../applet/cpmpsubtitles.c:207 ../applet/cpmpsubtitles.c:208 -msgid "Chinese Simplified" -msgstr "Forenklet kinesisk" - -#: ../applet/cpmpsubtitles.c:210 ../applet/cpmpsubtitles.c:211 -#: ../applet/cpmpsubtitles.c:212 -msgid "Chinese Traditional" -msgstr "Traditionel kinesisk" - -#: ../applet/cpmpsubtitles.c:214 -msgid "Croatian" -msgstr "Kroatisk" - -#: ../applet/cpmpsubtitles.c:216 ../applet/cpmpsubtitles.c:217 -#: ../applet/cpmpsubtitles.c:218 ../applet/cpmpsubtitles.c:219 -#: ../applet/cpmpsubtitles.c:220 ../applet/cpmpsubtitles.c:221 -msgid "Cyrillic" -msgstr "Kyrillisk" - -#: ../applet/cpmpsubtitles.c:223 -msgid "Cyrillic/Russian" -msgstr "Kyrillisk/russisk" - -#: ../applet/cpmpsubtitles.c:225 ../applet/cpmpsubtitles.c:226 -msgid "Cyrillic/Ukrainian" -msgstr "Kyrillisk/ukrainsk" - -#: ../applet/cpmpsubtitles.c:228 -msgid "Georgian" -msgstr "Georgisk" - -#: ../applet/cpmpsubtitles.c:230 ../applet/cpmpsubtitles.c:231 -#: ../applet/cpmpsubtitles.c:232 -msgid "Greek" -msgstr "Græsk" - -#: ../applet/cpmpsubtitles.c:234 -msgid "Gujarati" -msgstr "Gujarati" - -#: ../applet/cpmpsubtitles.c:236 -msgid "Gurmukhi" -msgstr "Gurmukhi" - -#: ../applet/cpmpsubtitles.c:238 ../applet/cpmpsubtitles.c:239 -#: ../applet/cpmpsubtitles.c:240 ../applet/cpmpsubtitles.c:241 -msgid "Hebrew" -msgstr "Hebraisk" - -#: ../applet/cpmpsubtitles.c:243 -msgid "Hebrew Visual" -msgstr "Visuelt hebraisk" - -#: ../applet/cpmpsubtitles.c:245 -msgid "Hindi" -msgstr "Hindi" - -#: ../applet/cpmpsubtitles.c:247 -msgid "Icelandic" -msgstr "Islandsk" - -#: ../applet/cpmpsubtitles.c:249 ../applet/cpmpsubtitles.c:250 -#: ../applet/cpmpsubtitles.c:251 -msgid "Japanese" -msgstr "Japansk" - -#: ../applet/cpmpsubtitles.c:253 ../applet/cpmpsubtitles.c:254 -#: ../applet/cpmpsubtitles.c:255 ../applet/cpmpsubtitles.c:256 -msgid "Korean" -msgstr "Koreansk" - -#: ../applet/cpmpsubtitles.c:258 -msgid "Nordic" -msgstr "Nordisk" - -#: ../applet/cpmpsubtitles.c:260 -msgid "Persian" -msgstr "Iransk" - -#: ../applet/cpmpsubtitles.c:262 ../applet/cpmpsubtitles.c:263 -msgid "Romanian" -msgstr "Rumænsk" - -#: ../applet/cpmpsubtitles.c:265 -msgid "South European" -msgstr "Sydeuropæisk" - -#: ../applet/cpmpsubtitles.c:267 -msgid "Thai" -msgstr "Thai" - -#: ../applet/cpmpsubtitles.c:269 ../applet/cpmpsubtitles.c:270 -#: ../applet/cpmpsubtitles.c:271 ../applet/cpmpsubtitles.c:272 -msgid "Turkish" -msgstr "Tyrkisk" - -#: ../applet/cpmpsubtitles.c:274 ../applet/cpmpsubtitles.c:275 -#: ../applet/cpmpsubtitles.c:276 ../applet/cpmpsubtitles.c:277 -#: ../applet/cpmpsubtitles.c:278 -msgid "Unicode" -msgstr "Unicode" - -#: ../applet/cpmpsubtitles.c:280 ../applet/cpmpsubtitles.c:281 -#: ../applet/cpmpsubtitles.c:282 ../applet/cpmpsubtitles.c:283 -#: ../applet/cpmpsubtitles.c:284 -msgid "Western" -msgstr "Vestligt" - -#: ../applet/cpmpsubtitles.c:286 ../applet/cpmpsubtitles.c:287 -#: ../applet/cpmpsubtitles.c:288 -msgid "Vietnamese" -msgstr "Vietnamesisk" - -#: ../applet/cpmpsubtitles.c:290 -msgid "Current Locale" -msgstr "Nuværende sprog" - -#: ../applet/cpmpsubtitles.c:432 ../applet/cpmpsubtitles.c:631 -msgid "Font" -msgstr "Skrifttype" - -#: ../applet/cpmpsubtitles.c:602 -msgid "Automatically load subtitle files" -msgstr "Indlæs automatisk undertekstfiler" - -# Det skal vel ikke være kodning her, men sprog? -#: ../applet/cpmpsubtitles.c:668 -msgid "Encoding" -msgstr "Sprog" - -#: ../applet/cpmpsubtitles.c:733 -msgid "Subtitles" -msgstr "Undertekster" diff --git a/po/de.po b/po/de.po deleted file mode 100644 index 75ccce0..0000000 --- a/po/de.po +++ /dev/null @@ -1,219 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: mafw-gst-subtitles-render\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-01-20 14:17+0000\n" -"PO-Revision-Date: 2010-01-20 19:01+0100\n" -"Last-Translator: Philipp Zabel \n" -"Language-Team: German\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Poedit-Language: German\n" -"X-Poedit-Country: GERMANY\n" - -#: ../applet/cpmpsubtitles.c:59 -msgid "Regular" -msgstr "Normal" - -#: ../applet/cpmpsubtitles.c:60 -msgid "Italic" -msgstr "Kursiv" - -#: ../applet/cpmpsubtitles.c:61 -msgid "Bold" -msgstr "Fett" - -#: ../applet/cpmpsubtitles.c:62 -msgid "Italic Bold" -msgstr "Fett Kursiv" - -#: ../applet/cpmpsubtitles.c:187 -#: ../applet/cpmpsubtitles.c:188 -#: ../applet/cpmpsubtitles.c:189 -#: ../applet/cpmpsubtitles.c:190 -msgid "Arabic" -msgstr "Arabisch" - -#: ../applet/cpmpsubtitles.c:192 -msgid "Armenian" -msgstr "Armenisch" - -#: ../applet/cpmpsubtitles.c:194 -#: ../applet/cpmpsubtitles.c:195 -#: ../applet/cpmpsubtitles.c:196 -msgid "Baltic" -msgstr "Baltisch" - -#: ../applet/cpmpsubtitles.c:198 -msgid "Celtic" -msgstr "Keltisch" - -#: ../applet/cpmpsubtitles.c:200 -#: ../applet/cpmpsubtitles.c:201 -#: ../applet/cpmpsubtitles.c:202 -#: ../applet/cpmpsubtitles.c:203 -msgid "Central European" -msgstr "Zentraleuropäisch" - -#: ../applet/cpmpsubtitles.c:205 -#: ../applet/cpmpsubtitles.c:206 -#: ../applet/cpmpsubtitles.c:207 -#: ../applet/cpmpsubtitles.c:208 -msgid "Chinese Simplified" -msgstr "Vereinfachtes Chinesisch" - -#: ../applet/cpmpsubtitles.c:210 -#: ../applet/cpmpsubtitles.c:211 -#: ../applet/cpmpsubtitles.c:212 -msgid "Chinese Traditional" -msgstr "Traditionelles Chinesisch" - -#: ../applet/cpmpsubtitles.c:214 -msgid "Croatian" -msgstr "Kroatisch" - -#: ../applet/cpmpsubtitles.c:216 -#: ../applet/cpmpsubtitles.c:217 -#: ../applet/cpmpsubtitles.c:218 -#: ../applet/cpmpsubtitles.c:219 -#: ../applet/cpmpsubtitles.c:220 -#: ../applet/cpmpsubtitles.c:221 -msgid "Cyrillic" -msgstr "Kyrillisch" - -#: ../applet/cpmpsubtitles.c:223 -msgid "Cyrillic/Russian" -msgstr "Kyrillisch/Russisch" - -#: ../applet/cpmpsubtitles.c:225 -#: ../applet/cpmpsubtitles.c:226 -msgid "Cyrillic/Ukrainian" -msgstr "Kyrillisch/Ukrainisch" - -#: ../applet/cpmpsubtitles.c:228 -msgid "Georgian" -msgstr "Georgisch" - -#: ../applet/cpmpsubtitles.c:230 -#: ../applet/cpmpsubtitles.c:231 -#: ../applet/cpmpsubtitles.c:232 -msgid "Greek" -msgstr "Griechisch" - -#: ../applet/cpmpsubtitles.c:234 -msgid "Gujarati" -msgstr "Gujarati" - -#: ../applet/cpmpsubtitles.c:236 -msgid "Gurmukhi" -msgstr "Gurmukhi" - -#: ../applet/cpmpsubtitles.c:238 -#: ../applet/cpmpsubtitles.c:239 -#: ../applet/cpmpsubtitles.c:240 -#: ../applet/cpmpsubtitles.c:241 -msgid "Hebrew" -msgstr "Hebräisch" - -#: ../applet/cpmpsubtitles.c:243 -msgid "Hebrew Visual" -msgstr "Visuelles Hebräisch" - -#: ../applet/cpmpsubtitles.c:245 -msgid "Hindi" -msgstr "Hindi" - -#: ../applet/cpmpsubtitles.c:247 -msgid "Icelandic" -msgstr "Isländisch" - -#: ../applet/cpmpsubtitles.c:249 -#: ../applet/cpmpsubtitles.c:250 -#: ../applet/cpmpsubtitles.c:251 -msgid "Japanese" -msgstr "Japanisch" - -#: ../applet/cpmpsubtitles.c:253 -#: ../applet/cpmpsubtitles.c:254 -#: ../applet/cpmpsubtitles.c:255 -#: ../applet/cpmpsubtitles.c:256 -msgid "Korean" -msgstr "Koreanisch" - -#: ../applet/cpmpsubtitles.c:258 -msgid "Nordic" -msgstr "Nordisch" - -#: ../applet/cpmpsubtitles.c:260 -msgid "Persian" -msgstr "Persisch" - -#: ../applet/cpmpsubtitles.c:262 -#: ../applet/cpmpsubtitles.c:263 -msgid "Romanian" -msgstr "Rumänisch" - -#: ../applet/cpmpsubtitles.c:265 -msgid "South European" -msgstr "Südeuropäisch" - -#: ../applet/cpmpsubtitles.c:267 -msgid "Thai" -msgstr "Thai" - -#: ../applet/cpmpsubtitles.c:269 -#: ../applet/cpmpsubtitles.c:270 -#: ../applet/cpmpsubtitles.c:271 -#: ../applet/cpmpsubtitles.c:272 -msgid "Turkish" -msgstr "Türkisch" - -#: ../applet/cpmpsubtitles.c:274 -#: ../applet/cpmpsubtitles.c:275 -#: ../applet/cpmpsubtitles.c:276 -#: ../applet/cpmpsubtitles.c:277 -#: ../applet/cpmpsubtitles.c:278 -msgid "Unicode" -msgstr "Unicode" - -#: ../applet/cpmpsubtitles.c:280 -#: ../applet/cpmpsubtitles.c:281 -#: ../applet/cpmpsubtitles.c:282 -#: ../applet/cpmpsubtitles.c:283 -#: ../applet/cpmpsubtitles.c:284 -msgid "Western" -msgstr "Westlich" - -#: ../applet/cpmpsubtitles.c:286 -#: ../applet/cpmpsubtitles.c:287 -#: ../applet/cpmpsubtitles.c:288 -msgid "Vietnamese" -msgstr "Vietnamesisch" - -#: ../applet/cpmpsubtitles.c:290 -msgid "Current Locale" -msgstr "Aktuelle Sprache/Region" - -#: ../applet/cpmpsubtitles.c:432 -#: ../applet/cpmpsubtitles.c:631 -msgid "Font" -msgstr "Schrift" - -#: ../applet/cpmpsubtitles.c:602 -msgid "Automatically load subtitle files" -msgstr "Untertiteldateien automatisch laden" - -#: ../applet/cpmpsubtitles.c:668 -msgid "Encoding" -msgstr "Codierung" - -#: ../applet/cpmpsubtitles.c:733 -msgid "Subtitles" -msgstr "Untertitel" - diff --git a/po/fi.po b/po/fi.po deleted file mode 100644 index 29d6309..0000000 --- a/po/fi.po +++ /dev/null @@ -1,193 +0,0 @@ -# This is an fi_FI "translation" file for MAFW Gstreamer renderer with subtitles -# support. To translate this to another language, first copy this file to a -# filename with the appropriate language/country code, then modify the "msgstr" -# strings with the correct translation. -# Copyright (C) 2010, Marko Vertainen -# This file is distributed under the same license as the MAFW Gstreamer renderer -# with subtitles support package. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: 0.1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-04-21 04:04+0000\n" -"PO-Revision-Date: 2010-02-02 16:46+0200\n" -"Last-Translator: Marko Vertainen \n" -"Language-Team: FI \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../applet/cpmpsubtitles.c:59 -msgid "Regular" -msgstr "Tavallinen" - -#: ../applet/cpmpsubtitles.c:60 -msgid "Italic" -msgstr "Kursivoitu" - -#: ../applet/cpmpsubtitles.c:61 -msgid "Bold" -msgstr "Lihavoitu" - -#: ../applet/cpmpsubtitles.c:62 -msgid "Italic Bold" -msgstr "Lihavoitu kursiivi" - -#: ../applet/cpmpsubtitles.c:187 ../applet/cpmpsubtitles.c:188 -#: ../applet/cpmpsubtitles.c:189 ../applet/cpmpsubtitles.c:190 -msgid "Arabic" -msgstr "Arabialainen" - -#: ../applet/cpmpsubtitles.c:192 -msgid "Armenian" -msgstr "Armenialainen" - -#: ../applet/cpmpsubtitles.c:194 ../applet/cpmpsubtitles.c:195 -#: ../applet/cpmpsubtitles.c:196 -msgid "Baltic" -msgstr "Balttialainen" - -#: ../applet/cpmpsubtitles.c:198 -msgid "Celtic" -msgstr "Kelttiläinen" - -#: ../applet/cpmpsubtitles.c:200 ../applet/cpmpsubtitles.c:201 -#: ../applet/cpmpsubtitles.c:202 ../applet/cpmpsubtitles.c:203 -msgid "Central European" -msgstr "Keskieurooppalainen" - -#: ../applet/cpmpsubtitles.c:205 ../applet/cpmpsubtitles.c:206 -#: ../applet/cpmpsubtitles.c:207 ../applet/cpmpsubtitles.c:208 -msgid "Chinese Simplified" -msgstr "Kiinalainen, yksinkertaistettu" - -#: ../applet/cpmpsubtitles.c:210 ../applet/cpmpsubtitles.c:211 -#: ../applet/cpmpsubtitles.c:212 -msgid "Chinese Traditional" -msgstr "Kiinalainen, perinteinen" - -#: ../applet/cpmpsubtitles.c:214 -msgid "Croatian" -msgstr "Kroatialainen" - -#: ../applet/cpmpsubtitles.c:216 ../applet/cpmpsubtitles.c:217 -#: ../applet/cpmpsubtitles.c:218 ../applet/cpmpsubtitles.c:219 -#: ../applet/cpmpsubtitles.c:220 ../applet/cpmpsubtitles.c:221 -msgid "Cyrillic" -msgstr "Kyriilinen" - -#: ../applet/cpmpsubtitles.c:223 -msgid "Cyrillic/Russian" -msgstr "Kyriilinen/Venäläinen" - -#: ../applet/cpmpsubtitles.c:225 ../applet/cpmpsubtitles.c:226 -msgid "Cyrillic/Ukrainian" -msgstr "Kyriilinen/Ukrainalainen" - -#: ../applet/cpmpsubtitles.c:228 -msgid "Georgian" -msgstr "Georgialainen" - -#: ../applet/cpmpsubtitles.c:230 ../applet/cpmpsubtitles.c:231 -#: ../applet/cpmpsubtitles.c:232 -msgid "Greek" -msgstr "Kreikkalainen" - -#: ../applet/cpmpsubtitles.c:234 -msgid "Gujarati" -msgstr "Gujarati" - -#: ../applet/cpmpsubtitles.c:236 -msgid "Gurmukhi" -msgstr "Gurmukhi" - -#: ../applet/cpmpsubtitles.c:238 ../applet/cpmpsubtitles.c:239 -#: ../applet/cpmpsubtitles.c:240 ../applet/cpmpsubtitles.c:241 -msgid "Hebrew" -msgstr "Hebrealainen" - -#: ../applet/cpmpsubtitles.c:243 -msgid "Hebrew Visual" -msgstr "Hebrealainen, visuaalinen" - -#: ../applet/cpmpsubtitles.c:245 -msgid "Hindi" -msgstr "Hindilainen" - -#: ../applet/cpmpsubtitles.c:247 -msgid "Icelandic" -msgstr "Islantilainen" - -#: ../applet/cpmpsubtitles.c:249 ../applet/cpmpsubtitles.c:250 -#: ../applet/cpmpsubtitles.c:251 -msgid "Japanese" -msgstr "Japanilainen" - -#: ../applet/cpmpsubtitles.c:253 ../applet/cpmpsubtitles.c:254 -#: ../applet/cpmpsubtitles.c:255 ../applet/cpmpsubtitles.c:256 -msgid "Korean" -msgstr "Korealainen" - -#: ../applet/cpmpsubtitles.c:258 -msgid "Nordic" -msgstr "Pohjoismaalainen" - -#: ../applet/cpmpsubtitles.c:260 -msgid "Persian" -msgstr "Persialainen" - -#: ../applet/cpmpsubtitles.c:262 ../applet/cpmpsubtitles.c:263 -msgid "Romanian" -msgstr "Romanialainen" - -#: ../applet/cpmpsubtitles.c:265 -msgid "South European" -msgstr "Eteläeurooppalainen" - -#: ../applet/cpmpsubtitles.c:267 -msgid "Thai" -msgstr "Thaimaalainen" - -#: ../applet/cpmpsubtitles.c:269 ../applet/cpmpsubtitles.c:270 -#: ../applet/cpmpsubtitles.c:271 ../applet/cpmpsubtitles.c:272 -msgid "Turkish" -msgstr "Turkkilainen" - -#: ../applet/cpmpsubtitles.c:274 ../applet/cpmpsubtitles.c:275 -#: ../applet/cpmpsubtitles.c:276 ../applet/cpmpsubtitles.c:277 -#: ../applet/cpmpsubtitles.c:278 -msgid "Unicode" -msgstr "Unicode" - -#: ../applet/cpmpsubtitles.c:280 ../applet/cpmpsubtitles.c:281 -#: ../applet/cpmpsubtitles.c:282 ../applet/cpmpsubtitles.c:283 -#: ../applet/cpmpsubtitles.c:284 -msgid "Western" -msgstr "Länsimainen" - -#: ../applet/cpmpsubtitles.c:286 ../applet/cpmpsubtitles.c:287 -#: ../applet/cpmpsubtitles.c:288 -msgid "Vietnamese" -msgstr "Vietnamilainen" - -#: ../applet/cpmpsubtitles.c:290 -msgid "Current Locale" -msgstr "Nykyinen maa-asetus" - -#: ../applet/cpmpsubtitles.c:432 ../applet/cpmpsubtitles.c:631 -msgid "Font" -msgstr "Kirjasin" - -#: ../applet/cpmpsubtitles.c:602 -msgid "Automatically load subtitle files" -msgstr "Lataa tekstitystiedosto automaattisesti" - -#: ../applet/cpmpsubtitles.c:668 -msgid "Encoding" -msgstr "Merkistökoodaus" - -#: ../applet/cpmpsubtitles.c:733 -msgid "Subtitles" -msgstr "Tekstitys" diff --git a/po/hu.po b/po/hu.po deleted file mode 100644 index 7ebdd20..0000000 --- a/po/hu.po +++ /dev/null @@ -1,219 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: subtitle hun loc\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-02-20 04:03+0000\n" -"PO-Revision-Date: 2010-02-21 13:47-0000\n" -"Last-Translator: Gyorgy Lakatos \n" -"Language-Team: Hungarian Translater Team \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Poedit-Language: Hungarian\n" -"X-Poedit-Country: UNITED KINGDOM\n" - -#: ../applet/cpmpsubtitles.c:59 -msgid "Regular" -msgstr "Szabályos" - -#: ../applet/cpmpsubtitles.c:60 -msgid "Italic" -msgstr "Itáliai" - -#: ../applet/cpmpsubtitles.c:61 -msgid "Bold" -msgstr "Félkövér" - -#: ../applet/cpmpsubtitles.c:62 -msgid "Italic Bold" -msgstr "Itáliai félkövér" - -#: ../applet/cpmpsubtitles.c:187 -#: ../applet/cpmpsubtitles.c:188 -#: ../applet/cpmpsubtitles.c:189 -#: ../applet/cpmpsubtitles.c:190 -msgid "Arabic" -msgstr "Arab" - -#: ../applet/cpmpsubtitles.c:192 -msgid "Armenian" -msgstr "Armenian" - -#: ../applet/cpmpsubtitles.c:194 -#: ../applet/cpmpsubtitles.c:195 -#: ../applet/cpmpsubtitles.c:196 -msgid "Baltic" -msgstr "Balti" - -#: ../applet/cpmpsubtitles.c:198 -msgid "Celtic" -msgstr "Kelta" - -#: ../applet/cpmpsubtitles.c:200 -#: ../applet/cpmpsubtitles.c:201 -#: ../applet/cpmpsubtitles.c:202 -#: ../applet/cpmpsubtitles.c:203 -msgid "Central European" -msgstr "Közép Európai" - -#: ../applet/cpmpsubtitles.c:205 -#: ../applet/cpmpsubtitles.c:206 -#: ../applet/cpmpsubtitles.c:207 -#: ../applet/cpmpsubtitles.c:208 -msgid "Chinese Simplified" -msgstr "Kínai (egyszerü)" - -#: ../applet/cpmpsubtitles.c:210 -#: ../applet/cpmpsubtitles.c:211 -#: ../applet/cpmpsubtitles.c:212 -msgid "Chinese Traditional" -msgstr "Kínai (hagyományos)" - -#: ../applet/cpmpsubtitles.c:214 -msgid "Croatian" -msgstr "Horvát" - -#: ../applet/cpmpsubtitles.c:216 -#: ../applet/cpmpsubtitles.c:217 -#: ../applet/cpmpsubtitles.c:218 -#: ../applet/cpmpsubtitles.c:219 -#: ../applet/cpmpsubtitles.c:220 -#: ../applet/cpmpsubtitles.c:221 -msgid "Cyrillic" -msgstr "Ciril" - -#: ../applet/cpmpsubtitles.c:223 -msgid "Cyrillic/Russian" -msgstr "Ciril/Orosz" - -#: ../applet/cpmpsubtitles.c:225 -#: ../applet/cpmpsubtitles.c:226 -msgid "Cyrillic/Ukrainian" -msgstr "Ciril/Ukrán" - -#: ../applet/cpmpsubtitles.c:228 -msgid "Georgian" -msgstr "Georgian" - -#: ../applet/cpmpsubtitles.c:230 -#: ../applet/cpmpsubtitles.c:231 -#: ../applet/cpmpsubtitles.c:232 -msgid "Greek" -msgstr "Görög" - -#: ../applet/cpmpsubtitles.c:234 -msgid "Gujarati" -msgstr "Gujarati" - -#: ../applet/cpmpsubtitles.c:236 -msgid "Gurmukhi" -msgstr "Gurmukhi" - -#: ../applet/cpmpsubtitles.c:238 -#: ../applet/cpmpsubtitles.c:239 -#: ../applet/cpmpsubtitles.c:240 -#: ../applet/cpmpsubtitles.c:241 -msgid "Hebrew" -msgstr "Héber" - -#: ../applet/cpmpsubtitles.c:243 -msgid "Hebrew Visual" -msgstr "Héber (hagyományos)" - -#: ../applet/cpmpsubtitles.c:245 -msgid "Hindi" -msgstr "Hindi" - -#: ../applet/cpmpsubtitles.c:247 -msgid "Icelandic" -msgstr "Izlandi" - -#: ../applet/cpmpsubtitles.c:249 -#: ../applet/cpmpsubtitles.c:250 -#: ../applet/cpmpsubtitles.c:251 -msgid "Japanese" -msgstr "Japán" - -#: ../applet/cpmpsubtitles.c:253 -#: ../applet/cpmpsubtitles.c:254 -#: ../applet/cpmpsubtitles.c:255 -#: ../applet/cpmpsubtitles.c:256 -msgid "Korean" -msgstr "Koreai" - -#: ../applet/cpmpsubtitles.c:258 -msgid "Nordic" -msgstr "Nordic" - -#: ../applet/cpmpsubtitles.c:260 -msgid "Persian" -msgstr "Perzsa" - -#: ../applet/cpmpsubtitles.c:262 -#: ../applet/cpmpsubtitles.c:263 -msgid "Romanian" -msgstr "Román" - -#: ../applet/cpmpsubtitles.c:265 -msgid "South European" -msgstr "Dél Európai" - -#: ../applet/cpmpsubtitles.c:267 -msgid "Thai" -msgstr "Thai" - -#: ../applet/cpmpsubtitles.c:269 -#: ../applet/cpmpsubtitles.c:270 -#: ../applet/cpmpsubtitles.c:271 -#: ../applet/cpmpsubtitles.c:272 -msgid "Turkish" -msgstr "Török" - -#: ../applet/cpmpsubtitles.c:274 -#: ../applet/cpmpsubtitles.c:275 -#: ../applet/cpmpsubtitles.c:276 -#: ../applet/cpmpsubtitles.c:277 -#: ../applet/cpmpsubtitles.c:278 -msgid "Unicode" -msgstr "Unicode" - -#: ../applet/cpmpsubtitles.c:280 -#: ../applet/cpmpsubtitles.c:281 -#: ../applet/cpmpsubtitles.c:282 -#: ../applet/cpmpsubtitles.c:283 -#: ../applet/cpmpsubtitles.c:284 -msgid "Western" -msgstr "Nyugati" - -#: ../applet/cpmpsubtitles.c:286 -#: ../applet/cpmpsubtitles.c:287 -#: ../applet/cpmpsubtitles.c:288 -msgid "Vietnamese" -msgstr "Vietnám" - -#: ../applet/cpmpsubtitles.c:290 -msgid "Current Locale" -msgstr "Helyi" - -#: ../applet/cpmpsubtitles.c:432 -#: ../applet/cpmpsubtitles.c:631 -msgid "Font" -msgstr "Betű méret" - -#: ../applet/cpmpsubtitles.c:602 -msgid "Automatically load subtitle files" -msgstr "Autómatikus felirat file megnyitás" - -#: ../applet/cpmpsubtitles.c:668 -msgid "Encoding" -msgstr "Kódolás" - -#: ../applet/cpmpsubtitles.c:733 -msgid "Subtitles" -msgstr "Felirat" - diff --git a/po/sk.po b/po/sk.po deleted file mode 100644 index c6f2605..0000000 --- a/po/sk.po +++ /dev/null @@ -1,189 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# Roman Moravčík , 2010. -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: mafw-gst-subtitles-renderer\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-01-18 14:36+0100\n" -"PO-Revision-Date: 2010-01-19 09:52+0100\n" -"Last-Translator: Roman Moravčík \n" -"Language-Team: Slovak \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: ../applet/cpmpsubtitles.c:56 -msgid "Regular" -msgstr "Normálne" - -#: ../applet/cpmpsubtitles.c:57 -msgid "Italic" -msgstr "Kurzíva" - -#: ../applet/cpmpsubtitles.c:58 -msgid "Bold" -msgstr "Tučné" - -#: ../applet/cpmpsubtitles.c:59 -msgid "Italic Bold" -msgstr "Tučné kurzíva" - -#: ../applet/cpmpsubtitles.c:184 -msgid "Current Locale" -msgstr "Súčasné miestne" - -#: ../applet/cpmpsubtitles.c:186 ../applet/cpmpsubtitles.c:187 -#: ../applet/cpmpsubtitles.c:188 ../applet/cpmpsubtitles.c:189 -msgid "Arabic" -msgstr "Arabské" - -#: ../applet/cpmpsubtitles.c:191 -msgid "Armenian" -msgstr "Arménske" - -#: ../applet/cpmpsubtitles.c:193 ../applet/cpmpsubtitles.c:194 -#: ../applet/cpmpsubtitles.c:195 -msgid "Baltic" -msgstr "Baltské" - -#: ../applet/cpmpsubtitles.c:197 -msgid "Celtic" -msgstr "Keltské" - -#: ../applet/cpmpsubtitles.c:199 ../applet/cpmpsubtitles.c:200 -#: ../applet/cpmpsubtitles.c:201 ../applet/cpmpsubtitles.c:202 -msgid "Central European" -msgstr "Stredná Európa" - -#: ../applet/cpmpsubtitles.c:204 ../applet/cpmpsubtitles.c:205 -#: ../applet/cpmpsubtitles.c:206 ../applet/cpmpsubtitles.c:207 -msgid "Chinese Simplified" -msgstr "Zjednodušené čínske" - -#: ../applet/cpmpsubtitles.c:209 ../applet/cpmpsubtitles.c:210 -#: ../applet/cpmpsubtitles.c:211 -msgid "Chinese Traditional" -msgstr "Tradičné čínske" - -#: ../applet/cpmpsubtitles.c:213 -msgid "Croatian" -msgstr "Chorvátske" - -#: ../applet/cpmpsubtitles.c:215 ../applet/cpmpsubtitles.c:216 -#: ../applet/cpmpsubtitles.c:217 ../applet/cpmpsubtitles.c:218 -#: ../applet/cpmpsubtitles.c:219 ../applet/cpmpsubtitles.c:220 -msgid "Cyrillic" -msgstr "Cyrilika" - -#: ../applet/cpmpsubtitles.c:222 -msgid "Cyrillic/Russian" -msgstr "Cyrilika/Ruské" - -#: ../applet/cpmpsubtitles.c:224 ../applet/cpmpsubtitles.c:225 -msgid "Cyrillic/Ukrainian" -msgstr "Cyrilika/Ukrajinské" - -#: ../applet/cpmpsubtitles.c:227 -msgid "Georgian" -msgstr "Gruzínske" - -#: ../applet/cpmpsubtitles.c:229 ../applet/cpmpsubtitles.c:230 -#: ../applet/cpmpsubtitles.c:231 -msgid "Greek" -msgstr "Grécke" - -#: ../applet/cpmpsubtitles.c:233 -msgid "Gujarati" -msgstr "Gudžarátske" - -#: ../applet/cpmpsubtitles.c:235 -msgid "Gurmukhi" -msgstr "Chorvátske" - -#: ../applet/cpmpsubtitles.c:237 ../applet/cpmpsubtitles.c:238 -#: ../applet/cpmpsubtitles.c:239 ../applet/cpmpsubtitles.c:240 -msgid "Hebrew" -msgstr "Chorvátske" - -#: ../applet/cpmpsubtitles.c:242 -msgid "Hebrew Visual" -msgstr "Hebrejské vizuálne" - -#: ../applet/cpmpsubtitles.c:244 -msgid "Hindi" -msgstr "Hindustánske" - -#: ../applet/cpmpsubtitles.c:246 -msgid "Icelandic" -msgstr "Islandské" - -#: ../applet/cpmpsubtitles.c:248 ../applet/cpmpsubtitles.c:249 -#: ../applet/cpmpsubtitles.c:250 -msgid "Japanese" -msgstr "Japonské" - -#: ../applet/cpmpsubtitles.c:252 ../applet/cpmpsubtitles.c:253 -#: ../applet/cpmpsubtitles.c:254 ../applet/cpmpsubtitles.c:255 -msgid "Korean" -msgstr "Kórejské" - -#: ../applet/cpmpsubtitles.c:257 -msgid "Nordic" -msgstr "Severogermánske" - -#: ../applet/cpmpsubtitles.c:259 -msgid "Persian" -msgstr "Perzské" - -#: ../applet/cpmpsubtitles.c:261 ../applet/cpmpsubtitles.c:262 -msgid "Romanian" -msgstr "Rumunské" - -#: ../applet/cpmpsubtitles.c:264 -msgid "South European" -msgstr "Južná Európa" - -#: ../applet/cpmpsubtitles.c:266 -msgid "Thai" -msgstr "Thajské" - -#: ../applet/cpmpsubtitles.c:268 ../applet/cpmpsubtitles.c:269 -#: ../applet/cpmpsubtitles.c:270 ../applet/cpmpsubtitles.c:271 -msgid "Turkish" -msgstr "Turecké" - -#: ../applet/cpmpsubtitles.c:273 ../applet/cpmpsubtitles.c:274 -#: ../applet/cpmpsubtitles.c:275 ../applet/cpmpsubtitles.c:276 -#: ../applet/cpmpsubtitles.c:277 -msgid "Unicode" -msgstr "Unicode" - -#: ../applet/cpmpsubtitles.c:279 ../applet/cpmpsubtitles.c:280 -#: ../applet/cpmpsubtitles.c:281 ../applet/cpmpsubtitles.c:282 -#: ../applet/cpmpsubtitles.c:283 -msgid "Western" -msgstr "Západné" - -#: ../applet/cpmpsubtitles.c:285 ../applet/cpmpsubtitles.c:286 -#: ../applet/cpmpsubtitles.c:287 -msgid "Vietnamese" -msgstr "Vietnamské" - -#: ../applet/cpmpsubtitles.c:424 ../applet/cpmpsubtitles.c:584 -msgid "Font" -msgstr "Písmo" - -#: ../applet/cpmpsubtitles.c:555 -msgid "Automatically load subtitle files" -msgstr "Automaticky načítať súbor s titulkami" - -#: ../applet/cpmpsubtitles.c:620 -msgid "Encoding" -msgstr "Kódovanie" - -#: ../applet/cpmpsubtitles.c:685 -msgid "Subtitles" -msgstr "Titulky" diff --git a/tests/Makefile.am b/tests/Makefile.am deleted file mode 100644 index a894fbf..0000000 --- a/tests/Makefile.am +++ /dev/null @@ -1,60 +0,0 @@ -# -# 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 deleted file mode 100644 index ae4982b..0000000 --- a/tests/check-mafw-gst-renderer.c +++ /dev/null @@ -1,4174 +0,0 @@ -/* - * 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 deleted file mode 100644 index c5c5172..0000000 --- a/tests/check-main.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 deleted file mode 100644 index 0720261..0000000 --- a/tests/mafw-mock-playlist.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * 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 deleted file mode 100644 index dcc2da1..0000000 --- a/tests/mafw-mock-playlist.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 deleted file mode 100644 index 1675073..0000000 --- a/tests/mafw-mock-pulseaudio.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * 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 deleted file mode 100644 index bfb30a7..0000000 --- a/tests/mafw-mock-pulseaudio.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 deleted file mode 100644 index a340672..0000000 --- a/tests/mafw-test-player.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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 deleted file mode 100644 index f868c625c4bd780ab3e33dc5cfb83ee6a2b28049..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86540 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%0Aun=rc|E*dFqHw3+5l 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