From f5562ca5db1cf3cff42a26921964fcfd22c621ae Mon Sep 17 00:00:00 2001 From: Barun Kumar Singh Date: Wed, 30 Sep 2015 11:51:14 +0530 Subject: [PATCH] Adding SN12/ST12 format support Adding gst-omx version 1.2.0 base code. Signed-off-by: Barun Kr. Singh Change-Id: I61a89aff3b294b993fc4c5540a492a51b886bf76 --- Android.mk | 30 - ChangeLog | 1099 ++++++++++++- INSTALL | 365 ----- Makefile.am | 8 +- NEWS | 2 +- RELEASE | 83 +- common | 1 - common/Makefile.am | 22 + common/extract-release-date-from-doap-file | 32 + common/gst-autogen.sh | 345 +++++ common/m4/Makefile.am | 40 + common/m4/README | 3 + common/m4/as-ac-expand.m4 | 43 + common/m4/as-auto-alt.m4 | 50 + common/m4/as-compiler-flag.m4 | 96 ++ common/m4/as-compiler.m4 | 44 + common/m4/as-docbook.m4 | 78 + common/m4/as-gcc-inline-assembly.m4 | 52 + common/m4/as-libtool-tags.m4 | 83 + common/m4/as-libtool.m4 | 46 + common/m4/as-python.m4 | 152 ++ common/m4/as-version.m4 | 75 + common/m4/ax_create_stdint_h.m4 | 734 +++++++++ common/m4/check.m4 | 181 +++ common/m4/glib-gettext.m4 | 432 ++++++ common/m4/gst-arch.m4 | 140 ++ common/m4/gst-args.m4 | 360 +++++ common/m4/gst-check.m4 | 294 ++++ common/m4/gst-debuginfo.m4 | 46 + common/m4/gst-default.m4 | 120 ++ common/m4/gst-doc.m4 | 92 ++ common/m4/gst-dowhile.m4 | 24 + common/m4/gst-error.m4 | 298 ++++ common/m4/gst-feature.m4 | 297 ++++ common/m4/gst-function.m4 | 63 + common/m4/gst-gettext.m4 | 28 + common/m4/gst-glib2.m4 | 126 ++ common/m4/gst-libxml2.m4 | 52 + common/m4/gst-package-release-datetime.m4 | 89 ++ common/m4/gst-parser.m4 | 55 + common/m4/gst-platform.m4 | 67 + common/m4/gst-plugin-docs.m4 | 25 + common/m4/gst-plugindir.m4 | 17 + common/m4/gst-valgrind.m4 | 35 + common/m4/gst-x11.m4 | 74 + common/m4/gst.m4 | 36 + common/m4/gtk-doc.m4 | 70 + common/m4/introspection.m4 | 94 ++ common/m4/orc.m4 | 70 + common/m4/pkg.m4 | 157 ++ config/Makefile.am | 2 +- config/bellagio/gstomx.conf | 4 +- config/exynos/Makefile.am | 0 config/exynos/gstomx.conf | 17 +- config/{odroid => exynos64}/Makefile.am | 2 +- config/{odroid => exynos64}/gstomx.conf | 32 +- config/rpi/gstomx.conf | 40 +- configure.ac | 80 +- examples/Makefile.am | 5 + examples/egl/Makefile.am | 24 + examples/egl/cube_texture_and_coords.h | 100 ++ examples/egl/testegl.c | 1605 +++++++++++++++++++ gst-omx.doap | 10 + gst-omx.manifest | 5 + m4/Makefile.am | 1 + omx/Makefile.am | 32 +- omx/gstomx.c | 448 +++--- omx/gstomx.h | 50 +- omx/gstomxaacdec.c | 296 ++++ omx/gstomxaacdec.h | 60 + omx/gstomxamrdec.c | 220 +++ omx/gstomxamrdec.h | 61 + omx/gstomxanalogaudiosink.c | 64 + omx/gstomxanalogaudiosink.h | 61 + omx/gstomxaudiodec.c | 1379 +++++++++++++++++ omx/gstomxaudiodec.h | 100 ++ omx/gstomxaudioenc.c | 241 ++- omx/gstomxaudioenc.h | 3 - omx/gstomxaudiosink.c | 1228 +++++++++++++++ omx/gstomxaudiosink.h | 103 ++ omx/gstomxbufferpool.c | 642 ++++++++ omx/gstomxbufferpool.h | 94 ++ omx/gstomxh263enc.c | 9 +- omx/gstomxh264dec.c | 25 + omx/gstomxh264enc.c | 247 ++- omx/gstomxh264enc.h | 8 + omx/gstomxhdmiaudiosink.c | 66 + omx/gstomxhdmiaudiosink.h | 61 + omx/gstomxmp3dec.c | 246 +++ omx/gstomxmp3dec.h | 60 + omx/gstomxmpeg4videoenc.c | 9 +- omx/gstomxvideo.c | 205 +++ omx/gstomxvideo.h | 60 + omx/gstomxvideodec.c | 2283 +++++++++++++--------------- omx/gstomxvideodec.h | 14 +- omx/gstomxvideoenc.c | 521 +++---- omx/gstomxvideoenc.h | 3 - omx/gstomxvp8dec.h | 2 +- packaging/common.tar.bz2 | Bin 95459 -> 0 bytes packaging/gitmodules.sh | 25 - packaging/gst-omx.spec | 19 +- 101 files changed, 15202 insertions(+), 2495 deletions(-) delete mode 100644 Android.mk delete mode 100644 INSTALL delete mode 160000 common create mode 100644 common/Makefile.am create mode 100644 common/extract-release-date-from-doap-file create mode 100644 common/gst-autogen.sh create mode 100644 common/m4/Makefile.am create mode 100644 common/m4/README create mode 100644 common/m4/as-ac-expand.m4 create mode 100644 common/m4/as-auto-alt.m4 create mode 100644 common/m4/as-compiler-flag.m4 create mode 100644 common/m4/as-compiler.m4 create mode 100644 common/m4/as-docbook.m4 create mode 100644 common/m4/as-gcc-inline-assembly.m4 create mode 100644 common/m4/as-libtool-tags.m4 create mode 100644 common/m4/as-libtool.m4 create mode 100644 common/m4/as-python.m4 create mode 100644 common/m4/as-version.m4 create mode 100644 common/m4/ax_create_stdint_h.m4 create mode 100644 common/m4/check.m4 create mode 100644 common/m4/glib-gettext.m4 create mode 100644 common/m4/gst-arch.m4 create mode 100644 common/m4/gst-args.m4 create mode 100644 common/m4/gst-check.m4 create mode 100644 common/m4/gst-debuginfo.m4 create mode 100644 common/m4/gst-default.m4 create mode 100644 common/m4/gst-doc.m4 create mode 100644 common/m4/gst-dowhile.m4 create mode 100644 common/m4/gst-error.m4 create mode 100644 common/m4/gst-feature.m4 create mode 100644 common/m4/gst-function.m4 create mode 100644 common/m4/gst-gettext.m4 create mode 100644 common/m4/gst-glib2.m4 create mode 100644 common/m4/gst-libxml2.m4 create mode 100644 common/m4/gst-package-release-datetime.m4 create mode 100644 common/m4/gst-parser.m4 create mode 100644 common/m4/gst-platform.m4 create mode 100644 common/m4/gst-plugin-docs.m4 create mode 100644 common/m4/gst-plugindir.m4 create mode 100644 common/m4/gst-valgrind.m4 create mode 100644 common/m4/gst-x11.m4 create mode 100644 common/m4/gst.m4 create mode 100644 common/m4/gtk-doc.m4 create mode 100644 common/m4/introspection.m4 create mode 100644 common/m4/orc.m4 create mode 100644 common/m4/pkg.m4 mode change 100755 => 100644 config/Makefile.am mode change 100755 => 100644 config/exynos/Makefile.am mode change 100755 => 100644 config/exynos/gstomx.conf rename config/{odroid => exynos64}/Makefile.am (76%) rename config/{odroid => exynos64}/gstomx.conf (72%) mode change 100755 => 100644 configure.ac create mode 100644 examples/Makefile.am create mode 100644 examples/egl/Makefile.am create mode 100644 examples/egl/cube_texture_and_coords.h create mode 100644 examples/egl/testegl.c create mode 100644 gst-omx.manifest create mode 100644 m4/Makefile.am mode change 100755 => 100644 omx/gstomx.c create mode 100644 omx/gstomxaacdec.c create mode 100644 omx/gstomxaacdec.h create mode 100644 omx/gstomxamrdec.c create mode 100644 omx/gstomxamrdec.h create mode 100644 omx/gstomxanalogaudiosink.c create mode 100644 omx/gstomxanalogaudiosink.h create mode 100644 omx/gstomxaudiodec.c create mode 100644 omx/gstomxaudiodec.h create mode 100644 omx/gstomxaudiosink.c create mode 100644 omx/gstomxaudiosink.h create mode 100644 omx/gstomxbufferpool.c create mode 100644 omx/gstomxbufferpool.h create mode 100644 omx/gstomxhdmiaudiosink.c create mode 100644 omx/gstomxhdmiaudiosink.h create mode 100644 omx/gstomxmp3dec.c create mode 100644 omx/gstomxmp3dec.h create mode 100644 omx/gstomxvideo.c create mode 100644 omx/gstomxvideo.h mode change 100755 => 100644 omx/gstomxvideodec.c mode change 100755 => 100644 omx/gstomxvideoenc.c mode change 100755 => 100644 omx/gstomxvideoenc.h delete mode 100644 packaging/common.tar.bz2 delete mode 100755 packaging/gitmodules.sh mode change 100755 => 100644 packaging/gst-omx.spec diff --git a/Android.mk b/Android.mk deleted file mode 100644 index bc99d9f..0000000 --- a/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -GST_OMX_TOP := $(LOCAL_PATH) - -GST_OMX_BUILT_SOURCES := omx/Android.mk - -GST_OMX_BUILT_SOURCES := $(patsubst %, $(abspath $(GST_OMX_TOP))/%, $(GST_OMX_BUILT_SOURCES)) - -.PHONY: gst-omx-configure -gst-omx-configure: - cd $(GST_OMX_TOP) ; \ - CC="$(CONFIGURE_CC)" \ - CFLAGS="$(CONFIGURE_CFLAGS)" \ - LD=$(TARGET_LD) \ - LDFLAGS="$(CONFIGURE_LDFLAGS)" \ - CPP=$(CONFIGURE_CPP) \ - CPPFLAGS="$(CONFIGURE_CPPFLAGS)" \ - PKG_CONFIG_LIBDIR="$(CONFIGURE_PKG_CONFIG_LIBDIR)" \ - PKG_CONFIG_TOP_BUILD_DIR=/ \ - $(abspath $(GST_OMX_TOP))/$(CONFIGURE) --host=arm-linux-androideabi \ - --prefix=/system --disable-orc --disable-valgrind --disable-gtk-doc && \ - for file in $(GST_OMX_BUILT_SOURCES); do \ - rm -f $$file && \ - make -C $$(dirname $$file) $$(basename $$file) ; \ - done - -CONFIGURE_TARGETS += gst-omx-configure - --include $(GST_OMX_TOP)/omx/Android.mk diff --git a/ChangeLog b/ChangeLog index 1f254ce..3ba19b7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,1104 @@ +=== release 1.2.0 === + +2014-07-23 Sebastian Dröge + + * configure.ac: + releasing 1.2.0 + +2014-07-22 09:23:00 +0200 Sebastian Dröge + + * config/bellagio/gstomx.conf: + * config/rpi/gstomx.conf: + config: Update ranks to PRIMARY+1 to have higher preference than avdec_* + See https://bugzilla.gnome.org/show_bug.cgi?id=732161 + +2014-07-20 17:46:30 +0200 Sebastian Dröge + + * omx/gstomxaudiosink.c: + omxaudiosink: Set port to not flushing in prepare() and keep it at flushing in unprepare() + https://bugzilla.gnome.org/show_bug.cgi?id=733168 + +2014-07-13 22:15:18 +0200 Sebastian Dröge + + * omx/gstomxaacdec.c: + * omx/gstomxaudiodec.c: + * omx/gstomxaudiodec.h: + * omx/gstomxmp3dec.c: + omxaudiodec: Implement setting of fallback channel positions + +2014-07-13 18:22:39 +0200 Sebastian Dröge + + * omx/Makefile.am: + * omx/gstomx.c: + * omx/gstomxaacdec.c: + * omx/gstomxaacdec.h: + * omx/gstomxmp3dec.c: + omx: Add AAC audio decoder + +2014-07-02 09:22:28 +0200 Sebastian Dröge + + * omx/gstomxaudiodec.c: + omxaudiodec: Get PCM parameters from the out port, not the in port + +2014-05-15 13:24:39 +0200 Sebastian Dröge + + * omx/gstomxaudiodec.c: + omxaudiodec: Implement hack for not disabling the output port after set_format until the output format is known + Needed on some OMX implementations, e.g. the one from Atmel. It does + not send the settings-changed event on the output port if it is + disabled. + +2014-05-10 23:12:54 +0200 Sebastian Dröge + + * omx/Makefile.am: + * omx/gstomx.c: + * omx/gstomxaudiodec.c: + * omx/gstomxaudiodec.h: + * omx/gstomxmp3dec.c: + * omx/gstomxmp3dec.h: + omx: Add audio decoder base class and a subclass for MP3 + +2014-07-01 09:38:01 +0200 Sebastian Dröge + + * configure.ac: + * omx/Makefile.am: + omx: Link to gmodule-2.0-no-export for being able to use the g_module_*() API + https://bugzilla.gnome.org/show_bug.cgi?id=732518 + +2014-06-30 15:00:54 +0200 Sebastian Dröge + + * examples/egl/testegl.c: + examples: #define GST_USE_UNSTABLE_API for libgstgl + +2014-06-29 19:10:19 +0200 Sebastian Dröge + + * omx/gstomxh264enc.c: + * omx/gstomxh264enc.h: + omxh264enc: Properly accumulate headers and push before the next frame + Fixes output of encoding on RPi, where each header buffer (SPS and PPS) + is in a separate OMX buffer. + https://bugzilla.gnome.org/show_bug.cgi?id=726669 + +2014-06-29 19:04:54 +0200 Sebastian Dröge + + * omx/gstomxvideoenc.c: + omxvideoenc: Implement flush() instead of the deprecated reset() + +2014-06-25 17:14:18 +0200 Sebastian Dröge + + * config/rpi/gstomx.conf: + rpi: It's 44100Hz, not 41400Hz + +2014-06-25 11:12:51 +0100 Julien Isorce + + * configure.ac: + configure.ac: require gstgl >= 1.3.3 + +2014-04-25 13:25:05 +0100 Julien Isorce + + * Makefile.am: + * configure.ac: + example: enable testegl + See https://bugzilla.gnome.org/show_bug.cgi?id=728940 + +2014-06-25 10:19:54 +0100 Julien Isorce + + * examples/egl/testegl.c: + testegl: do matrix mutlplication in the shader + See https://bugzilla.gnome.org/show_bug.cgi?id=728940 + +2014-06-25 09:36:38 +0100 Julien Isorce + + * examples/egl/testegl.c: + testegl: add a comment for the parse command + See https://bugzilla.gnome.org/show_bug.cgi?id=728940 + +2014-04-25 17:32:16 +0100 Julien Isorce + + * examples/egl/Makefile.am: + * examples/egl/cube_texture_and_coords.h: + * examples/egl/testegl.c: + testegl: convert code from GLESv1 to GLESv2 + See https://bugzilla.gnome.org/show_bug.cgi?id=728940 + +2014-04-25 13:21:59 +0100 Julien Isorce + + * examples/egl/Makefile.am: + * examples/egl/testegl.c: + testegl: port to gstgl API + - append a glfilter just before fakesink + So that we get gltexture or eglimages + - propagate our EGLDisplay to the pipeline + see GST_QUERY_CONTEXT + - share our EGLContext with the iternal gl context + of the pipeline, see GST_QUERY_ALLOCATION + - use GstVideoGLTextureUploadMeta to upload + the incoming gltexture or eglimage to our gl texture + TODO: convert from GLESv1 to GLESv2 + See https://bugzilla.gnome.org/show_bug.cgi?id=728940 + +2014-06-24 14:52:58 +0200 Sebastian Dröge + + * omx/gstomxbufferpool.c: + * omx/gstomxbufferpool.h: + * omx/gstomxvideodec.c: + omxbufferpool: Copy buffers if the stride does not match and we can't use video meta + https://bugzilla.gnome.org/show_bug.cgi?id=731672 + +2014-06-24 14:52:43 +0200 Sebastian Dröge + + * configure.ac: + * omx/gstomx.h: + * omx/gstomxvp8dec.h: + omx: Only include OMX_VideoExt.h conditionally + It does not exist on the RPi for example. + +2014-06-24 13:59:44 +0200 Sebastian Dröge + + * configure.ac: + configure.ac: Require GStreamer core/base >= 1.2.2 + Needed at least for gst_video_decoder_release_frame(). + +2014-06-24 13:02:13 +0200 Sebastian Dröge + + * omx/gstomxbufferpool.c: + omxbufferpool: Fix format string compiler warning + +2014-06-22 21:11:45 +0000 Michal Lazo + + * omx/gstomxbufferpool.c: + omxbufferpool: Initialize debug category + +2014-06-24 12:42:22 +0200 Sebastian Dröge + + * omx/gstomxbufferpool.c: + omxbufferpool: Properly convert OMX alignment to GStreamer alignment + GStreamer uses a bitmask for the alignment while OMX uses the + alignment itself. Let's convert. + https://bugzilla.gnome.org/show_bug.cgi?id=710564 + +2014-06-24 11:11:28 +0200 Sebastian Dröge + + * omx/gstomxh264enc.c: + omxh264enc: Don't let baseclass finish frames for SPS/PPS buffers + Otherwise we a) send them twice, and b) finish a frame for something + that does not even include a frame. + https://bugzilla.gnome.org/show_bug.cgi?id=726669 + +2014-06-24 10:22:37 +0200 Sebastian Dröge + + * omx/gstomxvideo.h: + omxvideo: Include the separate headers too for compatibility with 1.0.x + +2014-03-24 16:09:40 +0800 Zhao, Halley + + * configure.ac: + * omx/gstomxvp8dec.h: + omxvp8dec: use VP8 definition from OMX_VideoExt.h + https://bugzilla.gnome.org/show_bug.cgi?id=726957 + +2014-03-24 15:33:26 +0800 Zhao, Halley + + * configure.ac: + configure: add --with-omx-header-path option for external omx headers + https://bugzilla.gnome.org/show_bug.cgi?id=726957 + +2014-06-18 23:04:33 +0200 Aurélien Zanelli + + * omx/gstomxvideodec.c: + omxvideodec: fix a query leak + Also add a debug message if query fails. + https://bugzilla.gnome.org/show_bug.cgi?id=731898 + +2014-05-30 15:29:15 +0200 Aurélien Zanelli + + * omx/gstomxvideodec.c: + omxvideodec: release frames with old PTS to avoid memory issue + Interlaced stream could make the decoder use two input frames to produce + one output frame causing the gstvideodecoder frame list to grow. + Assuming the video decoder output frame in display order rather than in + decoding order, this commit add a way to release frames with PTS less + than current output frame. + https://bugzilla.gnome.org/show_bug.cgi?id=730995 + +2013-06-27 21:59:29 +0900 Kazunori Kobayashi + + * omx/gstomx.c: + omx: Fix a missing g_free() in error path + This fixes a memory leak with g_strdup() when an error occurs. + https://bugzilla.gnome.org/show_bug.cgi?id=731141 + +2014-06-02 15:34:09 +0200 Aurélien Zanelli + + * omx/gstomxvideodec.c: + omxvideodec: add missing stream unlock in error path + +2014-05-31 15:12:05 +0200 Sebastian Dröge + + * omx/gstomx.c: + omx: Don't handle disabling/enabling ports exactly like flushing + Otherwise we might abort a flush operation in another thread when + enabling/disabling ports, leading to deadlocks sometimes. + https://bugzilla.gnome.org/show_bug.cgi?id=730989 + +2014-05-26 11:02:10 +0200 Sebastian Dröge + + * omx/gstomxvideodec.c: + omxvideodec: Don't leak buffer pool config in error cases + CID 1216158 + +2014-05-21 10:53:43 +0200 Sebastian Dröge + + * common: + Automatic update of common submodule + From 211fa5f to 1f5d3c3 + +2014-05-19 09:10:07 +0200 Sebastian Dröge + + * omx/gstomxvideoenc.c: + omxvideoenc: Don't forget to unref codec state + CID 1214603 + +2014-05-19 09:08:33 +0200 Sebastian Dröge + + * omx/gstomxvideodec.c: + omxvideodec: Make output buffer pointer always initialized + CID 1214605 + +2014-05-19 09:06:42 +0200 Sebastian Dröge + + * omx/gstomxvideodec.c: + omxvideodec: Check return value of gst_buffer_map() + CID 1214599 + +2014-05-19 09:04:09 +0200 Sebastian Dröge + + * omx/gstomxvideodec.c: + omxvideodec: Check return value of gst_omx_port_set_enabled() for errors + CID 1214589 + +2014-05-19 09:01:46 +0200 Sebastian Dröge + + * omx/gstomxvideodec.c: + omxvideodec: Check return values of buffer pool config parsing functions + CID 1214588 + +2014-05-19 08:48:50 +0200 Sebastian Dröge + + * omx/gstomx.c: + omx: Remove dead code, buf can never be NULL here as we just check for that the line above + CID 1214596 + +2014-05-19 08:47:36 +0200 Sebastian Dröge + + * omx/gstomx.c: + omx: Fix comparisons in gst_omx_state_to_string() case to actually make sense + CID 1214593 + +2014-05-19 08:45:10 +0200 Sebastian Dröge + + * omx/gstomx.c: + omx: Make sure to compare the error codes as unsigned integers so that comparisons >2**31 actually work + CID 1214592 + +2014-05-19 08:40:23 +0200 Sebastian Dröge + + * omx/gstomx.c: + omx: Fix comparisons in gst_omx_command_to_string() default cause to actually work + CID 1214591 + +2014-05-15 13:22:56 +0200 Sebastian Dröge + + * omx/gstomxaudioenc.c: + omxaudioenc: Implement hack for not disabling the output port after set_format until the output format is known + Needed on some OMX implementations, e.g. the one from Atmel. It does + not send the settings-changed event on the output port if it is + disabled. + +2014-05-15 13:21:07 +0200 Sebastian Dröge + + * omx/gstomxvideoenc.c: + omxvideoenc: Implement hack for not disabling the output port after set_format until the output format is known + Needed on some OMX implementations, e.g. the one from Atmel. It does + not send the settings-changed event on the output port if it is + disabled. + +2014-05-15 10:58:34 +0200 Sebastian Dröge + + * omx/gstomx.c: + * omx/gstomx.h: + * omx/gstomxvideodec.c: + omx: Add a hack for not disabling the output port after set_format until the output format is known + Needed on some OMX implementations, e.g. the one from Atmel. It does + not send the settings-changed event on the output port if it is + disabled. + +2014-05-12 12:33:32 +0200 Josep Torra + + * omx/gstomxaudiosink.c: + omxaudiosink: implement _delay only in the RaspberryPI + Make code implementation conditionally built for RaspberryPI because + OMX_IndexConfigAudioRenderingLatency seems to be a Broadcom extension. + On other targets the query position might not be accurate without + implementing _delay appropriatelly. + +2014-05-12 08:56:15 +0200 Sebastian Dröge + + * omx/gstomxaudioenc.c: + omxaudioenc: Correctly scale nTickCount by OMX_TICKS_PER_SECOND + +2014-05-10 22:48:23 +0200 Sebastian Dröge + + * omx/gstomxaudioenc.c: + omxaudioenc: Drain encoder on NULL buffer and don't drain on flushing + +2014-05-10 22:47:56 +0200 Sebastian Dröge + + * omx/gstomxvideodec.c: + omxvideodec: Set nTickCount based on the buffer's duration instead of something wrong + +2014-05-10 22:47:21 +0200 Sebastian Dröge + + * omx/gstomxvideoenc.c: + omxvideoenc: Set nTickCount to the whole duration of the buffer instead of a wrong calculation + +2014-05-10 22:46:51 +0200 Sebastian Dröge + + * omx/gstomxaudiosink.c: + omxaudiosink: Fix format string compiler warnings + +2014-04-04 14:11:58 +0200 Josep Torra + + * config/rpi/gstomx.conf: + * omx/Makefile.am: + * omx/gstomx.c: + * omx/gstomxanalogaudiosink.c: + * omx/gstomxanalogaudiosink.h: + * omx/gstomxaudiosink.c: + * omx/gstomxaudiosink.h: + * omx/gstomxhdmiaudiosink.c: + * omx/gstomxhdmiaudiosink.h: + omxaudiosink: Implements OpenMAX based audio sinks + Provides omxanalogaudiosink and omxhdmiaudiosink elements on + the Raspberry PI. + - omxanalogaudiosink is capable to render raw mono or stereo audio + through the jack output. + - omxhdmiaudiosink is capable to render raw audio up to 8 channels + and transmit ac3/dts(IEC 61937) through the HDMI output. + - sinks provide a clock derived from rendered samples + - sinks support the GstStreamVolume interface by implementing + the volume and mute properties. + https://bugzilla.gnome.org/show_bug.cgi?id=728962 + +2014-05-03 10:17:35 +0200 Sebastian Dröge + + * common: + Automatic update of common submodule + From bcb1518 to 211fa5f + +2014-04-29 15:16:16 +0100 Julien Isorce + + * omx/gstomxvideodec.c: + omxvideodec: can negotiate caps with memory:EGLImage feature when using EGLImage allocator + Previously when using gst EGLImage allocator the caps was + video/x-raw, format=RGBA instead of + video/x-raw(memory:EGLImage), format=RGBA + Kepp previous behavior in case negotiation fails with caps feature. + It means it will still have a chance to use EGLImage even if the + feature is not in the caps. + https://bugzilla.gnome.org/show_bug.cgi?id=729196 + +2014-04-23 09:57:48 +0200 Aurélien Zanelli + + * omx/gstomxvp8dec.h: + omxvp8dec: fix typo in GST_TYPE_OMX_VP8_DEC define + https://bugzilla.gnome.org/show_bug.cgi?id=728774 + +2014-04-16 11:00:55 +0200 Aurélien Zanelli + + * omx/gstomxvideodec.c: + omxvideodec: don't unref caps before logging field from it + https://bugzilla.gnome.org/show_bug.cgi?id=728322 + +2014-04-15 17:30:13 +0100 Julien Isorce + + * Makefile.am: + * configure.ac: + example: disable testegl since libgstegl has been removed + As decided in bug #703343 + Not compatible with the new libgstgl API. + A portage has been started, attachment 272800. + https://bugzilla.gnome.org/show_bug.cgi?id=703343 + +2014-04-15 17:11:08 +0100 Julien Isorce + + * omx/gstomxvideodec.c: + omxvideodec: use new libgstgl API since libgstegl has been removed + There is no point to retrieve a ref/unref type + instead of an EGLDisplay directly. It's like for EGLImage. + https://bugzilla.gnome.org/show_bug.cgi?id=703343 + +2014-04-15 17:06:38 +0100 Julien Isorce + + * configure.ac: + * examples/Makefile.am: + * omx/Makefile.am: + * omx/gstomxvideodec.c: + configure.ac: check for libgstgl since libgstegl has been removed + https://bugzilla.gnome.org/show_bug.cgi?id=703343 + +2014-04-09 18:52:16 +0200 Aurélien Zanelli + + * omx/gstomxbufferpool.c: + * omx/gstomxvideodec.c: + omxvideodec: add support of more color format + Add support for ABGR, ARGB, RGB16, BGR16, YUY2, UYVY, YVYU, GRAY8 and + NV16 color format. + +2014-04-09 18:51:57 +0200 Aurélien Zanelli + + * omx/gstomxvideodec.c: + omxvideodec: simplify color format conversion in fill_buffer function + +2014-04-09 18:51:41 +0200 Aurélien Zanelli + + * omx/gstomxbufferpool.c: + omxbufferpool: make video stride and offset calculation easier + It will be easier to support more color format. + +2014-04-09 18:51:12 +0200 Aurélien Zanelli + + * omx/gstomxvideo.c: + * omx/gstomxvideo.h: + * omx/gstomxvideodec.c: + omx: add an helper to convert OMX color format to GStreamer color format + +2014-03-10 17:43:50 +0100 Josep Torra + + * examples/egl/testegl.c: + * omx/gstomxvideodec.c: + omxvideodec: Implement pipeline draining to support adaptive scenarios + When draining due a format change also drain + the pipeline to reclaim back all buffers. + https://bugzilla.gnome.org/show_bug.cgi?id=726107 + +2014-03-27 13:57:32 +0100 Josep Torra + + * examples/egl/testegl.c: + examples: fix several memory leaks in the testegl example + Ensure to call to image_data_free in order to release GPU resources. + Also ensure to destroy EGLImage and GLTexture from proper + thread/context. + https://bugzilla.gnome.org/show_bug.cgi?id=726107 + +2014-03-25 17:16:31 +0000 Julien Isorce + + * examples/egl/testegl.c: + examples: keep a ref on the buffer instead of the memory + Like in eglglessink + https://bugzilla.gnome.org/show_bug.cgi?id=726107 + +2014-03-07 20:08:05 +0100 Josep Torra + + * omx/gstomxvideodec.c: + omxvideodec: fixes race condition during seeks + Acording 6.1.3 Seek Event Sequence in the OpenMAX IL 1.1.2 spec + document in order to flush the component it needs to be in + paused state. + https://bugzilla.gnome.org/show_bug.cgi?id=726038 + +2014-01-29 18:31:26 +0000 Julien Isorce + + * omx/gstomxvideodec.c: + omxvideodec: use flush because reset is deprecated + https://bugzilla.gnome.org/show_bug.cgi?id=726038 + +2014-01-27 17:03:50 +0000 Julien Isorce + + * omx/gstomxvideodec.c: + omxvideodec: populate the most downstream output port on reset + Make seeking work when using egl_render component + https://bugzilla.gnome.org/show_bug.cgi?id=726038 + +2014-03-24 17:49:59 +0100 Josep Torra + + * omx/gstomxbufferpool.c: + omxbufferpool: return buffers to the pool instead of freeing them + We have to return the buffers back to the pool in when stopping to + not mess with the GstBufferPool accounting. + The OMX buffers will be freed when those won't be in charge of the + pool in the chained up call to 'stop'. + Fixes segfaults on finalize and pool not being properly deactivated. + https://bugzilla.gnome.org/show_bug.cgi?id=726337 + +2014-03-19 12:12:49 +0100 Christian König + + * omx/gstomxvideodec.c: + omxvideodec: add missing unlock in the error path + Signed-off-by: Christian König + https://bugzilla.gnome.org/show_bug.cgi?id=726958 + +2014-03-18 23:36:59 +0100 Michal Lazo + + * omx/gstomxh264enc.c: + * omx/gstomxh264enc.h: + fix filemode + +2014-03-17 09:57:11 +0000 Julien Isorce + + * omx/gstomxbufferpool.c: + omxbufferpool: fix memory leak if used on output port + When using GstOMXBufferPool on an output port, it internally uses + a GPtrArray to manage the GstBuffers instead of the default queue + from the GstBufferPool base class. + In this case GstBufferPool::default_free_buffer is not called when + the pool is stopped. Because the queue is empty. So explicitely + call gst_omx_buffer_pool_free_buffer on each buffer contained in + the GPtrArray. + https://bugzilla.gnome.org/show_bug.cgi?id=726337 + +2014-03-16 17:32:05 +0100 Sebastian Dröge + + * omx/gstomxh264enc.c: + omxh264enc: Fix compiler warnings + +2014-03-16 17:19:08 +0100 Michal Lazo + + * omx/gstomxh264enc.c: + * omx/gstomxh264enc.h: + omxh264enc: IDR interval, SPS and PPS headers for rpi + https://bugzilla.gnome.org/show_bug.cgi?id=720031 + +2014-03-13 14:26:58 +0100 Christian König + + * omx/gstomx.c: + * omx/gstomx.h: + * omx/gstomxvideo.c: + omxvideo: fix debug category initialisation + https://bugzilla.gnome.org/show_bug.cgi?id=726024 + +2014-03-13 19:04:47 +0100 Christian König + + * omx/gstomxbufferpool.h: + omxbufferpool: add proper type definitions + https://bugzilla.gnome.org/show_bug.cgi?id=726325 + +2014-03-02 10:30:04 +0100 Christian König + + * omx/gstomx.c: + omx: consolidate message waiting code + Add a wait_message helper function and remove all those duplicated code. + https://bugzilla.gnome.org/show_bug.cgi?id=725493 + +2014-03-12 12:48:12 +0100 Sebastian Dröge + + * omx/gstomxvideo.c: + omx: Copy old copyright notice into the new file + +2014-03-12 12:47:34 +0100 Sebastian Dröge + + * omx/gstomxvideo.c: + * omx/gstomxvideo.h: + * omx/gstomxvideodec.c: + * omx/gstomxvideoenc.c: + omx: Rename function from _4_ to _for_ for clarity + +2014-03-03 16:15:24 +0100 Christian König + + * omx/Makefile.am: + * omx/gstomxvideo.c: + * omx/gstomxvideo.h: + * omx/gstomxvideodec.c: + * omx/gstomxvideoenc.c: + omxvideo: start sharing more code between video decoder and encoder + Identical functionality spread of two different components. + We can't use a common base class because of different inheritance, + but let's try to share the code anyway. + https://bugzilla.gnome.org/show_bug.cgi?id=726024 + +2014-03-12 12:43:49 +0100 Sebastian Dröge + + * examples/egl/Makefile.am: + examples: Only build RPi EGL example if RPi was chosen as target + +2014-03-12 12:42:23 +0100 Sebastian Dröge + + * omx/gstomxbufferpool.h: + omxbufferpool: Fix header include guard + +2014-03-05 11:41:02 +0100 Christian König + + * omx/Makefile.am: + * omx/gstomxbufferpool.c: + * omx/gstomxbufferpool.h: + * omx/gstomxvideodec.c: + omxvideodec: separate the buffer pool from the decoder + https://bugzilla.gnome.org/show_bug.cgi?id=726025 + +2014-03-04 17:41:20 +0100 Christian König + + * omx/gstomx.c: + * omx/gstomx.h: + * omx/gstomxvideodec.c: + omx: simplify tunnel functions + Specifying the component is error prone and unnecessary. + https://bugzilla.gnome.org/show_bug.cgi?id=726021 + +2014-03-07 17:12:24 +0100 Christian König + + * omx/gstomxvideodec.c: + omxvideodec: fix memory leak in gst_omx_video_dec_allocate_output_buffers + https://bugzilla.gnome.org/show_bug.cgi?id=725907 + +2014-03-07 13:18:49 +0100 Christian König + + * omx/gstomxvideodec.c: + omxvideodec: fix memory leak gst_omx_video_dec_negotiate + https://bugzilla.gnome.org/show_bug.cgi?id=725907 + +2014-03-05 18:54:05 +0100 Christian König + + * omx/gstomxvideoenc.c: + omxvideoenc: fix a memory leak in gst_omx_video_enc_getcaps + https://bugzilla.gnome.org/show_bug.cgi?id=725826 + +2014-03-05 17:43:33 +0100 Josep Torra + + * Makefile.am: + Makefile.am: build examples + There's no reason to not build the examples now that are ported to 1.x. + +2014-03-05 17:25:09 +0100 Josep Torra + + * examples/egl/testegl.c: + examples: avoid a clashing name with the recently added GstEGLImagePool + Fixes build with current master. + +2013-09-05 03:41:10 -0600 Christian König + + * omx/gstomxvideoenc.c: + omxvideoenc: simplify _find_nearest_frame + Just the same as we did with the decoder. Also give the + function a gst_omx_video_enc prefix to distinct it from + the decoder function. + https://bugzilla.gnome.org/show_bug.cgi?id=724236 + +2013-09-05 02:23:39 -0600 Christian König + + * omx/gstomxvideodec.c: + omxvideodec: simplify _find_nearest_frame + No need to make it more complicated and error prone than + necessary. Also give the function a gst_omx_video_dec prefix + to distinct it from the encoder function. + https://bugzilla.gnome.org/show_bug.cgi?id=724236 + +2013-09-05 02:05:52 -0600 Christian König + + * omx/gstomxvideodec.c: + omxvideodec: remove dead code + This code doesn't seems to be used for quite a while, + remove it before it starts to rot. + https://bugzilla.gnome.org/show_bug.cgi?id=724236 + +2014-03-01 22:28:24 +0100 Christian König + + * omx/gstomx.c: + omx: fix two serious message handling bugs + Waiting for the next message if we already got one + is nonsense and can lead to lockups. + https://bugzilla.gnome.org/show_bug.cgi?id=725468 + +2014-03-01 18:49:41 +0100 Christian König + + * omx/gstomxvideoenc.c: + omxvideoenc: fix startup race condition + The reset function shouldn't start the src pad + loop if it wasn't started before. + Signed-off-by: Christian König + +2014-03-01 18:48:17 +0100 Christian König + + * omx/gstomxvideodec.c: + omxvideodec: fix startup race condition + The reset function shouldn't start the src pad + loop if it wasn't started before. + Signed-off-by: Christian König + +2014-02-28 09:36:13 +0100 Sebastian Dröge + + * common: + Automatic update of common submodule + From fe1672e to bcb1518 + +2014-02-26 22:15:00 +0100 Stefan Sauer + + * common: + Automatic update of common submodule + From 1a07da9 to fe1672e + +2014-01-30 10:45:18 +0100 Edward Hervey + + * common: + Automatic update of common submodule + From d48bed3 to 1a07da9 + +2014-01-25 17:44:14 +0100 Sebastian Dröge + + * omx/gstomxaudioenc.c: + * omx/gstomxvideodec.c: + * omx/gstomxvideoenc.c: + omx: Don't handle FLUSHING and NOT_LINKED as errors + Also don't stop the task on NOT_LINKED. We're not a demuxer. + +2013-12-22 22:35:31 +0000 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From dbedaa0 to d48bed3 + +2013-11-05 11:22:02 +0000 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From 865aa20 to dbedaa0 + +2013-09-28 13:32:37 +0200 Josep Torra + + * examples/egl/testegl.c: + examples: simplify the thread synchronization code + Make everithing more simple and fix the races conditions remaining in + the previous approaches. + +2013-09-25 19:17:17 +0200 Sebastian Dröge + + * config/rpi/gstomx.conf: + rpi: The WMV/VC1 decoder can only do WMV9 and VC1, no older versions + +2013-09-24 18:34:42 +0100 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From 6b03ba7 to 865aa20 + +2013-09-24 18:48:24 +0200 Josep Torra + + * examples/egl/testegl.c: + examples: fix another race condition + Fix a race condition that caused randome deadlocks on EOS. + +2013-09-20 17:19:53 +0200 Josep Torra + + * config/rpi/gstomx.conf: + rpi: fix a copy paste error in the config file + +2013-09-20 17:09:52 +0200 Josep Torra + + * examples/egl/testegl.c: + examples: fix a race condition when seeking + Fixes a race condition that caused pipeline deadlock during seeks. + +2013-09-20 10:38:12 +0200 Josep Torra + + * examples/egl/testegl.c: + examples: display QoS statistics + +2013-09-20 09:34:37 +0200 Josep Torra + + * examples/egl/testegl.c: + examples: use dedicated thread for rendering the scene + Produces smother animation and prevents dropping frames due busy + mainloop. + +2013-09-20 08:25:21 +0200 Josep Torra + + * examples/egl/testegl.c: + examples: don't force an specific audio sink + Let playbin2 choose the audiosink available in the system. + +2013-09-20 08:19:48 +0200 Josep Torra + + * examples/egl/Makefile.am: + examples: drop remnants of initial appsink attempt + +2013-09-20 16:18:18 +0200 Edward Hervey + + * common: + Automatic update of common submodule + From b613661 to 6b03ba7 + +2013-09-19 18:45:36 +0100 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From 74a6857 to b613661 + +2013-09-19 17:38:30 +0100 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From 01a7a46 to 74a6857 + +2013-08-20 16:00:07 +0100 Tim-Philipp Müller + + * omx/gstomx.c: + * omx/gstomxvideodec.c: + omx: don't use the 'z' modifier to print size_t + gcc will warn in some cases even if the size of the type + is exactly that of size_t on the platform. + https://bugzilla.gnome.org/show_bug.cgi?id=699008 + +2013-07-01 15:48:47 +0200 Roman Arutyunyan + + * omx/gstomxvideoenc.c: + gstomxvideoenc: Set bitrate in setcaps + Otherwise it gets lost whenever we configure new caps + https://bugzilla.gnome.org/show_bug.cgi?id=698049 + +2013-06-30 18:17:05 +0700 Ilya Smelykh + + * examples/egl/testegl.c: + examples: enable audio in testegl example + +2013-06-12 09:38:22 +0200 Sebastian Dröge + + * configure.ac: + configure: Allow build without gstreamer-egl + +2013-06-07 12:39:18 +0700 Ilya Smelykh + + * examples/egl/Makefile.am: + * examples/egl/testegl.c: + examples: testegl example port to 1.x + https://bugzilla.gnome.org/show_bug.cgi?id=701706 + +2013-06-05 15:17:16 +0200 Sebastian Dröge + + * common: + Automatic update of common submodule + From 098c0d7 to 01a7a46 + +2013-05-20 12:06:34 +0200 Josep Torra + + * omx/gstomxaudioenc.c: + * omx/gstomxvideodec.c: + * omx/gstomxvideoenc.c: + omx: Take lock on EOS to update the flow return value + Fixes "GThread-ERROR **: file gthread-posix.c: line 171 + (g_mutex_free_posix_impl): error 'Device or resource busy' during + 'pthread_mutex_destroy ((pthread_mutex_t *) mutex)'" in _finalize. + +2013-05-15 10:54:12 +0200 Sebastian Dröge + + * common: + Automatic update of common submodule + From 5edcd85 to 098c0d7 + +2013-04-11 17:35:19 +0200 Josep Torra + + * Makefile.am: + * configure.ac: + * examples/Makefile.am: + * examples/egl/Makefile.am: + * examples/egl/cube_texture_and_coords.h: + * examples/egl/testegl.c: + examples: add an example aplication based OpenGL ES + EGL + Application that shows how to integrate playbin with an OpenGL ES + scene through EGL. Renders a video on the surfaces of an animated cube. + The code is not ported to 1.x so it's not built by default. + +2013-05-10 12:25:07 +0200 Josep Torra + + * omx/gstomxvideodec.c: + omxvideodec: Redesign video size change reconfiguration code + Ensure stop the decoder before clossing the tunnel. + +2013-05-06 16:25:27 +0200 Sebastian Dröge + + * Makefile.am: + Makefile.am: Add -I common/m4 + This allows autoreconf to work correctly and automatic regeneration + of autotools files if something changed. + +2013-05-06 19:03:59 +0530 jitendra + + * omx/gstomx.c: + * omx/gstomx.h: + * omx/gstomxaudioenc.c: + * omx/gstomxvideodec.c: + * omx/gstomxvideoenc.c: + omx: Add pads based on element type + This allows to later add sources and sink that only have a srcpad + or sinkpad. + https://bugzilla.gnome.org/show_bug.cgi?id=699754 + +2013-04-27 02:50:25 +0200 Carlos Rafael Giani + + * omx/gstomx.c: + omx: fixed type error in printf call + %zu expects size_t + https://bugzilla.gnome.org/show_bug.cgi?id=699008 + +2013-04-08 17:26:16 +0100 Tim-Philipp Müller + + * configure.ac: + * omx/Makefile.am: + * omx/gstomxvideodec.c: + Check for gstreamer-egl + And don't use if not available. + https://bugzilla.gnome.org/show_bug.cgi?id=697574 + +2013-04-23 09:53:18 +0100 Tim-Philipp Müller + + * configure.ac: + configure: error out if no OMX target has been set explicitly with --with-omx-target=x + Avoids people building for e.g. the Raspberry Pi and then wondering + why things don't work as expected (since structs are packed differently + there). + +2013-04-22 23:55:03 +0100 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From 3cb3d3c to 5edcd85 + +2013-04-18 22:07:28 +0000 Tim-Philipp Müller + + * omx/gstomx.c: + * omx/gstomxaudioenc.c: + * omx/gstomxh263enc.c: + * omx/gstomxh264enc.c: + * omx/gstomxmpeg4videoenc.c: + * omx/gstomxvideodec.c: + * omx/gstomxvideoenc.c: + omx: more printf format fixes + Fix printf formats again, so that gst-omx compiles warning- + free on the Raspberry Pi as well. Unfortunately OMX_UINT32 + maybe be typedefed to uint32_t or unsigned long, which + doesn't work well with our debugging printf format strings, + so just use %u for those and cast to guint. + +2013-04-18 16:40:06 +0200 Josep Torra + + * omx/gstomx.c: + omx: fixes unused variable 'comp' when GStreamer is built without debug + +2013-04-18 16:03:56 +0200 Josep Torra + + * omx/gstomxvideodec.c: + omxvideodec: don't use 'self->dec_out_port' anymore and use just 'port' + Fixes some criticals. + +2013-04-18 15:21:32 +0200 Josep Torra + + * omx/gstomxvideodec.c: + omxvideodec: fixes 'port' may be used uninitialized in this function + +2013-04-18 12:03:31 +0200 Josep Torra + + * omx/gstomxvideodec.c: + omxvideodec: silence warnings building for RPI related to 'vcos_*' + +2013-04-18 11:19:52 +0200 Josep Torra + + * omx/gstomxvideodec.c: + omxvideodec: Use new type from libgstvideo + +2013-04-16 14:50:49 +0530 jitendra + + * omx/gstomxaudioenc.c: + * omx/gstomxvideodec.c: + * omx/gstomxvideoenc.c: + omx: Disable output port before transition to idle state + https://bugzilla.gnome.org/show_bug.cgi?id=698109 + +2012-10-24 12:19:41 +0200 Sebastian Dröge + + * configure.ac: + gst: Add better support for static plugins + +2013-04-14 17:57:34 +0100 Tim-Philipp Müller + + * common: + Automatic update of common submodule + From aed87ae to 3cb3d3c + +2013-04-12 17:58:30 +0100 Tim-Philipp Müller + + * configure.ac: + configure: add --disable-fatal-warnings for disabling -Werror + +2013-04-09 21:02:09 +0200 Stefan Sauer + + * common: + Automatic update of common submodule + From 04c7a1e to aed87ae + +2013-04-08 17:02:32 +0100 Tim-Philipp Müller + + * omx/gstomx.c: + * omx/gstomxaudioenc.c: + * omx/gstomxh263enc.c: + * omx/gstomxh264enc.c: + * omx/gstomxmpeg4videoenc.c: + * omx/gstomxvideodec.c: + * omx/gstomxvideoenc.c: + omx: fix printf formats in debug messages + OMX_U32 is typedefed to an unsigned long, + OMX_TICKS to a 64-bit integer. + +2013-04-08 16:52:19 +0200 Josep Torra + + * omx/gstomxvideodec.c: + omxvideodec: use the correct printf format in a debug message + +2013-04-08 16:31:33 +0200 Josep Torra + + * omx/gstomxvideodec.c: + omxvideodec: use the correct OMX_IndexParam value + Fixes playback is not smooth in the EGL path. + +2013-04-05 13:45:24 +0200 Sebastian Dröge + + * omx/gstomxvideodec.c: + omxvideodec: Don't use API that is not in master yet + It's not really needed here yet, will be needed in future versions + +2013-02-25 11:55:04 +0100 Sebastian Dröge + + * configure.ac: + * omx/Makefile.am: + * omx/gstomx.c: + * omx/gstomx.h: + * omx/gstomxvideodec.c: + * omx/gstomxvideodec.h: + omxvideodec: Add support for egl_render on RPi + +2013-03-22 19:26:54 +0000 Tim-Philipp Müller + + * configure.ac: + Back to development + +2013-03-22 19:23:14 +0000 Tim-Philipp Müller + + * omx/gstomxh263enc.c: + * omx/gstomxh264enc.c: + * omx/gstomxmpeg4videoenc.c: + omx: fix compiler warnings when compiling with -DG_DISABLE_ASSERT + As we do for releases. Fixes 'variable may be used uninitialized' + warnings. + === release 1.0.0 === -2013-03-22 Sebastian Dröge +2013-03-22 17:16:33 +0100 Sebastian Dröge + * AUTHORS: + * ChangeLog: + * Makefile.am: + * NEWS: + * RELEASE: * configure.ac: - releasing 1.0.0 + * gst-omx.doap: + Release 1.0.0 2013-03-19 16:40:09 +0000 Tim-Philipp Müller diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 7d1c323..0000000 --- a/INSTALL +++ /dev/null @@ -1,365 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. - - Copying and distribution of this file, with or without modification, -are permitted in any medium without royalty provided the copyright -notice and this notice are preserved. This file is offered as-is, -without warranty of any kind. - -Basic Installation -================== - - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. Some packages provide this -`INSTALL' file but do not implement all of the features documented -below. The lack of an optional feature in a given package is not -necessarily a bug. More recommendations for GNU packages can be found -in *note Makefile Conventions: (standards)Makefile Conventions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - - The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package, generally using the just-built uninstalled binaries. - - 4. Type `make install' to install the programs and any data files and - documentation. When installing into a prefix owned by root, it is - recommended that the package be configured and built as a regular - user, and only the `make install' phase executed with root - privileges. - - 5. Optionally, type `make installcheck' to repeat any self-tests, but - this time using the binaries in their final installed location. - This target does not install anything. Running this target as a - regular user, particularly if the prior `make install' required - root privileges, verifies that the installation completed - correctly. - - 6. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - 7. Often, you can also type `make uninstall' to remove the installed - files again. In practice, not all packages have tested that - uninstallation works correctly, even though it is required by the - GNU Coding Standards. - - 8. Some packages, particularly those that use Automake, provide `make - distcheck', which can by used by developers to test that all other - targets like `make install' and `make uninstall' work correctly. - This target is generally not run by end users. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. Run `./configure --help' -for details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. This -is known as a "VPATH" build. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - - On MacOS X 10.5 and later systems, you can create libraries and -executables that work on multiple system types--known as "fat" or -"universal" binaries--by specifying multiple `-arch' options to the -compiler but only a single `-arch' option to the preprocessor. Like -this: - - ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CPP="gcc -E" CXXCPP="g++ -E" - - This is not guaranteed to produce working output in all cases, you -may have to build one architecture at a time and combine the results -using the `lipo' tool if you have problems. - -Installation Names -================== - - By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX', where PREFIX must be an -absolute file name. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. In general, the -default for these options is expressed in terms of `${prefix}', so that -specifying just `--prefix' will affect all of the other directory -specifications that were not explicitly provided. - - The most portable way to affect installation locations is to pass the -correct locations to `configure'; however, many packages provide one or -both of the following shortcuts of passing variable assignments to the -`make install' command line to change installation locations without -having to reconfigure or recompile. - - The first method involves providing an override variable for each -affected directory. For example, `make install -prefix=/alternate/directory' will choose an alternate location for all -directory configuration variables that were expressed in terms of -`${prefix}'. Any directories that were specified during `configure', -but not in terms of `${prefix}', must each be overridden at install -time for the entire installation to be relocated. The approach of -makefile variable overrides for each directory variable is required by -the GNU Coding Standards, and ideally causes no recompilation. -However, some platforms have known limitations with the semantics of -shared libraries that end up requiring recompilation when using this -method, particularly noticeable in packages that use GNU Libtool. - - The second method involves providing the `DESTDIR' variable. For -example, `make install DESTDIR=/alternate/directory' will prepend -`/alternate/directory' before all installation names. The approach of -`DESTDIR' overrides is not required by the GNU Coding Standards, and -does not work on platforms that have drive letters. On the other hand, -it does better at avoiding recompilation issues, and works well even -when some directory options were not specified in terms of `${prefix}' -at `configure' time. - -Optional Features -================= - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - - Some packages offer the ability to configure how verbose the -execution of `make' will be. For these packages, running `./configure ---enable-silent-rules' sets the default to minimal output, which can be -overridden with `make V=1'; while running `./configure ---disable-silent-rules' sets the default to verbose, which can be -overridden with `make V=0'. - -Particular systems -================== - - On HP-UX, the default C compiler is not ANSI C compatible. If GNU -CC is not installed, it is recommended to use the following options in -order to use an ANSI C compiler: - - ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" - -and if that doesn't work, install pre-built binaries of GCC for HP-UX. - - On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot -parse its `' header file. The option `-nodtk' can be used as -a workaround. If GNU CC is not installed, it is therefore recommended -to try - - ./configure CC="cc" - -and if that doesn't work, try - - ./configure CC="cc -nodtk" - - On Solaris, don't put `/usr/ucb' early in your `PATH'. This -directory contains several dysfunctional programs; working variants of -these programs are available in `/usr/bin'. So, if you need `/usr/ucb' -in your `PATH', put it _after_ `/usr/bin'. - - On Haiku, software installed for all users goes in `/boot/common', -not `/usr/local'. It is recommended to use the following options: - - ./configure --prefix=/boot/common - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS - KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of all of the options to `configure', and exit. - -`--help=short' -`--help=recursive' - Print a summary of the options unique to this package's - `configure', and exit. The `short' variant lists options used - only in the top level, while the `recursive' variant lists options - also present in any nested packages. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--prefix=DIR' - Use DIR as the installation prefix. *note Installation Names:: - for more details, including other options available for fine-tuning - the installation locations. - -`--no-create' -`-n' - Run the configure checks, but stop before creating any output - files. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/Makefile.am b/Makefile.am index 424630a..fd79a40 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,10 @@ -SUBDIRS = common omx tools config +SUBDIRS = common omx tools config -ACLOCAL_AMFLAGS = -I m4 +if BUILD_EXAMPLES +SUBDIRS += examples +endif + +ACLOCAL_AMFLAGS = -I m4 -I common/m4 DISTCLEANFILES = _stdint.h diff --git a/NEWS b/NEWS index cffbaae..41a6e0a 100644 --- a/NEWS +++ b/NEWS @@ -1 +1 @@ -This is gst-omx 1.0.0 +This is gst-omx 1.2.0 diff --git a/RELEASE b/RELEASE index b304649..50d7090 100644 --- a/RELEASE +++ b/RELEASE @@ -1,15 +1,9 @@ -Release notes for GStreamer OpenMAX IL wrapper plugin 1.0.0 - -The GStreamer team is proud to announce a new feature release -in the 1.x stable series of the GStreamer OpenMAX IL -wrapper plugin. - - -The 1.x series is a stable series targeted at end users. -It is not API or ABI compatible with the stable 0.10.x series. -It is, however, parallel installable with the 0.10.x series and -will not affect an existing 0.10.x installation. +Release notes for GStreamer OpenMAX IL wrapper plugin 1.2.0 + +The GStreamer project is proud to announce a new release of the +GStreamer OpenMAX IL wrapper plugin for the API and ABI-stable +1.x series of the GStreamer multimedia framework. @@ -40,13 +34,53 @@ contains a set of less supported plugins that haven't passed the Features of this release - * Parallel installability with 0.10.x series * Generic support for OpenMAX IL implementations - * Special support for features of: Raspberry Pi + * Support for audio decoders + * Support for raw and encoded audio sink + * Improved support for the Raspberry Pi + * Many bugfixes to work properly with AMD's, NVIDIA's, Qualcomm's and other OpenMAX IL implementations Bugs fixed in this release - * 692446 : Add mpeg2 support + * 697574 : Only use EGL code paths conditionally if gstreamer-egl is available + * 698049 : omxh264enc: openmax API ignores output bitrate + * 698109 : Disable output port before transition to idle state + * 699008 : Fix printf format compiler warning + * 699754 : Don't force elements of type source/sink to add two pads + * 701706 : gst-omx testegl.c example not compatible with gstreamer 1.x + * 710564 : Need to convert OMX alignments to GStreamer alignments + * 710948 : omxvideodec: Doesn't recover after aspect ratio changes + * 720031 : omxh264enc: key frame interval missing + * 723176 : omx: Examples need to be ported to 1.0 + * 723386 : Bad port index playing VC-1 WMV in Raspberry Pi + * 723851 : omxvideoenc/dec: Fix for a startup race condition + * 724236 : omx: Enhancements/cleanup for decoder/encoder frame handling + * 725468 : Fix two serious message handling bugs + * 725493 : Consolidate message waiting code + * 725826 : omxvideoenc: Fix a minor memory leak in gst_omx_video_enc_getcaps + * 725907 : omxvideodec: Two more minor fixes for memory leaks + * 726021 : Simplify tunnel functions + * 726024 : Share more code between video decoder and encoder + * 726025 : omxvideodec: separate the buffer pool from the decoder + * 726038 : omxvideodec: Multiple issues during seeks + * 726107 : omxvideodec: Drain pipeline to support adaptive streaming scenarios and partially fix gpu resource leaks + * 726337 : omxbufferpool: leak buffers when stopped + * 726669 : omxh264enc: Properly accumulate headers and provide them to the base class + * 726957 : Add configure option for external omxil headers + * 726958 : omxvideodec: add missing unlock in the error path + * 727825 : omxvideodec: Enhance colorformat support + * 728322 : omvideodec: don't unref caps before logging field from it + * 728774 : omxvp8dec: fix typo in GST_TYPE_OMX_VP8_DEC define + * 728940 : gst-omx: port testegl example to new libgstgl + * 728962 : Add audiosink elements to support raw and ac3/dts passthrough on the RPI + * 729196 : omxvideodec: no memory:EGLImage feature in the caps when using eglimage allocator + * 730989 : omxdec: Get stuck while doing ctrl+c during preroll + * 730995 : omxvideodec: Memory leak with interlaced h264 streams + * 731141 : Fix a missing g_free() in error path + * 731672 : omxvideodec: uses non-standard stride without videometa + * 731898 : omxvideodec: fix a query leak + * 732518 : Link with gmodule-2.0-no-export for g_module_*() API + * 733168 : omxaudiosink: Does not set ports to non-flushing after prepare ==== Download ==== @@ -83,20 +117,19 @@ subscribe to the gstreamer-devel list. Contributors to this release - * Alessandro Decina - * Arun Raghavan - * Dake Gu + * Aurélien Zanelli + * Carlos Rafael Giani + * Christian König * Edward Hervey - * George Kiagiadakis - * Jonas Larsson + * Ilya Smelykh * Josep Torra - * Julian Scheel - * Matej Knopp - * Nicolas Dufresne - * Olivier Crête - * Reynaldo H. Verdejo Pinochet + * Julien Isorce + * Kazunori Kobayashi + * Michal Lazo + * Roman Arutyunyan * Sebastian Dröge * Stefan Sauer * Tim-Philipp Müller - * Vincent Penquerc'h + * Zhao, Halley + * jitendra   \ No newline at end of file diff --git a/common b/common deleted file mode 160000 index 04c7a1e..0000000 --- a/common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 04c7a1ec1b9ced0b4359b3bbb2d8dc87bc7642c8 diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..25966fc --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS = m4 + +EXTRA_DIST = \ + ChangeLog \ + gettext.patch \ + glib-gen.mak gtk-doc.mak upload-doc.mak \ + cruft.mak release.mak win32.mak po.mak \ + parallel-subdirs.mak \ + gst-autogen.sh \ + check-exports \ + c-to-xml.py mangle-tmpl.py scangobj-merge.py \ + gtk-doc-plugins.mak \ + plugins.xsl gstdoc-scangobj \ + gst.supp check.mak \ + coverage/lcov.mak \ + coverage/coverage-report.pl \ + coverage/coverage-report.xsl \ + coverage/coverage-report-entry.pl \ + download-translations \ + extract-release-date-from-doap-file \ + gst-indent \ + orc.mak diff --git a/common/extract-release-date-from-doap-file b/common/extract-release-date-from-doap-file new file mode 100644 index 0000000..f57e307 --- /dev/null +++ b/common/extract-release-date-from-doap-file @@ -0,0 +1,32 @@ +#!/bin/sh +# Shell script to extract the date given a release version and a .doap file + +if test "x$1" = "x" -o "x$2" = "x" -o ! -s "$2"; then + echo "Usage: $0 RELEASE-VERSION-NUMBER DOAP-FILE" >&2; + exit 1 +fi + +if ! grep '/dev/null ; then + echo "$2 does not look lika a .doap file" >&2; + exit 1 +fi + +if ! grep "$1" "$2" >/dev/null ; then + echo "$2 contains no reference to a version $1" >&2; + exit 1 +fi + +awk 'BEGIN {x=0} +{ +if ( $0 ~ // ) {x=1; chunk=""} +if (x==1) { + if ($0 ~ //) { chunk = chunk $0 } + if ($0 ~ //) { chunk = chunk $0 } +} +if ($0 ~ /<\/release>/) {x=0; print chunk} +}' < "$2" | \ +\ +grep ''"$1"'' | \ +\ +sed -e 's/^.*//' -e 's/<\/created>.*$//' + diff --git a/common/gst-autogen.sh b/common/gst-autogen.sh new file mode 100644 index 0000000..78cab66 --- /dev/null +++ b/common/gst-autogen.sh @@ -0,0 +1,345 @@ +# a silly hack that generates autoregen.sh but it's handy +# Remove the old autoregen.sh first to create a new file, +# as the current one may be being read by the shell executing +# this script. +if [ -f "autoregen.sh" ]; then + rm autoregen.sh +fi +echo "#!/bin/sh" > autoregen.sh +echo "./autogen.sh $@ \$@" >> autoregen.sh +chmod +x autoregen.sh + +# helper functions for autogen.sh + +debug () +# print out a debug message if DEBUG is a defined variable +{ + if test ! -z "$DEBUG" + then + echo "DEBUG: $1" + fi +} + +version_get () +# based on the command's version output, set variables +# _MAJOR, _MINOR, _MICRO, _VERSION, using the given prefix as variable prefix +# +# arg 1: command binary name +# arg 2: (uppercased) variable name prefix +{ + COMMAND=$1 + VARPREFIX=`echo $2 | tr .,- _` + local ${VARPREFIX}_VERSION + + # strip everything that's not a digit, then use cut to get the first field + pkg_version=`$COMMAND --version|head -n 1|sed 's/^.*)[^0-9]*//'|cut -d' ' -f1` + debug "pkg_version $pkg_version" + # remove any non-digit characters from the version numbers to permit numeric + # comparison + pkg_major=`echo $pkg_version | cut -d. -f1 | sed s/[a-zA-Z\-].*//g` + pkg_minor=`echo $pkg_version | cut -d. -f2 | sed s/[a-zA-Z\-].*//g` + pkg_micro=`echo $pkg_version | cut -d. -f3 | sed s/[a-zA-Z\-].*//g` + test -z "$pkg_major" && pkg_major=0 + test -z "$pkg_minor" && pkg_minor=0 + test -z "$pkg_micro" && pkg_micro=0 + debug "found major $pkg_major minor $pkg_minor micro $pkg_micro" + eval ${VARPREFIX}_MAJOR=$pkg_major + eval ${VARPREFIX}_MINOR=$pkg_minor + eval ${VARPREFIX}_MICRO=$pkg_micro + eval ${VARPREFIX}_VERSION=$pkg_version +} + +version_compare () +# Checks whether the version of VARPREFIX is equal to or +# newer than the requested version +# arg1: VARPREFIX +# arg2: MAJOR +# arg3: MINOR +# arg4: MICRO +{ + VARPREFIX=`echo $1 | tr .,- _` + MAJOR=$2 + MINOR=$3 + MICRO=$4 + + eval pkg_major=\$${VARPREFIX}_MAJOR; + eval pkg_minor=\$${VARPREFIX}_MINOR; + eval pkg_micro=\$${VARPREFIX}_MICRO; + + #start checking the version + debug "version_compare: $VARPREFIX against $MAJOR.$MINOR.$MICRO" + + # reset check + WRONG= + + if [ ! "$pkg_major" -gt "$MAJOR" ]; then + debug "major: $pkg_major <= $MAJOR" + if [ "$pkg_major" -lt "$MAJOR" ]; then + debug "major: $pkg_major < $MAJOR" + WRONG=1 + elif [ ! "$pkg_minor" -gt "$MINOR" ]; then + debug "minor: $pkg_minor <= $MINOR" + if [ "$pkg_minor" -lt "$MINOR" ]; then + debug "minor: $pkg_minor < $MINOR" + WRONG=1 + elif [ "$pkg_micro" -lt "$MICRO" ]; then + debug "micro: $pkg_micro < $MICRO" + WRONG=1 + fi + fi + fi + if test ! -z "$WRONG"; then + debug "version_compare: $VARPREFIX older than $MAJOR.$MINOR.$MICRO" + return 1 + fi + debug "version_compare: $VARPREFIX equal to/newer than $MAJOR.$MINOR.$MICRO" + return 0 +} + + +version_check () +# check the version of a package +# first argument : package name (executable) +# second argument : optional path where to look for it instead +# third argument : source download url +# rest of arguments : major, minor, micro version +# all consecutive ones : suggestions for binaries to use +# (if not specified in second argument) +{ + PACKAGE=$1 + PKG_PATH=$2 + URL=$3 + MAJOR=$4 + MINOR=$5 + MICRO=$6 + + # for backwards compatibility, we let PKG_PATH=PACKAGE when PKG_PATH null + if test -z "$PKG_PATH"; then PKG_PATH=$PACKAGE; fi + debug "major $MAJOR minor $MINOR micro $MICRO" + VERSION=$MAJOR + if test ! -z "$MINOR"; then VERSION=$VERSION.$MINOR; else MINOR=0; fi + if test ! -z "$MICRO"; then VERSION=$VERSION.$MICRO; else MICRO=0; fi + + debug "major $MAJOR minor $MINOR micro $MICRO" + + for SUGGESTION in $PKG_PATH; do + COMMAND="$SUGGESTION" + + # don't check if asked not to + test -z "$NOCHECK" && { + printf " checking for $COMMAND >= $VERSION ... " + } || { + # we set a var with the same name as the package, but stripped of + # unwanted chars + VAR=`echo $PACKAGE | sed 's/-//g'` + debug "setting $VAR" + eval $VAR="$COMMAND" + return 0 + } + + which $COMMAND > /dev/null 2>&1 + if test $? -eq 1; + then + debug "$COMMAND not found" + continue + fi + + VARPREFIX=`echo $COMMAND | sed 's/-//g' | tr [:lower:] [:upper:]` + version_get $COMMAND $VARPREFIX + + version_compare $VARPREFIX $MAJOR $MINOR $MICRO + if test $? -ne 0; then + echo "found $pkg_version, not ok !" + continue + else + echo "found $pkg_version, ok." + # we set a var with the same name as the package, but stripped of + # unwanted chars + VAR=`echo $PACKAGE | sed 's/-//g'` + debug "setting $VAR" + eval $VAR="$COMMAND" + return 0 + fi + done + + echo "$PACKAGE not found !" + echo "You must have $PACKAGE installed to compile $package." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at $URL" + return 1; +} + +aclocal_check () +{ + # normally aclocal is part of automake + # so we expect it to be in the same place as automake + # so if a different automake is supplied, we need to adapt as well + # so how's about replacing automake with aclocal in the set var, + # and saving that in $aclocal ? + # note, this will fail if the actual automake isn't called automake* + # or if part of the path before it contains it + if [ -z "$automake" ]; then + echo "Error: no automake variable set !" + return 1 + else + aclocal=`echo $automake | sed s/automake/aclocal/` + debug "aclocal: $aclocal" + if [ "$aclocal" != "aclocal" ]; + then + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-aclocal=$aclocal" + fi + if [ ! -x `which $aclocal` ]; then + echo "Error: cannot execute $aclocal !" + return 1 + fi + fi +} + +autoheader_check () +{ + # same here - autoheader is part of autoconf + # use the same voodoo + if [ -z "$autoconf" ]; then + echo "Error: no autoconf variable set !" + return 1 + else + autoheader=`echo $autoconf | sed s/autoconf/autoheader/` + debug "autoheader: $autoheader" + if [ "$autoheader" != "autoheader" ]; + then + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-autoheader=$autoheader" + fi + if [ ! -x `which $autoheader` ]; then + echo "Error: cannot execute $autoheader !" + return 1 + fi + fi + +} + +die_check () +{ + # call with $DIE + # if set to 1, we need to print something helpful then die + DIE=$1 + if test "x$DIE" = "x1"; + then + echo + echo "- Please get the right tools before proceeding." + echo "- Alternatively, if you're sure we're wrong, run with --nocheck." + exit 1 + fi +} + +autogen_options () +{ + if test "x$1" = "x"; then + return 0 + fi + + while test "x$1" != "x" ; do + optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + case "$1" in + --noconfigure) + NOCONFIGURE=defined + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --noconfigure" + echo "+ configure run disabled" + shift + ;; + --nocheck) + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --nocheck" + NOCHECK=defined + echo "+ autotools version check disabled" + shift + ;; + -d|--debug) + DEBUG=defined + AUTOGEN_EXT_OPT="$AUTOGEN_EXT_OPT --debug" + echo "+ debug output enabled" + shift + ;; + -h|--help) + echo "autogen.sh (autogen options) -- (configure options)" + echo "autogen.sh help options: " + echo " --noconfigure don't run the configure script" + echo " --nocheck don't do version checks" + echo " --debug debug the autogen process" + echo + echo " --with-autoconf PATH use autoconf in PATH" + echo " --with-automake PATH use automake in PATH" + echo + echo "Any argument either not in the above list or after a '--' will be " + echo "passed to ./configure." + exit 1 + ;; + --with-automake=*) + AUTOMAKE=$optarg + echo "+ using alternate automake in $optarg" + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-automake=$AUTOMAKE" + shift + ;; + --with-autoconf=*) + AUTOCONF=$optarg + echo "+ using alternate autoconf in $optarg" + CONFIGURE_DEF_OPT="$CONFIGURE_DEF_OPT --with-autoconf=$AUTOCONF" + shift + ;; + --) shift ; break ;; + *) + echo "+ passing argument $1 to configure" + CONFIGURE_EXT_OPT="$CONFIGURE_EXT_OPT $1" + shift + ;; + esac + done + + for arg do CONFIGURE_EXT_OPT="$CONFIGURE_EXT_OPT $arg"; done + if test ! -z "$CONFIGURE_EXT_OPT" + then + echo "+ options passed to configure: $CONFIGURE_EXT_OPT" + fi +} + +toplevel_check () +{ + srcfile=$1 + test -f $srcfile || { + echo "You must run this script in the top-level $package directory" + exit 1 + } +} + +tool_run () +{ + tool=$1 + options=$2 + run_if_fail=$3 + echo "+ running $tool $options..." + $tool $options || { + echo + echo $tool failed + eval $run_if_fail + exit 1 + } +} + +install_git_hooks () +{ + if test -d .git; then + # install pre-commit hook for doing clean commits + for hook in pre-commit; do + if test ! \( -x .git/hooks/$hook -a -L .git/hooks/$hook \); then + echo "+ Installing git $hook hook" + rm -f .git/hooks/$hook + ln -s ../../common/hooks/$hook.hook .git/hooks/$hook || { + # if we couldn't create a symbolic link, try doing a plain cp + if cp common/hooks/pre-commit.hook .git/hooks/pre-commit; then + chmod +x .git/hooks/pre-commit; + else + echo "********** Couldn't install git $hook hook **********"; + fi + } + fi + done + fi +} diff --git a/common/m4/Makefile.am b/common/m4/Makefile.am new file mode 100644 index 0000000..3d387ee --- /dev/null +++ b/common/m4/Makefile.am @@ -0,0 +1,40 @@ +EXTRA_DIST = \ + README \ + as-ac-expand.m4 \ + as-auto-alt.m4 \ + as-compiler-flag.m4 \ + as-compiler.m4 \ + as-docbook.m4 \ + as-gcc-inline-assembly.m4 \ + as-libtool.m4 \ + as-libtool-tags.m4 \ + as-python.m4 \ + as-version.m4 \ + ax_create_stdint_h.m4 \ + glib-gettext.m4 \ + gst-arch.m4 \ + gst-args.m4 \ + gst-check.m4 \ + gst-debuginfo.m4 \ + gst-default.m4 \ + gst-doc.m4 \ + gst-dowhile.m4 \ + gst-error.m4 \ + gst-feature.m4 \ + gst-function.m4 \ + gst-gettext.m4 \ + gst-glib2.m4 \ + gst-libxml2.m4 \ + gst-parser.m4 \ + gst-package-release-datetime.m4 \ + gst-platform.m4 \ + gst-plugindir.m4 \ + gst-plugin-docs.m4 \ + gst-valgrind.m4 \ + gst-x11.m4 \ + gst.m4 \ + gtk-doc.m4 \ + introspection.m4 \ + pkg.m4 \ + check.m4 \ + orc.m4 diff --git a/common/m4/README b/common/m4/README new file mode 100644 index 0000000..867a344 --- /dev/null +++ b/common/m4/README @@ -0,0 +1,3 @@ +All aclocal .m4 files we need are put here and cat'd to acinclude.m4 in +the source root. Official ones (taken from the relevant devel packages) +are named as-is, unofficial ones (or changed ones) get a gst-prefix. diff --git a/common/m4/as-ac-expand.m4 b/common/m4/as-ac-expand.m4 new file mode 100644 index 0000000..d6c9e33 --- /dev/null +++ b/common/m4/as-ac-expand.m4 @@ -0,0 +1,43 @@ +dnl as-ac-expand.m4 0.2.0 +dnl autostars m4 macro for expanding directories using configure's prefix +dnl thomas@apestaart.org + +dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) +dnl example +dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) +dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local + +AC_DEFUN([AS_AC_EXPAND], +[ + EXP_VAR=[$1] + FROM_VAR=[$2] + + dnl first expand prefix and exec_prefix if necessary + prefix_save=$prefix + exec_prefix_save=$exec_prefix + + dnl if no prefix given, then use /usr/local, the default prefix + if test "x$prefix" = "xNONE"; then + prefix="$ac_default_prefix" + fi + dnl if no exec_prefix given, then use prefix + if test "x$exec_prefix" = "xNONE"; then + exec_prefix=$prefix + fi + + full_var="$FROM_VAR" + dnl loop until it doesn't change anymore + while true; do + new_full_var="`eval echo $full_var`" + if test "x$new_full_var" = "x$full_var"; then break; fi + full_var=$new_full_var + done + + dnl clean up + full_var=$new_full_var + AC_SUBST([$1], "$full_var") + + dnl restore prefix and exec_prefix + prefix=$prefix_save + exec_prefix=$exec_prefix_save +]) diff --git a/common/m4/as-auto-alt.m4 b/common/m4/as-auto-alt.m4 new file mode 100644 index 0000000..3f7920d --- /dev/null +++ b/common/m4/as-auto-alt.m4 @@ -0,0 +1,50 @@ +dnl as-auto-alt.m4 0.0.2 +dnl autostars m4 macro for supplying alternate autotools versions to configure +dnl thomas@apestaart.org +dnl +dnl AS_AUTOTOOLS_ALTERNATE() +dnl +dnl supplies --with arguments for autoconf, autoheader, automake, aclocal + +AC_DEFUN([AS_AUTOTOOLS_ALTERNATE], +[ + dnl allow for different autoconf version + AC_ARG_WITH(autoconf, + AC_HELP_STRING([--with-autoconf], + [use a different autoconf for regeneration of Makefiles]), + [ + unset AUTOCONF + AM_MISSING_PROG(AUTOCONF, ${withval}) + AC_MSG_NOTICE([Using $AUTOCONF as autoconf]) + ]) + + dnl allow for different autoheader version + AC_ARG_WITH(autoheader, + AC_HELP_STRING([--with-autoheader], + [use a different autoheader for regeneration of Makefiles]), + [ + unset AUTOHEADER + AM_MISSING_PROG(AUTOHEADER, ${withval}) + AC_MSG_NOTICE([Using $AUTOHEADER as autoheader]) + ]) + + dnl allow for different automake version + AC_ARG_WITH(automake, + AC_HELP_STRING([--with-automake], + [use a different automake for regeneration of Makefiles]), + [ + unset AUTOMAKE + AM_MISSING_PROG(AUTOMAKE, ${withval}) + AC_MSG_NOTICE([Using $AUTOMAKE as automake]) + ]) + + dnl allow for different aclocal version + AC_ARG_WITH(aclocal, + AC_HELP_STRING([--with-aclocal], + [use a different aclocal for regeneration of Makefiles]), + [ + unset ACLOCAL + AM_MISSING_PROG(ACLOCAL, ${withval}) + AC_MSG_NOTICE([Using $ACLOCAL as aclocal]) + ]) +]) diff --git a/common/m4/as-compiler-flag.m4 b/common/m4/as-compiler-flag.m4 new file mode 100644 index 0000000..8bb853a --- /dev/null +++ b/common/m4/as-compiler-flag.m4 @@ -0,0 +1,96 @@ +dnl as-compiler-flag.m4 0.1.0 + +dnl autostars m4 macro for detection of compiler flags + +dnl David Schleef +dnl Tim-Philipp Müller + +dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_COMPILER_FLAG], +[ + AC_MSG_CHECKING([to see if compiler understands $1]) + + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CFLAGS="$save_CFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + +dnl AS_CXX_COMPILER_FLAG(CPPFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CPPFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_CXX_COMPILER_FLAG], +[ + AC_REQUIRE([AC_PROG_CXX]) + + AC_MSG_CHECKING([to see if c++ compiler understands $1]) + + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $1" + + AC_LANG_PUSH(C++) + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CPPFLAGS="$save_CPPFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + + AC_LANG_POP(C++) + + AC_MSG_RESULT([$flag_ok]) +]) + +dnl AS_OBJC_COMPILER_FLAG(CPPFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CPPFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_OBJC_COMPILER_FLAG], +[ + AC_REQUIRE([AC_PROG_OBJC]) + + AC_MSG_CHECKING([to see if Objective C compiler understands $1]) + + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $1" + + AC_LANG_PUSH([Objective C]) + + AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no]) + CPPFLAGS="$save_CPPFLAGS" + + if test "X$flag_ok" = Xyes ; then + $2 + true + else + $3 + true + fi + + AC_LANG_POP([Objective C]) + + AC_MSG_RESULT([$flag_ok]) +]) + diff --git a/common/m4/as-compiler.m4 b/common/m4/as-compiler.m4 new file mode 100644 index 0000000..309a060 --- /dev/null +++ b/common/m4/as-compiler.m4 @@ -0,0 +1,44 @@ +dnl as-compiler.m4 0.1.0 + +dnl autostars m4 macro for detection of compiler flavor + +dnl Thomas Vander Stichele + +dnl $Id: as-compiler.m4,v 1.4 2004/06/01 09:44:19 thomasvs Exp $ + +dnl AS_COMPILER(COMPILER) +dnl will set variable COMPILER to +dnl - gcc +dnl - forte +dnl - (empty) if no guess could be made + +AC_DEFUN([AS_COMPILER], +[ + as_compiler= + AC_MSG_CHECKING(for compiler flavour) + + dnl is it gcc ? + if test "x$GCC" = "xyes"; then + as_compiler="gcc" + fi + + dnl is it forte ? + AC_TRY_RUN([ +int main +(int argc, char *argv[]) +{ +#ifdef __sun + return 0; +#else + return 1; +#endif +} + ], as_compiler="forte", ,) + + if test "x$as_compiler" = "x"; then + AC_MSG_RESULT([unknown !]) + else + AC_MSG_RESULT($as_compiler) + fi + [$1]=$as_compiler +]) diff --git a/common/m4/as-docbook.m4 b/common/m4/as-docbook.m4 new file mode 100644 index 0000000..2e27050 --- /dev/null +++ b/common/m4/as-docbook.m4 @@ -0,0 +1,78 @@ +dnl AS_DOCBOOK([, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl checks if xsltproc can build docbook documentation +dnl (which is possible if the catalog is set up properly +dnl I also tried checking for a specific version and type of docbook +dnl but xsltproc seemed to happily run anyway, so we can't check for that +dnl and version +dnl this macro takes inspiration from +dnl http://www.movement.uklinux.net/docs/docbook-autotools/configure.html +AC_DEFUN([AS_DOCBOOK], +[ + XSLTPROC_FLAGS=--nonet + DOCBOOK_ROOT= + TYPE_LC=xml + TYPE_UC=XML + DOCBOOK_VERSION=4.1.2 + + if test -n "$XML_CATALOG_FILES"; then + oldIFS=$IFS + IFS=' ' + for xml_catalog_file in $XML_CATALOG_FILES; do + if test -f $xml_catalog_file; then + XML_CATALOG=$xml_catalog_file + CAT_ENTRY_START='' + break + fi + done + IFS=$oldIFS + elif test ! -f /etc/xml/catalog; then + for i in /usr/share/sgml/docbook/stylesheet/xsl/nwalsh /usr/share/sgml/docbook/xsl-stylesheets/ /usr/local/share/xsl/docbook ; + do + if test -d "$i"; then + DOCBOOK_ROOT=$i + fi + done + else + XML_CATALOG=/etc/xml/catalog + CAT_ENTRY_START='' + fi + + dnl We need xsltproc to process the test + AC_CHECK_PROG(XSLTPROC,xsltproc,xsltproc,) + XSLTPROC_WORKS=no + if test -n "$XSLTPROC"; then + AC_MSG_CHECKING([whether xsltproc docbook processing works]) + + if test -n "$XML_CATALOG"; then + DB_FILE="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl" + else + DB_FILE="$DOCBOOK_ROOT/xhtml/docbook.xsl" + fi + $XSLTPROC $XSLTPROC_FLAGS $DB_FILE >/dev/null 2>&1 << END + + + + +END + if test "$?" = 0; then + XSLTPROC_WORKS=yes + fi + AC_MSG_RESULT($XSLTPROC_WORKS) + fi + + if test "x$XSLTPROC_WORKS" = "xyes"; then + dnl execute ACTION-IF-FOUND + ifelse([$1], , :, [$1]) + else + dnl execute ACTION-IF-NOT-FOUND + ifelse([$2], , :, [$2]) + fi + + AC_SUBST(XML_CATALOG) + AC_SUBST(XSLTPROC_FLAGS) + AC_SUBST(DOCBOOK_ROOT) + AC_SUBST(CAT_ENTRY_START) + AC_SUBST(CAT_ENTRY_END) +]) diff --git a/common/m4/as-gcc-inline-assembly.m4 b/common/m4/as-gcc-inline-assembly.m4 new file mode 100644 index 0000000..af32104 --- /dev/null +++ b/common/m4/as-gcc-inline-assembly.m4 @@ -0,0 +1,52 @@ +dnl as-gcc-inline-assembly.m4 0.1.0 + +dnl autostars m4 macro for detection of gcc inline assembly + +dnl David Schleef + +dnl $Id$ + +dnl AS_COMPILER_FLAG(ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED]) +dnl Tries to compile with the given CFLAGS. +dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags, +dnl and ACTION-IF-NOT-ACCEPTED otherwise. + +AC_DEFUN([AS_GCC_INLINE_ASSEMBLY], +[ + AC_MSG_CHECKING([if compiler supports gcc-style inline assembly]) + + AC_TRY_COMPILE([], [ +#ifdef __GNUC_MINOR__ +#if (__GNUC__ * 1000 + __GNUC_MINOR__) < 3004 +#error GCC before 3.4 has critical bugs compiling inline assembly +#endif +#endif +__asm__ (""::) ], [flag_ok=yes], [flag_ok=no]) + + if test "X$flag_ok" = Xyes ; then + $1 + true + else + $2 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + + +AC_DEFUN([AS_GCC_ASM_POWERPC_FPU], +[ + AC_MSG_CHECKING([if compiler supports FPU instructions on PowerPC]) + + AC_TRY_COMPILE([], [__asm__ ("fadd 0,0,0"::) ], [flag_ok=yes], [flag_ok=no]) + + if test "X$flag_ok" = Xyes ; then + $1 + true + else + $2 + true + fi + AC_MSG_RESULT([$flag_ok]) +]) + diff --git a/common/m4/as-libtool-tags.m4 b/common/m4/as-libtool-tags.m4 new file mode 100644 index 0000000..06f0ae4 --- /dev/null +++ b/common/m4/as-libtool-tags.m4 @@ -0,0 +1,83 @@ +dnl as-libtool-tags.m4 0.1.4 + +dnl autostars m4 macro for selecting libtool "tags" (languages) + +dnl Andy Wingo does not claim credit for this macro +dnl backported from libtool 1.6 by Paolo Bonzini +dnl see http://lists.gnu.org/archive/html/libtool/2003-12/msg00007.html + +dnl $Id$ + +dnl AS_LIBTOOL_TAGS([tags...]) + +dnl example +dnl AS_LIBTOOL_TAGS([]) for only C (no fortran, etc) + +dnl When AC_LIBTOOL_TAGS is used, I redefine _LT_AC_TAGCONFIG +dnl to be more similar to the libtool 1.6 implementation, which +dnl uses an m4 loop and m4 case instead of a shell loop. This +dnl way the CXX/GCJ/F77/RC tests are not always expanded. + +dnl AS_LIBTOOL_TAGS +dnl --------------- +dnl tags to enable +AC_DEFUN([AS_LIBTOOL_TAGS], +[m4_define([_LT_TAGS],[$1]) +m4_define([_LT_AC_TAGCONFIG], [ + # redefined LT AC TAGCONFIG + if test -f "$ltmain"; then + if test ! -f "${ofile}"; then + AC_MSG_WARN([output file `$ofile' does not exist]) + fi + + if test -z "$LTCC"; then + eval "`$SHELL ${ofile} --config | grep '^LTCC='`" + if test -z "$LTCC"; then + AC_MSG_WARN([output file `$ofile' does not look like a libtool script]) + else + AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile']) + fi + fi + + AC_FOREACH([_LT_TAG], _LT_TAGS, + echo THOMAS: tag _LT_TAG + [m4_case(_LT_TAG, + [CXX], [ + if test -n "$CXX" && test "X$CXX" != "Xno"; then + echo "THOMAS: YAY CXX" + AC_LIBTOOL_LANG_CXX_CONFIG + available_tags="$available_tags _LT_TAG" + fi], + [F77], [ + if test -n "$F77" && test "X$F77" != "Xno"; then + AC_LIBTOOL_LANG_F77_CONFIG + available_tags="$available_tags _LT_TAG" + fi], + [GCJ], [ + if test -n "$GCJ" && test "X$GCJ" != "Xno"; then + AC_LIBTOOL_LANG_GCJ_CONFIG + available_tags="$available_tags _LT_TAG" + fi], + [RC], [ + if test -n "$RC" && test "X$RC" != "Xno"; then + AC_LIBTOOL_LANG_RC_CONFIG + available_tags="$available_tags _LT_TAG" + fi], + [m4_errprintn(m4_location[: error: invalid tag name: ]"_LT_TAG") + m4_exit(1)]) + ]) + echo THOMAS: available tags: $available_tags + fi + # Now substitute the updated list of available tags. + if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then + mv "${ofile}T" "$ofile" + chmod +x "$ofile" + AC_MSG_NOTICE([updated available libtool tags with $available_tags.]) + else + rm -f "${ofile}T" + AC_MSG_ERROR([unable to update list of available tagged configurations.]) + + fi + +])dnl _LT_AC_TAG_CONFIG +]) diff --git a/common/m4/as-libtool.m4 b/common/m4/as-libtool.m4 new file mode 100644 index 0000000..3b16095 --- /dev/null +++ b/common/m4/as-libtool.m4 @@ -0,0 +1,46 @@ +dnl as-libtool.m4 0.1.4 + +dnl autostars m4 macro for libtool versioning + +dnl Thomas Vander Stichele + +dnl $Id: as-libtool.m4,v 1.10 2005/10/15 13:44:23 thomasvs Exp $ + +dnl AS_LIBTOOL(PREFIX, CURRENT, REVISION, AGE, [RELEASE]) + +dnl example +dnl AS_LIBTOOL(GST, 2, 0, 0) + +dnl this macro +dnl - defines [$PREFIX]_CURRENT, REVISION and AGE +dnl - defines [$PREFIX]_LIBVERSION +dnl - defines [$PREFIX]_LT_LDFLAGS to set versioning +dnl - AC_SUBST's them all + +dnl if RELEASE is given, then add a -release option to the LDFLAGS +dnl with the given release version +dnl then use [$PREFIX]_LT_LDFLAGS in the relevant Makefile.am's + +dnl call AM_PROG_LIBTOOL after this call + +AC_DEFUN([AS_LIBTOOL], +[ + [$1]_CURRENT=[$2] + [$1]_REVISION=[$3] + [$1]_AGE=[$4] + [$1]_LIBVERSION=[$2]:[$3]:[$4] + AC_SUBST([$1]_CURRENT) + AC_SUBST([$1]_REVISION) + AC_SUBST([$1]_AGE) + AC_SUBST([$1]_LIBVERSION) + + [$1]_LT_LDFLAGS="$[$1]_LT_LDFLAGS -version-info $[$1]_LIBVERSION" + if test ! -z "[$5]" + then + [$1]_LT_LDFLAGS="$[$1]_LT_LDFLAGS -release [$5]" + fi + AC_SUBST([$1]_LT_LDFLAGS) + + LT_PREREQ([2.2.6]) + LT_INIT([dlopen win32-dll disable-static]) +]) diff --git a/common/m4/as-python.m4 b/common/m4/as-python.m4 new file mode 100644 index 0000000..eb9b175 --- /dev/null +++ b/common/m4/as-python.m4 @@ -0,0 +1,152 @@ +## ------------------------ +## Python file handling +## From Andrew Dalke +## Updated by James Henstridge +## Updated by Andy Wingo to loop through possible pythons +## ------------------------ + +# AS_PATH_PYTHON([MINIMUM-VERSION]) + +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. + +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. + +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). + +# If the MINIMUM-VERSION argument is passed, AS_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. + +# Updated to loop over all possible python binaries by Andy Wingo +# +# Updated to only warn and unset PYTHON if no good one is found + +AC_DEFUN([AS_PATH_PYTHON], + [ + dnl Find a version of Python. I could check for python versions 1.4 + dnl or earlier, but the default installation locations changed from + dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages + dnl in 1.5, and I don't want to maintain that logic. + + dnl should we do the version check? + PYTHON_CANDIDATES="python python2.2 python2.1 python2.0 python2 \ + python1.6 python1.5" + ifelse([$1],[], + [AC_PATH_PROG(PYTHON, $PYTHON_CANDIDATES)], + [ + AC_MSG_NOTICE(Looking for Python version >= $1) + changequote(<<, >>)dnl + prog=" +import sys, string +minver = '$1' +# split string by '.' and convert to numeric +minver_info = map(string.atoi, string.split(minver, '.')) +# we can now do comparisons on the two lists: +if sys.version_info >= tuple(minver_info): + sys.exit(0) +else: + sys.exit(1)" + changequote([, ])dnl + + python_good=false + for python_candidate in $PYTHON_CANDIDATES; do + unset PYTHON + AC_PATH_PROG(PYTHON, $python_candidate) 1> /dev/null 2> /dev/null + + if test "x$PYTHON" = "x"; then continue; fi + + if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC; then + AC_MSG_CHECKING(["$PYTHON":]) + AC_MSG_RESULT([okay]) + python_good=true + break; + else + dnl clear the cache val + unset ac_cv_path_PYTHON + fi + done + ]) + + if test "$python_good" != "true"; then + AC_MSG_WARN([No suitable version of python found]) + PYTHON= + else + + AC_MSG_CHECKING([local Python configuration]) + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. Need to change quote character because of [:3] + + AC_SUBST(PYTHON_VERSION) + changequote(<<, >>)dnl + PYTHON_VERSION=`$PYTHON -c "import sys; print sys.version[:3]"` + changequote([, ])dnl + + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST(PYTHON_PREFIX) + PYTHON_PREFIX='${prefix}' + + AC_SUBST(PYTHON_EXEC_PREFIX) + PYTHON_EXEC_PREFIX='${exec_prefix}' + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_SUBST(PYTHON_PLATFORM) + PYTHON_PLATFORM=`$PYTHON -c "import sys; print sys.platform"` + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behaviour + dnl is more consistent with lispdir.m4 for example. + dnl + dnl Also, if the package prefix isn't the same as python's prefix, + dnl then the old $(pythondir) was pretty useless. + + AC_SUBST(pythondir) + pythondir=$PYTHON_PREFIX"/lib/python"$PYTHON_VERSION/site-packages + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + dnl Maybe this should be put in python.am? + + AC_SUBST(pkgpythondir) + pkgpythondir=\${pythondir}/$PACKAGE + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) Was PYTHON_SITE_EXEC in previous betas. + + AC_SUBST(pyexecdir) + pyexecdir=$PYTHON_EXEC_PREFIX"/lib/python"$PYTHON_VERSION/site-packages + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + dnl Maybe this should be put in python.am? + + AC_SUBST(pkgpyexecdir) + pkgpyexecdir=\${pyexecdir}/$PACKAGE + + AC_MSG_RESULT([looks good]) + + fi +]) diff --git a/common/m4/as-version.m4 b/common/m4/as-version.m4 new file mode 100644 index 0000000..22ff774 --- /dev/null +++ b/common/m4/as-version.m4 @@ -0,0 +1,75 @@ +dnl as-version.m4 0.2.0 + +dnl autostars m4 macro for versioning + +dnl Thomas Vander Stichele + +dnl $Id: as-version.m4,v 1.15 2006/04/01 09:40:24 thomasvs Exp $ + +dnl AS_VERSION + +dnl example +dnl AS_VERSION + +dnl this macro +dnl - AC_SUBST's PACKAGE_VERSION_MAJOR, _MINOR, _MICRO +dnl - AC_SUBST's PACKAGE_VERSION_RELEASE, +dnl which can be used for rpm release fields +dnl - doesn't call AM_INIT_AUTOMAKE anymore because it prevents +dnl maintainer mode from running correctly +dnl +dnl don't forget to put #undef PACKAGE_VERSION_RELEASE in acconfig.h +dnl if you use acconfig.h + +AC_DEFUN([AS_VERSION], +[ + PACKAGE_VERSION_MAJOR=$(echo AC_PACKAGE_VERSION | cut -d'.' -f1) + PACKAGE_VERSION_MINOR=$(echo AC_PACKAGE_VERSION | cut -d'.' -f2) + PACKAGE_VERSION_MICRO=$(echo AC_PACKAGE_VERSION | cut -d'.' -f3) + + AC_SUBST(PACKAGE_VERSION_MAJOR) + AC_SUBST(PACKAGE_VERSION_MINOR) + AC_SUBST(PACKAGE_VERSION_MICRO) +]) + +dnl AS_NANO(ACTION-IF-NANO-NON-NULL, [ACTION-IF-NANO-NULL]) + +dnl requires AC_INIT to be called before +dnl For projects using a fourth or nano number in your versioning to indicate +dnl development or prerelease snapshots, this macro allows the build to be +dnl set up differently accordingly. + +dnl this macro: +dnl - parses AC_PACKAGE_VERSION, set by AC_INIT, and extracts the nano number +dnl - sets the variable PACKAGE_VERSION_NANO +dnl - sets the variable PACKAGE_VERSION_RELEASE, which can be used +dnl for rpm release fields +dnl - executes ACTION-IF-NANO-NON-NULL or ACTION-IF-NANO-NULL + +dnl example: +dnl AS_NANO(RELEASE="yes", RELEASE="no") + +AC_DEFUN([AS_NANO], +[ + AC_MSG_CHECKING(nano version) + + NANO=$(echo AC_PACKAGE_VERSION | cut -d'.' -f4) + + if test x"$NANO" = x || test "x$NANO" = "x0" ; then + AC_MSG_RESULT([0 (release)]) + NANO=0 + PACKAGE_VERSION_RELEASE=1 + ifelse([$1], , :, [$1]) + else + AC_MSG_RESULT($NANO) + PACKAGE_VERSION_RELEASE=0.`date +%Y%m%d.%H%M%S` + if test "x$NANO" != "x1" ; then + ifelse([$1], , :, [$1]) + else + ifelse([$2], , :, [$2]) + fi + fi + PACKAGE_VERSION_NANO=$NANO + AC_SUBST(PACKAGE_VERSION_NANO) + AC_SUBST(PACKAGE_VERSION_RELEASE) +]) diff --git a/common/m4/ax_create_stdint_h.m4 b/common/m4/ax_create_stdint_h.m4 new file mode 100644 index 0000000..13bf699 --- /dev/null +++ b/common/m4/ax_create_stdint_h.m4 @@ -0,0 +1,734 @@ +##### http://autoconf-archive.cryp.to/ax_create_stdint_h.html +# +# SYNOPSIS +# +# AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])] +# +# DESCRIPTION +# +# the "ISO C9X: 7.18 Integer types " section requires the +# existence of an include file that defines a set of +# typedefs, especially uint8_t,int32_t,uintptr_t. Many older +# installations will not provide this file, but some will have the +# very same definitions in . In other enviroments we can +# use the inet-types in which would define the typedefs +# int8_t and u_int8_t respectivly. +# +# This macros will create a local "_stdint.h" or the headerfile given +# as an argument. In many cases that file will just "#include +# " or "#include ", while in other environments +# it will provide the set of basic 'stdint's definitions/typedefs: +# +# int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +# int_least32_t.. int_fast32_t.. intmax_t +# +# which may or may not rely on the definitions of other files, or +# using the AC_CHECK_SIZEOF macro to determine the actual sizeof each +# type. +# +# if your header files require the stdint-types you will want to +# create an installable file mylib-int.h that all your other +# installable header may include. So if you have a library package +# named "mylib", just use +# +# AX_CREATE_STDINT_H(mylib-int.h) +# +# in configure.ac and go to install that very header file in +# Makefile.am along with the other headers (mylib.h) - and the +# mylib-specific headers can simply use "#include " to +# obtain the stdint-types. +# +# Remember, if the system already had a valid , the +# generated file will include it directly. No need for fuzzy +# HAVE_STDINT_H things... (oops, GCC 4.2.x has deliberatly disabled +# its stdint.h for non-c99 compilation and the c99-mode is not the +# default. Therefore this macro will not use the compiler's stdint.h +# - please complain to the GCC developers). +# +# LAST MODIFICATION +# +# 2007-06-27 +# +# COPYLEFT +# +# Copyright (c) 2007 Guido U. Draheim +# +# 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. +# +# As a special exception, the respective Autoconf Macro's copyright +# owner gives unlimited permission to copy, distribute and modify the +# configure scripts that are the output of Autoconf when processing +# the Macro. You need not follow the terms of the GNU General Public +# License when using or distributing such scripts, even though +# portions of the text of the Macro appear in them. The GNU General +# Public License (GPL) does govern all other use of the material that +# constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the +# Autoconf Macro released by the Autoconf Macro Archive. When you +# make and distribute a modified version of the Autoconf Macro, you +# may extend this special exception to the GPL to apply to your +# modified version as well. + +AC_DEFUN([AX_CHECK_DATA_MODEL],[ + AC_CHECK_SIZEOF(char) + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(void*) + ac_cv_char_data_model="" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp" + AC_MSG_CHECKING([data model]) + case "$ac_cv_char_data_model/$ac_cv_long_data_model" in + 122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;; + 122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;; + 122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;; + 124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;; + 124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;; + 124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;; + 124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;; + 128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;; + 128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;; + 222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;; + 333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;; + 444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;; + 666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;; + 888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;; + 222/*|333/*|444/*|666/*|888/*) : + ac_cv_data_model="iDSP" ; n="unusual dsptype" ;; + *) ac_cv_data_model="none" ; n="very unusual model" ;; + esac + AC_MSG_RESULT([$ac_cv_data_model ($ac_cv_long_data_model, $n)]) +]) + +dnl AX_CHECK_HEADER_STDINT_X([HEADERLIST][,ACTION-IF]) +AC_DEFUN([AX_CHECK_HEADER_STDINT_X],[ +AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[ + ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[stdint.h inttypes.h sys/inttypes.h sys/types.h]) + do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uintptr_t,[ac_cv_header_stdint_x=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + done + AC_MSG_CHECKING([for stdint uintptr_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_O],[ +AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[ + ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[inttypes.h sys/inttypes.h sys/types.h stdint.h]) + do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + break; + done + AC_MSG_CHECKING([for stdint uint32_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_U],[ +AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[ + ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[sys/types.h inttypes.h sys/inttypes.h]) ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$2],[$2]) break + break; + done + AC_MSG_CHECKING([for stdint u_int32_t]) + ]) +]) + +AC_DEFUN([AX_CREATE_STDINT_H], +[# ------ AX CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint types]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +# try to shortcircuit - if the default include path of the compiler +# can find a "stdint.h" header then we assume that all compilers can. +AC_CACHE_VAL([ac_cv_header_stdint_t],[ +old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" +old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" +old_CFLAGS="$CFLAGS" ; CFLAGS="" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[ac_cv_stdint_result="(assuming C99 compatible system)" + ac_cv_header_stdint_t="stdint.h"; ], +[ac_cv_header_stdint_t=""]) +if test "$GCC" = "yes" && test ".$ac_cv_header_stdint_t" = "."; then +CFLAGS="-std=c99" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[AC_MSG_WARN(your GCC compiler has a defunct stdint.h for its default-mode)]) +fi +CXXFLAGS="$old_CXXFLAGS" +CPPFLAGS="$old_CPPFLAGS" +CFLAGS="$old_CFLAGS" ]) + +v="... $ac_cv_header_stdint_h" +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)]) +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)]) +elif test "_$ac_cv_header_stdint_t" = "_" ; then + AC_MSG_RESULT([(putting them into $ac_stdint_h)$v]) +else + ac_cv_header_stdint="$ac_cv_header_stdint_t" + AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)]) +fi + +if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. + +dnl .....intro message done, now do a few system checks..... +dnl btw, all old CHECK_TYPE macros do automatically "DEFINE" a type, +dnl therefore we use the autoconf implementation detail CHECK_TYPE_NEW +dnl instead that is triggered with 3 or more arguments (see types.m4) + +inttype_headers=`echo $2 | sed -e 's/,/ /g'` + +ac_cv_stdint_result="(no helpful system typedefs seen)" +AX_CHECK_HEADER_STDINT_X(dnl + stdint.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen uintptr_t$and64 in $i)") + +if test "_$ac_cv_header_stdint_x" = "_" ; then +AX_CHECK_HEADER_STDINT_O(dnl, + inttypes.h sys/inttypes.h stdint.h $inttype_headers, + ac_cv_stdint_result="(seen uint32_t$and64 in $i)") +fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then +if test "_$ac_cv_header_stdint_o" = "_" ; then +AX_CHECK_HEADER_STDINT_U(dnl, + sys/types.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen u_int32_t$and64 in $i)") +fi fi + +dnl if there was no good C99 header file, do some typedef checks... +if test "_$ac_cv_header_stdint_x" = "_" ; then + AC_MSG_CHECKING([for stdint datatype model]) + AC_MSG_RESULT([(..)]) + AX_CHECK_DATA_MODEL +fi + +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "_$ac_cv_header_stdint_o" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "_$ac_cv_header_stdint_u" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +AC_MSG_CHECKING([for extra inttypes in chosen header]) +AC_MSG_RESULT([($ac_cv_header_stdint)]) +dnl see if int_least and int_fast types are present in _this_ header. +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) +AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>]) + +fi # shortcircut to system "stdint.h" +# ------------------ PREPARE VARIABLES ------------------------------ +if test "$GCC" = "yes" ; then +ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1` +else +ac_cv_stdint_message="using $CC" +fi + +AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl +$ac_cv_stdint_result]) + +dnl ----------------------------------------------------------------- +# ----------------- DONE inttypes.h checks START header ------------- +AC_CONFIG_COMMANDS([$ac_stdint_h],[ +AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h) +ac_stdint=$tmp/_stdint.h + +echo "#ifndef" $_ac_stdint_h >$ac_stdint +echo "#define" $_ac_stdint_h "1" >>$ac_stdint +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint +echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint +if test "_$ac_cv_header_stdint_t" != "_" ; then +echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint +echo "#include " >>$ac_stdint +echo "#endif" >>$ac_stdint +echo "#endif" >>$ac_stdint +else + +cat >>$ac_stdint < +#else +#include + +/* .................... configured part ............................ */ + +STDINT_EOF + +echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_header="$ac_cv_header_stdint_x" + echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint +fi + +echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_o" != "_" ; then + ac_header="$ac_cv_header_stdint_o" + echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint +fi + +echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint +if test "_$ac_cv_header_stdint_u" != "_" ; then + ac_header="$ac_cv_header_stdint_u" + echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint +fi + +echo "" >>$ac_stdint + +if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then + echo "#include <$ac_header>" >>$ac_stdint + echo "" >>$ac_stdint +fi fi + +echo "/* which 64bit typedef has been found */" >>$ac_stdint +if test "$ac_cv_type_uint64_t" = "yes" ; then +echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint +fi +if test "$ac_cv_type_u_int64_t" = "yes" ; then +echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* which type model has been detected */" >>$ac_stdint +if test "_$ac_cv_char_data_model" != "_" ; then +echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint +echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint +else +echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint +echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* whether int_least types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_least32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint +fi +echo "/* whether int_fast types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_fast32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint +fi +echo "/* whether intmax_t type was detected */" >>$ac_stdint +if test "$ac_cv_type_intmax_t" = "yes"; then +echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + + cat >>$ac_stdint <= 199901L +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +/* note: all ELF-systems seem to have loff-support which needs 64-bit */ +#if !defined _NO_LONGLONG +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) +#if !defined _NO_LONGLONG +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + /* compiler/cpu type to define int64_t */ +#endif +#endif +#endif + +#if defined _STDINT_HAVE_U_INT_TYPES +/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; + +/* glibc compatibility */ +#ifndef __int8_t_defined +#define __int8_t_defined +#endif +#endif + +#ifdef _STDINT_NEED_INT_MODEL_T +/* we must guess all the basic types. Apart from byte-adressable system, */ +/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ +/* (btw, those nibble-addressable systems are way off, or so we assume) */ + +dnl /* have a look at "64bit and data size neutrality" at */ +dnl /* http://unix.org/version2/whatsnew/login_64bit.html */ +dnl /* (the shorthand "ILP" types always have a "P" part) */ + +#if defined _STDINT_BYTE_MODEL +#if _STDINT_LONG_MODEL+0 == 242 +/* 2:4:2 = IP16 = a normal 16-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 +/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ +/* 4:4:4 = ILP32 = a normal 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 +/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ +/* 4:8:8 = LP64 = a normal 64-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* this system has a "long" of 64bit */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long uint64_t; +typedef long int64_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 448 +/* LLP64 a 64-bit system derived from a 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* assuming the system has a "long long" */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef unsigned long long uint64_t; +typedef long long int64_t; +#endif +#else +#define _STDINT_NO_INT32_T +#endif +#else +#define _STDINT_NO_INT8_T +#define _STDINT_NO_INT32_T +#endif +#endif + +/* + * quote from SunOS-5.8 sys/inttypes.h: + * Use at your own risk. As of February 1996, the committee is squarely + * behind the fixed sized types; the "least" and "fast" types are still being + * discussed. The probability that the "fast" types may be removed before + * the standard is finalized is high enough that they are not currently + * implemented. + */ + +#if defined _STDINT_NEED_INT_LEAST_T +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_least64_t; +#endif + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_least64_t; +#endif + /* least types */ +#endif + +#if defined _STDINT_NEED_INT_FAST_T +typedef int8_t int_fast8_t; +typedef int int_fast16_t; +typedef int32_t int_fast32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_fast64_t; +#endif + +typedef uint8_t uint_fast8_t; +typedef unsigned uint_fast16_t; +typedef uint32_t uint_fast32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_fast64_t; +#endif + /* fast types */ +#endif + +#ifdef _STDINT_NEED_INTMAX_T +#ifdef _HAVE_UINT64_T +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + +#ifdef _STDINT_NEED_INTPTR_T +#ifndef __intptr_t_defined +#define __intptr_t_defined +/* we encourage using "long" to store pointer values, never use "int" ! */ +#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 +typedef unsigned int uintptr_t; +typedef int intptr_t; +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T +typedef uint64_t uintptr_t; +typedef int64_t intptr_t; +#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +#endif +#endif + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ +#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS +#ifndef UINT32_C + +/* Signed. */ +# define INT8_C(c) c +# define INT16_C(c) c +# define INT32_C(c) c +# ifdef _HAVE_LONGLONG_UINT64_T +# define INT64_C(c) c ## L +# else +# define INT64_C(c) c ## LL +# endif + +/* Unsigned. */ +# define UINT8_C(c) c ## U +# define UINT16_C(c) c ## U +# define UINT32_C(c) c ## U +# ifdef _HAVE_LONGLONG_UINT64_T +# define UINT64_C(c) c ## UL +# else +# define UINT64_C(c) c ## ULL +# endif + +/* Maximal type. */ +# ifdef _HAVE_LONGLONG_UINT64_T +# define INTMAX_C(c) c ## L +# define UINTMAX_C(c) c ## UL +# else +# define INTMAX_C(c) c ## LL +# define UINTMAX_C(c) c ## ULL +# endif + + /* literalnumbers */ +#endif +#endif + +/* These limits are merily those of a two complement byte-oriented system */ + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of unsigned integral types. */ +# define UINT8_MAX (255) +# define UINT16_MAX (65535) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (__UINT64_C(18446744073709551615)) + +/* Minimum of signed integral types having a minimum size. */ +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# define INT_LEAST64_MIN INT64_MIN +/* Maximum of signed integral types having a minimum size. */ +# define INT_LEAST8_MAX INT8_MAX +# define INT_LEAST16_MAX INT16_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST64_MAX INT64_MAX + +/* Maximum of unsigned integral types having a minimum size. */ +# define UINT_LEAST8_MAX UINT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define UINT_LEAST64_MAX UINT64_MAX + + /* shortcircuit*/ +#endif + /* once */ +#endif +#endif +STDINT_EOF +fi + if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then + AC_MSG_NOTICE([$ac_stdint_h is unchanged]) + else + ac_dir=`AS_DIRNAME(["$ac_stdint_h"])` + AS_MKDIR_P(["$ac_dir"]) + rm -f $ac_stdint_h + mv $ac_stdint $ac_stdint_h + fi +],[# variables for create stdint.h replacement +PACKAGE="$PACKAGE" +VERSION="$VERSION" +ac_stdint_h="$ac_stdint_h" +_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h) +ac_cv_stdint_message="$ac_cv_stdint_message" +ac_cv_header_stdint_t="$ac_cv_header_stdint_t" +ac_cv_header_stdint_x="$ac_cv_header_stdint_x" +ac_cv_header_stdint_o="$ac_cv_header_stdint_o" +ac_cv_header_stdint_u="$ac_cv_header_stdint_u" +ac_cv_type_uint64_t="$ac_cv_type_uint64_t" +ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" +ac_cv_char_data_model="$ac_cv_char_data_model" +ac_cv_long_data_model="$ac_cv_long_data_model" +ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" +ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" +ac_cv_type_intmax_t="$ac_cv_type_intmax_t" +]) +]) diff --git a/common/m4/check.m4 b/common/m4/check.m4 new file mode 100644 index 0000000..afd26eb --- /dev/null +++ b/common/m4/check.m4 @@ -0,0 +1,181 @@ +dnl _AM_TRY_CHECK(MINIMUM-VERSION, EXTRA-CFLAGS, EXTRA-LIBS, CHECK-LIB-NAME +dnl [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Test for check, and define CHECK_CFLAGS and CHECK_LIBS +dnl Done this way because of the brokenness that is +dnl https://launchpad.net/distros/ubuntu/+source/check/+bug/5840 +dnl + +AC_DEFUN([_AM_TRY_CHECK], +[ + min_check_version=$1 + extra_cflags=$2 + extra_libs=$3 + check_lib_name=$4 + + CHECK_CFLAGS="$extra_cflags" + CHECK_LIBS="$extra_libs -l$check_lib_name" + + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + + CFLAGS="$CFLAGS $CHECK_CFLAGS" + LIBS="$CHECK_LIBS $LIBS" + + AC_MSG_CHECKING(for check named $check_lib_name - version >= $min_check_version) + + rm -f conf.check-test + dnl unset no_check, since in our second run it would have been set to yes + dnl before + no_check= + 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([$5], , :, [$5]) + 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([$6], , AC_MSG_ERROR([check not found]), [$6]) + fi +]) + + +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]]) + + AC_ARG_WITH(checklibname, + AC_HELP_STRING([--with-check-lib-name=NAME], + [name of the PIC check library (default=check)])) + + min_check_version=ifelse([$1], ,0.8.2,$1) + + 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_EXTRA_CFLAGS="-I$with_check/include" + CHECK_EXTRA_LIBS="-L$with_check/lib" + else + CHECK_EXTRA_CFLAGS="" + CHECK_EXTRA_LIBS="" + fi + + if test x$with_checklibname = x; then + _AM_TRY_CHECK($min_check_version, $CHECK_EXTRA_CFLAGS, $CHECK_EXTRA_LIBS, + check_pic, [have_check=true], [have_check=false]) + if test x$have_check = xtrue; then + ifelse([$2], , :, [$2]) + else + _AM_TRY_CHECK($min_check_version, $CHECK_EXTRA_CFLAGS, $CHECK_EXTRA_LIBS, + check, [have_check=true], [have_check=false]) + if test x$have_check = xtrue; then + ifelse([$2], , :, [$2]) + else + ifelse([$3], , AC_MSG_ERROR([check not found]), [$3]) + fi + fi + else + _AM_TRY_CHECK($min_check_version, $CHECK_EXTRA_CFLAGS, $CHECK_EXTRA_LIBS, + $with_checklibname, [have_check=true], [have_check=false]) + if test x$have_check = xtrue; then + ifelse([$2], , :, [$2]) + else + ifelse([$3], , AC_MSG_ERROR([check not found]), [$3]) + fi + fi + + AC_SUBST(CHECK_CFLAGS) + AC_SUBST(CHECK_LIBS) + rm -f conf.check-test + fi +]) diff --git a/common/m4/glib-gettext.m4 b/common/m4/glib-gettext.m4 new file mode 100644 index 0000000..f8d442f --- /dev/null +++ b/common/m4/glib-gettext.m4 @@ -0,0 +1,432 @@ +# Copyright (C) 1995-2002 Free Software Foundation, Inc. +# Copyright (C) 2001-2003,2004 Red Hat, Inc. +# +# This file is free software, distributed under the terms of the GNU +# General Public License. As a special exception to the GNU General +# Public License, this file may be distributed as part of a program +# that contains a configuration script generated by Autoconf, under +# the same distribution terms as the rest of that program. +# +# This file can be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# +# Macro to add for using GNU gettext. +# Ulrich Drepper , 1995, 1996 +# +# Modified to never use included libintl. +# Owen Taylor , 12/15/1998 +# +# Major rework to remove unused code +# Owen Taylor , 12/11/2002 +# +# Added better handling of ALL_LINGUAS from GNU gettext version +# written by Bruno Haible, Owen Taylor 5/30/3002 +# +# Modified to require ngettext +# Matthias Clasen 08/06/2004 +# +# We need this here as well, since someone might use autoconf-2.5x +# to configure GLib then an older version to configure a package +# using AM_GLIB_GNU_GETTEXT +AC_PREREQ(2.53) + +dnl +dnl We go to great lengths to make sure that aclocal won't +dnl try to pull in the installed version of these macros +dnl when running aclocal in the glib directory. +dnl +m4_copy([AC_DEFUN],[glib_DEFUN]) +m4_copy([AC_REQUIRE],[glib_REQUIRE]) +dnl +dnl At the end, if we're not within glib, we'll define the public +dnl definitions in terms of our private definitions. +dnl + +# GLIB_LC_MESSAGES +#-------------------- +glib_DEFUN([GLIB_LC_MESSAGES], + [AC_CHECK_HEADERS([locale.h]) + if test $ac_cv_header_locale_h = yes; then + AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, + [AC_TRY_LINK([#include ], [return LC_MESSAGES], + am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) + if test $am_cv_val_LC_MESSAGES = yes; then + AC_DEFINE(HAVE_LC_MESSAGES, 1, + [Define if your file defines LC_MESSAGES.]) + fi + fi]) + +# GLIB_PATH_PROG_WITH_TEST +#---------------------------- +dnl GLIB_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, +dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) +glib_DEFUN([GLIB_PATH_PROG_WITH_TEST], +[# Extract the first word of "$2", so it can be a program name with args. +set dummy $2; ac_word=[$]2 +AC_MSG_CHECKING([for $ac_word]) +AC_CACHE_VAL(ac_cv_path_$1, +[case "[$]$1" in + /*) + ac_cv_path_$1="[$]$1" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in ifelse([$5], , $PATH, [$5]); do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if [$3]; then + ac_cv_path_$1="$ac_dir/$ac_word" + break + fi + fi + done + IFS="$ac_save_ifs" +dnl If no 4th arg is given, leave the cache variable unset, +dnl so AC_PATH_PROGS will keep looking. +ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" +])dnl + ;; +esac])dnl +$1="$ac_cv_path_$1" +if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then + AC_MSG_RESULT([$]$1) +else + AC_MSG_RESULT(no) +fi +AC_SUBST($1)dnl +]) + +# GLIB_WITH_NLS +#----------------- +glib_DEFUN([GLIB_WITH_NLS], + dnl NLS is obligatory + [USE_NLS=yes + AC_SUBST(USE_NLS) + + gt_cv_have_gettext=no + + CATOBJEXT=NONE + XGETTEXT=: + INTLLIBS= + + AC_CHECK_HEADER(libintl.h, + [gt_cv_func_dgettext_libintl="no" + libintl_extra_libs="" + + # + # First check in libc + # + AC_CACHE_CHECK([for ngettext in libc], gt_cv_func_ngettext_libc, + [AC_TRY_LINK([ +#include +], + [return !ngettext ("","", 1)], + gt_cv_func_ngettext_libc=yes, + gt_cv_func_ngettext_libc=no) + ]) + + if test "$gt_cv_func_ngettext_libc" = "yes" ; then + AC_CACHE_CHECK([for dgettext in libc], gt_cv_func_dgettext_libc, + [AC_TRY_LINK([ +#include +], + [return !dgettext ("","")], + gt_cv_func_dgettext_libc=yes, + gt_cv_func_dgettext_libc=no) + ]) + fi + + if test "$gt_cv_func_ngettext_libc" = "yes" ; then + AC_CHECK_FUNCS(bind_textdomain_codeset) + fi + + # + # If we don't have everything we want, check in libintl + # + if test "$gt_cv_func_dgettext_libc" != "yes" \ + || test "$gt_cv_func_ngettext_libc" != "yes" \ + || test "$ac_cv_func_bind_textdomain_codeset" != "yes" ; then + + AC_CHECK_LIB(intl, bindtextdomain, + [AC_CHECK_LIB(intl, ngettext, + [AC_CHECK_LIB(intl, dgettext, + gt_cv_func_dgettext_libintl=yes)])]) + + if test "$gt_cv_func_dgettext_libintl" != "yes" ; then + AC_MSG_CHECKING([if -liconv is needed to use gettext]) + AC_MSG_RESULT([]) + AC_CHECK_LIB(intl, ngettext, + [AC_CHECK_LIB(intl, dcgettext, + [gt_cv_func_dgettext_libintl=yes + libintl_extra_libs=-liconv], + :,-liconv)], + :,-liconv) + fi + + # + # If we found libintl, then check in it for bind_textdomain_codeset(); + # we'll prefer libc if neither have bind_textdomain_codeset(), + # and both have dgettext and ngettext + # + if test "$gt_cv_func_dgettext_libintl" = "yes" ; then + glib_save_LIBS="$LIBS" + LIBS="$LIBS -lintl $libintl_extra_libs" + unset ac_cv_func_bind_textdomain_codeset + AC_CHECK_FUNCS(bind_textdomain_codeset) + LIBS="$glib_save_LIBS" + + if test "$ac_cv_func_bind_textdomain_codeset" = "yes" ; then + gt_cv_func_dgettext_libc=no + else + if test "$gt_cv_func_dgettext_libc" = "yes" \ + && test "$gt_cv_func_ngettext_libc" = "yes"; then + gt_cv_func_dgettext_libintl=no + fi + fi + fi + fi + + if test "$gt_cv_func_dgettext_libc" = "yes" \ + || test "$gt_cv_func_dgettext_libintl" = "yes"; then + gt_cv_have_gettext=yes + fi + + if test "$gt_cv_func_dgettext_libintl" = "yes"; then + INTLLIBS="-lintl $libintl_extra_libs" + fi + + if test "$gt_cv_have_gettext" = "yes"; then + AC_DEFINE(HAVE_GETTEXT,1, + [Define if the GNU gettext() function is already present or preinstalled.]) + GLIB_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"], no)dnl + if test "$MSGFMT" != "no"; then + glib_save_LIBS="$LIBS" + LIBS="$LIBS $INTLLIBS" + AC_CHECK_FUNCS(dcgettext) + MSGFMT_OPTS= + AC_MSG_CHECKING([if msgfmt accepts -c]) + GLIB_RUN_PROG([$MSGFMT -c -o /dev/null],[ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: test 1.0\n" +"PO-Revision-Date: 2007-02-15 12:01+0100\n" +"Last-Translator: test \n" +"Language-Team: C \n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" +], [MSGFMT_OPTS=-c; AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) + AC_SUBST(MSGFMT_OPTS) + AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) + GLIB_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, + [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :) + AC_TRY_LINK(, [extern int _nl_msg_cat_cntr; + return _nl_msg_cat_cntr], + [CATOBJEXT=.gmo + DATADIRNAME=share], + [case $host in + *-*-solaris*) + dnl On Solaris, if bind_textdomain_codeset is in libc, + dnl GNU format message catalog is always supported, + dnl since both are added to the libc all together. + dnl Hence, we'd like to go with DATADIRNAME=share and + dnl and CATOBJEXT=.gmo in this case. + AC_CHECK_FUNC(bind_textdomain_codeset, + [CATOBJEXT=.gmo + DATADIRNAME=share], + [CATOBJEXT=.mo + DATADIRNAME=lib]) + ;; + *) + CATOBJEXT=.mo + DATADIRNAME=lib + ;; + esac]) + LIBS="$glib_save_LIBS" + INSTOBJEXT=.mo + else + gt_cv_have_gettext=no + fi + fi + ]) + + if test "$gt_cv_have_gettext" = "yes" ; then + AC_DEFINE(ENABLE_NLS, 1, + [always defined to indicate that i18n is enabled]) + fi + + dnl Test whether we really found GNU xgettext. + if test "$XGETTEXT" != ":"; then + dnl If it is not GNU xgettext we define it as : so that the + dnl Makefiles still can work. + if $XGETTEXT --omit-header /dev/null 2> /dev/null; then + : ; + else + AC_MSG_RESULT( + [found xgettext program is not GNU xgettext; ignore it]) + XGETTEXT=":" + fi + fi + + # We need to process the po/ directory. + POSUB=po + + AC_OUTPUT_COMMANDS( + [case "$CONFIG_FILES" in *po/Makefile.in*) + sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile + esac]) + + dnl These rules are solely for the distribution goal. While doing this + dnl we only have to keep exactly one list of the available catalogs + dnl in configure.in. + for lang in $ALL_LINGUAS; do + GMOFILES="$GMOFILES $lang.gmo" + POFILES="$POFILES $lang.po" + done + + dnl Make all variables we use known to autoconf. + AC_SUBST(CATALOGS) + AC_SUBST(CATOBJEXT) + AC_SUBST(DATADIRNAME) + AC_SUBST(GMOFILES) + AC_SUBST(INSTOBJEXT) + AC_SUBST(INTLLIBS) + AC_SUBST(PO_IN_DATADIR_TRUE) + AC_SUBST(PO_IN_DATADIR_FALSE) + AC_SUBST(POFILES) + AC_SUBST(POSUB) + ]) + +# AM_GLIB_GNU_GETTEXT +# ------------------- +# Do checks necessary for use of gettext. If a suitable implementation +# of gettext is found in either in libintl or in the C library, +# it will set INTLLIBS to the libraries needed for use of gettext +# and AC_DEFINE() HAVE_GETTEXT and ENABLE_NLS. (The shell variable +# gt_cv_have_gettext will be set to "yes".) It will also call AC_SUBST() +# on various variables needed by the Makefile.in.in installed by +# glib-gettextize. +dnl +glib_DEFUN([GLIB_GNU_GETTEXT], + [AC_REQUIRE([AC_PROG_CC])dnl + AC_REQUIRE([AC_HEADER_STDC])dnl + + GLIB_LC_MESSAGES + GLIB_WITH_NLS + + if test "$gt_cv_have_gettext" = "yes"; then + if test "x$ALL_LINGUAS" = "x"; then + LINGUAS= + else + AC_MSG_CHECKING(for catalogs to be installed) + NEW_LINGUAS= + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "${LINGUAS-%UNSET%}"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + NEW_LINGUAS="$NEW_LINGUAS $presentlang" + fi + done + LINGUAS=$NEW_LINGUAS + AC_MSG_RESULT($LINGUAS) + fi + + dnl Construct list of names of catalog files to be constructed. + if test -n "$LINGUAS"; then + for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done + fi + fi + + dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly + dnl find the mkinstalldirs script in another subdir but ($top_srcdir). + dnl Try to locate is. + MKINSTALLDIRS= + if test -n "$ac_aux_dir"; then + MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" + fi + if test -z "$MKINSTALLDIRS"; then + MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" + fi + AC_SUBST(MKINSTALLDIRS) + + dnl Generate list of files to be processed by xgettext which will + dnl be included in po/Makefile. + test -d po || mkdir po + if test "x$srcdir" != "x."; then + if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then + posrcprefix="$srcdir/" + else + posrcprefix="../$srcdir/" + fi + else + posrcprefix="../" + fi + rm -f po/POTFILES + sed -e "/^#/d" -e "/^\$/d" -e "s,.*, $posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \ + < $srcdir/po/POTFILES.in > po/POTFILES + ]) + +# AM_GLIB_DEFINE_LOCALEDIR(VARIABLE) +# ------------------------------- +# Define VARIABLE to the location where catalog files will +# be installed by po/Makefile. +glib_DEFUN([GLIB_DEFINE_LOCALEDIR], +[glib_REQUIRE([GLIB_GNU_GETTEXT])dnl +glib_save_prefix="$prefix" +glib_save_exec_prefix="$exec_prefix" +glib_save_datarootdir="$datarootdir" +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix=$prefix +datarootdir=`eval echo "${datarootdir}"` +if test "x$CATOBJEXT" = "x.mo" ; then + localedir=`eval echo "${libdir}/locale"` +else + localedir=`eval echo "${datadir}/locale"` +fi +prefix="$glib_save_prefix" +exec_prefix="$glib_save_exec_prefix" +datarootdir="$glib_save_datarootdir" +AC_DEFINE_UNQUOTED($1, "$localedir", + [Define the location where the catalogs will be installed]) +]) + +dnl +dnl Now the definitions that aclocal will find +dnl +ifdef(glib_configure_in,[],[ +AC_DEFUN([AM_GLIB_GNU_GETTEXT],[GLIB_GNU_GETTEXT($@)]) +AC_DEFUN([AM_GLIB_DEFINE_LOCALEDIR],[GLIB_DEFINE_LOCALEDIR($@)]) +])dnl + +# GLIB_RUN_PROG(PROGRAM, TEST-FILE, [ACTION-IF-PASS], [ACTION-IF-FAIL]) +# +# Create a temporary file with TEST-FILE as its contents and pass the +# file name to PROGRAM. Perform ACTION-IF-PASS if PROGRAM exits with +# 0 and perform ACTION-IF-FAIL for any other exit status. +AC_DEFUN([GLIB_RUN_PROG], +[cat >conftest.foo <<_ACEOF +$2 +_ACEOF +if AC_RUN_LOG([$1 conftest.foo]); then + m4_ifval([$3], [$3], [:]) +m4_ifvaln([$4], [else $4])dnl +echo "$as_me: failed input was:" >&AS_MESSAGE_LOG_FD +sed 's/^/| /' conftest.foo >&AS_MESSAGE_LOG_FD +fi]) + diff --git a/common/m4/gst-arch.m4 b/common/m4/gst-arch.m4 new file mode 100644 index 0000000..077a20b --- /dev/null +++ b/common/m4/gst-arch.m4 @@ -0,0 +1,140 @@ +dnl AG_GST_ARCH +dnl sets up defines and automake conditionals for host architecture +dnl checks endianness +dnl defines HOST_CPU + +AC_DEFUN([AG_GST_ARCH], +[ + dnl Determine CPU + case "x${target_cpu}" in + xi?86 | xk? | xi?86_64) + case $target_os in + solaris*) + AC_CHECK_DECL([__i386], [I386_ABI="yes"], [I386_ABI="no"]) + AC_CHECK_DECL([__amd64], [AMD64_ABI="yes"], [AMD64_ABI="no"]) + + if test "x$I386_ABI" = "xyes" ; then + HAVE_CPU_I386=yes + AC_DEFINE(HAVE_CPU_I386, 1, [Define if the target CPU is an x86]) + fi + if test "x$AMD64_ABI" = "xyes" ; then + HAVE_CPU_X86_64=yes + AC_DEFINE(HAVE_CPU_X86_64, 1, [Define if the target CPU is a x86_64]) + fi + ;; + *) + HAVE_CPU_I386=yes + AC_DEFINE(HAVE_CPU_I386, 1, [Define if the target CPU is an x86]) + + dnl FIXME could use some better detection + dnl (ie CPUID) + case "x${target_cpu}" in + xi386 | xi486) ;; + *) + AC_DEFINE(HAVE_RDTSC, 1, [Define if RDTSC is available]) ;; + esac + ;; + esac + ;; + xpowerpc) + HAVE_CPU_PPC=yes + AC_DEFINE(HAVE_CPU_PPC, 1, [Define if the target CPU is a PowerPC]) ;; + xpowerpc64) + HAVE_CPU_PPC64=yes + AC_DEFINE(HAVE_CPU_PPC64, 1, [Define if the target CPU is a 64 bit PowerPC]) ;; + xalpha*) + HAVE_CPU_ALPHA=yes + AC_DEFINE(HAVE_CPU_ALPHA, 1, [Define if the target CPU is an Alpha]) ;; + xarm*) + HAVE_CPU_ARM=yes + AC_DEFINE(HAVE_CPU_ARM, 1, [Define if the target CPU is an ARM]) ;; + xsparc*) + HAVE_CPU_SPARC=yes + AC_DEFINE(HAVE_CPU_SPARC, 1, [Define if the target CPU is a SPARC]) ;; + xmips*) + HAVE_CPU_MIPS=yes + AC_DEFINE(HAVE_CPU_MIPS, 1, [Define if the target CPU is a MIPS]) ;; + xhppa*) + HAVE_CPU_HPPA=yes + AC_DEFINE(HAVE_CPU_HPPA, 1, [Define if the target CPU is a HPPA]) ;; + xs390*) + HAVE_CPU_S390=yes + AC_DEFINE(HAVE_CPU_S390, 1, [Define if the target CPU is a S390]) ;; + xia64*) + HAVE_CPU_IA64=yes + AC_DEFINE(HAVE_CPU_IA64, 1, [Define if the target CPU is a IA64]) ;; + xm68k*) + HAVE_CPU_M68K=yes + AC_DEFINE(HAVE_CPU_M68K, 1, [Define if the target CPU is a M68K]) ;; + xx86_64) + HAVE_CPU_X86_64=yes + AC_DEFINE(HAVE_CPU_X86_64, 1, [Define if the target CPU is a x86_64]) ;; + xcris) + HAVE_CPU_CRIS=yes + AC_DEFINE(HAVE_CPU_CRIS, 1, [Define if the target CPU is a CRIS]) ;; + xcrisv32) + HAVE_CPU_CRISV32=yes + AC_DEFINE(HAVE_CPU_CRISV32, 1, [Define if the target CPU is a CRISv32]) ;; + esac + + dnl Determine endianness + AC_C_BIGENDIAN + + AM_CONDITIONAL(HAVE_CPU_I386, test "x$HAVE_CPU_I386" = "xyes") + AM_CONDITIONAL(HAVE_CPU_PPC, test "x$HAVE_CPU_PPC" = "xyes") + AM_CONDITIONAL(HAVE_CPU_PPC64, test "x$HAVE_CPU_PPC64" = "xyes") + AM_CONDITIONAL(HAVE_CPU_ALPHA, test "x$HAVE_CPU_ALPHA" = "xyes") + AM_CONDITIONAL(HAVE_CPU_ARM, test "x$HAVE_CPU_ARM" = "xyes") + AM_CONDITIONAL(HAVE_CPU_SPARC, test "x$HAVE_CPU_SPARC" = "xyes") + AM_CONDITIONAL(HAVE_CPU_HPPA, test "x$HAVE_CPU_HPPA" = "xyes") + AM_CONDITIONAL(HAVE_CPU_MIPS, test "x$HAVE_CPU_MIPS" = "xyes") + AM_CONDITIONAL(HAVE_CPU_S390, test "x$HAVE_CPU_S390" = "xyes") + AM_CONDITIONAL(HAVE_CPU_IA64, test "x$HAVE_CPU_IA64" = "xyes") + AM_CONDITIONAL(HAVE_CPU_M68K, test "x$HAVE_CPU_M68K" = "xyes") + AM_CONDITIONAL(HAVE_CPU_X86_64, test "x$HAVE_CPU_X86_64" = "xyes") + AM_CONDITIONAL(HAVE_CPU_CRIS, test "x$HAVE_CPU_CRIS" = "xyes") + AM_CONDITIONAL(HAVE_CPU_CRISV32, test "x$HAVE_CPU_CRISV32" = "xyes") + + AC_DEFINE_UNQUOTED(HOST_CPU, "$host_cpu", [the host CPU]) + AC_DEFINE_UNQUOTED(TARGET_CPU, "$target_cpu", [the target CPU]) +]) + +dnl check if unaligned memory access works correctly +AC_DEFUN([AG_GST_UNALIGNED_ACCESS], [ + AC_MSG_CHECKING([if unaligned memory access works correctly]) + if test x"$as_cv_unaligned_access" = x ; then + case $host in + alpha*|arm*|hp*|mips*|sh*|sparc*|ia64*) + _AS_ECHO_N([(blacklisted) ]) + as_cv_unaligned_access=no + ;; + i?86*|x86_64*|amd64*|powerpc*|m68k*|cris*) + _AS_ECHO_N([(whitelisted) ]) + as_cv_unaligned_access=yes + ;; + esac + else + _AS_ECHO_N([(cached) ]) + fi + if test x"$as_cv_unaligned_access" = x ; then + AC_TRY_RUN([ +int main(int argc, char **argv) +{ + char array[] = "ABCDEFGH"; + unsigned int iarray[2]; + memcpy(iarray,array,8); +#define GET(x) (*(unsigned int *)((char *)iarray + (x))) + if(GET(0) != 0x41424344 && GET(0) != 0x44434241) return 1; + if(GET(1) != 0x42434445 && GET(1) != 0x45444342) return 1; + if(GET(2) != 0x43444546 && GET(2) != 0x46454443) return 1; + if(GET(3) != 0x44454647 && GET(3) != 0x47464544) return 1; + return 0; +} + ], as_cv_unaligned_access="yes", as_cv_unaligned_access="no") + fi + AC_MSG_RESULT($as_cv_unaligned_access) + if test "$as_cv_unaligned_access" = "yes"; then + AC_DEFINE_UNQUOTED(HAVE_UNALIGNED_ACCESS, 1, + [defined if unaligned memory access works correctly]) + fi +]) diff --git a/common/m4/gst-args.m4 b/common/m4/gst-args.m4 new file mode 100644 index 0000000..b478c82 --- /dev/null +++ b/common/m4/gst-args.m4 @@ -0,0 +1,360 @@ +dnl configure-time options shared among gstreamer modules + +dnl AG_GST_ARG_DEBUG +dnl AG_GST_ARG_PROFILING +dnl AG_GST_ARG_VALGRIND +dnl AG_GST_ARG_GCOV + +dnl AG_GST_ARG_EXAMPLES + +dnl AG_GST_ARG_WITH_PKG_CONFIG_PATH +dnl AG_GST_ARG_WITH_PACKAGE_NAME +dnl AG_GST_ARG_WITH_PACKAGE_ORIGIN + +dnl AG_GST_ARG_WITH_PLUGINS +dnl AG_GST_CHECK_PLUGIN +dnl AG_GST_DISABLE_PLUGIN + +dnl AG_GST_ARG_ENABLE_EXTERNAL +dnl AG_GST_ARG_ENABLE_EXPERIMENTAL +dnl AG_GST_ARG_ENABLE_BROKEN + +dnl AG_GST_ARG_DISABLE_FATAL_WARNINGS +AC_DEFUN([AG_GST_ARG_DEBUG], +[ + dnl debugging stuff + AC_ARG_ENABLE(debug, + AC_HELP_STRING([--disable-debug],[disable addition of -g debugging info]), + [ + case "${enableval}" in + yes) USE_DEBUG=yes ;; + no) USE_DEBUG=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; + esac + ], + [USE_DEBUG=yes]) dnl Default value +]) + +AC_DEFUN([AG_GST_ARG_PROFILING], +[ + AC_ARG_ENABLE(profiling, + AC_HELP_STRING([--enable-profiling], + [adds -pg to compiler commandline, for profiling]), + [ + case "${enableval}" in + yes) USE_PROFILING=yes ;; + no) USE_PROFILING=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-profiling) ;; + esac + ], + [USE_PROFILING=no]) dnl Default value +]) + +AC_DEFUN([AG_GST_ARG_VALGRIND], +[ + dnl valgrind inclusion + AC_ARG_ENABLE(valgrind, + AC_HELP_STRING([--disable-valgrind],[disable run-time valgrind detection]), + [ + case "${enableval}" in + yes) USE_VALGRIND="$USE_DEBUG" ;; + no) USE_VALGRIND=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-valgrind) ;; + esac + ], + [USE_VALGRIND="$USE_DEBUG"]) dnl Default value + VALGRIND_REQ="3.0" + if test "x$USE_VALGRIND" = xyes; then + PKG_CHECK_MODULES(VALGRIND, valgrind >= $VALGRIND_REQ, + USE_VALGRIND="yes", + USE_VALGRIND="no") + fi + if test "x$USE_VALGRIND" = xyes; then + AC_DEFINE(HAVE_VALGRIND, 1, [Define if valgrind should be used]) + AC_MSG_NOTICE(Using extra code paths for valgrind) + fi +]) + +AC_DEFUN([AG_GST_ARG_GCOV], +[ + AC_ARG_ENABLE(gcov, + AC_HELP_STRING([--enable-gcov], + [compile with coverage profiling instrumentation (gcc only)]), + enable_gcov=$enableval, + enable_gcov=no) + if test x$enable_gcov = xyes ; then + if test "x$GCC" != "xyes" + then + AC_MSG_ERROR([gcov only works if gcc is used]) + fi + + AS_COMPILER_FLAG(["-fprofile-arcs"], + [GCOV_CFLAGS="$GCOV_CFLAGS -fprofile-arcs"], + true) + AS_COMPILER_FLAG(["-ftest-coverage"], + [GCOV_CFLAGS="$GCOV_CFLAGS -ftest-coverage"], + true) + dnl remove any -O flags - FIXME: is this needed ? + GCOV_CFLAGS=`echo "$GCOV_CFLAGS" | sed -e 's/-O[[0-9]]*//g'` + dnl libtool 1.5.22 and lower strip -fprofile-arcs from the flags + dnl passed to the linker, which is a bug; -fprofile-arcs implicitly + dnl links in -lgcov, so we do it explicitly here for the same effect + GCOV_LIBS=-lgcov + AC_SUBST(GCOV_CFLAGS) + AC_SUBST(GCOV_LIBS) + GCOV=`echo $CC | sed s/gcc/gcov/g` + AC_SUBST(GCOV) + + GST_GCOV_ENABLED=yes + AC_DEFINE_UNQUOTED(GST_GCOV_ENABLED, 1, + [Defined if gcov is enabled to force a rebuild due to config.h changing]) + dnl if gcov is used, we do not want default -O2 CFLAGS + if test "x$GST_GCOV_ENABLED" = "xyes" + then + CFLAGS="$CFLAGS -O0" + AC_SUBST(CFLAGS) + CXXFLAGS="$CXXFLAGS -O0" + AC_SUBST(CXXFLAGS) + FFLAGS="$FFLAGS -O0" + AC_SUBST(FFLAGS) + CCASFLAGS="$CCASFLAGS -O0" + AC_SUBST(CCASFLAGS) + AC_MSG_NOTICE([gcov enabled, setting CFLAGS and friends to $CFLAGS]) + fi + fi + AM_CONDITIONAL(GST_GCOV_ENABLED, test x$enable_gcov = xyes) +]) + +AC_DEFUN([AG_GST_ARG_EXAMPLES], +[ + AC_ARG_ENABLE(examples, + AC_HELP_STRING([--disable-examples], [disable building examples]), + [ + case "${enableval}" in + yes) BUILD_EXAMPLES=yes ;; + no) BUILD_EXAMPLES=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-examples) ;; + esac + ], + [BUILD_EXAMPLES=yes]) dnl Default value + AM_CONDITIONAL(BUILD_EXAMPLES, test "x$BUILD_EXAMPLES" = "xyes") +]) + +AC_DEFUN([AG_GST_ARG_WITH_PKG_CONFIG_PATH], +[ + dnl possibly modify pkg-config path + AC_ARG_WITH(pkg-config-path, + AC_HELP_STRING([--with-pkg-config-path], + [colon-separated list of pkg-config(1) dirs]), + [ + export PKG_CONFIG_PATH=${withval} + AC_MSG_NOTICE(Set PKG_CONFIG_PATH to $PKG_CONFIG_PATH) + ]) +]) + + +dnl This macro requires that GST_GIT or GST_CVS is set to yes or no (release) +AC_DEFUN([AG_GST_ARG_WITH_PACKAGE_NAME], +[ + dnl package name in plugins + AC_ARG_WITH(package-name, + AC_HELP_STRING([--with-package-name], + [specify package name to use in plugins]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-package-name) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-package-name) ;; + *) GST_PACKAGE_NAME="${withval}" ;; + esac + ], + [ + P=$1 + if test "x$P" = "x" + then + P=$PACKAGE_NAME + fi + + if test "x$PACKAGE_VERSION_NANO" = "x0" + then + GST_PACKAGE_NAME="$P source release" + else + if test "x$PACKAGE_VERSION_NANO" = "x1" + then + GST_PACKAGE_NAME="$P git" + else + GST_PACKAGE_NAME="$P prerelease" + fi + fi + ] + ) + AC_MSG_NOTICE(Using $GST_PACKAGE_NAME as package name) + AC_DEFINE_UNQUOTED(GST_PACKAGE_NAME, "$GST_PACKAGE_NAME", + [package name in plugins]) + AC_SUBST(GST_PACKAGE_NAME) +]) + +AC_DEFUN([AG_GST_ARG_WITH_PACKAGE_ORIGIN], +[ + dnl package origin URL + AC_ARG_WITH(package-origin, + AC_HELP_STRING([--with-package-origin], + [specify package origin URL to use in plugins]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-package-origin) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-package-origin) ;; + *) GST_PACKAGE_ORIGIN="${withval}" ;; + esac + ], + [GST_PACKAGE_ORIGIN="[Unknown package origin]"] dnl Default value + ) + AC_MSG_NOTICE(Using $GST_PACKAGE_ORIGIN as package origin) + AC_DEFINE_UNQUOTED(GST_PACKAGE_ORIGIN, "$GST_PACKAGE_ORIGIN", + [package origin]) + AC_SUBST(GST_PACKAGE_ORIGIN) +]) + +dnl sets WITH_PLUGINS to the list of plug-ins given as an argument +dnl also clears GST_PLUGINS_ALL and GST_PLUGINS_SELECTED +AC_DEFUN([AG_GST_ARG_WITH_PLUGINS], +[ + AC_ARG_WITH(plugins, + AC_HELP_STRING([--with-plugins], + [comma-separated list of dependencyless plug-ins to compile]), + [WITH_PLUGINS=$withval], + [WITH_PLUGINS=]) + + GST_PLUGINS_ALL="" + GST_PLUGINS_SELECTED="" + GST_PLUGINS_NONPORTED="" + + AC_SUBST(GST_PLUGINS_ALL) + AC_SUBST(GST_PLUGINS_SELECTED) + AC_SUBST(GST_PLUGINS_NONPORTED) +]) + +dnl AG_GST_CHECK_PLUGIN(PLUGIN-NAME) +dnl +dnl This macro adds the plug-in to GST_PLUGINS_ALL. Then it +dnl checks if WITH_PLUGINS is empty or the plugin is present in WITH_PLUGINS, +dnl and if so adds it to GST_PLUGINS_SELECTED. Then it checks if the plugin +dnl is present in WITHOUT_PLUGINS (ie. was disabled specifically) and if so +dnl removes it from GST_PLUGINS_SELECTED. +dnl +dnl The macro will call AM_CONDITIONAL(USE_PLUGIN_, ...) to allow +dnl control of what is built in Makefile.ams. +AC_DEFUN([AG_GST_CHECK_PLUGIN], +[ + GST_PLUGINS_ALL="$GST_PLUGINS_ALL [$1]" + + define([pname_def],translit([$1], -a-z, _a-z)) + + AC_ARG_ENABLE([$1], + AC_HELP_STRING([--disable-[$1]], [disable dependency-less $1 plugin]), + [ + case "${enableval}" in + yes) [gst_use_]pname_def=yes ;; + no) [gst_use_]pname_def=no ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-$1]) ;; + esac + ], + [[gst_use_]pname_def=yes]) dnl Default value + + if test x$[gst_use_]pname_def = xno; then + AC_MSG_NOTICE(disabling dependency-less plugin $1) + WITHOUT_PLUGINS="$WITHOUT_PLUGINS [$1]" + fi + undefine([pname_def]) + + dnl First check inclusion + if [[ -z "$WITH_PLUGINS" ]] || echo " [$WITH_PLUGINS] " | tr , ' ' | grep -i " [$1] " > /dev/null; then + GST_PLUGINS_SELECTED="$GST_PLUGINS_SELECTED [$1]" + fi + dnl Then check exclusion + if echo " [$WITHOUT_PLUGINS] " | tr , ' ' | grep -i " [$1] " > /dev/null; then + GST_PLUGINS_SELECTED=`echo " $GST_PLUGINS_SELECTED " | $SED -e 's/ [$1] / /'` + fi + dnl Finally check if the plugin is ported or not + if echo " [$GST_PLUGINS_NONPORTED] " | tr , ' ' | grep -i " [$1] " > /dev/null; then + GST_PLUGINS_SELECTED=`echo " $GST_PLUGINS_SELECTED " | $SED -e 's/ [$1] / /'` + fi + AM_CONDITIONAL([USE_PLUGIN_]translit([$1], a-z, A-Z), echo " $GST_PLUGINS_SELECTED " | grep -i " [$1] " > /dev/null) +]) + +dnl AG_GST_DISABLE_PLUGIN(PLUGIN-NAME) +dnl +dnl This macro disables the plug-in by removing it from +dnl GST_PLUGINS_SELECTED. +AC_DEFUN([AG_GST_DISABLE_PLUGIN], +[ + GST_PLUGINS_SELECTED=`echo " $GST_PLUGINS_SELECTED " | $SED -e 's/ [$1] / /'` + AM_CONDITIONAL([USE_PLUGIN_]translit([$1], a-z, A-Z), false) +]) + +AC_DEFUN([AG_GST_ARG_ENABLE_EXTERNAL], +[ + AG_GST_CHECK_FEATURE(EXTERNAL, [building of plug-ins with external deps],, + HAVE_EXTERNAL=yes, enabled, + [ + AC_MSG_NOTICE(building external plug-ins) + BUILD_EXTERNAL="yes" + ],[ + AC_MSG_WARN(all plug-ins with external dependencies will not be built) + BUILD_EXTERNAL="no" + ]) + # make BUILD_EXTERNAL available to Makefile.am + AM_CONDITIONAL(BUILD_EXTERNAL, test "x$BUILD_EXTERNAL" = "xyes") +]) + +dnl experimental plug-ins; stuff that hasn't had the dust settle yet +dnl read 'builds, but might not work' +AC_DEFUN([AG_GST_ARG_ENABLE_EXPERIMENTAL], +[ + AG_GST_CHECK_FEATURE(EXPERIMENTAL, [building of experimental plug-ins],, + HAVE_EXPERIMENTAL=yes, disabled, + [ + AC_MSG_WARN(building experimental plug-ins) + BUILD_EXPERIMENTAL="yes" + ],[ + AC_MSG_NOTICE(not building experimental plug-ins) + BUILD_EXPERIMENTAL="no" + ]) + # make BUILD_EXPERIMENTAL available to Makefile.am + AM_CONDITIONAL(BUILD_EXPERIMENTAL, test "x$BUILD_EXPERIMENTAL" = "xyes") +]) + +dnl broken plug-ins; stuff that doesn't seem to build at the moment +AC_DEFUN([AG_GST_ARG_ENABLE_BROKEN], +[ + AG_GST_CHECK_FEATURE(BROKEN, [building of broken plug-ins],, + HAVE_BROKEN=yes, disabled, + [ + AC_MSG_WARN([building broken plug-ins -- no bug reports on these, only patches ...]) + ],[ + AC_MSG_NOTICE([not building broken plug-ins]) + ]) +]) + +dnl allow people (or build tools) to override default behaviour +dnl for fatal compiler warnings +dnl Enable fatal warnings by default only for development versions +AC_DEFUN([AG_GST_ARG_DISABLE_FATAL_WARNINGS], +[ + AC_ARG_ENABLE(fatal-warnings, + AC_HELP_STRING([--disable-fatal-warnings], + [Don't turn compiler warnings into fatal errors]), + [ + case "${enableval}" in + yes) FATAL_WARNINGS=yes ;; + no) FATAL_WARNINGS=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-fatal-warnings) ;; + esac + ], + [ + if test "x`expr $PACKAGE_VERSION_MINOR % 2`" = "x1" -a "x`expr $PACKAGE_VERSION_MICRO '<' 90`" = "x1"; then + FATAL_WARNINGS=yes + else + FATAL_WARNINGS=no + fi + ]) +]) diff --git a/common/m4/gst-check.m4 b/common/m4/gst-check.m4 new file mode 100644 index 0000000..4277053 --- /dev/null +++ b/common/m4/gst-check.m4 @@ -0,0 +1,294 @@ +dnl pkg-config-based checks for GStreamer modules and dependency modules + +dnl generic: +dnl AG_GST_PKG_CHECK_MODULES([PREFIX], [WHICH], [REQUIRED]) +dnl sets HAVE_[$PREFIX], [$PREFIX]_* +dnl AG_GST_CHECK_MODULES([PREFIX], [MODULE], [MINVER], [NAME], [REQUIRED]) +dnl sets HAVE_[$PREFIX], [$PREFIX]_* + +dnl specific: +dnl AG_GST_CHECK_GST([MAJMIN], [MINVER], [REQUIRED]) +dnl also sets/ACSUBSTs GST_TOOLS_DIR and GST_PLUGINS_DIR +dnl AG_GST_CHECK_GST_BASE([MAJMIN], [MINVER], [REQUIRED]) +dnl AG_GST_CHECK_GST_CONTROLLER([MAJMIN], [MINVER], [REQUIRED]) +dnl AG_GST_CHECK_GST_NET([MAJMIN], [MINVER], [REQUIRED]) +dnl AG_GST_CHECK_GST_CHECK([MAJMIN], [MINVER], [REQUIRED]) +dnl AG_GST_CHECK_GST_PLUGINS_BASE([MAJMIN], [MINVER], [REQUIRED]) +dnl also sets/ACSUBSTs GSTPB_PLUGINS_DIR + +AC_DEFUN([AG_GST_PKG_CHECK_MODULES], +[ + which="[$2]" + dnl not required by default, since we use this mostly for plugin deps + required=ifelse([$3], , "no", [$3]) + + PKG_CHECK_MODULES([$1], $which, + [ + HAVE_[$1]="yes" + ], + [ + HAVE_[$1]="no" + if test "x$required" = "xyes"; then + AC_MSG_ERROR($[$1]_PKG_ERRORS) + else + AC_MSG_NOTICE($[$1]_PKG_ERRORS) + fi + ]) + + dnl AC_SUBST of CFLAGS and LIBS was not done before automake 1.7 + dnl It gets done automatically in automake >= 1.7, which we now require +])) + +AC_DEFUN([AG_GST_CHECK_MODULES], +[ + module=[$2] + minver=[$3] + name="[$4]" + required=ifelse([$5], , "yes", [$5]) dnl required by default + + PKG_CHECK_MODULES([$1], $module >= $minver, + [ + HAVE_[$1]="yes" + ], + [ + HAVE_[$1]="no" + AC_MSG_NOTICE($[$1]_PKG_ERRORS) + if test "x$required" = "xyes"; then + AC_MSG_ERROR([no $module >= $minver ($name) found]) + else + AC_MSG_NOTICE([no $module >= $minver ($name) found]) + fi + ]) + + dnl AC_SUBST of CFLAGS and LIBS was not done before automake 1.7 + dnl It gets done automatically in automake >= 1.7, which we now require +])) + +AC_DEFUN([AG_GST_CHECK_GST], +[ + AG_GST_CHECK_MODULES(GST, gstreamer-[$1], [$2], [GStreamer], [$3]) + dnl allow setting before calling this macro to override + if test -z $GST_TOOLS_DIR; then + GST_TOOLS_DIR=`$PKG_CONFIG --variable=toolsdir gstreamer-[$1]` + if test -z $GST_TOOLS_DIR; then + AC_MSG_ERROR( + [no tools dir set in GStreamer pkg-config file, core upgrade needed.]) + fi + fi + AC_MSG_NOTICE([using GStreamer tools in $GST_TOOLS_DIR]) + AC_SUBST(GST_TOOLS_DIR) + + dnl check for where core plug-ins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_DIR; then + GST_PLUGINS_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-[$1]` + if test -z $GST_PLUGINS_DIR; then + AC_MSG_ERROR( + [no pluginsdir set in GStreamer pkg-config file, core upgrade needed.]) + fi + fi + AC_MSG_NOTICE([using GStreamer plug-ins in $GST_PLUGINS_DIR]) + AC_SUBST(GST_PLUGINS_DIR) +]) + +AC_DEFUN([AG_GST_CHECK_GST_BASE], +[ + AG_GST_CHECK_MODULES(GST_BASE, gstreamer-base-[$1], [$2], + [GStreamer Base Libraries], [$3]) +]) + +AC_DEFUN([AG_GST_CHECK_GST_CONTROLLER], +[ + AG_GST_CHECK_MODULES(GST_CONTROLLER, gstreamer-controller-[$1], [$2], + [GStreamer Controller Library], [$3]) +]) + +AC_DEFUN([AG_GST_CHECK_GST_NET], +[ + AG_GST_CHECK_MODULES(GST_NET, gstreamer-net-[$1], [$2], + [GStreamer Network Library], [$3]) +]) + +AC_DEFUN([AG_GST_CHECK_GST_CHECK], +[ + AG_GST_CHECK_MODULES(GST_CHECK, gstreamer-check-[$1], [$2], + [GStreamer Check unittest Library], [$3]) +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_UNINSTALLED_SETUP([ACTION-IF-UNINSTALLED], [ACTION-IF-NOT]) +dnl +dnl ACTION-IF-UNINSTALLED (optional) extra actions to perform if the setup +dnl is an uninstalled setup +dnl ACTION-IF-NOT (optional) extra actions to perform if the setup +dnl is not an uninstalled setup +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_UNINSTALLED_SETUP], +[ + AC_MSG_CHECKING([whether this is an uninstalled GStreamer setup]) + AC_CACHE_VAL(gst_cv_is_uninstalled_setup,[ + gst_cv_is_uninstalled_setup=no + if (set -u; : $GST_PLUGIN_SYSTEM_PATH) 2>/dev/null ; then + if test -z "$GST_PLUGIN_SYSTEM_PATH" \ + -a -n "$GST_PLUGIN_SCANNER" \ + -a -n "$GST_PLUGIN_PATH" \ + -a -n "$GST_REGISTRY" \ + -a -n "$DYLD_LIBRARY_PATH" \ + -a -n "$LD_LIBRARY_PATH"; then + gst_cv_is_uninstalled_setup=yes; + fi + fi + ]) + AC_MSG_RESULT($gst_cv_is_uninstalled_setup) + if test "x$gst_cv_is_uninstalled_setup" = "xyes"; then + ifelse([$1], , :, [$1]) + else + ifelse([$2], , :, [$2]) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_BASE([GST-API_VERSION], [MIN-VERSION], [REQUIRED]) +dnl +dnl Sets GST_PLUGINS_BASE_CFLAGS and GST_PLUGINS_BASE_LIBS. +dnl +dnl Also sets GSTPB_PLUGINS_DIR (and for consistency also GST_PLUGINS_BASE_DIR) +dnl for use in Makefile.am. This is only really needed/useful in uninstalled +dnl setups, since in an installed setup all plugins will be found in +dnl GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_BASE], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_BASE, gstreamer-plugins-base-[$1], [$2], + [GStreamer Base Plugins], [$3]) + + if test "x$HAVE_GST_PLUGINS_BASE" = "xyes"; then + dnl check for where base plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GSTPB_PLUGINS_DIR; then + GSTPB_PLUGINS_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-base-[$1]` + if test -z $GSTPB_PLUGINS_DIR; then + AC_MSG_ERROR( + [no pluginsdir set in GStreamer Base Plugins pkg-config file]) + fi + fi + AC_MSG_NOTICE([using GStreamer Base Plugins in $GSTPB_PLUGINS_DIR]) + GST_PLUGINS_BASE_DIR="$GSTPB_PLUGINS_DIR/gst:$GSTPB_PLUGINS_DIR/sys:$GSTPB_PLUGINS_DIR/ext" + AC_SUBST(GST_PLUGINS_BASE_DIR) + AC_SUBST(GSTPB_PLUGINS_DIR) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_GOOD([GST-API_VERSION], [MIN-VERSION]) +dnl +dnl Will set GST_PLUGINS_GOOD_DIR for use in Makefile.am. Note that this will +dnl only be set in an uninstalled setup, since -good ships no .pc file and in +dnl an installed setup all plugins will be found in GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_GOOD], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_GOOD, gstreamer-plugins-good-[$1], [$2], + [GStreamer Good Plugins], [no]) + + if test "x$HAVE_GST_PLUGINS_GOOD" = "xyes"; then + dnl check for where good plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_GOOD_DIR; then + GST_PLUGINS_GOOD_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-good-[$1]` + if test -z $GST_PLUGINS_GOOD_DIR; then + AC_MSG_ERROR([no pluginsdir set in GStreamer Good Plugins pkg-config file]) + fi + fi + AC_MSG_NOTICE([using GStreamer Good Plugins in $GST_PLUGINS_GOOD_DIR]) + GST_PLUGINS_GOOD_DIR="$GST_PLUGINS_GOOD_DIR/gst:$GST_PLUGINS_GOOD_DIR/sys:$GST_PLUGINS_GOOD_DIR/ext" + AC_SUBST(GST_PLUGINS_GOOD_DIR) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_UGLY([GST-API_VERSION], [MIN-VERSION]) +dnl +dnl Will set GST_PLUGINS_UGLY_DIR for use in Makefile.am. Note that this will +dnl only be set in an uninstalled setup, since -bad ships no .pc file and in +dnl an installed setup all plugins will be found in GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_UGLY], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_UGLY, gstreamer-plugins-ugly-[$1], [$2], + [GStreamer Ugly Plugins], [no]) + + if test "x$HAVE_GST_PLUGINS_UGLY" = "xyes"; then + dnl check for where ugly plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_UGLY_DIR; then + GST_PLUGINS_UGLY_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-ugly-[$1]` + if test -z $GST_PLUGINS_UGLY_DIR; then + AC_MSG_ERROR([no pluginsdir set in GStreamer Ugly Plugins pkg-config file]) + fi + fi + AC_MSG_NOTICE([using GStreamer Ugly Plugins in $GST_PLUGINS_UGLY_DIR]) + GST_PLUGINS_UGLY_DIR="$GST_PLUGINS_UGLY_DIR/gst:$GST_PLUGINS_UGLY_DIR/sys:$GST_PLUGINS_UGLY_DIR/ext" + AC_SUBST(GST_PLUGINS_UGLY_DIR) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_BAD([GST-API_VERSION], [MIN-VERSION]) +dnl +dnl Will set GST_PLUGINS_BAD_DIR for use in Makefile.am. Note that this will +dnl only be set in an uninstalled setup, since -ugly ships no .pc file and in +dnl an installed setup all plugins will be found in GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_BAD], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_BAD, gstreamer-plugins-bad-[$1], [$2], + [GStreamer Bad Plugins], [no]) + + if test "x$HAVE_GST_PLUGINS_BAD" = "xyes"; then + dnl check for where bad plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_BAD_DIR; then + GST_PLUGINS_BAD_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-bad-[$1]` + if test -z $GST_PLUGINS_BAD_DIR; then + AC_MSG_ERROR([no pluginsdir set in GStreamer Bad Plugins pkg-config file]) + fi + fi + AC_MSG_NOTICE([using GStreamer Bad Plugins in $GST_PLUGINS_BAD_DIR]) + GST_PLUGINS_BAD_DIR="$GST_PLUGINS_BAD_DIR/gst:$GST_PLUGINS_BAD_DIR/sys:$GST_PLUGINS_BAD_DIR/ext" + AC_SUBST(GST_PLUGINS_BAD_DIR) + fi +]) + +dnl =========================================================================== +dnl AG_GST_CHECK_GST_PLUGINS_LIBAV([GST-API_VERSION], [MIN-VERSION]) +dnl +dnl Will set GST_PLUGINS_LIBAV_DIR for use in Makefile.am. Note that this will +dnl only be set in an uninstalled setup, since -libav ships no .pc file and in +dnl an installed setup all plugins will be found in GST_PLUGINS_DIR anyway. +dnl =========================================================================== +AC_DEFUN([AG_GST_CHECK_GST_PLUGINS_LIBAV], +[ + AG_GST_CHECK_MODULES(GST_PLUGINS_LIBAV, gstreamer-plugins-libav-[$1], [$2], + [GStreamer Libav Plugins], [no]) + + if test "x$HAVE_GST_PLUGINS_LIBAV" = "xyes"; then + dnl check for where libav plugins got installed + dnl this is used for unit tests + dnl allow setting before calling this macro to override + if test -z $GST_PLUGINS_LIBAV_DIR; then + GST_PLUGINS_LIBAV_DIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-plugins-libav-[$1]` + if test -z $GST_PLUGINS_LIBAV_DIR; then + AC_MSG_ERROR([no pluginsdir set in GStreamer Libav Plugins pkg-config file]) + fi + fi + GST_PLUGINS_LIBAV_DIR="$GST_PLUGINS_LIBAV_DIR/ext/libav" + AC_MSG_NOTICE([using GStreamer Libav Plugins in $GST_PLUGINS_LIBAV_DIR]) + AC_SUBST(GST_PLUGINS_LIBAV_DIR) + fi +]) diff --git a/common/m4/gst-debuginfo.m4 b/common/m4/gst-debuginfo.m4 new file mode 100644 index 0000000..b48854d --- /dev/null +++ b/common/m4/gst-debuginfo.m4 @@ -0,0 +1,46 @@ +AC_DEFUN([AG_GST_DEBUGINFO], [ +AC_ARG_ENABLE(debug, +AC_HELP_STRING([--disable-debug],[disable addition of -g debugging info]), +[case "${enableval}" in + yes) USE_DEBUG=yes ;; + no) USE_DEBUG=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; +esac], +[USE_DEBUG=yes]) dnl Default value + +AC_ARG_ENABLE(DEBUG, +AC_HELP_STRING([--disable-DEBUG],[disables compilation of debugging messages]), +[case "${enableval}" in + yes) ENABLE_DEBUG=yes ;; + no) ENABLE_DEBUG=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-DEBUG) ;; +esac], +[ENABLE_DEBUG=yes]) dnl Default value +if test x$ENABLE_DEBUG = xyes; then + AC_DEFINE(GST_DEBUG_ENABLED, 1, [Define if DEBUG statements should be compiled in]) +fi + +AC_ARG_ENABLE(INFO, +AC_HELP_STRING([--disable-INFO],[disables compilation of informational messages]), +[case "${enableval}" in + yes) ENABLE_INFO=yes ;; + no) ENABLE_INFO=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-INFO) ;; +esac], +[ENABLE_INFO=yes]) dnl Default value +if test x$ENABLE_INFO = xyes; then + AC_DEFINE(GST_INFO_ENABLED, 1, [Define if INFO statements should be compiled in]) +fi + +AC_ARG_ENABLE(debug-color, +AC_HELP_STRING([--disable-debug-color],[disables color output of DEBUG and INFO output]), +[case "${enableval}" in + yes) ENABLE_DEBUG_COLOR=yes ;; + no) ENABLE_DEBUG_COLOR=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug-color) ;; +esac], +[ENABLE_DEBUG_COLOR=yes]) dnl Default value +if test "x$ENABLE_DEBUG_COLOR" = xyes; then + AC_DEFINE(GST_DEBUG_COLOR, 1, [Define if debugging messages should be colorized]) +fi +]) diff --git a/common/m4/gst-default.m4 b/common/m4/gst-default.m4 new file mode 100644 index 0000000..8de9756 --- /dev/null +++ b/common/m4/gst-default.m4 @@ -0,0 +1,120 @@ +dnl default elements used for tests and such + +dnl AG_GST_DEFAULT_ELEMENTS + +AC_DEFUN([AG_GST_DEFAULT_ELEMENTS], +[ + dnl decide on default elements + dnl FIXME: describe where exactly this gets used + dnl FIXME: decide if it's a problem that this could point to sinks from + dnl depending plugin modules + dnl FIXME: when can we just use autoaudiosrc and autovideosrc? + DEFAULT_AUDIOSINK="autoaudiosink" + DEFAULT_VIDEOSINK="autovideosink" + DEFAULT_AUDIOSRC="alsasrc" + DEFAULT_VIDEOSRC="v4l2src" + DEFAULT_VISUALIZER="goom" + case "$host" in + *-sun-* | *pc-solaris* ) + DEFAULT_AUDIOSRC="sunaudiosrc" + ;; + *-darwin* ) + DEFAULT_AUDIOSRC="osxaudiosrc" + ;; + esac + + dnl Default audio sink + AC_ARG_WITH(default-audiosink, + AC_HELP_STRING([--with-default-audiosink], [specify default audio sink]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-audiosink) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-audiosink) ;; + *) DEFAULT_AUDIOSINK="${withval}" ;; + esac + ], + [ + DEFAULT_AUDIOSINK="$DEFAULT_AUDIOSINK" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_AUDIOSINK as default audio sink) + AC_SUBST(DEFAULT_AUDIOSINK) + AC_DEFINE_UNQUOTED(DEFAULT_AUDIOSINK, "$DEFAULT_AUDIOSINK", + [Default audio sink]) + + dnl Default audio source + AC_ARG_WITH(default-audiosrc, + AC_HELP_STRING([--with-default-audiosrc], [specify default audio source]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-audiosrc) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-audiosrc) ;; + *) DEFAULT_AUDIOSRC="${withval}" ;; + esac + ], + [ + DEFAULT_AUDIOSRC="$DEFAULT_AUDIOSRC" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_AUDIOSRC as default audio source) + AC_SUBST(DEFAULT_AUDIOSRC) + AC_DEFINE_UNQUOTED(DEFAULT_AUDIOSRC, "$DEFAULT_AUDIOSRC", + [Default audio source]) + + dnl Default video sink + AC_ARG_WITH(default-videosink, + AC_HELP_STRING([--with-default-videosink], [specify default video sink]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-videosink) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-videosink) ;; + *) DEFAULT_VIDEOSINK="${withval}" ;; + esac + ], + [ + DEFAULT_VIDEOSINK="$DEFAULT_VIDEOSINK" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_VIDEOSINK as default video sink) + AC_SUBST(DEFAULT_VIDEOSINK) + AC_DEFINE_UNQUOTED(DEFAULT_VIDEOSINK, "$DEFAULT_VIDEOSINK", + [Default video sink]) + + dnl Default video source + AC_ARG_WITH(default-videosrc, + AC_HELP_STRING([--with-default-videosrc], [specify default video source]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-videosrc) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-videosrc) ;; + *) DEFAULT_VIDEOSRC="${withval}" ;; + esac + ], + [ + DEFAULT_VIDEOSRC="$DEFAULT_VIDEOSRC" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_VIDEOSRC as default video source) + AC_SUBST(DEFAULT_VIDEOSRC) + AC_DEFINE_UNQUOTED(DEFAULT_VIDEOSRC, "$DEFAULT_VIDEOSRC", + [Default video source]) + + dnl Default visualizer + AC_ARG_WITH(default-visualizer, + AC_HELP_STRING([--with-default-visualizer], [specify default visualizer]), + [ + case "${withval}" in + yes) AC_MSG_ERROR(bad value ${withval} for --with-default-visualizer) ;; + no) AC_MSG_ERROR(bad value ${withval} for --with-default-visualizer) ;; + *) DEFAULT_VISUALIZER="${withval}" ;; + esac + ], + [ + DEFAULT_VISUALIZER="$DEFAULT_VISUALIZER" + ] dnl Default value as determined above + ) + AC_MSG_NOTICE(Using $DEFAULT_VISUALIZER as default visualizer) + AC_SUBST(DEFAULT_VISUALIZER) + AC_DEFINE_UNQUOTED(DEFAULT_VISUALIZER, "$DEFAULT_VISUALIZER", + [Default visualizer]) +]) diff --git a/common/m4/gst-doc.m4 b/common/m4/gst-doc.m4 new file mode 100644 index 0000000..5d3f0fd --- /dev/null +++ b/common/m4/gst-doc.m4 @@ -0,0 +1,92 @@ +AC_DEFUN([AG_GST_DOCBOOK_CHECK], +[ + dnl choose a location to install docbook docs in + if test "x$PACKAGE_TARNAME" = "x" + then + AC_MSG_ERROR([Internal error - PACKAGE_TARNAME not set]) + fi + docdir="\$(datadir)/doc/$PACKAGE_TARNAME-$GST_API_VERSION" + + dnl enable/disable docbook documentation building + AC_ARG_ENABLE(docbook, + AC_HELP_STRING([--enable-docbook], + [use docbook to build documentation [default=no]]),, + enable_docbook=no) + + have_docbook=no + + if test x$enable_docbook = xyes; then + dnl check if we actually have everything we need + + dnl check for docbook tools + AC_CHECK_PROG(HAVE_DOCBOOK2PS, docbook2ps, yes, no) + AC_CHECK_PROG(HAVE_XSLTPROC, xsltproc, yes, no) + AC_CHECK_PROG(HAVE_JADETEX, jadetex, yes, no) + AC_CHECK_PROG(HAVE_PS2PDF, ps2pdf, yes, no) + + dnl check if we can process docbook stuff + AS_DOCBOOK(have_docbook=yes, have_docbook=no) + + dnl check for extra tools + AC_CHECK_PROG(HAVE_DVIPS, dvips, yes, no) + AC_CHECK_PROG(HAVE_XMLLINT, xmllint, yes, no) + + AC_CHECK_PROG(HAVE_PNGTOPNM, pngtopnm, yes, no) + AC_CHECK_PROG(HAVE_PNMTOPS, pnmtops, yes, no) + AC_CHECK_PROG(HAVE_EPSTOPDF, epstopdf, yes, no) + + dnl check if we can generate HTML + if test "x$HAVE_XSLTPROC" = "xyes" && \ + test "x$enable_docbook" = "xyes" && \ + test "x$HAVE_XMLLINT" = "xyes"; then + DOC_HTML=yes + AC_MSG_NOTICE(Will output HTML documentation) + else + DOC_HTML=no + AC_MSG_NOTICE(Will not output HTML documentation) + fi + + dnl check if we can generate PS + if test "x$HAVE_DOCBOOK2PS" = "xyes" && \ + test "x$enable_docbook" = "xyes" && \ + test "x$HAVE_XMLLINT" = "xyes" && \ + test "x$HAVE_JADETEX" = "xyes" && \ + test "x$HAVE_DVIPS" = "xyes" && \ + test "x$HAVE_PNGTOPNM" = "xyes" && \ + test "x$HAVE_PNMTOPS" = "xyes"; then + DOC_PS=yes + AC_MSG_NOTICE(Will output PS documentation) + else + DOC_PS=no + AC_MSG_NOTICE(Will not output PS documentation) + fi + + dnl check if we can generate PDF - using only ps2pdf + if test "x$DOC_PS" = "xyes" && \ + test "x$enable_docbook" = "xyes" && \ + test "x$HAVE_XMLLINT" = "xyes" && \ + test "x$HAVE_PS2PDF" = "xyes"; then + DOC_PDF=yes + AC_MSG_NOTICE(Will output PDF documentation) + else + DOC_PDF=no + AC_MSG_NOTICE(Will not output PDF documentation) + fi + + dnl if we don't have everything, we should disable + if test "x$have_docbook" != "xyes"; then + enable_docbook=no + fi + fi + + dnl if we're going to install documentation, tell us where + if test "x$have_docbook" = "xyes"; then + AC_MSG_NOTICE(Installing documentation in $docdir) + AC_SUBST(docdir) + fi + + AM_CONDITIONAL(ENABLE_DOCBOOK, test x$enable_docbook = xyes) + AM_CONDITIONAL(DOC_HTML, test x$DOC_HTML = xyes) + AM_CONDITIONAL(DOC_PDF, test x$DOC_PDF = xyes) + AM_CONDITIONAL(DOC_PS, test x$DOC_PS = xyes) +]) diff --git a/common/m4/gst-dowhile.m4 b/common/m4/gst-dowhile.m4 new file mode 100644 index 0000000..069808d --- /dev/null +++ b/common/m4/gst-dowhile.m4 @@ -0,0 +1,24 @@ +dnl +dnl Check for working do while(0) macros. This is used by G_STMT_START +dnl and G_STMT_END in glib/gmacros.h. Without having this defined we +dnl get "ambigious if-else" compiler warnings when compling C++ code. +dnl +dnl Copied from GLib's configure.in +dnl +AC_DEFUN([AG_GST_CHECK_DOWHILE_MACROS],[ + +dnl *** check for working do while(0) macros *** +AC_CACHE_CHECK([for working do while(0) macros], _cv_g_support_dowhile_macros, [ + AC_TRY_COMPILE([],[ + #define STMT_START do + #define STMT_END while(0) + #define STMT_TEST STMT_START { i = 0; } STMT_END + int main(void) { int i = 1; STMT_TEST; return i; }], + [_cv_g_support_dowhile_macros=yes], + [_cv_g_support_dowhile_macros=no], + [_cv_g_support_dowhile_macros=yes]) +]) +if test x$_cv_g_support_dowhile_macros = xyes; then + AC_DEFINE(HAVE_DOWHILE_MACROS, 1, [define for working do while(0) macros]) +fi +]) diff --git a/common/m4/gst-error.m4 b/common/m4/gst-error.m4 new file mode 100644 index 0000000..bce80d2 --- /dev/null +++ b/common/m4/gst-error.m4 @@ -0,0 +1,298 @@ +dnl handle various error-related things + +dnl Thomas Vander Stichele +dnl Tim-Philipp Müller + +dnl Last modification: 2008-02-18 + +dnl AG_GST_SET_ERROR_CFLAGS([ADD-WERROR], [MORE_FLAGS]) +dnl AG_GST_SET_ERROR_CXXFLAGS([ADD-WERROR], [MORE_FLAGS]) +dnl AG_GST_SET_LEVEL_DEFAULT([IS-GIT-VERSION]) + + +dnl Sets WARNING_CFLAGS and ERROR_CFLAGS to something the compiler +dnl will accept and AC_SUBST them so they are available in Makefile +dnl +dnl WARNING_CFLAGS will contain flags to make the compiler emit more +dnl warnings. +dnl ERROR_CFLAGS will contain flags to make those warnings fatal, +dnl unless ADD-WERROR is set to "no" +dnl +dnl If MORE_FLAGS is set, tries to add each of the given flags +dnl to WARNING_CFLAGS if the compiler supports them. Each flag is +dnl tested separately. +dnl +dnl These flags can be overridden at make time: +dnl make ERROR_CFLAGS= +AC_DEFUN([AG_GST_SET_ERROR_CFLAGS], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AS_COMPILER_FLAG]) + + WARNING_CFLAGS="" + ERROR_CFLAGS="" + + dnl if we support -Wall, set it unconditionally + AS_COMPILER_FLAG(-Wall, + WARNING_CFLAGS="$WARNING_CFLAGS -Wall") + + dnl Warn if declarations after statements are used (C99 extension) + AS_COMPILER_FLAG(-Wdeclaration-after-statement, + WARNING_CFLAGS="$WARNING_CFLAGS -Wdeclaration-after-statement") + + dnl Warn if variable length arrays are used (C99 extension) + AS_COMPILER_FLAG(-Wvla, + WARNING_CFLAGS="$WARNING_CFLAGS -Wvla") + + dnl Warn for invalid pointer arithmetic + AS_COMPILER_FLAG(-Wpointer-arith, + WARNING_CFLAGS="$WARNING_CFLAGS -Wpointer-arith") + + dnl if asked for, add -Werror if supported + if test "x$1" != "xno" + then + AS_COMPILER_FLAG(-Werror, ERROR_CFLAGS="$ERROR_CFLAGS -Werror") + + dnl if -Werror isn't suported, try -errwarn=%all (Sun Forte case) + if test "x$ERROR_CFLAGS" = "x" + then + AS_COMPILER_FLAG([-errwarn=%all], [ + ERROR_CFLAGS="-errwarn=%all" + dnl try -errwarn=%all,no%E_EMPTY_DECLARATION, + dnl no%E_STATEMENT_NOT_REACHED,no%E_ARGUEMENT_MISMATCH, + dnl no%E_MACRO_REDEFINED (Sun Forte case) + dnl For Forte we need disable "empty declaration" warning produced by un-needed semicolon + dnl "statement not reached" disabled because there is g_assert_not_reached () in some places + dnl "macro redefined" because of gst/gettext.h + dnl FIXME: is it really supposed to be 'ARGUEMENT' and not 'ARGUMENT'? + for f in 'no%E_EMPTY_DECLARATION' \ + 'no%E_STATEMENT_NOT_REACHED' \ + 'no%E_ARGUEMENT_MISMATCH' \ + 'no%E_MACRO_REDEFINED' \ + 'no%E_LOOP_NOT_ENTERED_AT_TOP' + do + AS_COMPILER_FLAG([-errwarn=%all,$f], [ + ERROR_CFLAGS="$ERROR_CFLAGS,$f" + ]) + done + ]) + else + dnl Add -fno-strict-aliasing for GLib versions before 2.19.8 + dnl as before G_LOCK and friends caused strict aliasing compiler + dnl warnings. + PKG_CHECK_EXISTS([glib-2.0 < 2.19.8], [ + AS_COMPILER_FLAG(-fno-strict-aliasing, + ERROR_CFLAGS="$ERROR_CFLAGS -fno-strict-aliasing") + ]) + fi + fi + + if test "x$2" != "x" + then + UNSUPPORTED="" + list="$2" + for each in $list + do + AS_COMPILER_FLAG($each, + WARNING_CFLAGS="$WARNING_CFLAGS $each", + UNSUPPORTED="$UNSUPPORTED $each") + done + if test "X$UNSUPPORTED" != X ; then + AC_MSG_NOTICE([unsupported compiler flags: $UNSUPPORTED]) + fi + fi + + AC_SUBST(WARNING_CFLAGS) + AC_SUBST(ERROR_CFLAGS) + AC_MSG_NOTICE([set WARNING_CFLAGS to $WARNING_CFLAGS]) + AC_MSG_NOTICE([set ERROR_CFLAGS to $ERROR_CFLAGS]) +]) + +dnl Sets WARNING_CXXFLAGS and ERROR_CXXFLAGS to something the compiler +dnl will accept and AC_SUBST them so they are available in Makefile +dnl +dnl WARNING_CXXFLAGS will contain flags to make the compiler emit more +dnl warnings. +dnl ERROR_CXXFLAGS will contain flags to make those warnings fatal, +dnl unless ADD-WERROR is set to "no" +dnl +dnl If MORE_FLAGS is set, tries to add each of the given flags +dnl to WARNING_CFLAGS if the compiler supports them. Each flag is +dnl tested separately. +dnl +dnl These flags can be overridden at make time: +dnl make ERROR_CXXFLAGS= +AC_DEFUN([AG_GST_SET_ERROR_CXXFLAGS], +[ + AC_REQUIRE([AC_PROG_CXX]) + AC_REQUIRE([AS_CXX_COMPILER_FLAG]) + + ERROR_CXXFLAGS="" + WARNING_CXXFLAGS="" + + dnl if we support -Wall, set it unconditionally + AS_CXX_COMPILER_FLAG(-Wall, WARNING_CXXFLAGS="$WARNING_CXXFLAGS -Wall") + + dnl if asked for, add -Werror if supported + if test "x$1" != "xno" + then + AS_CXX_COMPILER_FLAG(-Werror, ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror") + + if test "x$ERROR_CXXFLAGS" != "x" + then + dnl add exceptions + AS_CXX_COMPILER_FLAG([-Wno-non-virtual-dtor], ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Wno-non-virtual-dtor") + + dnl Add -fno-strict-aliasing for GLib versions before 2.19.8 + dnl as before G_LOCK and friends caused strict aliasing compiler + dnl warnings. + PKG_CHECK_EXISTS([glib-2.0 < 2.19.8], [ + AS_CXX_COMPILER_FLAG([-fno-strict-aliasing], + ERROR_CXXFLAGS="$ERROR_CXXFLAGS -fno-strict-aliasing") + ]) + else + dnl if -Werror isn't suported, try -errwarn=%all + AS_CXX_COMPILER_FLAG([-errwarn=%all], ERROR_CXXFLAGS="$ERROR_CXXFLAGS -errwarn=%all") + if test "x$ERROR_CXXFLAGS" != "x"; then + dnl try -errwarn=%all,no%E_EMPTY_DECLARATION, + dnl no%E_STATEMENT_NOT_REACHED,no%E_ARGUEMENT_MISMATCH, + dnl no%E_MACRO_REDEFINED (Sun Forte case) + dnl For Forte we need disable "empty declaration" warning produced by un-needed semicolon + dnl "statement not reached" disabled because there is g_assert_not_reached () in some places + dnl "macro redefined" because of gst/gettext.h + dnl FIXME: is it really supposed to be 'ARGUEMENT' and not 'ARGUMENT'? + dnl FIXME: do any of these work with the c++ compiler? if not, why + dnl do we check at all? + for f in 'no%E_EMPTY_DECLARATION' \ + 'no%E_STATEMENT_NOT_REACHED' \ + 'no%E_ARGUEMENT_MISMATCH' \ + 'no%E_MACRO_REDEFINED' \ + 'no%E_LOOP_NOT_ENTERED_AT_TOP' + do + AS_CXX_COMPILER_FLAG([-errwarn=%all,$f], [ERROR_CXXFLAGS="$ERROR_CXXFLAGS,$f"]) + done + fi + fi + fi + + if test "x$2" != "x" + then + UNSUPPORTED="" + list="$2" + for each in $list + do + AS_CXX_COMPILER_FLAG($each, + WARNING_CXXFLAGS="$WARNING_CXXFLAGS $each", + UNSUPPORTED="$UNSUPPORTED $each") + done + if test "X$UNSUPPORTED" != X ; then + AC_MSG_NOTICE([unsupported compiler flags: $UNSUPPORTED]) + fi + fi + + AC_SUBST(WARNING_CXXFLAGS) + AC_SUBST(ERROR_CXXFLAGS) + AC_MSG_NOTICE([set WARNING_CXXFLAGS to $WARNING_CXXFLAGS]) + AC_MSG_NOTICE([set ERROR_CXXFLAGS to $ERROR_CXXFLAGS]) +]) + +dnl Sets WARNING_OBJCFLAGS and ERROR_OBJCFLAGS to something the compiler +dnl will accept and AC_SUBST them so they are available in Makefile +dnl +dnl WARNING_OBJCFLAGS will contain flags to make the compiler emit more +dnl warnings. +dnl ERROR_OBJCFLAGS will contain flags to make those warnings fatal, +dnl unless ADD-WERROR is set to "no" +dnl +dnl If MORE_FLAGS is set, tries to add each of the given flags +dnl to WARNING_CFLAGS if the compiler supports them. Each flag is +dnl tested separately. +dnl +dnl These flags can be overridden at make time: +dnl make ERROR_OBJCFLAGS= +AC_DEFUN([AG_GST_SET_ERROR_OBJCFLAGS], +[ + AC_REQUIRE([AC_PROG_OBJC]) + AC_REQUIRE([AS_OBJC_COMPILER_FLAG]) + + ERROR_OBJCFLAGS="" + WARNING_OBJCFLAGS="" + + dnl if we support -Wall, set it unconditionally + AS_OBJC_COMPILER_FLAG(-Wall, WARNING_OBJCFLAGS="$WARNING_OBJCFLAGS -Wall") + + dnl if asked for, add -Werror if supported + if test "x$1" != "xno" + then + AS_OBJC_COMPILER_FLAG(-Werror, ERROR_OBJCFLAGS="$ERROR_OBJCFLAGS -Werror") + + if test "x$ERROR_OBJCFLAGS" != "x" + then + dnl Add -fno-strict-aliasing for GLib versions before 2.19.8 + dnl as before G_LOCK and friends caused strict aliasing compiler + dnl warnings. + PKG_CHECK_EXISTS([glib-2.0 < 2.19.8], [ + AS_OBJC_COMPILER_FLAG([-fno-strict-aliasing], + ERROR_OBJCFLAGS="$ERROR_OBJCFLAGS -fno-strict-aliasing") + ]) + else + dnl if -Werror isn't suported, try -errwarn=%all + AS_OBJC_COMPILER_FLAG([-errwarn=%all], ERROR_OBJCFLAGS="$ERROR_OBJCFLAGS -errwarn=%all") + if test "x$ERROR_OBJCFLAGS" != "x"; then + dnl try -errwarn=%all,no%E_EMPTY_DECLARATION, + dnl no%E_STATEMENT_NOT_REACHED,no%E_ARGUEMENT_MISMATCH, + dnl no%E_MACRO_REDEFINED (Sun Forte case) + dnl For Forte we need disable "empty declaration" warning produced by un-needed semicolon + dnl "statement not reached" disabled because there is g_assert_not_reached () in some places + dnl "macro redefined" because of gst/gettext.h + dnl FIXME: is it really supposed to be 'ARGUEMENT' and not 'ARGUMENT'? + dnl FIXME: do any of these work with the c++ compiler? if not, why + dnl do we check at all? + for f in 'no%E_EMPTY_DECLARATION' \ + 'no%E_STATEMENT_NOT_REACHED' \ + 'no%E_ARGUEMENT_MISMATCH' \ + 'no%E_MACRO_REDEFINED' \ + 'no%E_LOOP_NOT_ENTERED_AT_TOP' + do + AS_OBJC_COMPILER_FLAG([-errwarn=%all,$f], [ERROR_OBJCFLAGS="$ERROR_OBJCFLAGS,$f"]) + done + fi + fi + fi + + if test "x$2" != "x" + then + UNSUPPORTED="" + list="$2" + for each in $list + do + AS_OBJC_COMPILER_FLAG($each, + WARNING_OBJCFLAGS="$WARNING_OBJCFLAGS $each", + UNSUPPORTED="$UNSUPPORTED $each") + done + if test "X$UNSUPPORTED" != X ; then + AC_MSG_NOTICE([unsupported compiler flags: $UNSUPPORTED]) + fi + fi + + AC_SUBST(WARNING_OBJCFLAGS) + AC_SUBST(ERROR_OBJCFLAGS) + AC_MSG_NOTICE([set WARNING_OBJCFLAGS to $WARNING_OBJCFLAGS]) + AC_MSG_NOTICE([set ERROR_OBJCFLAGS to $ERROR_OBJCFLAGS]) +]) + +dnl Sets the default error level for debugging messages +AC_DEFUN([AG_GST_SET_LEVEL_DEFAULT], +[ + dnl define correct errorlevel for debugging messages. We want to have + dnl GST_ERROR messages printed when running cvs builds + if test "x[$1]" = "xyes"; then + GST_LEVEL_DEFAULT=GST_LEVEL_ERROR + else + GST_LEVEL_DEFAULT=GST_LEVEL_NONE + fi + AC_DEFINE_UNQUOTED(GST_LEVEL_DEFAULT, $GST_LEVEL_DEFAULT, + [Default errorlevel to use]) + dnl AC_SUBST so we can use it for win32/common/config.h + AC_SUBST(GST_LEVEL_DEFAULT) +]) diff --git a/common/m4/gst-feature.m4 b/common/m4/gst-feature.m4 new file mode 100644 index 0000000..876215e --- /dev/null +++ b/common/m4/gst-feature.m4 @@ -0,0 +1,297 @@ +dnl Perform a check for a feature for GStreamer +dnl Richard Boulton +dnl Thomas Vander Stichele added useful stuff +dnl Last modification: 25/06/2001 +dnl +dnl AG_GST_CHECK_FEATURE(FEATURE-NAME, FEATURE-DESCRIPTION, +dnl DEPENDENT-PLUGINS, TEST-FOR-FEATURE, +dnl DISABLE-BY-DEFAULT, ACTION-IF-USE, ACTION-IF-NOTUSE) +dnl +dnl This macro adds a command line argument to allow the user to enable +dnl or disable a feature, and if the feature is enabled, performs a supplied +dnl test to check if the feature is available. +dnl +dnl The test should define HAVE_ to "yes" or "no" depending +dnl on whether the feature is available. +dnl +dnl The macro will set USE_ to "yes" or "no" depending on +dnl whether the feature is to be used. +dnl Thomas changed this, so that when USE_ was already set +dnl to no, then it stays that way. +dnl +dnl The macro will call AM_CONDITIONAL(USE_, ...) to allow +dnl the feature to control what is built in Makefile.ams. If you want +dnl additional actions resulting from the test, you can add them with the +dnl ACTION-IF-USE and ACTION-IF-NOTUSE parameters. +dnl +dnl FEATURE-NAME is the name of the feature, and should be in +dnl purely upper case characters. +dnl FEATURE-DESCRIPTION is used to describe the feature in help text for +dnl the command line argument. +dnl DEPENDENT-PLUGINS lists any plug-ins which depend on this feature. +dnl TEST-FOR-FEATURE is a test which sets HAVE_ to "yes" +dnl or "no" depending on whether the feature is +dnl available. +dnl DISABLE-BY-DEFAULT if "disabled", the feature is disabled by default, +dnl if any other value, the feature is enabled by default. +dnl ACTION-IF-USE any extra actions to perform if the feature is to be +dnl used. +dnl ACTION-IF-NOTUSE any extra actions to perform if the feature is not to +dnl be used. +dnl +dnl +dnl thomas : +dnl we also added a history. +dnl GST_PLUGINS_YES will contain all plugins to be built +dnl that were checked through AG_GST_CHECK_FEATURE +dnl GST_PLUGINS_NO will contain those that won't be built + +AC_DEFUN([AG_GST_CHECK_FEATURE], +[echo +AC_MSG_NOTICE(*** checking feature: [$2] ***) +if test "x[$3]" != "x" +then + AC_MSG_NOTICE(*** for plug-ins: [$3] ***) +fi +dnl +builtin(define, [gst_endisable], ifelse($5, [disabled], [enable], [disable]))dnl +dnl if it is set to NO, then don't even consider it for building +NOUSE= +if test "x$USE_[$1]" = "xno"; then + NOUSE="yes" +fi +AC_ARG_ENABLE(translit([$1], A-Z, a-z), + [ ]builtin(format, --%-26s gst_endisable %s, gst_endisable-translit([$1], A-Z, a-z), [$2]ifelse([$3],,,: [$3])), + [ case "${enableval}" in + yes) USE_[$1]=yes;; + no) USE_[$1]=no;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-translit([$1], A-Z, a-z)) ;; + esac], + [ USE_$1=]ifelse($5, [disabled], [no], [yes])) dnl DEFAULT + +dnl *** set it back to no if it was preset to no +if test "x$NOUSE" = "xyes"; then + USE_[$1]="no" + AC_MSG_WARN(*** $3 pre-configured not to be built) +fi +NOUSE= + +dnl *** Check if it is ported or not +if echo " [$GST_PLUGINS_NONPORTED] " | tr , ' ' | grep -i " [$1] " > /dev/null; then + USE_[$1]="no" + AC_MSG_WARN(*** $3 not ported) +fi + +dnl *** If it's enabled + +if test x$USE_[$1] = xyes; then + dnl save compile variables before the test + + gst_check_save_LIBS=$LIBS + gst_check_save_LDFLAGS=$LDFLAGS + gst_check_save_CFLAGS=$CFLAGS + gst_check_save_CPPFLAGS=$CPPFLAGS + gst_check_save_CXXFLAGS=$CXXFLAGS + + HAVE_[$1]=no + dnl TEST_FOR_FEATURE + $4 + + LIBS=$gst_check_save_LIBS + LDFLAGS=$gst_check_save_LDFLAGS + CFLAGS=$gst_check_save_CFLAGS + CPPFLAGS=$gst_check_save_CPPFLAGS + CXXFLAGS=$gst_check_save_CXXFLAGS + + dnl If it isn't found, unset USE_[$1] + if test x$HAVE_[$1] = xno; then + USE_[$1]=no + else + ifelse([$3], , :, [AC_MSG_NOTICE(*** These plugins will be built: [$3])]) + fi +fi +dnl *** Warn if it's disabled or not found +if test x$USE_[$1] = xyes; then + ifelse([$6], , :, [$6]) + if test "x$3" != "x"; then + GST_PLUGINS_YES="\t[$3]\n$GST_PLUGINS_YES" + fi + AC_DEFINE(HAVE_[$1], , [Define to enable $2]ifelse($3,,, [ (used by $3)]).) +else + ifelse([$3], , :, [AC_MSG_NOTICE(*** These plugins will not be built: [$3])]) + if test "x$3" != "x"; then + GST_PLUGINS_NO="\t[$3]\n$GST_PLUGINS_NO" + fi + ifelse([$7], , :, [$7]) +fi +dnl *** Define the conditional as appropriate +AM_CONDITIONAL(USE_[$1], test x$USE_[$1] = xyes) +]) + +dnl Use AC_CHECK_LIB and AC_CHECK_HEADER to do both tests at once +dnl sets HAVE_module if we have it +dnl Richard Boulton +dnl Last modification: 26/06/2001 +dnl AG_GST_CHECK_LIBHEADER(FEATURE-NAME, LIB NAME, LIB FUNCTION, EXTRA LD FLAGS, +dnl HEADER NAME, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND) +dnl +dnl This check was written for GStreamer: it should be renamed and checked +dnl for portability if you decide to use it elsewhere. +dnl +AC_DEFUN([AG_GST_CHECK_LIBHEADER], +[ + AC_CHECK_LIB([$2], [$3], HAVE_[$1]=yes, HAVE_[$1]=no,[$4]) + if test "x$HAVE_[$1]" = "xyes"; then + AC_CHECK_HEADER([$5], :, HAVE_[$1]=no) + if test "x$HAVE_[$1]" = "xyes"; then + dnl execute what needs to be + ifelse([$6], , :, [$6]) + else + ifelse([$7], , :, [$7]) + fi + else + ifelse([$7], , :, [$7]) + fi + AC_SUBST(HAVE_[$1]) +] +) + +dnl 2004-02-14 Thomas - changed to get set properly and use proper output +dnl 2003-06-27 Benjamin Otte - changed to make this work with gstconfig.h +dnl +dnl Add a subsystem --disable flag and all the necessary symbols and substitions +dnl +dnl AG_GST_CHECK_SUBSYSTEM_DISABLE(SYSNAME, [subsystem name]) +dnl +AC_DEFUN([AG_GST_CHECK_SUBSYSTEM_DISABLE], +[ + dnl this define will replace each literal subsys_def occurrence with + dnl the lowercase hyphen-separated subsystem + dnl e.g. if $1 is GST_DEBUG then subsys_def will be a macro with gst-debug + define([subsys_def],translit([$1], _A-Z, -a-z)) + + AC_ARG_ENABLE(subsys_def, + AC_HELP_STRING(--disable-subsys_def, [disable $2]), + [ + case "${enableval}" in + yes) GST_DISABLE_[$1]=no ;; + no) GST_DISABLE_[$1]=yes ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-subsys_def]) ;; + esac + ], + [GST_DISABLE_[$1]=no]) dnl Default value + + if test x$GST_DISABLE_[$1] = xyes; then + AC_MSG_NOTICE([disabled subsystem [$2]]) + GST_DISABLE_[$1]_DEFINE="#define GST_DISABLE_$1 1" + else + GST_DISABLE_[$1]_DEFINE="/* #undef GST_DISABLE_$1 */" + fi + AC_SUBST(GST_DISABLE_[$1]_DEFINE) + undefine([subsys_def]) +]) + + +dnl Parse gstconfig.h for feature and defines add the symbols and substitions +dnl +dnl AG_GST_PARSE_SUBSYSTEM_DISABLE(GST_CONFIGPATH, FEATURE) +dnl +AC_DEFUN([AG_GST_PARSE_SUBSYSTEM_DISABLE], +[ + grep >/dev/null "#undef GST_DISABLE_$2" $1 + if test $? = 0; then + GST_DISABLE_[$2]=0 + else + GST_DISABLE_[$2]=1 + fi + AC_SUBST(GST_DISABLE_[$2]) +]) + +dnl Parse gstconfig.h and defines add the symbols and substitions +dnl +dnl GST_CONFIGPATH=`$PKG_CONFIG --variable=includedir gstreamer-1.0`"/gst/gstconfig.h" +dnl AG_GST_PARSE_SUBSYSTEM_DISABLES(GST_CONFIGPATH) +dnl +AC_DEFUN([AG_GST_PARSE_SUBSYSTEM_DISABLES], +[ + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,GST_DEBUG) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,LOADSAVE) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,PARSE) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,TRACE) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,ALLOC_TRACE) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,REGISTRY) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,PLUGIN) + AG_GST_PARSE_SUBSYSTEM_DISABLE($1,XML) +]) + +dnl AG_GST_CHECK_GST_DEBUG_DISABLED(ACTION-IF-DISABLED, ACTION-IF-NOT-DISABLED) +dnl +dnl Checks if the GStreamer debugging system is disabled in the core version +dnl we are compiling against (by checking gstconfig.h) +dnl +AC_DEFUN([AG_GST_CHECK_GST_DEBUG_DISABLED], +[ + AC_REQUIRE([AG_GST_CHECK_GST]) + + AC_MSG_CHECKING([whether the GStreamer debugging system is enabled]) + AC_LANG_PUSH([C]) + save_CFLAGS="$CFLAGS" + CFLAGS="$GST_CFLAGS $CFLAGS" + AC_COMPILE_IFELSE([ + AC_LANG_SOURCE([[ + #include + #ifdef GST_DISABLE_GST_DEBUG + #error "debugging disabled, make compiler fail" + #endif]])], [ debug_system_enabled=yes], [debug_system_enabled=no]) + CFLAGS="$save_CFLAGS" + AC_LANG_POP([C]) + + AC_MSG_RESULT([$debug_system_enabled]) + + if test "x$debug_system_enabled" = "xyes" ; then + $2 + true + else + $1 + true + fi +]) + +dnl relies on GST_PLUGINS_ALL, GST_PLUGINS_SELECTED, GST_PLUGINS_YES, +dnl GST_PLUGINS_NO, and BUILD_EXTERNAL +AC_DEFUN([AG_GST_OUTPUT_PLUGINS], [ + +printf "configure: *** Plug-ins without external dependencies that will be built:\n" +( for i in $GST_PLUGINS_SELECTED; do printf '\t'$i'\n'; done ) | sort +printf "\n" + +printf "configure: *** Plug-ins without external dependencies that will NOT be built:\n" +( for i in $GST_PLUGINS_ALL; do + case " $GST_PLUGINS_SELECTED " in + *\ $i\ *) + ;; + *) + printf '\t'$i'\n' + ;; + esac + done ) | sort +printf "\n" + +printf "configure: *** Plug-ins that have NOT been ported:\n" +( for i in $GST_PLUGINS_NONPORTED; do + printf '\t'$i'\n' + done ) | sort +printf "\n" + +if test "x$BUILD_EXTERNAL" = "xno"; then + printf "configure: *** No plug-ins with external dependencies will be built\n" +else + printf "configure: *** Plug-ins with dependencies that will be built:" + printf "$GST_PLUGINS_YES\n" | sort + printf "\n" + printf "configure: *** Plug-ins with dependencies that will NOT be built:" + printf "$GST_PLUGINS_NO\n" | sort + printf "\n" +fi +]) + diff --git a/common/m4/gst-function.m4 b/common/m4/gst-function.m4 new file mode 100644 index 0000000..61adfd3 --- /dev/null +++ b/common/m4/gst-function.m4 @@ -0,0 +1,63 @@ +dnl +dnl Check for compiler mechanism to show functions in debugging +dnl copied from an Ali patch floating on the internet +dnl +AC_DEFUN([AG_GST_CHECK_FUNCTION],[ + dnl #1: __PRETTY_FUNCTION__ + AC_MSG_CHECKING(whether $CC implements __PRETTY_FUNCTION__) + AC_CACHE_VAL(gst_cv_have_pretty_function,[ + AC_TRY_LINK([#include ], + [printf("%s", __PRETTY_FUNCTION__);], + gst_cv_have_pretty_function=yes, + gst_cv_have_pretty_function=no) + ]) + AC_MSG_RESULT($gst_cv_have_pretty_function) + if test "$gst_cv_have_pretty_function" = yes; then + AC_DEFINE(HAVE_PRETTY_FUNCTION, 1, + [defined if the compiler implements __PRETTY_FUNCTION__]) + fi + +dnl #2: __FUNCTION__ + AC_MSG_CHECKING(whether $CC implements __FUNCTION__) + AC_CACHE_VAL(gst_cv_have_function,[ + AC_TRY_LINK([#include ], + [printf("%s", __FUNCTION__);], + gst_cv_have_function=yes, + gst_cv_have_function=no) + ]) + AC_MSG_RESULT($gst_cv_have_function) + if test "$gst_cv_have_function" = yes; then + AC_DEFINE(HAVE_FUNCTION, 1, + [defined if the compiler implements __FUNCTION__]) + fi + +dnl #3: __func__ + AC_MSG_CHECKING(whether $CC implements __func__) + AC_CACHE_VAL(gst_cv_have_func,[ + AC_TRY_LINK([#include ], + [printf("%s", __func__);], + gst_cv_have_func=yes, + gst_cv_have_func=no) + ]) + AC_MSG_RESULT($gst_cv_have_func) + if test "$gst_cv_have_func" = yes; then + AC_DEFINE(HAVE_FUNC, 1, + [defined if the compiler implements __func__]) + fi + +dnl now define FUNCTION to whatever works, and fallback to "" + if test "$gst_cv_have_pretty_function" = yes; then + function=__PRETTY_FUNCTION__ + else + if test "$gst_cv_have_function" = yes; then + function=__FUNCTION__ + else + if test "$gst_cv_have_func" = yes; then + function=__func__ + else + function=\"\" + fi + fi + fi + AC_DEFINE_UNQUOTED(GST_FUNCTION, $function, [macro to use to show function name]) +]) diff --git a/common/m4/gst-gettext.m4 b/common/m4/gst-gettext.m4 new file mode 100644 index 0000000..df817eb --- /dev/null +++ b/common/m4/gst-gettext.m4 @@ -0,0 +1,28 @@ +dnl gettext setup + +dnl AG_GST_GETTEXT([gettext-package]) +dnl defines GETTEXT_PACKAGE and LOCALEDIR + +AC_DEFUN([AG_GST_GETTEXT], +[ + if test "$USE_NLS" = "yes"; then + GETTEXT_PACKAGE=[$1] + else + GETTEXT_PACKAGE=[NULL] + fi + AC_SUBST(GETTEXT_PACKAGE) + AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], "$GETTEXT_PACKAGE", + [gettext package name]) + + dnl make sure po/Makevars is kept in sync with GETTEXT_PACKAGE + if test -e "${srcdir}/po/Makevars"; then + if ! grep -e "$1" "${srcdir}/po/Makevars"; then + AC_MSG_ERROR([DOMAIN in po/Makevars does not match GETTEXT_PACKAGE $1]) + fi + fi + + dnl define LOCALEDIR in config.h + AS_AC_EXPAND(LOCALEDIR, $datadir/locale) + AC_DEFINE_UNQUOTED([LOCALEDIR], "$LOCALEDIR", + [gettext locale dir]) +]) diff --git a/common/m4/gst-glib2.m4 b/common/m4/gst-glib2.m4 new file mode 100644 index 0000000..63f0f46 --- /dev/null +++ b/common/m4/gst-glib2.m4 @@ -0,0 +1,126 @@ +dnl check for a minimum version of GLib + +dnl AG_GST_GLIB_CHECK([minimum-version-required]) + +AC_DEFUN([AG_GST_GLIB_CHECK], +[ + AC_REQUIRE([AS_NANO]) + + dnl Minimum required version of GLib + GLIB_REQ=[$1] + if test "x$GLIB_REQ" = "x" + then + AC_MSG_ERROR([Please specify a required version for GLib 2.0]) + fi + AC_SUBST(GLIB_REQ) + + dnl Check for glib with everything + AG_GST_PKG_CHECK_MODULES(GLIB, + glib-2.0 >= $GLIB_REQ gobject-2.0 gmodule-no-export-2.0) + + if test "x$HAVE_GLIB" = "xno"; then + AC_MSG_ERROR([This package requires GLib >= $GLIB_REQ to compile.]) + fi + + dnl Add define to tell GLib that threading is always enabled within GStreamer + dnl code (optimisation, bypasses checks if the threading system is enabled + dnl when using threading primitives) + GLIB_EXTRA_CFLAGS="$GLIB_EXTRA_CFLAGS -DG_THREADS_MANDATORY" + + dnl Define G_DISABLE_DEPRECATED for development versions + if test "x`expr $PACKAGE_VERSION_MINOR % 2`" = "x1" -a "x`expr $PACKAGE_VERSION_MICRO '<' 90`" = "x1"; then + GLIB_EXTRA_CFLAGS="$GLIB_EXTRA_CFLAGS -DG_DISABLE_DEPRECATED" + fi + + AC_ARG_ENABLE(gobject-cast-checks, + AS_HELP_STRING([--enable-gobject-cast-checks[=@<:@no/auto/yes@:>@]], + [Enable GObject cast checks]),[enable_gobject_cast_checks=$enableval], + [enable_gobject_cast_checks=auto]) + + if test "x$enable_gobject_cast_checks" = "xauto"; then + dnl Turn on cast checks only for development versions + if test "x`expr $PACKAGE_VERSION_MINOR % 2`" = "x1" -a "x`expr $PACKAGE_VERSION_MICRO '<' 90`" = "x1"; then + enable_gobject_cast_checks=yes + else + enable_gobject_cast_checks=no + fi + fi + + if test "x$enable_gobject_cast_checks" = "xno"; then + GLIB_EXTRA_CFLAGS="$GLIB_EXTRA_CFLAGS -DG_DISABLE_CAST_CHECKS" + fi + + AC_ARG_ENABLE(glib-asserts, + AS_HELP_STRING([--enable-glib-asserts[=@<:@no/auto/yes@:>@]], + [Enable GLib assertion]),[enable_glib_assertions=$enableval], + [enable_glib_assertions=auto]) + + if test "x$enable_glib_assertions" = "xauto"; then + dnl Enable assertions only for development versions + if test "x`expr $PACKAGE_VERSION_MINOR % 2`" = "x1" -a "x`expr $PACKAGE_VERSION_MICRO '<' 90`" = "x1"; then + enable_glib_assertions=yes + else + enable_glib_assertions=no + fi + fi + + if test "x$enable_glib_assertions" = "xno"; then + GLIB_EXTRA_CFLAGS="$GLIB_EXTRA_CFLAGS -DG_DISABLE_ASSERT" + fi + + dnl Find location of glib utils. People may want to or have to override these, + dnl e.g. in a cross-compile situation where PATH is a bit messed up. We need + dnl for these tools to work on the host, so can't just use the one from the + dnl GLib installation that pkg-config picks up, as that might be for a + dnl different target architecture. + dnl + dnl glib-genmarshal: + AC_MSG_CHECKING(for glib-genmarshal) + if test "x$GLIB_GENMARSHAL" != "x"; then + AC_MSG_RESULT([$GLIB_GENMARSHAL (from environment)]) + else + GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` + if $GLIB_GENMARSHAL --version 2>/dev/null >/dev/null; then + AC_MSG_RESULT([$GLIB_GENMARSHAL (from pkg-config path)]) + else + AC_PATH_PROG(GLIB_GENMARSHAL, [glib-genmarshal], [glib-genmarshal]) + AC_MSG_RESULT([$GLIB_GENMARSHAL]) + fi + fi + if ! $GLIB_GENMARSHAL --version 2>/dev/null >/dev/null; then + AC_MSG_WARN([$GLIB_GENMARSHAL does not seem to work!]) + fi + AC_SUBST(GLIB_GENMARSHAL) + + dnl glib-mkenums: + AC_MSG_CHECKING(for glib-mkenums) + if test "x$GLIB_MKENUMS" != "x"; then + AC_MSG_RESULT([$GLIB_MKENUMS (from environment)]) + else + dnl glib-mkenums is written in perl so should always work really + GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` + AC_MSG_RESULT([$GLIB_MKENUMS]) + fi + if ! $GLIB_MKENUMS --version 2>/dev/null >/dev/null; then + AC_MSG_WARN([$GLIB_MKENUMS does not seem to work!]) + fi + AC_SUBST(GLIB_MKENUMS) + + AC_SUBST(GLIB_EXTRA_CFLAGS) + + dnl Now check for GIO + PKG_CHECK_MODULES(GIO, gio-2.0 >= $GLIB_REQ) + if test "x$HAVE_GIO" = "xno"; then + AC_MSG_ERROR([This package requires GIO >= $GLIB_REQ to compile.]) + fi + + GIO_MODULE_DIR="`$PKG_CONFIG --variable=giomoduledir gio-2.0`" + AC_DEFINE_UNQUOTED(GIO_MODULE_DIR, "$GIO_MODULE_DIR", + [The GIO modules directory.]) + GIO_LIBDIR="`$PKG_CONFIG --variable=libdir gio-2.0`" + AC_DEFINE_UNQUOTED(GIO_LIBDIR, "$GIO_LIBDIR", + [The GIO library directory.]) + AC_SUBST(GIO_CFLAGS) + AC_SUBST(GIO_LIBS) + AC_SUBST(GIO_LDFLAGS) +]) diff --git a/common/m4/gst-libxml2.m4 b/common/m4/gst-libxml2.m4 new file mode 100644 index 0000000..4a843f0 --- /dev/null +++ b/common/m4/gst-libxml2.m4 @@ -0,0 +1,52 @@ +dnl call this macro with the minimum required version as an argument +dnl this macro sets and AC_SUBSTs XML_CFLAGS and XML_LIBS +dnl it also sets LIBXML_PKG, used for the pkg-config file + +AC_DEFUN([AG_GST_LIBXML2_CHECK], +[ + dnl Minimum required version of libxml2 + dnl default to 2.4.9 if not specified + LIBXML2_REQ=ifelse([$1],,2.4.9,[$1]) + AC_SUBST(LIBXML2_REQ) + + dnl check for libxml2 + PKG_CHECK_MODULES(XML, libxml-2.0 >= $LIBXML2_REQ, + HAVE_LIBXML2=yes, [ + AC_MSG_RESULT(no) + HAVE_LIBXML2=no + ]) + if test "x$HAVE_LIBXML2" = "xyes"; then + AC_DEFINE(HAVE_LIBXML2, 1, [Define if libxml2 is available]) + else + AC_MSG_ERROR([ + Need libxml2 and development headers/files to build GStreamer. + + You can do without libxml2 if you pass --disable-loadsave to + configure, but that breaks ABI, so don't do that unless you + are building for an embedded setup and know what you are doing. + ]) + fi + dnl this is for the .pc file + LIBXML_PKG=', libxml-2.0' + AC_SUBST(LIBXML_PKG) + AC_SUBST(XML_LIBS) + AC_SUBST(XML_CFLAGS) + + dnl XML_LIBS might pull in -lz without zlib actually being on the system, so + dnl try linking with these LIBS and CFLAGS + ac_save_CFLAGS=$CFLAGS + ac_save_LIBS=$LIBS + CFLAGS="$CFLAGS $XML_CFLAGS" + LIBS="$LIBS $XML_LIBS" + AC_TRY_LINK([ +#include +#include +],[ +/* function body */ +], + AC_MSG_NOTICE([Test xml2 program linked]), + AC_MSG_ERROR([Could not link libxml2 test program. Check if you have the necessary dependencies.]) + ) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" +]) diff --git a/common/m4/gst-package-release-datetime.m4 b/common/m4/gst-package-release-datetime.m4 new file mode 100644 index 0000000..bc885e3 --- /dev/null +++ b/common/m4/gst-package-release-datetime.m4 @@ -0,0 +1,89 @@ +dnl macros to set GST_PACKAGE_RELEASE_DATETIME + +dnl =========================================================================== +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME +dnl +dnl Usage: +dnl +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME() +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([no]...) +dnl sets the release datetime to the current date +dnl (no = this is not a release, but git or prerelease) +dnl +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([YYYY-MM-DD]) +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([yes], [YYYY-MM-DD]) +dnl sets the release datetime to the specified date (and time, if given) +dnl (yes = this is a release, not git or prerelease) +dnl +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([yes], [DOAP-FILE], [RELEASE-VERSION]) +dnl sets the release date to the release date associated with version +dnl RELEASE-VERSION in the .doap file DOAP-FILE +dnl (yes = this is a release, not git or prerelease) +dnl +dnl We need to treat pre-releases like git because there won't be an entry +dnl in the .doap file for pre-releases yet, and we don't want to use the +dnl date of the last release either. +dnl =========================================================================== +AC_DEFUN([AG_GST_SET_PACKAGE_RELEASE_DATETIME], +[ + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME() + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([yes]...) + if test "x$1" = "xno" -o "x$1" = "x"; then + GST_PACKAGE_RELEASE_DATETIME=`date -u "+%Y-%m-%dT%H:%MZ"` + elif test "x$1" = "xyes"; then + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([no], ["YYYY-MM-DD"]) + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([no], [DOAP-FILE], [RELEASE-VERSION]) + if ( echo $1 | grep '^20[1-9][0-9]-[0-1][0-9]-[0-3][0-9]' >/dev/null ) ; then + GST_PACKAGE_RELEASE_DATETIME=$1 + else + dnl we assume the .doap file contains the date as YYYY-MM-DD + YYYY_MM_DD=`sh "${srcdir}/common/extract-release-date-from-doap-file" $3 $2`; + if test "x$YYYY_MM_DD" != "x"; then + GST_PACKAGE_RELEASE_DATETIME=$YYYY_MM_DD + else + AC_MSG_ERROR([SET_PACKAGE_RELEASE_DATETIME: could not extract + release date for release version $3 from $2]) + GST_PACKAGE_RELEASE_DATETIME="" + fi + fi + dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([YYYY-MM-DD]) + elif ( echo $1 | grep '^20[1-9][0-9]-[0-1][0-9]-[0-3][0-9]' >/dev/null ) ; then + GST_PACKAGE_RELEASE_DATETIME=$1 + else + AC_MSG_WARN([SET_PACKAGE_RELEASE_DATETIME: invalid first argument]) + GST_PACKAGE_RELEASE_DATETIME="" + fi + + if test "x$GST_PACKAGE_RELEASE_DATETIME" = "x"; then + AC_MSG_WARN([Invalid package release date time: $GST_PACKAGE_RELEASE_DATETIME]) + else + AC_MSG_NOTICE([Setting GST_PACKAGE_RELEASE_DATETIME to $GST_PACKAGE_RELEASE_DATETIME]) + + AC_DEFINE_UNQUOTED([GST_PACKAGE_RELEASE_DATETIME], + ["$GST_PACKAGE_RELEASE_DATETIME"], + [GStreamer package release date/time for plugins as YYYY-MM-DD]) + fi +]) + +dnl =========================================================================== +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME_WITH_NANO +dnl +dnl Usage: +dnl +dnl AG_GST_SET_PACKAGE_RELEASE_DATETIME([NANO-VERSION], [DOAP-FILE], [RELEASE-VERSION]) +dnl if NANO-VERSION is 0, sets the release date to the release date associated +dnl with version RELEASE-VERSION in the .doap file DOAP-FILE, otherwise sets +dnl the release date and time to the current date/time. +dnl +dnl We need to treat pre-releases like git because there won't be an entry +dnl in the .doap file for pre-releases yet, and we don't want to use the +dnl date of the last release either. +dnl =========================================================================== +AC_DEFUN([AG_GST_SET_PACKAGE_RELEASE_DATETIME_WITH_NANO], +[ + if test "x$1" = "x0"; then + AG_GST_SET_PACKAGE_RELEASE_DATETIME([yes], [ $2 ], [ $3 ]) + else + AG_GST_SET_PACKAGE_RELEASE_DATETIME([no]) + fi +]) diff --git a/common/m4/gst-parser.m4 b/common/m4/gst-parser.m4 new file mode 100644 index 0000000..b002047 --- /dev/null +++ b/common/m4/gst-parser.m4 @@ -0,0 +1,55 @@ +AC_DEFUN([AG_GST_BISON_CHECK], +[ + dnl FIXME: check if AC_PROG_YACC is suitable here + dnl FIXME: make precious + AC_PATH_PROG(BISON_PATH, bison, no) + if test x$BISON_PATH = xno; then + AC_MSG_ERROR(Could not find bison) + fi + + dnl check bison version + dnl we need version >= 2.4 for the '<>' support + dnl in the parser. + dnl First lines observed: 'bison (GNU Bison) 2.3' or 'GNU Bison version 1.28' + bison_min_version=2.4 + bison_version=`$BISON_PATH --version | head -n 1 | sed 's/^[[^0-9]]*//' | sed 's/[[^0-9]]*$//' | cut -d' ' -f1` + AC_MSG_CHECKING([bison version $bison_version >= $bison_min_version]) + + if perl -we "exit ((v$bison_version ge v$bison_min_version) ? 0 : 1)"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_ERROR([no]) + fi +]) + +AC_DEFUN([AG_GST_FLEX_CHECK], +[ + dnl we require flex for building the parser + AC_PATH_PROG(FLEX_PATH, flex, no) + if test x$FLEX_PATH = xno; then + AC_MSG_ERROR(Could not find flex) + fi + + dnl check flex version + dnl we need version >= 2.5.31 for the reentrancy support + dnl in the parser. + flex_min_version=2.5.31 + flex_version=`$FLEX_PATH --version | head -n 1 | awk '{print $2}'` + AC_MSG_CHECKING([flex version $flex_version >= $flex_min_version]) + if perl -w < \$min_version_major) || + ((\$flex_version_major == \$min_version_major) && + (\$flex_version_minor > \$min_version_minor)) || + ((\$flex_version_major == \$min_version_major) && + (\$flex_version_minor == \$min_version_minor) && + (\$flex_version_micro >= \$min_version_micro))) + ? 0 : 1); +EOF + then + AC_MSG_RESULT(yes) + else + AC_MSG_ERROR([no]) + fi +]) diff --git a/common/m4/gst-platform.m4 b/common/m4/gst-platform.m4 new file mode 100644 index 0000000..40d6faf --- /dev/null +++ b/common/m4/gst-platform.m4 @@ -0,0 +1,67 @@ +dnl AG_GST_PLATFORM +dnl Check for platform specific features and define some variables +dnl +dnl GST_EXTRA_MODULE_SUFFIX: contains a platform specific +dnl extra module suffix additional to G_MODULE_SUFFIX +dnl +dnl HAVE_OSX: Defined if compiling for OS X +dnl +dnl GST_HAVE_UNSAFE_FORK: Defined if fork is unsafe (Windows) +dnl +dnl HAVE_WIN32: Defined if compiling on Win32 +dnl + +AC_DEFUN([AG_GST_PLATFORM], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + + case $host_os in + rhapsody*) + AC_DEFINE_UNQUOTED(GST_EXTRA_MODULE_SUFFIX, [".dylib"], [Extra platform specific plugin suffix]) + ;; + darwin*) + AC_DEFINE_UNQUOTED(GST_EXTRA_MODULE_SUFFIX, [".dylib"], [Extra platform specific plugin suffix]) + AC_DEFINE_UNQUOTED(HAVE_OSX, 1, [Defined if compiling for OSX]) + ;; + cygwin*) + AC_DEFINE_UNQUOTED(GST_HAVE_UNSAFE_FORK, 1, [Defined when registry scanning through fork is unsafe]) + ;; + mingw* | msvc* | mks*) + dnl HAVE_WIN32 currently means "disable POSIXisms". + AC_DEFINE_UNQUOTED(HAVE_WIN32, 1, [Defined if compiling for Windows]) + + dnl define __MSVCRT_VERSION__ version if not set already by the + dnl compiler (ie. mostly for mingw). This is needed for things like + dnl __stat64 to be available. If set by the compiler, ensure it's + dnl new enough - we need at least WinXP SP2. + AC_TRY_COMPILE([ ], [ return __MSVCRT_VERSION__; ], [ + AC_TRY_COMPILE([ ], [ + #if __MSVCRT_VERSION__ < 0x0601 + #error "MSVCRT too old" + #endif + ], [ + AC_MSG_NOTICE([MSVCRT version looks ok]) + ], [ + AC_MSG_ERROR([MSVCRT version too old, need at least WinXP SP2]) + ]) + ], [ + AC_MSG_NOTICE([Setting MSVCRT version to 0x0601]) + AC_DEFINE_UNQUOTED(__MSVCRT_VERSION__, 0x0601, [We need at least WinXP SP2 for __stat64]) + ]) + ;; + *) + ;; + esac +]) + +AC_DEFUN([AG_GST_LIBTOOL_PREPARE], +[ + dnl Persuade libtool to also link (-l) a 'pure' (DirectX) static lib, + dnl i.e. as opposed to only import lib with dll counterpart. + dnl Needs to be tweaked before libtool's checks. + case $host_os in + cygwin* | mingw*) + lt_cv_deplibs_check_method=pass_all + ;; + esac +]) \ No newline at end of file diff --git a/common/m4/gst-plugin-docs.m4 b/common/m4/gst-plugin-docs.m4 new file mode 100644 index 0000000..0e2ab6e --- /dev/null +++ b/common/m4/gst-plugin-docs.m4 @@ -0,0 +1,25 @@ +dnl AG_GST_PLUGIN_DOCS([MINIMUM-GTK-DOC-VERSION]) +dnl +dnl checks for prerequisites for the common/mangle-tmpl.py script +dnl used when building the plugin documentation + +AC_DEFUN([AG_GST_PLUGIN_DOCS], +[ + AC_BEFORE([GTK_DOC_CHECK],[$0])dnl check for gtk-doc first + AC_REQUIRE([AM_PATH_PYTHON])dnl find python first + + build_plugin_docs=no + AC_MSG_CHECKING([whether to build plugin documentation]) + if test x$enable_gtk_doc = xyes; then + if test x$PYTHON != x; then + build_plugin_docs=yes + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no (python not found)]) + fi + else + AC_MSG_RESULT([no (gtk-doc disabled or not available)]) + fi + + AM_CONDITIONAL(ENABLE_PLUGIN_DOCS, test x$build_plugin_docs = xyes) +]) diff --git a/common/m4/gst-plugindir.m4 b/common/m4/gst-plugindir.m4 new file mode 100644 index 0000000..c9e1301 --- /dev/null +++ b/common/m4/gst-plugindir.m4 @@ -0,0 +1,17 @@ +dnl AG_GST_SET_PLUGINDIR + +dnl AC_DEFINE PLUGINDIR to the full location where plug-ins will be installed +dnl AC_SUBST plugindir, to be used in Makefile.am's + +AC_DEFUN([AG_GST_SET_PLUGINDIR], +[ + dnl define location of plugin directory + AS_AC_EXPAND(PLUGINDIR, ${libdir}/gstreamer-$GST_API_VERSION) + AC_DEFINE_UNQUOTED(PLUGINDIR, "$PLUGINDIR", + [directory where plugins are located]) + AC_MSG_NOTICE([Using $PLUGINDIR as the plugin install location]) + + dnl plugin directory configure-time variable for use in Makefile.am + plugindir="\$(libdir)/gstreamer-$GST_API_VERSION" + AC_SUBST(plugindir) +]) diff --git a/common/m4/gst-valgrind.m4 b/common/m4/gst-valgrind.m4 new file mode 100644 index 0000000..5c0d608 --- /dev/null +++ b/common/m4/gst-valgrind.m4 @@ -0,0 +1,35 @@ +AC_DEFUN([AG_GST_VALGRIND_CHECK], +[ + dnl valgrind inclusion + AC_ARG_ENABLE(valgrind, + AC_HELP_STRING([--disable-valgrind], [disable run-time valgrind detection]), + [ + case "${enableval}" in + yes) USE_VALGRIND="$USE_DEBUG" ;; + no) USE_VALGRIND=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-valgrind) ;; + esac], + [ + USE_VALGRIND="$USE_DEBUG" + ]) dnl Default value + + VALGRIND_REQ="3.0" + if test "x$USE_VALGRIND" = xyes; then + PKG_CHECK_MODULES(VALGRIND, valgrind >= $VALGRIND_REQ, + USE_VALGRIND="yes", + [ + USE_VALGRIND="no" + AC_MSG_RESULT([no]) + ]) + fi + + if test "x$USE_VALGRIND" = xyes; then + AC_DEFINE(HAVE_VALGRIND, 1, [Define if valgrind should be used]) + AC_MSG_NOTICE(Using extra code paths for valgrind) + fi + AC_SUBST(VALGRIND_CFLAGS) + AC_SUBST(VALGRIND_LIBS) + + AC_PATH_PROG(VALGRIND_PATH, valgrind, no) + AM_CONDITIONAL(HAVE_VALGRIND, test ! "x$VALGRIND_PATH" = "xno") +]) diff --git a/common/m4/gst-x11.m4 b/common/m4/gst-x11.m4 new file mode 100644 index 0000000..1723ca5 --- /dev/null +++ b/common/m4/gst-x11.m4 @@ -0,0 +1,74 @@ +dnl macros for X-related detections +dnl AC_SUBST's HAVE_X, X_CFLAGS, X_LIBS +AC_DEFUN([AG_GST_CHECK_X], +[ + AC_PATH_XTRA + ac_cflags_save="$CFLAGS" + ac_cppflags_save="$CPPFLAGS" + CFLAGS="$CFLAGS $X_CFLAGS" + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + + dnl now try to find the HEADER + HAVE_X="no" + AC_CHECK_HEADER([X11/Xlib.h], [ + dnl and then the library with the most uniquitous function + AC_CHECK_LIB(X11, [XSync], [HAVE_X="yes"], [], [$X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS]) + ]) + + if test "x$HAVE_X" = "xno" + then + AC_MSG_NOTICE([cannot find X11 development files]) + else + dnl this is much more than we want + X_LIBS="$X_LIBS $X_PRE_LIBS $X_EXTRA_LIBS" + dnl AC_PATH_XTRA only defines the path needed to find the X libs, + dnl it does not add the libs; therefore we add them here + X_LIBS="$X_LIBS -lX11" + AC_SUBST(X_CFLAGS) + AC_SUBST(X_LIBS) + fi + AC_SUBST(HAVE_X) + + CFLAGS="$ac_cflags_save" + CPPFLAGS="$ac_cppflags_save" +]) + +dnl *** XVideo *** +dnl Look for the PIC library first, Debian requires it. +dnl Check debian-devel archives for gory details. +dnl 20020110: +dnl At the moment XFree86 doesn't distribute shared libXv due +dnl to unstable API. On many platforms you CAN NOT link a shared +dnl lib to a static non-PIC lib. This is what the xvideo GStreamer +dnl plug-in wants to do. So Debian distributes a PIC compiled +dnl version of the static lib for plug-ins to link to when it is +dnl inappropriate to link the main application to libXv directly. +dnl FIXME: add check if this platform can support linking to a +dnl non-PIC libXv, if not then don not use Xv. +dnl FIXME: perhaps warn user if they have a shared libXv since +dnl this is an error until XFree86 starts shipping one +AC_DEFUN([AG_GST_CHECK_XV], +[ + if test x$HAVE_X = xyes; then + AC_CHECK_LIB(Xv_pic, XvQueryExtension, + HAVE_XVIDEO="yes", HAVE_XVIDEO="no", + $X_LIBS -lXext) + + if test x$HAVE_XVIDEO = xyes; then + XVIDEO_LIBS="-lXv_pic -lXext" + AC_SUBST(XVIDEO_LIBS) + else + dnl try again using something else if we didn't find it first + if test x$HAVE_XVIDEO = xno; then + AC_CHECK_LIB(Xv, XvQueryExtension, + HAVE_XVIDEO="yes", HAVE_XVIDEO="no", + $X_LIBS -lXext) + + if test x$HAVE_XVIDEO = xyes; then + XVIDEO_LIBS="-lXv -lXext" + AC_SUBST(XVIDEO_LIBS) + fi + fi + fi + fi +]) diff --git a/common/m4/gst.m4 b/common/m4/gst.m4 new file mode 100644 index 0000000..d4c53cb --- /dev/null +++ b/common/m4/gst.m4 @@ -0,0 +1,36 @@ +dnl AG_GST_INIT +dnl sets up use of GStreamer configure.ac macros +dnl all GStreamer autoconf macros are prefixed +dnl with AG_GST_ for public macros +dnl with _AG_GST_ for private macros +dnl +dnl We call AC_CANONICAL_TARGET and AC_CANONICAL_HOST so that +dnl it is valid before AC_ARG_PROGRAM is called + +AC_DEFUN([AG_GST_INIT], +[ + m4_pattern_forbid(^_?AG_GST_) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use host_ variables + AC_REQUIRE([AC_CANONICAL_TARGET]) dnl we use target_ variables +]) + +dnl AG_GST_PKG_CONFIG_PATH +dnl +dnl sets up a GST_PKG_CONFIG_PATH variable for use in Makefile.am +dnl which contains the path of the in-tree pkgconfig directory first +dnl and then any paths specified in PKG_CONFIG_PATH. +dnl +dnl We do this mostly so we don't have to use unportable shell constructs +dnl such as ${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH} in Makefile.am to handle +dnl the case where the environment variable is not set, but also in order +dnl to avoid a trailing ':' in the PKG_CONFIG_PATH which apparently causes +dnl problems with pkg-config on windows with msys/mingw. +AC_DEFUN([AG_GST_PKG_CONFIG_PATH], +[ + GST_PKG_CONFIG_PATH="\$(top_builddir)/pkgconfig" + if test "x$PKG_CONFIG_PATH" != "x"; then + GST_PKG_CONFIG_PATH="$GST_PKG_CONFIG_PATH:$PKG_CONFIG_PATH" + fi + AC_SUBST([GST_PKG_CONFIG_PATH]) + AC_MSG_NOTICE([Using GST_PKG_CONFIG_PATH = $GST_PKG_CONFIG_PATH]) +]) diff --git a/common/m4/gtk-doc.m4 b/common/m4/gtk-doc.m4 new file mode 100644 index 0000000..b243f1c --- /dev/null +++ b/common/m4/gtk-doc.m4 @@ -0,0 +1,70 @@ +dnl -*- mode: autoconf -*- + +# serial 1 + +dnl Usage: +dnl GTK_DOC_CHECK([minimum-gtk-doc-version]) +AC_DEFUN([GTK_DOC_CHECK], +[ + AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first + + dnl check for tools we added during development + AC_PATH_PROG([GTKDOC_CHECK],[gtkdoc-check]) + AC_PATH_PROGS([GTKDOC_REBASE],[gtkdoc-rebase],[true]) + AC_PATH_PROG([GTKDOC_MKPDF],[gtkdoc-mkpdf]) + + dnl for overriding the documentation installation directory + AC_ARG_WITH([html-dir], + AS_HELP_STRING([--with-html-dir=PATH], [path to installed docs]),, + [with_html_dir='${datadir}/gtk-doc/html']) + HTML_DIR="$with_html_dir" + AC_SUBST([HTML_DIR]) + + dnl enable/disable documentation building + AC_ARG_ENABLE([gtk-doc], + AS_HELP_STRING([--enable-gtk-doc], + [use gtk-doc to build documentation [[default=no]]]),, + [enable_gtk_doc=no]) + + if test x$enable_gtk_doc = xyes; then + ifelse([$1],[], + [PKG_CHECK_EXISTS([gtk-doc],, + AC_MSG_ERROR([gtk-doc not installed and --enable-gtk-doc requested]))], + [PKG_CHECK_EXISTS([gtk-doc >= $1],, + AC_MSG_ERROR([You need to have gtk-doc >= $1 installed to build $PACKAGE_NAME]))]) + dnl don't check for glib if we build glib + if test "x$PACKAGE_NAME" != "xglib"; then + dnl don't fail if someone does not have glib + PKG_CHECK_MODULES(GTKDOC_DEPS, glib-2.0 >= 2.10.0 gobject-2.0 >= 2.10.0,,) + fi + dnl don't rely on sed being pulled in implicitly. Fixes Solaris build. + if test -z "$SED"; then + AC_PROG_SED + fi + fi + + AC_MSG_CHECKING([whether to build gtk-doc documentation]) + AC_MSG_RESULT($enable_gtk_doc) + + dnl enable/disable output formats + AC_ARG_ENABLE([gtk-doc-html], + AS_HELP_STRING([--enable-gtk-doc-html], + [build documentation in html format [[default=yes]]]),, + [enable_gtk_doc_html=yes]) + AC_ARG_ENABLE([gtk-doc-pdf], + AS_HELP_STRING([--enable-gtk-doc-pdf], + [build documentation in pdf format [[default=no]]]),, + [enable_gtk_doc_pdf=no]) + + if test -z "$GTKDOC_MKPDF"; then + enable_gtk_doc_pdf=no + fi + + + AM_CONDITIONAL([ENABLE_GTK_DOC], [test x$enable_gtk_doc = xyes]) + AM_CONDITIONAL([GTK_DOC_BUILD_HTML], [test x$enable_gtk_doc_html = xyes]) + AM_CONDITIONAL([GTK_DOC_BUILD_PDF], [test x$enable_gtk_doc_pdf = xyes]) + AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test -n "$LIBTOOL"]) +]) diff --git a/common/m4/introspection.m4 b/common/m4/introspection.m4 new file mode 100644 index 0000000..589721c --- /dev/null +++ b/common/m4/introspection.m4 @@ -0,0 +1,94 @@ +dnl -*- mode: autoconf -*- +dnl Copyright 2009 Johan Dahlin +dnl +dnl This file is free software; the author(s) gives unlimited +dnl permission to copy and/or distribute it, with or without +dnl modifications, as long as this notice is preserved. +dnl + +# serial 1 + +m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], +[ + AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first + AC_BEFORE([LT_INIT],[$0])dnl setup libtool first + + dnl enable/disable introspection + m4_if([$2], [require], + [dnl + enable_introspection=yes + ],[dnl + AC_ARG_ENABLE(introspection, + AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], + [Enable introspection for this build]),, + [enable_introspection=auto]) + ])dnl + + AC_MSG_CHECKING([for gobject-introspection]) + + dnl presence/version checking + AS_CASE([$enable_introspection], + [no], [dnl + found_introspection="no (disabled, use --enable-introspection to enable)" + ],dnl + [yes],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0],, + AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], + found_introspection=yes, + AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) + ],dnl + [auto],[dnl + PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) + ],dnl + [dnl + AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) + ])dnl + + AC_MSG_RESULT([$found_introspection]) + + INTROSPECTION_SCANNER= + INTROSPECTION_COMPILER= + INTROSPECTION_GENERATE= + INTROSPECTION_GIRDIR= + INTROSPECTION_TYPELIBDIR= + if test "x$found_introspection" = "xyes"; then + INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` + INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` + INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` + INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` + INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" + INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` + INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` + INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection + fi + AC_SUBST(INTROSPECTION_SCANNER) + AC_SUBST(INTROSPECTION_COMPILER) + AC_SUBST(INTROSPECTION_GENERATE) + AC_SUBST(INTROSPECTION_GIRDIR) + AC_SUBST(INTROSPECTION_TYPELIBDIR) + AC_SUBST(INTROSPECTION_CFLAGS) + AC_SUBST(INTROSPECTION_LIBS) + AC_SUBST(INTROSPECTION_MAKEFILE) + + AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") +]) + + +dnl Usage: +dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) + +AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) +]) + +dnl Usage: +dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) + + +AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], +[ + _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) +]) diff --git a/common/m4/orc.m4 b/common/m4/orc.m4 new file mode 100644 index 0000000..26b2459 --- /dev/null +++ b/common/m4/orc.m4 @@ -0,0 +1,70 @@ +dnl pkg-config-based checks for Orc + +dnl specific: +dnl ORC_CHECK([REQUIRED_VERSION]) + +AC_DEFUN([ORC_CHECK], +[ + ORC_REQ=ifelse([$1], , "0.4.6", [$1]) + + AC_ARG_ENABLE(orc, + AC_HELP_STRING([--enable-orc],[use Orc if installed]), + [case "${enableval}" in + auto) enable_orc=auto ;; + yes) enable_orc=yes ;; + no) enable_orc=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-orc) ;; + esac + ], + [enable_orc=auto]) dnl Default value + + if test "x$enable_orc" != "xno" ; then + PKG_CHECK_MODULES(ORC, orc-0.4 >= $ORC_REQ, [ + AC_DEFINE(HAVE_ORC, 1, [Use Orc]) + HAVE_ORC=yes + if test "x$ORCC" = "x" ; then + AC_MSG_CHECKING(for usable orcc) + ORCC=`$PKG_CONFIG --variable=orcc orc-0.4` + dnl check whether the orcc found by pkg-config can be run from the build environment + dnl if this is not the case (e.g. when cross-compiling) fall back to orcc from PATH + AS_IF([$ORCC --version 1> /dev/null 2> /dev/null], [], [ORCC=`which orcc`]) + AC_MSG_RESULT($ORCC) + fi + AC_SUBST(ORCC) + ORCC_FLAGS="--compat $ORC_REQ" + AC_SUBST(ORCC_FLAGS) + AS_IF([test "x$ORCC" = "x"], [HAVE_ORCC=no], [HAVE_ORCC=yes]) + ], [ + if test "x$enable_orc" = "xyes" ; then + AC_MSG_ERROR([--enable-orc specified, but Orc >= $ORC_REQ not found]) + fi + AC_DEFINE(DISABLE_ORC, 1, [Disable Orc]) + HAVE_ORC=no + HAVE_ORCC=no + ]) + else + AC_DEFINE(DISABLE_ORC, 1, [Disable Orc]) + HAVE_ORC=no + HAVE_ORCC=no + fi + AM_CONDITIONAL(HAVE_ORC, [test "x$HAVE_ORC" = "xyes"]) + AM_CONDITIONAL(HAVE_ORCC, [test "x$HAVE_ORCC" = "xyes"]) + +])) + +AC_DEFUN([ORC_OUTPUT], +[ + if test "$HAVE_ORC" = yes ; then + printf "configure: *** Orc acceleration enabled.\n" + else + if test "x$enable_orc" = "xno" ; then + printf "configure: *** Orc acceleration disabled by --disable-orc. Slower code paths\n" + printf " will be used.\n" + else + printf "configure: *** Orc acceleration disabled. Requires Orc >= $ORC_REQ, which was\n" + printf " not found. Slower code paths will be used.\n" + fi + fi + printf "\n" +]) + diff --git a/common/m4/pkg.m4 b/common/m4/pkg.m4 new file mode 100644 index 0000000..996e294 --- /dev/null +++ b/common/m4/pkg.m4 @@ -0,0 +1,157 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant . +# +# 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. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$PKG_CONFIG"; then + if test -n "$$1"; then + pkg_cv_[]$1="$$1" + else + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + fi +else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES diff --git a/config/Makefile.am b/config/Makefile.am old mode 100755 new mode 100644 index b9bac60..cd13a1b --- a/config/Makefile.am +++ b/config/Makefile.am @@ -1 +1 @@ -SUBDIRS = bellagio rpi exynos +SUBDIRS = bellagio rpi exynos exynos64 diff --git a/config/bellagio/gstomx.conf b/config/bellagio/gstomx.conf index 78f8e7d..5ca8ba6 100644 --- a/config/bellagio/gstomx.conf +++ b/config/bellagio/gstomx.conf @@ -2,7 +2,7 @@ type-name=GstOMXMPEG4VideoDec core-name=/usr/local/lib/libomxil-bellagio.so.0 component-name=OMX.st.video_decoder.mpeg4 -rank=256 +rank=257 in-port-index=0 out-port-index=1 hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1 @@ -11,7 +11,7 @@ hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-chang type-name=GstOMXH264Dec core-name=/usr/local/lib/libomxil-bellagio.so.0 component-name=OMX.st.video_decoder.avc -rank=256 +rank=257 in-port-index=0 out-port-index=1 hacks=event-port-settings-changed-ndata-parameter-swap;event-port-settings-changed-port-0-to-1 diff --git a/config/exynos/Makefile.am b/config/exynos/Makefile.am old mode 100755 new mode 100644 diff --git a/config/exynos/gstomx.conf b/config/exynos/gstomx.conf old mode 100755 new mode 100644 index 0106ef2..68f559e --- a/config/exynos/gstomx.conf +++ b/config/exynos/gstomx.conf @@ -1,4 +1,4 @@ -[omxmpeg2videodec] +[omxdec_mpeg2] type-name=GstOMXMPEG2VideoDec core-name=/usr/lib/libExynosOMX_Core.so component-name=OMX.Exynos.MPEG2.Decoder @@ -7,7 +7,7 @@ in-port-index=0 out-port-index=1 hacks=no-component-role -[omxmpeg4videodec] +[omxdec_mpeg4] type-name=GstOMXMPEG4VideoDec core-name=/usr/lib/libExynosOMX_Core.so component-name=OMX.Exynos.MPEG4.Decoder @@ -16,7 +16,7 @@ in-port-index=0 out-port-index=1 hacks=no-component-role -[omxh263dec] +[omxdec_h263] type-name=GstOMXH263Dec core-name=/usr/lib/libExynosOMX_Core.so component-name=OMX.Exynos.H263.Decoder @@ -25,7 +25,7 @@ in-port-index=0 out-port-index=1 hacks=no-component-role -[omxh264dec] +[omxdec_h264] type-name=GstOMXH264Dec core-name=/usr/lib/libExynosOMX_Core.so component-name=OMX.Exynos.AVC.Decoder @@ -34,7 +34,7 @@ in-port-index=0 out-port-index=1 hacks=no-component-role -[omxvc1dec] +[omxdec_vc1] type-name=GstOMXWMVDec core-name=/usr/lib/libExynosOMX_Core.so component-name=OMX.Exynos.WMV.Decoder @@ -44,7 +44,7 @@ out-port-index=1 hacks=no-component-role sink-template-caps=video/x-wmv,wmvversion=(int)3,format=(string){WMV3,WVC1},width=(int)[1,MAX],height=(int)[1,MAX] -[omxh264enc] +[omxenc_h264] type-name=GstOMXH264Enc core-name=/usr/lib/libExynosOMX_Core.so component-name=OMX.Exynos.AVC.Encoder @@ -53,7 +53,7 @@ in-port-index=0 out-port-index=1 hacks=no-component-role -[omxmpeg4enc] +[omxenc_mpeg4] type-name=GstOMXMPEG4VideoEnc core-name=/usr/lib/libExynosOMX_Core.so component-name=OMX.Exynos.MPEG4.Encoder @@ -62,7 +62,7 @@ in-port-index=0 out-port-index=1 hacks=no-component-role -[omxh263enc] +[omxenc_h263] type-name=GstOMXH263Enc core-name=/usr/lib/libExynosOMX_Core.so component-name=OMX.Exynos.H263.Encoder @@ -70,3 +70,4 @@ rank=258 in-port-index=0 out-port-index=1 hacks=no-component-role + diff --git a/config/odroid/Makefile.am b/config/exynos64/Makefile.am similarity index 76% rename from config/odroid/Makefile.am rename to config/exynos64/Makefile.am index b2946fc..4c835c8 100755 --- a/config/odroid/Makefile.am +++ b/config/exynos64/Makefile.am @@ -1,6 +1,6 @@ EXTRA_DIST = gstomx.conf -if USE_OMX_TARGET_ODROID +if USE_OMX_TARGET_EXYNOS64 configdir = $(sysconfdir)/xdg config_DATA = gstomx.conf endif diff --git a/config/odroid/gstomx.conf b/config/exynos64/gstomx.conf similarity index 72% rename from config/odroid/gstomx.conf rename to config/exynos64/gstomx.conf index 0106ef2..f276db0 100755 --- a/config/odroid/gstomx.conf +++ b/config/exynos64/gstomx.conf @@ -1,42 +1,42 @@ -[omxmpeg2videodec] +[omxdec_mpeg2] type-name=GstOMXMPEG2VideoDec -core-name=/usr/lib/libExynosOMX_Core.so +core-name=/usr/lib64/libExynosOMX_Core.so component-name=OMX.Exynos.MPEG2.Decoder rank=258 in-port-index=0 out-port-index=1 hacks=no-component-role -[omxmpeg4videodec] +[omxdec_mpeg4] type-name=GstOMXMPEG4VideoDec -core-name=/usr/lib/libExynosOMX_Core.so +core-name=/usr/lib64/libExynosOMX_Core.so component-name=OMX.Exynos.MPEG4.Decoder rank=258 in-port-index=0 out-port-index=1 hacks=no-component-role -[omxh263dec] +[omxdec_h263] type-name=GstOMXH263Dec -core-name=/usr/lib/libExynosOMX_Core.so +core-name=/usr/lib64/libExynosOMX_Core.so component-name=OMX.Exynos.H263.Decoder rank=258 in-port-index=0 out-port-index=1 hacks=no-component-role -[omxh264dec] +[omxdec_h264] type-name=GstOMXH264Dec -core-name=/usr/lib/libExynosOMX_Core.so +core-name=/usr/lib64/libExynosOMX_Core.so component-name=OMX.Exynos.AVC.Decoder rank=258 in-port-index=0 out-port-index=1 hacks=no-component-role -[omxvc1dec] +[omxdec_vc1] type-name=GstOMXWMVDec -core-name=/usr/lib/libExynosOMX_Core.so +core-name=/usr/lib64/libExynosOMX_Core.so component-name=OMX.Exynos.WMV.Decoder rank=256 in-port-index=0 @@ -44,27 +44,27 @@ out-port-index=1 hacks=no-component-role sink-template-caps=video/x-wmv,wmvversion=(int)3,format=(string){WMV3,WVC1},width=(int)[1,MAX],height=(int)[1,MAX] -[omxh264enc] +[omxenc_h264] type-name=GstOMXH264Enc -core-name=/usr/lib/libExynosOMX_Core.so +core-name=/usr/lib64/libExynosOMX_Core.so component-name=OMX.Exynos.AVC.Encoder rank=258 in-port-index=0 out-port-index=1 hacks=no-component-role -[omxmpeg4enc] +[omxenc_mpeg4] type-name=GstOMXMPEG4VideoEnc -core-name=/usr/lib/libExynosOMX_Core.so +core-name=/usr/lib64/libExynosOMX_Core.so component-name=OMX.Exynos.MPEG4.Encoder rank=258 in-port-index=0 out-port-index=1 hacks=no-component-role -[omxh263enc] +[omxenc_h263] type-name=GstOMXH263Enc -core-name=/usr/lib/libExynosOMX_Core.so +core-name=/usr/lib64/libExynosOMX_Core.so component-name=OMX.Exynos.H263.Encoder rank=258 in-port-index=0 diff --git a/config/rpi/gstomx.conf b/config/rpi/gstomx.conf index 8b65059..827ebb6 100644 --- a/config/rpi/gstomx.conf +++ b/config/rpi/gstomx.conf @@ -2,7 +2,7 @@ type-name=GstOMXMPEG2VideoDec core-name=/opt/vc/lib/libopenmaxil.so component-name=OMX.broadcom.video_decode -rank=256 +rank=257 in-port-index=130 out-port-index=131 hacks=no-component-role @@ -11,7 +11,7 @@ hacks=no-component-role type-name=GstOMXMPEG4VideoDec core-name=/opt/vc/lib/libopenmaxil.so component-name=OMX.broadcom.video_decode -rank=256 +rank=257 in-port-index=130 out-port-index=131 hacks=no-component-role @@ -20,7 +20,7 @@ hacks=no-component-role type-name=GstOMXH263Dec core-name=/opt/vc/lib/libopenmaxil.so component-name=OMX.broadcom.video_decode -rank=256 +rank=257 in-port-index=130 out-port-index=131 hacks=no-component-role @@ -29,7 +29,7 @@ hacks=no-component-role type-name=GstOMXH264Dec core-name=/opt/vc/lib/libopenmaxil.so component-name=OMX.broadcom.video_decode -rank=256 +rank=257 in-port-index=130 out-port-index=131 hacks=no-component-role @@ -38,7 +38,7 @@ hacks=no-component-role type-name=GstOMXTheoraDec core-name=/opt/vc/lib/libopenmaxil.so component-name=OMX.broadcom.video_decode -rank=256 +rank=257 in-port-index=130 out-port-index=131 hacks=no-component-role @@ -47,7 +47,7 @@ hacks=no-component-role type-name=GstOMXVP8Dec core-name=/opt/vc/lib/libopenmaxil.so component-name=OMX.broadcom.video_decode -rank=256 +rank=257 in-port-index=130 out-port-index=131 hacks=no-component-role @@ -56,7 +56,7 @@ hacks=no-component-role type-name=GstOMXMJPEGDec core-name=/opt/vc/lib/libopenmaxil.so component-name=OMX.broadcom.video_decode -rank=256 +rank=257 in-port-index=130 out-port-index=131 hacks=no-component-role @@ -64,8 +64,8 @@ hacks=no-component-role [omxvc1dec] type-name=GstOMXWMVDec core-name=/opt/vc/lib/libopenmaxil.so -component-name=OMX.broadcom.video_encode -rank=256 +component-name=OMX.broadcom.video_decode +rank=257 in-port-index=130 out-port-index=131 hacks=no-component-role @@ -75,8 +75,28 @@ sink-template-caps=video/x-wmv,wmvversion=(int)3,format=(string){WMV3,WVC1},widt type-name=GstOMXH264Enc core-name=/opt/vc/lib/libopenmaxil.so component-name=OMX.broadcom.video_encode -rank=256 +rank=257 in-port-index=200 out-port-index=201 hacks=no-component-role +[omxanalogaudiosink] +type-name=GstOMXAnalogAudioSink +core-name=/opt/vc/lib/libopenmaxil.so +component-name=OMX.broadcom.audio_render +rank=257 +in-port-index=100 +out-port-index=101 +hacks=no-component-role +sink-template-caps=audio/x-raw,format=(string){S16LE,S32LE},layout=(string)interleaved,rate=(int){8000,11025,16000,22050,24000,32000,44100,48000,88200,96000,176400,192000},channels=(int)[1,2] + +[omxhdmiaudiosink] +type-name=GstOMXHdmiAudioSink +core-name=/opt/vc/lib/libopenmaxil.so +component-name=OMX.broadcom.audio_render +rank=258 +in-port-index=100 +out-port-index=101 +hacks=no-component-role +sink-template-caps=audio/x-raw,format=(string){S16LE,S32LE},layout=(string)interleaved,rate=(int){8000,11025,16000,22050,24000,32000,44100,48000,88200,96000,176400,192000},channels=(int)[1,8];audio/x-ac3,framed=(boolean)true;audio/x-dts,framed=(boolean)true,block-size=(int){512,1024,2048} + diff --git a/configure.ac b/configure.ac old mode 100755 new mode 100644 index 1db36cb..715724a --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ dnl please read gstreamer/docs/random/autotools before changing this file dnl initialize autoconf dnl releases only do -Wall, git and prerelease does -Werror too dnl use a three digit version number for releases, and four for git/prerelease -AC_INIT(GStreamer OpenMAX Plug-ins, 1.0.0, +AC_INIT(GStreamer OpenMAX Plug-ins, 1.2.0, http://bugzilla.gnome.org/enter_bug.cgi?product=GStreamer, gst-omx) @@ -48,7 +48,7 @@ AG_GST_LIBTOOL_PREPARE AS_LIBTOOL(GST, 0, 0, 0) dnl *** required versions of GStreamer stuff *** -GST_REQ=1.0.0 +GST_REQ=1.2.2 dnl *** autotools stuff **** @@ -60,6 +60,8 @@ AC_SUBST(ACLOCAL_AMFLAGS, "-I m4 -I common/m4") dnl *** check for arguments to configure *** +AG_GST_ARG_DISABLE_FATAL_WARNINGS + AG_GST_ARG_DEBUG AG_GST_ARG_PROFILING AG_GST_ARG_VALGRIND @@ -137,6 +139,9 @@ dnl *** checks for dependency libraries *** dnl GLib is required AG_GST_GLIB_CHECK([2.32]) +dnl Needed by plugins that use g_module_*() API +PKG_CHECK_MODULES(GMODULE_NO_EXPORT, gmodule-no-export-2.0) + dnl checks for gstreamer dnl uninstalled is selected preferentially -- see pkg-config(1) AG_GST_CHECK_GST($GST_API_VERSION, [$GST_REQ], yes) @@ -145,6 +150,11 @@ AG_GST_CHECK_GST_CONTROLLER($GST_API_VERSION, [$GST_REQ], yes) AG_GST_CHECK_GST_CHECK($GST_API_VERSION, [$GST_REQ], no) AG_GST_CHECK_GST_PLUGINS_BASE($GST_API_VERSION, [$GST_REQ], yes) AM_CONDITIONAL(HAVE_GST_CHECK, test "x$HAVE_GST_CHECK" = "xyes") +PKG_CHECK_MODULES([GST_GL], [gstreamer-gl-1.0 >= 1.4.0], [ + AC_DEFINE(HAVE_GST_GL, 1, [Have gstreamer-gl]) + GST_GL=yes +], [GST_GL=no]) +AM_CONDITIONAL(HAVE_GST_GL, test "x$GST_GL" = "xyes") dnl Check for documentation xrefs GLIB_PREFIX="`$PKG_CONFIG --variable=prefix glib-2.0`" @@ -152,17 +162,38 @@ GST_PREFIX="`$PKG_CONFIG --variable=prefix gstreamer-$GST_API_VERSION`" AC_SUBST(GLIB_PREFIX) AC_SUBST(GST_PREFIX) +AC_ARG_WITH([omx-header-path], + AS_HELP_STRING([--with-omx-header-path],[path of external OpenMAX IL header files]), + [omx_header_path="$withval"], [omx_header_path="none"]) + +if test x"$omx_header_path" != x"none"; then + CPPFLAGS="$CPPFLAGS -I$omx_header_path" +fi dnl Check for external OpenMAX IL headers AC_CHECK_HEADER([OMX_Core.h], [HAVE_EXTERNAL_OMX=yes], [HAVE_EXTERNAL_OMX=no], [AC_INCLUDES_DEFAULT]) AM_CONDITIONAL(HAVE_EXTERNAL_OMX, test "x$HAVE_EXTERNAL_OMX" = "xyes") +dnl Our internal OpenMAX IL headers have OMX_VideoExt.h +HAVE_VIDEO_EXT=yes +if test "x$HAVE_EXTERNAL_OMX" = "xyes"; then + AC_CHECK_HEADER([OMX_VideoExt.h], [HAVE_VIDEO_EXT=yes], [HAVE_VIDEO_EXT=no], [AC_INCLUDES_DEFAULT]) +fi + +VIDEO_HEADERS="#include " +if test "x$HAVE_VIDEO_EXT" = "xyes"; then + AC_DEFINE(HAVE_VIDEO_EXT, 1, [OpenMAX IL has OMX_VideoExt.h header]) + VIDEO_HEADERS="$VIDEO_HEADERS +#include +" +fi + AC_CHECK_DECLS([OMX_VIDEO_CodingVP8], [ AC_DEFINE(HAVE_VP8, 1, [OpenMAX IL has VP8 support]) HAVE_VP8=yes ], [ HAVE_VP8=no - ], [[#include ]]) + ], [[$VIDEO_HEADERS]]) AM_CONDITIONAL(HAVE_VP8, test "x$HAVE_VP8" = "xyes") AC_CHECK_DECLS([OMX_VIDEO_CodingTheora], @@ -171,7 +202,7 @@ AC_CHECK_DECLS([OMX_VIDEO_CodingTheora], HAVE_THEORA=yes ], [ HAVE_THEORA=no - ], [[#include ]]) + ], [[$VIDEO_HEADERS]]) AM_CONDITIONAL(HAVE_THEORA, test "x$HAVE_THEORA" = "xyes") dnl Check for -Bsymbolic-functions linker flag used to avoid @@ -190,8 +221,8 @@ AC_ARG_ENABLE(Bsymbolic, LDFLAGS="${SAVED_LDFLAGS}"]) AC_ARG_WITH([omx-target], - AS_HELP_STRING([--with-omx-target],[Use this OpenMAX IL target (generic, bellagio, rpi, exynos)]), - [ac_cv_omx_target="$withval"], [ac_cv_omx_target="generic"]) + AS_HELP_STRING([--with-omx-target],[Use this OpenMAX IL target (generic, bellagio, rpi)]), + [ac_cv_omx_target="$withval"], [ac_cv_omx_target="none"]) ac_cv_omx_target_struct_packing="none" AC_MSG_NOTICE([Using $ac_cv_omx_target as OpenMAX IL target]) @@ -206,9 +237,12 @@ case "${ac_cv_omx_target}" in bellagio) AC_DEFINE(USE_OMX_TARGET_BELLAGIO, 1, [Use Bellagio OpenMAX IL target]) ;; - exynos) + exynos) AC_DEFINE(USE_OMX_TARGET_EXYNOS, 1, [Use Exynos OpenMAX IL target]) ;; + exynos64) + AC_DEFINE(USE_OMX_TARGET_EXYNOS64, 1, [Use Exynos64 OpenMAX IL target]) + ;; *) AC_ERROR([invalid OpenMAX IL target]) ;; @@ -217,6 +251,7 @@ AM_CONDITIONAL(USE_OMX_TARGET_GENERIC, test "x$ac_cv_omx_target" = "xgeneric") AM_CONDITIONAL(USE_OMX_TARGET_BELLAGIO, test "x$ac_cv_omx_target" = "xbellagio") AM_CONDITIONAL(USE_OMX_TARGET_RPI, test "x$ac_cv_omx_target" = "xrpi") AM_CONDITIONAL(USE_OMX_TARGET_EXYNOS, test "x$ac_cv_omx_target" = "xexynos") +AM_CONDITIONAL(USE_OMX_TARGET_EXYNOS64, test "x$ac_cv_omx_target" = "xexynos64") AC_ARG_WITH([omx-struct-packing], AS_HELP_STRING([--with-omx-struct-packing],[Force OpenMAX struct packing, (default is none)]), @@ -245,8 +280,30 @@ AG_GST_SET_PACKAGE_RELEASE_DATETIME_WITH_NANO([$PACKAGE_VERSION_NANO], ["${srcdir}/gst-omx.doap"], [$PACKAGE_VERSION_MAJOR.$PACKAGE_VERSION_MINOR.$PACKAGE_VERSION_MICRO]) +dnl build static plugins or not +AC_MSG_CHECKING([whether to build static plugins or not]) +AC_ARG_ENABLE( + static-plugins, + AC_HELP_STRING( + [--enable-static-plugins], + [build static plugins @<:@default=no@:>@]), + [AS_CASE( + [$enableval], [no], [], [yes], [], + [AC_MSG_ERROR([bad value "$enableval" for --enable-static-plugins])])], + [enable_static_plugins=no]) +AC_MSG_RESULT([$enable_static_plugins]) +if test "x$enable_static_plugins" = xyes; then + AC_DEFINE(GST_PLUGIN_BUILD_STATIC, 1, + [Define if static plugins should be built]) + GST_PLUGIN_LIBTOOLFLAGS="" +else + GST_PLUGIN_LIBTOOLFLAGS="--tag=disable-static" +fi +AC_SUBST(GST_PLUGIN_LIBTOOLFLAGS) +AM_CONDITIONAL(GST_PLUGIN_BUILD_STATIC, test "x$enable_static_plugins" = "xyes") + dnl define an ERROR_CFLAGS Makefile variable -AG_GST_SET_ERROR_CFLAGS($GST_GIT, [ +AG_GST_SET_ERROR_CFLAGS($FATAL_WARNINGS, [ -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wundef -Wwrite-strings -Wformat-nonliteral -Wformat-security -Wformat-nonliteral -Winit-self -Wmissing-include-dirs -Waddress -Waggregate-return @@ -308,7 +365,7 @@ AC_SUBST(GST_OMX_CFLAGS) dnl FIXME: do we want to rename to GST_ALL_* ? dnl add GST_OPTION_CFLAGS, but overridable -GST_CFLAGS="$GLIB_CFLAGS $GST_CFLAGS $GLIB_EXTRA_CFLAGS \$(GST_OPTION_CFLAGS)" +GST_CFLAGS="$GST_CFLAGS $GLIB_EXTRA_CFLAGS \$(GST_OPTION_CFLAGS)" AC_SUBST(GST_CFLAGS) dnl add GCOV libs because libtool strips -fprofile-arcs -ftest-coverage GST_LIBS="$GST_LIBS \$(GCOV_LIBS)" @@ -324,7 +381,7 @@ AC_SUBST(GST_ALL_LDFLAGS) dnl this really should only contain flags, not libs - they get added before dnl whatevertarget_LIBS and -L flags here affect the rest of the linking -GST_PLUGIN_LDFLAGS="-module -avoid-version -export-symbols-regex '^[_]*gst_plugin_desc.*' $GST_ALL_LDFLAGS" +GST_PLUGIN_LDFLAGS="-module -avoid-version -export-symbols-regex '^[_]*gst_plugin_.*' $GST_ALL_LDFLAGS" AC_SUBST(GST_PLUGIN_LDFLAGS) dnl *** output files *** @@ -350,6 +407,9 @@ config/Makefile config/bellagio/Makefile config/rpi/Makefile config/exynos/Makefile +config/exynos64/Makefile +examples/Makefile +examples/egl/Makefile ) AC_OUTPUT diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..b7c064c --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,5 @@ +if HAVE_GST_GL +SUBDIRS = egl +endif + +DIST_SUBDIRS = egl diff --git a/examples/egl/Makefile.am b/examples/egl/Makefile.am new file mode 100644 index 0000000..2646cba --- /dev/null +++ b/examples/egl/Makefile.am @@ -0,0 +1,24 @@ +noinst_PROGRAMS = + +if USE_OMX_TARGET_RPI +noinst_PROGRAMS += testegl +endif + +testegl_SOURCES = testegl.c + +noinst_HEADERS = cube_texture_and_coords.h + +testegl_LDADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) \ + $(GST_LIBS) \ + $(GST_GL_LIBS) \ + -lm + +testegl_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(GST_GL_CFLAGS) + diff --git a/examples/egl/cube_texture_and_coords.h b/examples/egl/cube_texture_and_coords.h new file mode 100644 index 0000000..1832df3 --- /dev/null +++ b/examples/egl/cube_texture_and_coords.h @@ -0,0 +1,100 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Spatial coordinates for the cube + +static const GLfloat quadx[6*4*3] = { + /* FRONT */ + -1.f, -1.f, 1.f, + 1.f, -1.f, 1.f, + -1.f, 1.f, 1.f, + 1.f, 1.f, 1.f, + + /* BACK */ + -1.f, -1.f, -1.f, + -1.f, 1.f, -1.f, + 1.f, -1.f, -1.f, + 1.f, 1.f, -1.f, + + /* LEFT */ + -1.f, -1.f, 1.f, + -1.f, 1.f, 1.f, + -1.f, -1.f, -1.f, + -1.f, 1.f, -1.f, + + /* RIGHT */ + 1.f, -1.f, -1.f, + 1.f, 1.f, -1.f, + 1.f, -1.f, 1.f, + 1.f, 1.f, 1.f, + + /* TOP */ + -1.f, 1.f, 1.f, + 1.f, 1.f, 1.f, + -1.f, 1.f, -1.f, + 1.f, 1.f, -1.f, + + /* BOTTOM */ + -1.f, -1.f, 1.f, + -1.f, -1.f, -1.f, + 1.f, -1.f, 1.f, + 1.f, -1.f, -1.f, +}; + +/** Texture coordinates for the quad. */ +static const GLfloat texCoords[6 * 4 * 2] = { + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 0.f, 1.f, + 1.f, 1.f, +}; + diff --git a/examples/egl/testegl.c b/examples/egl/testegl.c new file mode 100644 index 0000000..bccf6c1 --- /dev/null +++ b/examples/egl/testegl.c @@ -0,0 +1,1605 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +Copyright (c) 2012, OtherCrashOverride +Copyright (C) 2013, Fluendo S.A. + @author: Josep Torra +Copyright (C) 2013, Video Experts Group LLC. + @author: Ilya Smelykh +Copyright (C) 2014 Julien Isorce +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* A rotating cube rendered with OpenGL|ES and video played using GStreamer on + * the cube faces. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__) +#ifndef __VCCOREVER__ +#define __VCCOREVER__ 0x04000000 +#endif +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#pragma GCC optimize ("gnu89-inline") +#endif + +#define GST_USE_UNSTABLE_API +#include +#include + +#if defined (USE_OMX_TARGET_RPI) +#include +#endif + +#if defined (USE_OMX_TARGET_RPI) && defined (__GNUC__) +#pragma GCC reset_options +#pragma GCC diagnostic pop +#endif + +#include "cube_texture_and_coords.h" + +#ifndef M_PI +#define M_PI 3.141592654 +#endif + +#define SYNC_BUFFERS TRUE + +#define TRACE_VC_MEMORY_ENABLED 0 + +#if defined (USE_OMX_TARGET_RPI) && TRACE_VC_MEMORY_ENABLED +#define TRACE_VC_MEMORY(str) \ + fprintf (stderr, "\n\n" str "\n"); \ + system ("vcdbg reloc >&2") + +#define TRACE_VC_MEMORY_DEFINE_ID(id) \ + static int id = 0 + +#define TRACE_VC_MEMORY_RESET_ID(id) \ + G_STMT_START { \ + id = 0; \ + } G_STMT_END + +#define TRACE_VC_MEMORY_ONCE_FOR_ID(str,id) \ + G_STMT_START { \ + if (id == 0) { \ + fprintf (stderr, "\n\n" str "\n"); \ + system ("vcdbg reloc >&2"); \ + id = 1; \ + } \ + } G_STMT_END + +#define TRACE_VC_MEMORY_ONCE(str,id) \ + G_STMT_START { \ + static int id = 0; \ + if (id == 0) { \ + fprintf (stderr, "\n\n" str "\n"); \ + system ("vcdbg reloc >&2"); \ + id = 1; \ + } \ + } G_STMT_END + +#else +#define TRACE_VC_MEMORY(str) while(0) +#define TRACE_VC_MEMORY_DEFINE_ID(id) +#define TRACE_VC_MEMORY_RESET_ID(id) while(0) +#define TRACE_VC_MEMORY_ONCE_FOR_ID(str,id) while(0) +#define TRACE_VC_MEMORY_ONCE(str,id) while(0) +#endif + +/* some helpers that we should provide in libgstgl */ + +typedef struct +{ + GLfloat m[4][4]; +} GstGLMatrix; + +static void +gst_gl_matrix_load_identity (GstGLMatrix * matrix) +{ + memset (matrix, 0x0, sizeof (GstGLMatrix)); + matrix->m[0][0] = 1.0f; + matrix->m[1][1] = 1.0f; + matrix->m[2][2] = 1.0f; + matrix->m[3][3] = 1.0f; +} + +static void +gst_gl_matrix_multiply (GstGLMatrix * matrix, GstGLMatrix * srcA, + GstGLMatrix * srcB) +{ + GstGLMatrix tmp; + int i; + + for (i = 0; i < 4; i++) { + tmp.m[i][0] = (srcA->m[i][0] * srcB->m[0][0]) + + (srcA->m[i][1] * srcB->m[1][0]) + + (srcA->m[i][2] * srcB->m[2][0]) + (srcA->m[i][3] * srcB->m[3][0]); + + tmp.m[i][1] = (srcA->m[i][0] * srcB->m[0][1]) + + (srcA->m[i][1] * srcB->m[1][1]) + + (srcA->m[i][2] * srcB->m[2][1]) + (srcA->m[i][3] * srcB->m[3][1]); + + tmp.m[i][2] = (srcA->m[i][0] * srcB->m[0][2]) + + (srcA->m[i][1] * srcB->m[1][2]) + + (srcA->m[i][2] * srcB->m[2][2]) + (srcA->m[i][3] * srcB->m[3][2]); + + tmp.m[i][3] = (srcA->m[i][0] * srcB->m[0][3]) + + (srcA->m[i][1] * srcB->m[1][3]) + + (srcA->m[i][2] * srcB->m[2][3]) + (srcA->m[i][3] * srcB->m[3][3]); + } + + memcpy (matrix, &tmp, sizeof (GstGLMatrix)); +} + +static void +gst_gl_matrix_translate (GstGLMatrix * matrix, GLfloat tx, GLfloat ty, + GLfloat tz) +{ + matrix->m[3][0] += + (matrix->m[0][0] * tx + matrix->m[1][0] * ty + matrix->m[2][0] * tz); + matrix->m[3][1] += + (matrix->m[0][1] * tx + matrix->m[1][1] * ty + matrix->m[2][1] * tz); + matrix->m[3][2] += + (matrix->m[0][2] * tx + matrix->m[1][2] * ty + matrix->m[2][2] * tz); + matrix->m[3][3] += + (matrix->m[0][3] * tx + matrix->m[1][3] * ty + matrix->m[2][3] * tz); +} + +static void +gst_gl_matrix_frustum (GstGLMatrix * matrix, GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, GLfloat nearZ, GLfloat farZ) +{ + GLfloat deltaX = right - left; + GLfloat deltaY = top - bottom; + GLfloat deltaZ = farZ - nearZ; + GstGLMatrix frust; + + if ((nearZ <= 0.0f) || (farZ <= 0.0f) || + (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f)) + return; + + frust.m[0][0] = 2.0f * nearZ / deltaX; + frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f; + + frust.m[1][1] = 2.0f * nearZ / deltaY; + frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f; + + frust.m[2][0] = (right + left) / deltaX; + frust.m[2][1] = (top + bottom) / deltaY; + frust.m[2][2] = -(nearZ + farZ) / deltaZ; + frust.m[2][3] = -1.0f; + + frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ; + frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f; + + gst_gl_matrix_multiply (matrix, &frust, matrix); +} + +static void +gst_gl_matrix_perspective (GstGLMatrix * matrix, GLfloat fovy, GLfloat aspect, + GLfloat nearZ, GLfloat farZ) +{ + GLfloat frustumW, frustumH; + + frustumH = tanf (fovy / 360.0f * M_PI) * nearZ; + frustumW = frustumH * aspect; + + gst_gl_matrix_frustum (matrix, -frustumW, frustumW, -frustumH, frustumH, + nearZ, farZ); +} + +/* *INDENT-OFF* */ + +/* vertex source */ +static const gchar *cube_v_src = + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform float u_rotx; \n" + "uniform float u_roty; \n" + "uniform float u_rotz; \n" + "uniform mat4 u_modelview; \n" + "uniform mat4 u_projection; \n" + "varying vec2 v_texCoord; \n" + "void main() \n" + "{ \n" + " float PI = 3.14159265; \n" + " float xrot = u_rotx*2.0*PI/360.0; \n" + " float yrot = u_roty*2.0*PI/360.0; \n" + " float zrot = u_rotz*2.0*PI/360.0; \n" + " mat4 matX = mat4 ( \n" + " 1.0, 0.0, 0.0, 0.0, \n" + " 0.0, cos(xrot), sin(xrot), 0.0, \n" + " 0.0, -sin(xrot), cos(xrot), 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " mat4 matY = mat4 ( \n" + " cos(yrot), 0.0, -sin(yrot), 0.0, \n" + " 0.0, 1.0, 0.0, 0.0, \n" + " sin(yrot), 0.0, cos(yrot), 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " mat4 matZ = mat4 ( \n" + " cos(zrot), sin(zrot), 0.0, 0.0, \n" + " -sin(zrot), cos(zrot), 0.0, 0.0, \n" + " 0.0, 0.0, 1.0, 0.0, \n" + " 0.0, 0.0, 0.0, 1.0 ); \n" + " gl_Position = u_projection * u_modelview * matZ * matY * matX * a_position;\n" + " v_texCoord = a_texCoord; \n" + "} \n"; + +/* fragment source */ +static const gchar *cube_f_src = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "void main() \n" + "{ \n" + " gl_FragColor = texture2D (s_texture, v_texCoord); \n" + "} \n"; +/* *INDENT-ON* */ + +typedef struct +{ +#if defined (USE_OMX_TARGET_RPI) + DISPMANX_DISPLAY_HANDLE_T dispman_display; + DISPMANX_ELEMENT_HANDLE_T dispman_element; +#endif + + uint32_t screen_width; + uint32_t screen_height; + gboolean animate; + + GstCaps *caps; + + /* OpenGL|ES objects */ + EGLDisplay display; + EGLSurface surface; + EGLContext context; + GLuint tex; + + GLint vshader; + GLint fshader; + GLint program; + + GLint u_modelviewmatrix; + GLint u_projectionmatrix; + GLint s_texture; + GLint u_rotx; + GLint u_roty; + GLint u_rotz; + + GstGLMatrix modelview; + GstGLMatrix projection; + GLfloat fov; + GLfloat aspect; + + /* model rotation vector and direction */ + GLfloat rot_angle_x_inc; + GLfloat rot_angle_y_inc; + GLfloat rot_angle_z_inc; + + /* current model rotation angles */ + GLfloat rot_angle_x; + GLfloat rot_angle_y; + GLfloat rot_angle_z; + + /* current distance from camera */ + GLfloat distance; + GLfloat distance_inc; + + /* GStreamer related resources */ + GstElement *pipeline; + GstElement *vsink; + GstGLDisplayEGL *gst_display; + gboolean can_avoid_upload; + + /* Interthread comunication */ + GAsyncQueue *queue; + GMutex queue_lock; + GCond cond; + gboolean flushing; + GstMiniObject *popped_obj; + GstBuffer *current_buffer; + + /* GLib mainloop */ + GMainLoop *main_loop; + GstBuffer *last_buffer; + + /* Rendering thread state */ + gboolean running; + + /* number of rendered and dropped frames */ + guint64 rendered; + guint64 dropped; +} APP_STATE_T; + +static void init_ogl (APP_STATE_T * state); +static void init_model_proj (APP_STATE_T * state); +static void reset_model (APP_STATE_T * state); +static GLfloat inc_and_wrap_angle (GLfloat angle, GLfloat angle_inc); +static void redraw_scene (APP_STATE_T * state); +static void update_model (APP_STATE_T * state); +static void init_textures (APP_STATE_T * state, GstBuffer * buffer); +static APP_STATE_T _state, *state = &_state; +static gboolean queue_object (APP_STATE_T * state, GstMiniObject * obj, + gboolean synchronous); + +TRACE_VC_MEMORY_DEFINE_ID (gid0); +TRACE_VC_MEMORY_DEFINE_ID (gid1); +TRACE_VC_MEMORY_DEFINE_ID (gid2); + +typedef enum +{ + GST_PLAY_FLAG_VIDEO = (1 << 0), + GST_PLAY_FLAG_AUDIO = (1 << 1), + GST_PLAY_FLAG_TEXT = (1 << 2), + GST_PLAY_FLAG_VIS = (1 << 3), + GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4), + GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5), + GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6), + GST_PLAY_FLAG_DOWNLOAD = (1 << 7), + GST_PLAY_FLAG_BUFFERING = (1 << 8), + GST_PLAY_FLAG_DEINTERLACE = (1 << 9), + GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10) +} GstPlayFlags; + +/*********************************************************** + * Name: init_ogl + * + * Arguments: + * APP_STATE_T *state - holds OGLES model info + * + * Description: Sets the display, OpenGL|ES context and screen stuff + * + * Returns: void + * + ***********************************************************/ +static void +init_ogl (APP_STATE_T * state) +{ + int32_t success = 0; + EGLBoolean result; + EGLint num_config; + EGLNativeWindowType window_handle = (EGLNativeWindowType) 0; + +#if defined (USE_OMX_TARGET_RPI) + static EGL_DISPMANX_WINDOW_T nativewindow; + + DISPMANX_UPDATE_HANDLE_T dispman_update; + VC_RECT_T dst_rect; + VC_RECT_T src_rect; + + VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 }; +#endif + + static const EGLint attribute_list[] = { + EGL_DEPTH_SIZE, 16, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + static const EGLint context_attributes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLConfig config; + + /* get an EGL display connection */ + state->display = eglGetDisplay (EGL_DEFAULT_DISPLAY); + assert (state->display != EGL_NO_DISPLAY); + + /* initialize the EGL display connection */ + result = eglInitialize (state->display, NULL, NULL); + assert (EGL_FALSE != result); + +#if defined (USE_OMX_TARGET_RPI) + /* get an appropriate EGL frame buffer configuration + * this uses a BRCM extension that gets the closest match, rather + * than standard which returns anything that matches. */ + result = + eglSaneChooseConfigBRCM (state->display, attribute_list, &config, 1, + &num_config); + assert (EGL_FALSE != result); +#else + result = + eglChooseConfig (state->display, attribute_list, &config, 1, &num_config); +#endif + + /* create an EGL rendering context */ + state->context = + eglCreateContext (state->display, config, EGL_NO_CONTEXT, + context_attributes); + assert (state->context != EGL_NO_CONTEXT); + +#if defined (USE_OMX_TARGET_RPI) + /* create an EGL window surface */ + success = graphics_get_display_size (0 /* LCD */ , &state->screen_width, + &state->screen_height); + assert (success >= 0); + + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.width = state->screen_width; + dst_rect.height = state->screen_height; + + src_rect.x = 0; + src_rect.y = 0; + src_rect.width = state->screen_width << 16; + src_rect.height = state->screen_height << 16; + + state->dispman_display = vc_dispmanx_display_open (0 /* LCD */ ); + dispman_update = vc_dispmanx_update_start (0); + + state->dispman_element = + vc_dispmanx_element_add (dispman_update, state->dispman_display, + 0 /*layer */ , &dst_rect, 0 /*src */ , + &src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0 /*clamp */ , + 0 /*transform */ ); + + nativewindow.element = state->dispman_element; + nativewindow.width = state->screen_width; + nativewindow.height = state->screen_height; + vc_dispmanx_update_submit_sync (dispman_update); + + window_handle = &nativewindow; +#endif + + state->surface = + eglCreateWindowSurface (state->display, config, window_handle, NULL); + assert (state->surface != EGL_NO_SURFACE); + + /* connect the context to the surface */ + result = + eglMakeCurrent (state->display, state->surface, state->surface, + state->context); + assert (EGL_FALSE != result); +} + +/*********************************************************** + * Name: init_model_proj + * + * Arguments: + * APP_STATE_T *state - holds OGLES model info + * + * Description: Sets the OpenGL|ES model to default values + * + * Returns: void + * + ***********************************************************/ +static void +init_model_proj (APP_STATE_T * state) +{ + GLint ret = 0; + + state->vshader = glCreateShader (GL_VERTEX_SHADER); + + glShaderSource (state->vshader, 1, &cube_v_src, NULL); + glCompileShader (state->vshader); + assert (glGetError () == GL_NO_ERROR); + + state->fshader = glCreateShader (GL_FRAGMENT_SHADER); + + glShaderSource (state->fshader, 1, &cube_f_src, NULL); + glCompileShader (state->fshader); + assert (glGetError () == GL_NO_ERROR); + + state->program = glCreateProgram (); + + glAttachShader (state->program, state->vshader); + glAttachShader (state->program, state->fshader); + + glBindAttribLocation (state->program, 0, "a_position"); + glBindAttribLocation (state->program, 1, "a_texCoord"); + + glLinkProgram (state->program); + + glGetProgramiv (state->program, GL_LINK_STATUS, &ret); + assert (ret == GL_TRUE); + + glUseProgram (state->program); + + state->u_rotx = glGetUniformLocation (state->program, "u_rotx"); + state->u_roty = glGetUniformLocation (state->program, "u_roty"); + state->u_rotz = glGetUniformLocation (state->program, "u_rotz"); + + state->u_modelviewmatrix = + glGetUniformLocation (state->program, "u_modelview"); + + state->u_projectionmatrix = + glGetUniformLocation (state->program, "u_projection"); + + state->s_texture = glGetUniformLocation (state->program, "s_texture"); + + glViewport (0, 0, (GLsizei) state->screen_width, + (GLsizei) state->screen_height); + + state->fov = 45.0f; + state->distance = 5.0f; + state->aspect = + (GLfloat) state->screen_width / (GLfloat) state->screen_height; + + gst_gl_matrix_load_identity (&state->projection); + gst_gl_matrix_perspective (&state->projection, state->fov, state->aspect, + 1.0f, 100.0f); + + gst_gl_matrix_load_identity (&state->modelview); + gst_gl_matrix_translate (&state->modelview, 0.0f, 0.0f, -state->distance); + + reset_model (state); +} + +/*********************************************************** + * Name: reset_model + * + * Arguments: + * APP_STATE_T *state - holds OGLES model info + * + * Description: Resets the Model projection and rotation direction + * + * Returns: void + * + ***********************************************************/ +static void +reset_model (APP_STATE_T * state) +{ + /* reset model rotation */ + state->rot_angle_x = 45.f; + state->rot_angle_y = 30.f; + state->rot_angle_z = 0.f; + state->rot_angle_x_inc = 0.5f; + state->rot_angle_y_inc = 0.5f; + state->rot_angle_z_inc = 0.f; +} + +/*********************************************************** + * Name: update_model + * + * Arguments: + * APP_STATE_T *state - holds OGLES model info + * + * Description: Updates model projection to current position/rotation + * + * Returns: void + * + ***********************************************************/ +static void +update_model (APP_STATE_T * state) +{ + if (state->animate) { + /* update position */ + state->rot_angle_x = + inc_and_wrap_angle (state->rot_angle_x, state->rot_angle_x_inc); + state->rot_angle_y = + inc_and_wrap_angle (state->rot_angle_y, state->rot_angle_y_inc); + state->rot_angle_z = + inc_and_wrap_angle (state->rot_angle_z, state->rot_angle_z_inc); + } +} + +/*********************************************************** + * Name: inc_and_wrap_angle + * + * Arguments: + * GLfloat angle current angle + * GLfloat angle_inc angle increment + * + * Description: Increments or decrements angle by angle_inc degrees + * Wraps to 0 at 360 deg. + * + * Returns: new value of angle + * + ***********************************************************/ +static GLfloat +inc_and_wrap_angle (GLfloat angle, GLfloat angle_inc) +{ + angle += angle_inc; + + if (angle >= 360.0) + angle -= 360.f; + else if (angle <= 0) + angle += 360.f; + + return angle; +} + +/*********************************************************** + * Name: redraw_scene + * + * Arguments: + * APP_STATE_T *state - holds OGLES model info + * + * Description: Draws the model and calls eglSwapBuffers + * to render to screen + * + * Returns: void + * + ***********************************************************/ +static void +redraw_scene (APP_STATE_T * state) +{ + glBindFramebuffer (GL_FRAMEBUFFER, 0); + + glEnable (GL_CULL_FACE); + glEnable (GL_DEPTH_TEST); + + /* Set background color and clear buffers */ + glClearColor (0.15f, 0.25f, 0.35f, 1.0f); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glUseProgram (state->program); + + glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, quadx); + glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 0, texCoords); + + glEnableVertexAttribArray (0); + glEnableVertexAttribArray (1); + + glActiveTexture (GL_TEXTURE0); + glBindTexture (GL_TEXTURE_2D, state->tex); + glUniform1i (state->s_texture, 0); + + glUniform1f (state->u_rotx, state->rot_angle_x); + glUniform1f (state->u_roty, state->rot_angle_y); + glUniform1f (state->u_rotz, state->rot_angle_z); + + glUniformMatrix4fv (state->u_modelviewmatrix, 1, GL_FALSE, + &state->modelview.m[0][0]); + + glUniformMatrix4fv (state->u_projectionmatrix, 1, GL_FALSE, + &state->projection.m[0][0]); + + /* draw first 4 vertices */ + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + glDrawArrays (GL_TRIANGLE_STRIP, 4, 4); + glDrawArrays (GL_TRIANGLE_STRIP, 8, 4); + glDrawArrays (GL_TRIANGLE_STRIP, 12, 4); + glDrawArrays (GL_TRIANGLE_STRIP, 16, 4); + glDrawArrays (GL_TRIANGLE_STRIP, 20, 4); + + eglSwapBuffers (state->display, state->surface); + + glDisable (GL_DEPTH_TEST); + glDisable (GL_CULL_FACE); +} + +/*********************************************************** + * Name: init_textures + * + * Arguments: + * APP_STATE_T *state - holds OGLES model info + * + * Description: Initialise OGL|ES texture surfaces to use image + * buffers + * + * Returns: void + * + ***********************************************************/ +static void +init_textures (APP_STATE_T * state, GstBuffer * buffer) +{ + GstCapsFeatures *feature = gst_caps_get_features (state->caps, 0); + + if (gst_caps_features_contains (feature, "memory:EGLImage")) { + /* nothing special to do */ + g_print ("Prepare texture for EGLImage\n"); + state->can_avoid_upload = FALSE; + glGenTextures (1, &state->tex); + glBindTexture (GL_TEXTURE_2D, state->tex); + } else if (gst_caps_features_contains (feature, "memory:GLMemory")) { + g_print ("Prepare texture for GLMemory\n"); + state->can_avoid_upload = TRUE; + state->tex = 0; + } else if (gst_caps_features_contains (feature, + "meta:GstVideoGLTextureUploadMeta")) { + GstVideoMeta *meta = NULL; + g_print ("Prepare texture for GstVideoGLTextureUploadMeta\n"); + meta = gst_buffer_get_video_meta (buffer); + state->can_avoid_upload = FALSE; + glGenTextures (1, &state->tex); + glBindTexture (GL_TEXTURE_2D, state->tex); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, meta->width, meta->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } else { + g_assert_not_reached (); + } + +#if 0 + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +#else + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#endif + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + assert (glGetError () == GL_NO_ERROR); +} + +static void +render_scene (APP_STATE_T * state) +{ + update_model (state); + redraw_scene (state); + TRACE_VC_MEMORY_ONCE_FOR_ID ("after render_scene", gid2); +} + +static void +update_image (APP_STATE_T * state, GstBuffer * buffer) +{ + GstVideoGLTextureUploadMeta *meta = NULL; + + if (state->current_buffer) { + gst_buffer_unref (state->current_buffer); + } else { + /* Setup the model world */ + init_model_proj (state); + TRACE_VC_MEMORY ("after init_model_proj"); + + /* initialize the OGLES texture(s) */ + init_textures (state, buffer); + TRACE_VC_MEMORY ("after init_textures"); + } + state->current_buffer = gst_buffer_ref (buffer); + + TRACE_VC_MEMORY_ONCE_FOR_ID ("before GstVideoGLTextureUploadMeta", gid0); + + if (state->can_avoid_upload) { + GstMemory *mem = gst_buffer_peek_memory (state->current_buffer, 0); + g_assert (gst_is_gl_memory (mem)); + state->tex = ((GstGLMemory *) mem)->tex_id; + } else if ((meta = gst_buffer_get_video_gl_texture_upload_meta (buffer))) { + if (meta->n_textures == 1) { + guint ids[4] = { state->tex, 0, 0, 0 }; + if (!gst_video_gl_texture_upload_meta_upload (meta, ids)) { + GST_WARNING ("failed to upload to texture"); + } + } + } + + TRACE_VC_MEMORY_ONCE_FOR_ID ("after GstVideoGLTextureUploadMeta", gid1); +} + +static void +init_intercom (APP_STATE_T * state) +{ + state->queue = + g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref); + g_mutex_init (&state->queue_lock); + g_cond_init (&state->cond); +} + +static void +terminate_intercom (APP_STATE_T * state) +{ + /* Release intercom */ + if (state->queue) { + g_async_queue_unref (state->queue); + } + + g_mutex_clear (&state->queue_lock); + g_cond_clear (&state->cond); +} + +static void +flush_internal (APP_STATE_T * state) +{ + if (state->current_buffer) { + gst_buffer_unref (state->current_buffer); + } + state->current_buffer = NULL; +} + +static void +flush_start (APP_STATE_T * state) +{ + GstMiniObject *object = NULL; + + g_mutex_lock (&state->queue_lock); + state->flushing = TRUE; + g_cond_broadcast (&state->cond); + g_mutex_unlock (&state->queue_lock); + + while ((object = g_async_queue_try_pop (state->queue))) { + gst_mini_object_unref (object); + } + g_mutex_lock (&state->queue_lock); + flush_internal (state); + state->popped_obj = NULL; + g_mutex_unlock (&state->queue_lock); +} + +static void +flush_stop (APP_STATE_T * state) +{ + GstMiniObject *object = NULL; + + g_mutex_lock (&state->queue_lock); + while ((object = GST_MINI_OBJECT_CAST (g_async_queue_try_pop (state->queue)))) { + gst_mini_object_unref (object); + } + flush_internal (state); + state->popped_obj = NULL; + state->flushing = FALSE; + g_mutex_unlock (&state->queue_lock); +} + +static void +pipeline_pause (APP_STATE_T * state) +{ + gst_element_set_state (state->pipeline, GST_STATE_PAUSED); +} + +static void +pipeline_play (APP_STATE_T * state) +{ + gst_element_set_state (state->pipeline, GST_STATE_PLAYING); +} + +static gint64 +pipeline_get_position (APP_STATE_T * state) +{ + gint64 position = -1; + + if (state->pipeline) { + gst_element_query_position (state->vsink, GST_FORMAT_TIME, &position); + } + + return position; +} + +static gint64 +pipeline_get_duration (APP_STATE_T * state) +{ + gint64 duration = -1; + + if (state->pipeline) { + gst_element_query_duration (state->pipeline, GST_FORMAT_TIME, &duration); + } + + return duration; +} + +static void +pipeline_seek (APP_STATE_T * state, gint64 position) +{ + if (state->pipeline) { + GstEvent *event; + event = gst_event_new_seek (1.0, + GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, + GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE); + if (!gst_element_send_event (state->vsink, event)) { + g_print ("seek failed\n"); + } + } +} + +static gboolean +handle_queued_objects (APP_STATE_T * state) +{ + GstMiniObject *object = NULL; + + g_mutex_lock (&state->queue_lock); + if (state->flushing) { + g_cond_broadcast (&state->cond); + goto beach; + } else if (g_async_queue_length (state->queue) == 0) { + goto beach; + } + + if ((object = g_async_queue_try_pop (state->queue))) { + if (GST_IS_BUFFER (object)) { + GstBuffer *buffer = GST_BUFFER_CAST (object); + update_image (state, buffer); + render_scene (state); + gst_buffer_unref (buffer); + if (!SYNC_BUFFERS) { + object = NULL; + } + } else if (GST_IS_QUERY (object)) { + GstQuery *query = GST_QUERY_CAST (object); + GstStructure *s = (GstStructure *) gst_query_get_structure (query); + + if (gst_structure_has_name (s, "not-used")) { + g_assert_not_reached (); + } else { + g_assert_not_reached (); + } + } else if (GST_IS_EVENT (object)) { + GstEvent *event = GST_EVENT_CAST (object); + g_print ("\nevent %p %s\n", event, + gst_event_type_get_name (GST_EVENT_TYPE (event))); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + flush_internal (state); + break; + default: + break; + } + gst_event_unref (event); + object = NULL; + } + } + + if (object) { + state->popped_obj = object; + g_cond_broadcast (&state->cond); + } + +beach: + g_mutex_unlock (&state->queue_lock); + + return FALSE; +} + +static gboolean +queue_object (APP_STATE_T * state, GstMiniObject * obj, gboolean synchronous) +{ + gboolean res = TRUE; + + g_mutex_lock (&state->queue_lock); + if (state->flushing) { + gst_mini_object_unref (obj); + res = FALSE; + goto beach; + } + + g_async_queue_push (state->queue, obj); + + if (synchronous) { + /* Waiting for object to be handled */ + do { + g_cond_wait (&state->cond, &state->queue_lock); + } while (!state->flushing && state->popped_obj != obj); + } + +beach: + g_mutex_unlock (&state->queue_lock); + return res; +} + +static void +preroll_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, + gpointer user_data) +{ + APP_STATE_T *state = (APP_STATE_T *) user_data; + queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)), FALSE); +} + +static void +buffers_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, + gpointer user_data) +{ + APP_STATE_T *state = (APP_STATE_T *) user_data; + queue_object (state, GST_MINI_OBJECT_CAST (gst_buffer_ref (buffer)), + SYNC_BUFFERS); +} + +static GstPadProbeReturn +events_cb (GstPad * pad, GstPadProbeInfo * probe_info, gpointer user_data) +{ + APP_STATE_T *state = (APP_STATE_T *) user_data; + GstEvent *event = GST_PAD_PROBE_INFO_EVENT (probe_info); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + if (state->caps) { + gst_caps_unref (state->caps); + state->caps = NULL; + } + gst_event_parse_caps (event, &state->caps); + if (state->caps) + gst_caps_ref (state->caps); + break; + } + case GST_EVENT_FLUSH_START: + flush_start (state); + break; + case GST_EVENT_FLUSH_STOP: + flush_stop (state); + break; + case GST_EVENT_EOS: + queue_object (state, GST_MINI_OBJECT_CAST (gst_event_ref (event)), FALSE); + break; + default: + break; + } + + return GST_PAD_PROBE_OK; +} + +static GstPadProbeReturn +query_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) +{ + APP_STATE_T *state = (APP_STATE_T *) user_data; + GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info); + GstStructure *external_gl_context_desc = NULL; + gchar *platform = NULL; + gchar *gl_apis = NULL; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_ALLOCATION: + { + platform = gst_gl_platform_to_string (GST_GL_PLATFORM_EGL); + gl_apis = gst_gl_api_to_string (GST_GL_API_GLES2); + + external_gl_context_desc = + gst_structure_new ("GstVideoGLTextureUploadMeta", + "gst.gl.context.handle", G_TYPE_POINTER, state->context, + "gst.gl.context.type", G_TYPE_STRING, platform, + "gst.gl.context.apis", G_TYPE_STRING, gl_apis, NULL); + gst_query_add_allocation_meta (query, + GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, external_gl_context_desc); + gst_structure_free (external_gl_context_desc); + + g_free (gl_apis); + g_free (platform); + + GST_DEBUG ("done alocation"); + return GST_PAD_PROBE_OK; + break; + } + case GST_QUERY_CONTEXT: + { + return gst_gl_handle_context_query (state->pipeline, query, + (GstGLDisplay **) & state->gst_display); + break; + } + case GST_QUERY_DRAIN: + { + flush_internal (state); + break; + } + default: + break; + } + + return GST_PAD_PROBE_OK; +} + +static gboolean +init_playbin_player (APP_STATE_T * state, const gchar * uri) +{ + GstPad *pad = NULL; + GstPad *ghostpad = NULL; + GstElement *vbin = gst_bin_new ("vbin"); + + /* insert a gl filter so that the GstGLBufferPool + * is managed automatically */ + GstElement *glfilter = gst_element_factory_make ("glcolorscale", "glfilter"); + GstElement *vsink = gst_element_factory_make ("fakesink", "vsink"); + g_object_set (vsink, "sync", TRUE, "silent", TRUE, "qos", TRUE, + "enable-last-sample", FALSE, + "max-lateness", 20 * GST_MSECOND, "signal-handoffs", TRUE, NULL); + + g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state); + g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state); + + gst_bin_add_many (GST_BIN (vbin), glfilter, vsink, NULL); + + pad = gst_element_get_static_pad (glfilter, "sink"); + ghostpad = gst_ghost_pad_new ("sink", pad); + gst_object_unref (pad); + gst_element_add_pad (vbin, ghostpad); + + pad = gst_element_get_static_pad (vsink, "sink"); + gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state, + NULL); + gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state, + NULL); + gst_object_unref (pad); + + gst_element_link (glfilter, vsink); + + /* Instantiate and configure playbin */ + state->pipeline = gst_element_factory_make ("playbin", "player"); + g_object_set (state->pipeline, "uri", uri, + "video-sink", vbin, "flags", + GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_AUDIO, NULL); + + state->vsink = gst_object_ref (vsink); + return TRUE; +} + +static gboolean +init_parse_launch_player (APP_STATE_T * state, const gchar * spipeline) +{ + GstElement *vsink; + GError *error = NULL; + + /* ex: + + ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \ + h264parse ! omxh264dec ! glcolorscale ! fakesink name=vsink" + + ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \ + h264parse ! omxh264dec ! glcolorscale ! \ + video/x-raw(memory:EGLImage) ! fakesink name=vsink" + + ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \ + h264parse ! omxh264dec ! glcolorscale ! \ + video/x-raw(memory:GLMemory) ! fakesink name=vsink" + + ./testegl "filesrc location=big_buck_bunny_720p_h264.mov ! qtdemux ! \ + h264parse ! omxh264dec ! glcolorscale ! \ + video/x-raw(meta:GstVideoGLTextureUploadMeta) ! \ + fakesink name=vsink" + + */ + + /* pipeline 1 and 2 are the same and the most efficient as glcolorscale + * will enter in passthrough mode and testegl will just bind the eglimage + * to a gl texture without any copy. */ + + state->pipeline = gst_parse_launch (spipeline, &error); + + if (!state->pipeline) { + g_printerr ("Unable to instatiate pipeline '%s': %s\n", + spipeline, error->message); + return FALSE; + } + + vsink = gst_bin_get_by_name (GST_BIN (state->pipeline), "vsink"); + + if (!vsink) { + g_printerr ("Unable to find a fakesink named 'vsink'"); + return FALSE; + } + + g_object_set (vsink, "sync", TRUE, "silent", TRUE, "qos", TRUE, + "enable-last-sample", FALSE, + "max-lateness", 20 * GST_MSECOND, "signal-handoffs", TRUE, NULL); + + g_signal_connect (vsink, "preroll-handoff", G_CALLBACK (preroll_cb), state); + g_signal_connect (vsink, "handoff", G_CALLBACK (buffers_cb), state); + + gst_pad_add_probe (gst_element_get_static_pad (vsink, "sink"), + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, events_cb, state, NULL); + gst_pad_add_probe (gst_element_get_static_pad (vsink, "sink"), + GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, query_cb, state, NULL); + + state->vsink = gst_object_ref (vsink); + return TRUE; +} + +//------------------------------------------------------------------------------ + +static void +report_position_duration (APP_STATE_T * state) +{ + gint64 position, duration; + + duration = pipeline_get_duration (state); + position = pipeline_get_position (state); + + if (position != -1) { + g_print ("\n position / duration: %" GST_TIME_FORMAT, + GST_TIME_ARGS (position)); + } else { + g_print ("\n position / duration: unknown"); + } + + if (duration != -1) { + g_print (" / %" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); + } else { + g_print (" / unknown"); + } + g_print ("\n"); +} + +static void +seek_forward (APP_STATE_T * state) +{ + gint64 position, duration; + + duration = pipeline_get_duration (state); + position = pipeline_get_position (state); + + if (position != -1) { + position += 30 * GST_SECOND; + if (duration != -1) { + position = MIN (position, duration); + } + pipeline_seek (state, position); + } +} + +static void +seek_backward (APP_STATE_T * state) +{ + gint64 position; + + position = pipeline_get_position (state); + + if (position != -1) { + position -= 30 * GST_SECOND; + position = MAX (position, 0); + pipeline_seek (state, position); + } +} + +#define SKIP(t) \ + while (*t) { \ + if ((*t == ' ') || (*t == '\n') || (*t == '\t') || (*t == '\r')) \ + t++; \ + else \ + break; \ + } + +/* Process keyboard input */ +static gboolean +handle_keyboard (GIOChannel * source, GIOCondition cond, APP_STATE_T * state) +{ + gchar *str = NULL; + char op; + + if (g_io_channel_read_line (source, &str, NULL, NULL, + NULL) == G_IO_STATUS_NORMAL) { + + gchar *cmd = str; + SKIP (cmd) + op = *cmd; + cmd++; + switch (op) { + case 'a': + if (state->animate) { + state->animate = FALSE; + } else { + state->animate = TRUE; + } + break; + case 'p': + pipeline_pause (state); + break; + case 'r': + pipeline_play (state); + break; + case 'l': + report_position_duration (state); + break; + case 'f': + seek_forward (state); + break; + case 'b': + seek_backward (state); + break; + case 'q': + flush_start (state); + gst_element_set_state (state->pipeline, GST_STATE_READY); + break; + } + } + g_free (str); + return TRUE; +} + +static GstBusSyncReply +bus_sync_handler (GstBus * bus, GstMessage * message, GstPipeline * data) +{ + return GST_BUS_PASS; +} + +/* on error print the error and quit the application */ +static void +error_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state) +{ + GError *err; + gchar *debug_info; + + gst_message_parse_error (msg, &err, &debug_info); + g_printerr ("Error received from element %s: %s\n", + GST_OBJECT_NAME (msg->src), err->message); + g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none"); + g_clear_error (&err); + g_free (debug_info); + flush_start (state); + gst_element_set_state (state->pipeline, GST_STATE_READY); +} + +/* buffering */ +static void +buffering_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state) +{ + gint percent; + + gst_message_parse_buffering (msg, &percent); + g_print ("Buffering %3d%%\r", percent); + if (percent < 100) + pipeline_pause (state); + else { + g_print ("\n"); + pipeline_play (state); + } +} + +/* on EOS just quit the application */ +static void +eos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state) +{ + if (GST_MESSAGE_SRC (msg) == GST_OBJECT (state->pipeline)) { + g_print ("End-Of-Stream reached.\n"); + gst_element_set_state (state->pipeline, GST_STATE_READY); + } +} + +static void +state_changed_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state) +{ + GstState old_state, new_state, pending_state; + if (GST_MESSAGE_SRC (msg) == GST_OBJECT (state->pipeline)) { + gst_message_parse_state_changed (msg, &old_state, &new_state, + &pending_state); + g_print ("State changed to %s\n", gst_element_state_get_name (new_state)); + if (old_state == GST_STATE_PAUSED && new_state == GST_STATE_READY) { + g_main_loop_quit (state->main_loop); + } + } +} + +static void +qos_cb (GstBus * bus, GstMessage * msg, APP_STATE_T * state) +{ + GstFormat fmt = GST_FORMAT_BUFFERS; + gchar *name = gst_element_get_name (GST_MESSAGE_SRC (msg)); + gst_message_parse_qos_stats (msg, &fmt, &state->rendered, &state->dropped); + g_print ("%s rendered: %" G_GUINT64_FORMAT " dropped: %" G_GUINT64_FORMAT + " %s\n", + name, state->rendered, state->dropped, + (fmt == GST_FORMAT_BUFFERS ? "frames" : "samples")); + g_free (name); +} + +//============================================================================== + +static void +close_ogl (void) +{ +#if defined (USE_OMX_TARGET_RPI) + DISPMANX_UPDATE_HANDLE_T dispman_update; +#endif + + if (state->fshader) { + glDeleteShader (state->fshader); + glDetachShader (state->program, state->fshader); + } + + if (state->vshader) { + glDeleteShader (state->vshader); + glDetachShader (state->program, state->vshader); + } + + if (state->program) + glDeleteProgram (state->program); + + if (state->tex) + glDeleteTextures (1, &state->tex); + + /* clear screen */ + glClear (GL_COLOR_BUFFER_BIT); + eglSwapBuffers (state->display, state->surface); + + /* Release OpenGL resources */ + eglMakeCurrent (state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + eglDestroySurface (state->display, state->surface); + eglDestroyContext (state->display, state->context); + gst_object_unref (state->gst_display); + +#if defined (USE_OMX_TARGET_RPI) + dispman_update = vc_dispmanx_update_start (0); + vc_dispmanx_element_remove (dispman_update, state->dispman_element); + vc_dispmanx_update_submit_sync (dispman_update); + vc_dispmanx_display_close (state->dispman_display); +#endif +} + +//============================================================================== + +static void +open_ogl (void) +{ + TRACE_VC_MEMORY ("state 0"); + +#if defined (USE_OMX_TARGET_RPI) + bcm_host_init (); + TRACE_VC_MEMORY ("after bcm_host_init"); +#endif + + /* Create surface and gl context */ + init_ogl (state); + TRACE_VC_MEMORY ("after init_ogl"); + + /* Wrap the EGLDisplay to GstGLDisplayEGL */ + state->gst_display = gst_gl_display_egl_new_with_egl_display (state->display); +} + +static gpointer +render_func (gpointer data) +{ + open_ogl (); + state->running = TRUE; + + do { + handle_queued_objects (state); + g_usleep (0); + } while (state->running == TRUE); + + close_ogl (); + return NULL; +} + +int +main (int argc, char **argv) +{ + GstBus *bus; + GOptionContext *ctx; + GIOChannel *io_stdin; + GError *err = NULL; + gboolean res; + GOptionEntry options[] = { + {NULL} + }; + GThread *rthread; + + /* Clear application state */ + memset (state, 0, sizeof (*state)); + state->animate = TRUE; + state->current_buffer = NULL; + state->caps = NULL; + +#if !GLIB_CHECK_VERSION (2, 31, 0) + /* must initialise the threading system before using any other GLib funtion */ + if (!g_thread_supported ()) + g_thread_init (NULL); +#endif + + ctx = g_option_context_new ("[ADDITIONAL ARGUMENTS]"); + g_option_context_add_main_entries (ctx, options, NULL); + g_option_context_add_group (ctx, gst_init_get_option_group ()); + if (!g_option_context_parse (ctx, &argc, &argv, &err)) { + g_print ("Error initializing: %s\n", GST_STR_NULL (err->message)); + exit (1); + } + g_option_context_free (ctx); + + if (argc != 2) { + g_print ("Usage: %s or \n", argv[0]); + exit (1); + } + + /* Initialize GStreamer */ + gst_init (&argc, &argv); + + /* initialize inter thread comunnication */ + init_intercom (state); + + TRACE_VC_MEMORY ("state 0"); + + if (!(rthread = g_thread_new ("render", (GThreadFunc) render_func, NULL))) { + g_print ("Render thread create failed\n"); + exit (1); + } + + /* Initialize player */ + if (gst_uri_is_valid (argv[1])) { + res = init_playbin_player (state, argv[1]); + } else { + res = init_parse_launch_player (state, argv[1]); + } + + if (!res) + goto done; + + /* Create a GLib Main Loop and set it to run */ + state->main_loop = g_main_loop_new (NULL, FALSE); + + /* Add a keyboard watch so we get notified of keystrokes */ + io_stdin = g_io_channel_unix_new (fileno (stdin)); + g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, state); + g_io_channel_unref (io_stdin); + + /* *INDENT-OFF* */ + g_print ("Available commands: \n" + " a - Toggle animation \n" + " p - Pause playback \n" + " r - Resume playback \n" + " l - Query position/duration\n" + " f - Seek 30 seconds forward \n" + " b - Seek 30 seconds backward \n" + " q - Quit \n"); + /* *INDENT-ON* */ + + /* Connect the bus handlers */ + bus = gst_element_get_bus (state->pipeline); + + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler, state, + NULL); + + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + gst_bus_enable_sync_message_emission (bus); + + g_signal_connect (G_OBJECT (bus), "message::error", (GCallback) error_cb, + state); + g_signal_connect (G_OBJECT (bus), "message::buffering", + (GCallback) buffering_cb, state); + g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback) eos_cb, state); + g_signal_connect (G_OBJECT (bus), "message::qos", (GCallback) qos_cb, state); + g_signal_connect (G_OBJECT (bus), "message::state-changed", + (GCallback) state_changed_cb, state); + gst_object_unref (bus); + + /* Make player start playing */ + gst_element_set_state (state->pipeline, GST_STATE_PLAYING); + + /* Start the mainloop */ + state->main_loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (state->main_loop); + +done: + /* Release pipeline */ + if (state->pipeline) { + gst_element_set_state (state->pipeline, GST_STATE_NULL); + if (state->vsink) { + gst_object_unref (state->vsink); + state->vsink = NULL; + } + + gst_object_unref (state->pipeline); + } + + /* Unref the mainloop */ + if (state->main_loop) { + g_main_loop_unref (state->main_loop); + } + + /* Stop rendering thread */ + state->running = FALSE; + g_thread_join (rthread); + + if (state->caps) { + gst_caps_unref (state->caps); + state->caps = NULL; + } + + terminate_intercom (state); + + TRACE_VC_MEMORY ("at exit"); + return 0; +} diff --git a/gst-omx.doap b/gst-omx.doap index c5c5401..3872f59 100644 --- a/gst-omx.doap +++ b/gst-omx.doap @@ -33,6 +33,16 @@ a basic collection of elements + 1.2.0 + 1.2 + + 2014-07-23 + + + + + + 1.0.0 1.0 diff --git a/gst-omx.manifest b/gst-omx.manifest new file mode 100644 index 0000000..97e8c31 --- /dev/null +++ b/gst-omx.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/m4/Makefile.am b/m4/Makefile.am new file mode 100644 index 0000000..741f850 --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = diff --git a/omx/Makefile.am b/omx/Makefile.am index b182506..5108c8c 100644 --- a/omx/Makefile.am +++ b/omx/Makefile.am @@ -12,8 +12,11 @@ endif libgstomx_la_SOURCES = \ gstomx.c \ + gstomxbufferpool.c \ + gstomxvideo.c \ gstomxvideodec.c \ gstomxvideoenc.c \ + gstomxaudiodec.c \ gstomxaudioenc.c \ gstomxmjpegdec.c \ gstomxmpeg4videodec.c \ @@ -26,12 +29,21 @@ libgstomx_la_SOURCES = \ gstomxmpeg4videoenc.c \ gstomxh264enc.c \ gstomxh263enc.c \ - gstomxaacenc.c + gstomxaacdec.c \ + gstomxmp3dec.c \ + gstomxaacenc.c \ + gstomxamrdec.c \ + gstomxaudiosink.c \ + gstomxanalogaudiosink.c \ + gstomxhdmiaudiosink.c noinst_HEADERS = \ gstomx.h \ + gstomxbufferpool.h \ + gstomxvideo.h \ gstomxvideodec.h \ gstomxvideoenc.h \ + gstomxaudiodec.h \ gstomxaudioenc.h \ gstomxmjpegdec.h \ gstomxmpeg2videodec.h \ @@ -44,7 +56,13 @@ noinst_HEADERS = \ gstomxmpeg4videoenc.h \ gstomxh264enc.h \ gstomxh263enc.h \ - gstomxaacenc.h + gstomxaacdec.h \ + gstomxmp3dec.h \ + gstomxaacenc.h \ + gstomxamrdec.h \ + gstomxaudiosink.h \ + gstomxanalogaudiosink.h \ + gstomxhdmiaudiosink.h if !HAVE_EXTERNAL_OMX OMX_INCLUDEPATH = -I$(abs_srcdir)/openmax @@ -52,17 +70,17 @@ endif libgstomx_la_CFLAGS = \ -DGST_USE_UNSTABLE_API=1 \ - $(CFLAGS) \ $(OMX_INCLUDEPATH) \ + $(GST_GL_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) \ $(GST_CFLAGS) \ $(TBM_CFLAGS) \ - $(MM_COMMON_CFLAGS) - + $(GMODULE_NO_EXPORT_CFLAGS) \ + $(MM_COMMON_CFLAGS) libgstomx_la_CFLAGS += -DUSE_MM_VIDEO_BUFFER - libgstomx_la_LIBADD = \ + $(GST_GL_LIBS) \ $(GST_PLUGINS_BASE_LIBS) \ -lgstaudio-@GST_API_VERSION@ \ -lgstpbutils-@GST_API_VERSION@ \ @@ -70,8 +88,8 @@ libgstomx_la_LIBADD = \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ $(TBM_LIBS) \ + $(GMODULE_NO_EXPORT_LIBS) \ $(MM_COMMON_LIBS) - libgstomx_la_LDFLAGS = \ $(GST_PLUGIN_LDFLAGS) \ $(TBM_LDFLAGS) \ diff --git a/omx/gstomx.c b/omx/gstomx.c old mode 100755 new mode 100644 index dddd0a4..5f829e9 --- a/omx/gstomx.c +++ b/omx/gstomx.c @@ -39,7 +39,12 @@ #include "gstomxmpeg4videoenc.h" #include "gstomxh264enc.h" #include "gstomxh263enc.h" +#include "gstomxaacdec.h" +#include "gstomxmp3dec.h" #include "gstomxaacenc.h" +#include "gstomxamrdec.h" +#include "gstomxanalogaudiosink.h" +#include "gstomxhdmiaudiosink.h" GST_DEBUG_CATEGORY (gstomx_debug); #define GST_CAT_DEFAULT gstomx_debug @@ -47,8 +52,6 @@ GST_DEBUG_CATEGORY (gstomx_debug); G_LOCK_DEFINE_STATIC (core_handles); static GHashTable *core_handles; - - GstOMXCore * gst_omx_core_acquire (const gchar * filename) { @@ -291,8 +294,8 @@ gst_omx_component_handle_messages (GstOMXComponent * comp) OMX_U32 index = msg->content.port_settings_changed.port; GList *outports = NULL, *l, *k; - GST_ERROR_OBJECT (comp->parent, "%s settings changed (port %u)", - comp->name, index); + GST_DEBUG_OBJECT (comp->parent, "%s settings changed (port %u)", + comp->name, (guint) index); /* FIXME: This probably can be done better */ @@ -338,7 +341,7 @@ gst_omx_component_handle_messages (GstOMXComponent * comp) break; GST_DEBUG_OBJECT (comp->parent, "%s port %u got buffer flags 0x%08x", - comp->name, port->index, flags); + comp->name, port->index, (guint) flags); if ((flags & OMX_BUFFERFLAG_EOS) && port->port_def.eDir == OMX_DirOutput) port->eos = TRUE; @@ -348,15 +351,14 @@ gst_omx_component_handle_messages (GstOMXComponent * comp) case GST_OMX_MESSAGE_BUFFER_DONE:{ GstOMXBuffer *buf = msg->content.buffer_done.buffer->pAppPrivate; GstOMXPort *port; - GstOMXComponent *comp; port = buf->port; - comp = port->comp; if (msg->content.buffer_done.empty) { /* Input buffer is empty again and can be used to contain new input */ - GST_LOG_OBJECT (comp->parent, "%s port %u emptied buffer %p (%p)", - comp->name, port->index, buf, buf->omx_buf->pBuffer); + GST_LOG_OBJECT (port->comp->parent, + "%s port %u emptied buffer %p (%p)", port->comp->name, + port->index, buf, buf->omx_buf->pBuffer); /* Reset offset and filled length */ buf->omx_buf->nOffset = 0; @@ -370,8 +372,9 @@ gst_omx_component_handle_messages (GstOMXComponent * comp) } else { /* Output buffer contains output now or * the port was flushed */ - GST_LOG_OBJECT (comp->parent, "%s port %u filled buffer %p (%p)", - comp->name, port->index, buf, buf->omx_buf->pBuffer); + GST_LOG_OBJECT (port->comp->parent, + "%s port %u filled buffer %p (%p)", port->comp->name, port->index, + buf, buf->omx_buf->pBuffer); if ((buf->omx_buf->nFlags & OMX_BUFFERFLAG_EOS) && port->port_def.eDir == OMX_DirOutput) @@ -409,6 +412,46 @@ gst_omx_component_send_message (GstOMXComponent * comp, GstOMXMessage * msg) g_mutex_unlock (&comp->messages_lock); } +/* NOTE: Call with comp->lock, comp->messages_lock will be used */ +static gboolean +gst_omx_component_wait_message (GstOMXComponent * comp, GstClockTime timeout) +{ + gboolean signalled; + gint64 wait_until = -1; + + if (timeout != GST_CLOCK_TIME_NONE) { + gint64 add = timeout / (GST_SECOND / G_TIME_SPAN_SECOND); + + if (add == 0) + return FALSE; + + wait_until = g_get_monotonic_time () + add; + GST_DEBUG_OBJECT (comp->parent, "%s waiting for %" G_GINT64_FORMAT "us", + comp->name, add); + } else { + GST_DEBUG_OBJECT (comp->parent, "%s waiting for signal", comp->name); + } + + g_mutex_lock (&comp->messages_lock); + g_mutex_unlock (&comp->lock); + + if (!g_queue_is_empty (&comp->messages)) { + signalled = TRUE; + } else if (timeout == GST_CLOCK_TIME_NONE) { + g_cond_wait (&comp->messages_cond, &comp->messages_lock); + signalled = TRUE; + } else { + signalled = + g_cond_wait_until (&comp->messages_cond, &comp->messages_lock, + wait_until); + } + + g_mutex_unlock (&comp->messages_lock); + g_mutex_lock (&comp->lock); + + return signalled; +} + static OMX_ERRORTYPE EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData) @@ -443,7 +486,7 @@ EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, msg->type = GST_OMX_MESSAGE_FLUSH; msg->content.flush.port = nData2; GST_DEBUG_OBJECT (comp->parent, "%s port %u flushed", comp->name, - msg->content.flush.port); + (guint) msg->content.flush.port); gst_omx_component_send_message (comp, msg); break; @@ -456,7 +499,7 @@ EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, msg->content.port_enable.port = nData2; msg->content.port_enable.enable = (cmd == OMX_CommandPortEnable); GST_DEBUG_OBJECT (comp->parent, "%s port %u %s", comp->name, - msg->content.port_enable.port, + (guint) msg->content.port_enable.port, (msg->content.port_enable.enable ? "enabled" : "disabled")); gst_omx_component_send_message (comp, msg); @@ -472,7 +515,7 @@ EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, GstOMXMessage *msg; /* Yes, this really happens... */ - if (nData2 == OMX_ErrorNone) + if (nData1 == OMX_ErrorNone) break; msg = g_slice_new (GstOMXMessage); @@ -507,8 +550,8 @@ EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, msg->type = GST_OMX_MESSAGE_PORT_SETTINGS_CHANGED; msg->content.port_settings_changed.port = index; - GST_DEBUG_OBJECT (comp->parent, "%s settings changed (port index: %d)", - comp->name, msg->content.port_settings_changed.port); + GST_DEBUG_OBJECT (comp->parent, "%s settings changed (port index: %u)", + comp->name, (guint) msg->content.port_settings_changed.port); gst_omx_component_send_message (comp, msg); break; @@ -522,8 +565,8 @@ EventHandler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, msg->content.buffer_flag.port = nData1; msg->content.buffer_flag.flags = nData2; GST_DEBUG_OBJECT (comp->parent, "%s port %u got buffer flags 0x%08x", - comp->name, msg->content.buffer_flag.port, - msg->content.buffer_flag.flags); + comp->name, (guint) msg->content.buffer_flag.port, + (guint) msg->content.buffer_flag.flags); gst_omx_component_send_message (comp, msg); break; @@ -802,7 +845,6 @@ OMX_STATETYPE gst_omx_component_get_state (GstOMXComponent * comp, GstClockTime timeout) { OMX_STATETYPE ret; - gint64 wait_until = -1; gboolean signalled = TRUE; g_return_val_if_fail (comp != NULL, OMX_StateInvalid); @@ -825,37 +867,10 @@ gst_omx_component_get_state (GstOMXComponent * comp, GstClockTime timeout) goto done; } - if (timeout != GST_CLOCK_TIME_NONE) { - gint64 add = timeout / (GST_SECOND / G_TIME_SPAN_SECOND); - - if (add == 0) - goto done; - - wait_until = g_get_monotonic_time () + add; - GST_DEBUG_OBJECT (comp->parent, "%s waiting for %" G_GINT64_FORMAT "us", - comp->name, add); - } else { - GST_DEBUG_OBJECT (comp->parent, "%s waiting for signal", comp->name); - } - - gst_omx_component_handle_messages (comp); while (signalled && comp->last_error == OMX_ErrorNone && comp->pending_state != OMX_StateInvalid) { - g_mutex_lock (&comp->messages_lock); - g_mutex_unlock (&comp->lock); - if (!g_queue_is_empty (&comp->messages)) { - signalled = TRUE; - } - if (timeout == GST_CLOCK_TIME_NONE) { - g_cond_wait (&comp->messages_cond, &comp->messages_lock); - signalled = TRUE; - } else { - signalled = - g_cond_wait_until (&comp->messages_cond, &comp->messages_lock, - wait_until); - } - g_mutex_unlock (&comp->messages_lock); - g_mutex_lock (&comp->lock); + + signalled = gst_omx_component_wait_message (comp, timeout); if (signalled) gst_omx_component_handle_messages (comp); }; @@ -1063,23 +1078,22 @@ gst_omx_component_set_config (GstOMXComponent * comp, OMX_INDEXTYPE index, } OMX_ERRORTYPE -gst_omx_component_setup_tunnel (GstOMXComponent * comp1, GstOMXPort * port1, - GstOMXComponent * comp2, GstOMXPort * port2) +gst_omx_setup_tunnel (GstOMXPort * port1, GstOMXPort * port2) { + GstOMXComponent *comp1; + GstOMXComponent *comp2; OMX_ERRORTYPE err; - g_return_val_if_fail (comp1 != NULL, OMX_ErrorUndefined); - g_return_val_if_fail (comp1->state == OMX_StateLoaded - || !port1->port_def.bEnabled, OMX_ErrorUndefined); g_return_val_if_fail (port1 != NULL, OMX_ErrorUndefined); g_return_val_if_fail (port1->port_def.eDir == OMX_DirOutput, OMX_ErrorUndefined); - g_return_val_if_fail (comp2 != NULL, OMX_ErrorUndefined); - g_return_val_if_fail (comp2->state == OMX_StateLoaded - || !port2->port_def.bEnabled, OMX_ErrorUndefined); + comp1 = port1->comp; + g_return_val_if_fail (port2 != NULL, OMX_ErrorUndefined); g_return_val_if_fail (port2->port_def.eDir == OMX_DirInput, OMX_ErrorUndefined); + comp2 = port2->comp; + g_return_val_if_fail (comp1->core == comp2->core, OMX_ErrorUndefined); g_mutex_lock (&comp1->lock); @@ -1108,23 +1122,22 @@ gst_omx_component_setup_tunnel (GstOMXComponent * comp1, GstOMXPort * port1, } OMX_ERRORTYPE -gst_omx_component_close_tunnel (GstOMXComponent * comp1, GstOMXPort * port1, - GstOMXComponent * comp2, GstOMXPort * port2) +gst_omx_close_tunnel (GstOMXPort * port1, GstOMXPort * port2) { + GstOMXComponent *comp1; + GstOMXComponent *comp2; OMX_ERRORTYPE err; - g_return_val_if_fail (comp1 != NULL, OMX_ErrorUndefined); - g_return_val_if_fail (comp1->state == OMX_StateLoaded - || !port1->port_def.bEnabled, OMX_ErrorUndefined); g_return_val_if_fail (port1 != NULL, OMX_ErrorUndefined); g_return_val_if_fail (port1->port_def.eDir == OMX_DirOutput, OMX_ErrorUndefined); - g_return_val_if_fail (comp2 != NULL, OMX_ErrorUndefined); - g_return_val_if_fail (comp2->state == OMX_StateLoaded - || !port2->port_def.bEnabled, OMX_ErrorUndefined); + comp1 = port1->comp; + g_return_val_if_fail (port2 != NULL, OMX_ErrorUndefined); g_return_val_if_fail (port2->port_def.eDir == OMX_DirInput, OMX_ErrorUndefined); + comp2 = port2->comp; + g_return_val_if_fail (comp1->core == comp2->core, OMX_ErrorUndefined); g_return_val_if_fail (port1->tunneled && port2->tunneled, OMX_ErrorUndefined); @@ -1184,7 +1197,7 @@ OMX_ERRORTYPE gst_omx_port_update_port_definition (GstOMXPort * port, OMX_PARAM_PORTDEFINITIONTYPE * port_def) { - OMX_ERRORTYPE err = OMX_ErrorNone; + OMX_ERRORTYPE err_get, err_set = OMX_ErrorNone; GstOMXComponent *comp; g_return_val_if_fail (port != NULL, FALSE); @@ -1192,16 +1205,19 @@ gst_omx_port_update_port_definition (GstOMXPort * port, comp = port->comp; if (port_def) - err = + err_set = gst_omx_component_set_parameter (comp, OMX_IndexParamPortDefinition, port_def); - gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition, + err_get = gst_omx_component_get_parameter (comp, OMX_IndexParamPortDefinition, &port->port_def); GST_DEBUG_OBJECT (comp->parent, "Updated %s port %u definition: %s (0x%08x)", - comp->name, port->index, gst_omx_error_to_string (err), err); + comp->name, port->index, gst_omx_error_to_string (err_set), err_set); - return err; + if (err_set != OMX_ErrorNone) + return err_set; + else + return err_get; } /* NOTE: Uses comp->lock and comp->messages_lock */ @@ -1254,12 +1270,7 @@ retry: (err = comp->last_error) == OMX_ErrorNone && !port->flushing) { GST_DEBUG_OBJECT (comp->parent, "Waiting for %s output ports to reconfigure", comp->name); - g_mutex_lock (&comp->messages_lock); - g_mutex_unlock (&comp->lock); - if (g_queue_is_empty (&comp->messages)) - g_cond_wait (&comp->messages_cond, &comp->messages_lock); - g_mutex_unlock (&comp->messages_lock); - g_mutex_lock (&comp->lock); + gst_omx_component_wait_message (comp, GST_CLOCK_TIME_NONE); gst_omx_component_handle_messages (comp); } goto retry; @@ -1318,30 +1329,19 @@ retry: * arrives, an error happens, the port is flushing * or the port needs to be reconfigured. */ - gst_omx_component_handle_messages (comp); if (g_queue_is_empty (&port->pending_buffers)) { GST_DEBUG_OBJECT (comp->parent, "Queue of %s port %u is empty", comp->name, port->index); - g_mutex_lock (&comp->messages_lock); - g_mutex_unlock (&comp->lock); - if (g_queue_is_empty (&comp->messages)) - g_cond_wait (&comp->messages_cond, &comp->messages_lock); - g_mutex_unlock (&comp->messages_lock); - g_mutex_lock (&comp->lock); - gst_omx_component_handle_messages (comp); + gst_omx_component_wait_message (comp, GST_CLOCK_TIME_NONE); /* And now check everything again and maybe get a buffer */ goto retry; - } else { - GST_DEBUG_OBJECT (comp->parent, "%s port %u has pending buffers", - comp->name, port->index); - _buf = g_queue_pop_head (&port->pending_buffers); - ret = GST_OMX_ACQUIRE_BUFFER_OK; - goto done; } - g_assert_not_reached (); - goto retry; + GST_DEBUG_OBJECT (comp->parent, "%s port %u has pending buffers", + comp->name, port->index); + _buf = g_queue_pop_head (&port->pending_buffers); + ret = GST_OMX_ACQUIRE_BUFFER_OK; done: g_mutex_unlock (&comp->lock); @@ -1399,9 +1399,10 @@ gst_omx_port_release_buffer (GstOMXPort * port, GstOMXBuffer * buf) goto done; } - if (port->flushing) { - GST_DEBUG_OBJECT (comp->parent, "%s port %u is flushing, not releasing " - "buffer", comp->name, port->index); + if (port->flushing || port->disabled_pending || !port->port_def.bEnabled) { + GST_DEBUG_OBJECT (comp->parent, + "%s port %u is flushing or disabled, not releasing " "buffer", + comp->name, port->index); g_queue_push_tail (&port->pending_buffers, buf); gst_omx_component_send_message (comp, NULL); goto done; @@ -1414,10 +1415,8 @@ gst_omx_port_release_buffer (GstOMXPort * port, GstOMXBuffer * buf) buf->used = TRUE; if (port->port_def.eDir == OMX_DirInput) { - GST_LOG_OBJECT (comp->parent,"Calling OMX_EmptyThisBuffer. BufHeader:[%p]\n",buf->omx_buf); err = OMX_EmptyThisBuffer (comp->handle, buf->omx_buf); } else { - GST_LOG_OBJECT (comp->parent,"Calling OMX_FillThisBuffer. BufHeader:[%p]\n",buf->omx_buf); err = OMX_FillThisBuffer (comp->handle, buf->omx_buf); } GST_DEBUG_OBJECT (comp->parent, "Released buffer %p to %s port %u: %s " @@ -1464,7 +1463,6 @@ gst_omx_port_set_flushing (GstOMXPort * port, GstClockTime timeout, port->flushing = flush; if (flush) { - gint64 wait_until = -1; gboolean signalled; OMX_ERRORTYPE last_error; @@ -1495,22 +1493,12 @@ gst_omx_port_set_flushing (GstOMXPort * port, GstClockTime timeout, goto done; } - if (timeout != GST_CLOCK_TIME_NONE) { - gint64 add = timeout / (GST_SECOND / G_TIME_SPAN_SECOND); - - if (add == 0) { - if (!port->flushed || (port->buffers - && port->buffers->len > - g_queue_get_length (&port->pending_buffers))) - err = OMX_ErrorTimeout; - goto done; - } - - wait_until = g_get_monotonic_time () + add; - GST_DEBUG_OBJECT (comp->parent, "%s waiting for %" G_GINT64_FORMAT "us", - comp->name, add); - } else { - GST_DEBUG_OBJECT (comp->parent, "%s waiting for signal", comp->name); + if (timeout == 0) { + if (!port->flushed || (port->buffers + && port->buffers->len > + g_queue_get_length (&port->pending_buffers))) + err = OMX_ErrorTimeout; + goto done; } /* Retry until timeout or until an error happend or @@ -1522,24 +1510,7 @@ gst_omx_port_set_flushing (GstOMXPort * port, GstClockTime timeout, while (signalled && last_error == OMX_ErrorNone && !port->flushed && port->buffers && port->buffers->len > g_queue_get_length (&port->pending_buffers)) { - g_mutex_lock (&comp->messages_lock); - g_mutex_unlock (&comp->lock); - - if (!g_queue_is_empty (&comp->messages)) { - signalled = TRUE; - } - if (timeout == GST_CLOCK_TIME_NONE) { - g_cond_wait (&comp->messages_cond, &comp->messages_lock); - signalled = TRUE; - } else { - signalled = - g_cond_wait_until (&comp->messages_cond, &comp->messages_lock, - wait_until); - } - - g_mutex_unlock (&comp->messages_lock); - g_mutex_lock (&comp->lock); - + signalled = gst_omx_component_wait_message (comp, timeout); if (signalled) gst_omx_component_handle_messages (comp); @@ -1642,8 +1613,8 @@ gst_omx_port_allocate_buffers_unlocked (GstOMXPort * port, OMX_ErrorBadParameter); GST_INFO_OBJECT (comp->parent, - "Allocating %d buffers of size %u for %s port %u", n, - port->port_def.nBufferSize, comp->name, port->index); + "Allocating %d buffers of size %" G_GSIZE_FORMAT " for %s port %u", n, + (size_t) port->port_def.nBufferSize, comp->name, (guint) port->index); if (!port->buffers) port->buffers = g_ptr_array_sized_new (n); @@ -1668,7 +1639,6 @@ gst_omx_port_allocate_buffers_unlocked (GstOMXPort * port, g_ptr_array_add (port->buffers, buf); if (buffers) { - err = OMX_UseBuffer (comp->handle, &buf->omx_buf, port->index, buf, port->port_def.nBufferSize, l->data); @@ -1699,10 +1669,7 @@ gst_omx_port_allocate_buffers_unlocked (GstOMXPort * port, g_assert (buf->omx_buf->pAppPrivate == buf); /* In the beginning all buffers are not owned by the component */ - GST_DEBUG_OBJECT (comp->parent, "Pushing pending_buffers, port:[%d], Buffer:[%p]", - port->index, buf); g_queue_push_tail (&port->pending_buffers, buf); - if (buffers || images) l = l->next; } @@ -1770,7 +1737,7 @@ gst_omx_port_tbm_allocate_dec_buffers (tbm_bufmgr bufMgr, GstOMXPort * port, int ptr->handle.dmabuf_fd[0] = gst_omx_tbm_get_bo_fd(ptr->handle.bo[0]); ptr->data[0] = gst_omx_tbm_get_bo_ptr(ptr->handle.bo[0]); ptr->size[0] = port->port_def.nBufferSize; - ptr->type = MM_VIDEO_BUFFER_TYPE_PHYSICAL_ADDRESS; + ptr->type = MM_VIDEO_BUFFER_TYPE_TBM_BO; } else { /* output port */ @@ -1867,7 +1834,7 @@ gst_omx_port_tbm_allocate_enc_buffers (tbm_bufmgr bufMgr, GstOMXPort * port, int ptr->handle.bo[0] = gst_omx_tbm_allocate_bo(bufMgr, port->port_def.nBufferSize); ptr->handle.dmabuf_fd[0] = gst_omx_tbm_get_bo_fd(ptr->handle.bo[0]); ptr->handle.paddr[0] = gst_omx_tbm_get_bo_ptr(ptr->handle.bo[0]); - ptr->type = MM_VIDEO_BUFFER_TYPE_PHYSICAL_ADDRESS; + ptr->type = MM_VIDEO_BUFFER_TYPE_TBM_BO; ptr->size[0] = port->port_def.nBufferSize; ptr->handle_num = 1; } @@ -2076,13 +2043,6 @@ gst_omx_port_set_enabled_unlocked (GstOMXPort * port, gboolean enabled) else port->disabled_pending = TRUE; - if (!enabled) { - /* This is also like flushing, i.e. all buffers are returned - * by the component and no new buffers should be passed to - * the component anymore */ - port->flushing = TRUE; - } - if (enabled) err = OMX_SendCommand (comp->handle, OMX_CommandPortEnable, port->index, @@ -2124,7 +2084,6 @@ gst_omx_port_wait_buffers_released_unlocked (GstOMXPort * port, GstOMXComponent *comp; OMX_ERRORTYPE err = OMX_ErrorNone; OMX_ERRORTYPE last_error; - gint64 wait_until = -1; gboolean signalled; comp = port->comp; @@ -2140,21 +2099,12 @@ gst_omx_port_wait_buffers_released_unlocked (GstOMXPort * port, GST_INFO_OBJECT (comp->parent, "Waiting for %s port %u to release all " "buffers", comp->name, port->index); - if (timeout != GST_CLOCK_TIME_NONE) { - gint64 add = timeout / (GST_SECOND / G_TIME_SPAN_SECOND); - - if (add == 0) { - if (port->buffers - && port->buffers->len > g_queue_get_length (&port->pending_buffers)) - err = OMX_ErrorTimeout; - goto done; - } - - wait_until = g_get_monotonic_time () + add; - GST_DEBUG_OBJECT (comp->parent, "%s waiting for %" G_GINT64_FORMAT "us", - comp->name, add); - } else { - GST_DEBUG_OBJECT (comp->parent, "%s waiting for signal", comp->name); + if (timeout == 0) { + if (!port->flushed || (port->buffers + && port->buffers->len > + g_queue_get_length (&port->pending_buffers))) + err = OMX_ErrorTimeout; + goto done; } /* Wait until all buffers are released by the port */ @@ -2164,20 +2114,7 @@ gst_omx_port_wait_buffers_released_unlocked (GstOMXPort * port, while (signalled && last_error == OMX_ErrorNone && (port->buffers && port->buffers->len > g_queue_get_length (&port->pending_buffers))) { - g_mutex_lock (&comp->messages_lock); - g_mutex_unlock (&comp->lock); - if (!g_queue_is_empty (&comp->messages)) { - signalled = TRUE; - } else if (timeout != GST_CLOCK_TIME_NONE) { - signalled = - g_cond_wait_until (&comp->messages_cond, &comp->messages_lock, - wait_until); - } else { - signalled = TRUE; - g_cond_wait (&comp->messages_cond, &comp->messages_lock); - } - g_mutex_unlock (&comp->messages_lock); - g_mutex_lock (&comp->lock); + signalled = gst_omx_component_wait_message (comp, timeout); if (signalled) gst_omx_component_handle_messages (comp); last_error = comp->last_error; @@ -2255,9 +2192,9 @@ gst_omx_port_populate_unlocked (GstOMXPort * port) gst_omx_component_handle_messages (comp); - if (port->flushing) { - GST_DEBUG_OBJECT (comp->parent, "%s port %u is flushing", comp->name, - port->index); + if (port->flushing || port->disabled_pending || !port->port_def.bEnabled) { + GST_DEBUG_OBJECT (comp->parent, "%s port %u is flushing or disabled", + comp->name, port->index); err = OMX_ErrorIncorrectStateOperation; goto done; } @@ -2271,9 +2208,6 @@ gst_omx_port_populate_unlocked (GstOMXPort * port) if (port->port_def.eDir == OMX_DirOutput && port->buffers && !port->tunneled) { /* Enqueue all buffers for the component to fill */ while ((buf = g_queue_pop_head (&port->pending_buffers))) { - if (!buf) - continue; - g_assert (!buf->used); /* Reset all flags, some implementations don't @@ -2281,7 +2215,7 @@ gst_omx_port_populate_unlocked (GstOMXPort * port) * valid anymore after the buffer was consumed */ buf->omx_buf->nFlags = 0; - GST_ERROR_OBJECT(comp->parent,"Calling OMX_FillThisBuffer. buffer[%p]. function:[%s]",buf->omx_buf,__func__); + err = OMX_FillThisBuffer (comp->handle, buf->omx_buf); if (err != OMX_ErrorNone) { @@ -2327,7 +2261,6 @@ gst_omx_port_wait_enabled_unlocked (GstOMXPort * port, GstClockTime timeout) { GstOMXComponent *comp; OMX_ERRORTYPE err = OMX_ErrorNone; - gint64 wait_until = -1; gboolean signalled; OMX_ERRORTYPE last_error; gboolean enabled; @@ -2355,20 +2288,10 @@ gst_omx_port_wait_enabled_unlocked (GstOMXPort * port, GstClockTime timeout) GST_INFO_OBJECT (comp->parent, "Waiting for %s port %u to be %s", comp->name, port->index, (enabled ? "enabled" : "disabled")); - if (timeout != GST_CLOCK_TIME_NONE) { - gint64 add = timeout / (GST_SECOND / G_TIME_SPAN_SECOND); - - if (add == 0) { - if (port->enabled_pending || port->disabled_pending) - err = OMX_ErrorTimeout; - goto done; - } - - wait_until = g_get_monotonic_time () + add; - GST_DEBUG_OBJECT (comp->parent, "%s waiting for %" G_GINT64_FORMAT "us", - comp->name, add); - } else { - GST_DEBUG_OBJECT (comp->parent, "%s waiting for signal", comp->name); + if (timeout == 0) { + if (port->enabled_pending || port->disabled_pending) + err = OMX_ErrorTimeout; + goto done; } /* And now wait until the enable/disable command is finished */ @@ -2379,20 +2302,7 @@ gst_omx_port_wait_enabled_unlocked (GstOMXPort * port, GstClockTime timeout) while (signalled && last_error == OMX_ErrorNone && (! !port->port_def.bEnabled != ! !enabled || port->enabled_pending || port->disabled_pending)) { - g_mutex_lock (&comp->messages_lock); - g_mutex_unlock (&comp->lock); - if (!g_queue_is_empty (&comp->messages)) { - signalled = TRUE; - } else if (timeout != GST_CLOCK_TIME_NONE) { - signalled = - g_cond_wait_until (&comp->messages_cond, &comp->messages_lock, - wait_until); - } else { - signalled = TRUE; - g_cond_wait (&comp->messages_cond, &comp->messages_lock); - } - g_mutex_unlock (&comp->messages_lock); - g_mutex_lock (&comp->lock); + signalled = gst_omx_component_wait_message (comp, timeout); if (signalled) gst_omx_component_handle_messages (comp); last_error = comp->last_error; @@ -2415,7 +2325,6 @@ gst_omx_port_wait_enabled_unlocked (GstOMXPort * port, GstClockTime timeout) err = last_error; } else { if (enabled) { - port->flushing = FALSE; /* Reset EOS flag */ port->eos = FALSE; } @@ -2451,18 +2360,15 @@ gst_omx_port_wait_enabled (GstOMXPort * port, GstClockTime timeout) gboolean gst_omx_port_is_enabled (GstOMXPort * port) { - GstOMXComponent *comp; gboolean enabled; g_return_val_if_fail (port != NULL, FALSE); - comp = port->comp; - gst_omx_port_update_port_definition (port, NULL); enabled = ! !port->port_def.bEnabled; - GST_DEBUG_OBJECT (comp->parent, "%s port %u is enabled: %d", comp->name, - port->index, enabled); + GST_DEBUG_OBJECT (port->comp->parent, "%s port %u is enabled: %d", + port->comp->name, port->index, enabled); return enabled; } @@ -2517,11 +2423,14 @@ done: typedef GType (*GGetTypeFunction) (void); static const GGetTypeFunction types[] = { + gst_omx_analog_audio_sink_get_type, gst_omx_hdmi_audio_sink_get_type, gst_omx_mpeg2_video_dec_get_type, gst_omx_mpeg4_video_dec_get_type, gst_omx_h264_dec_get_type, gst_omx_h263_dec_get_type, gst_omx_wmv_dec_get_type, gst_omx_mpeg4_video_enc_get_type, gst_omx_h264_enc_get_type, gst_omx_h263_enc_get_type, - gst_omx_aac_enc_get_type, gst_omx_mjpeg_dec_get_type + gst_omx_aac_enc_get_type, gst_omx_mjpeg_dec_get_type, + gst_omx_aac_dec_get_type, gst_omx_mp3_dec_get_type, + gst_omx_amr_dec_get_type #ifdef HAVE_VP8 , gst_omx_vp8_dec_get_type #endif @@ -2537,8 +2446,10 @@ struct TypeOffest }; static const struct TypeOffest base_types[] = { + {gst_omx_audio_sink_get_type, G_STRUCT_OFFSET (GstOMXAudioSinkClass, cdata)}, {gst_omx_video_dec_get_type, G_STRUCT_OFFSET (GstOMXVideoDecClass, cdata)}, {gst_omx_video_enc_get_type, G_STRUCT_OFFSET (GstOMXVideoEncClass, cdata)}, + {gst_omx_audio_dec_get_type, G_STRUCT_OFFSET (GstOMXAudioDecClass, cdata)}, {gst_omx_audio_enc_get_type, G_STRUCT_OFFSET (GstOMXAudioEncClass, cdata)}, }; @@ -2552,7 +2463,9 @@ gst_omx_get_configuration (void) const gchar * gst_omx_error_to_string (OMX_ERRORTYPE err) { - switch (err) { + guint err_u = (guint) err; + + switch (err_u) { case OMX_ErrorNone: return "None"; case OMX_ErrorInsufficientResources: @@ -2630,11 +2543,11 @@ gst_omx_error_to_string (OMX_ERRORTYPE err) case OMX_ErrorTunnelingUnsupported: return "Tunneling unsupported"; default: - if (err >= (guint32) OMX_ErrorKhronosExtensions - && err < (guint32) OMX_ErrorVendorStartUnused) { + if (err_u >= (guint) OMX_ErrorKhronosExtensions + && err_u < (guint) OMX_ErrorVendorStartUnused) { return "Khronos extension error"; - } else if (err >= (guint32) OMX_ErrorVendorStartUnused - && err < (guint32) OMX_ErrorMax) { + } else if (err_u >= (guint) OMX_ErrorVendorStartUnused + && err_u < (guint) OMX_ErrorMax) { return "Vendor specific error"; } else { return "Unknown error"; @@ -2659,9 +2572,10 @@ gst_omx_state_to_string (OMX_STATETYPE state) case OMX_StateWaitForResources: return "WaitForResources"; default: - if (state >= OMX_StateKhronosExtensions) + if (state >= OMX_StateKhronosExtensions + && state < OMX_StateVendorStartUnused) return "KhronosExtensionState"; - else if (state >= OMX_StateVendorStartUnused) + else if (state >= OMX_StateVendorStartUnused && state < OMX_StateMax) return "CustomVendorState"; break; } @@ -2683,9 +2597,10 @@ gst_omx_command_to_string (OMX_COMMANDTYPE cmd) case OMX_CommandMarkBuffer: return "MarkBuffer"; default: - if (cmd >= OMX_CommandKhronosExtensions) + if (cmd >= OMX_CommandKhronosExtensions + && cmd < OMX_CommandVendorStartUnused) return "KhronosExtensionCommand"; - else if (cmd >= OMX_CommandVendorStartUnused) + if (cmd >= OMX_CommandVendorStartUnused && cmd < OMX_CommandMax) return "VendorExtensionCommand"; break; } @@ -2725,6 +2640,8 @@ gst_omx_parse_hacks (gchar ** hacks) hacks_flags |= GST_OMX_HACK_DRAIN_MAY_NOT_RETURN; else if (g_str_equal (*hacks, "no-component-role")) hacks_flags |= GST_OMX_HACK_NO_COMPONENT_ROLE; + else if (g_str_equal (*hacks, "no-disable-outport")) + hacks_flags |= GST_OMX_HACK_NO_DISABLE_OUTPORT; else GST_WARNING ("Unknown hack: %s", *hacks); hacks++; @@ -2967,52 +2884,57 @@ _class_init (gpointer g_class, gpointer data) /* Add pad templates */ err = NULL; - if (!(template_caps = - g_key_file_get_string (config, element_name, "sink-template-caps", - &err))) { - GST_DEBUG - ("No sink template caps specified for element '%s', using default '%s'", - element_name, class_data->default_sink_template_caps); - caps = gst_caps_from_string (class_data->default_sink_template_caps); - g_assert (caps != NULL); - g_error_free (err); - } else { - caps = gst_caps_from_string (template_caps); - if (!caps) { + if (class_data->type != GST_OMX_COMPONENT_TYPE_SOURCE) { + if (!(template_caps = + g_key_file_get_string (config, element_name, "sink-template-caps", + &err))) { GST_DEBUG - ("Could not parse sink template caps '%s' for element '%s', using default '%s'", - template_caps, element_name, class_data->default_sink_template_caps); + ("No sink template caps specified for element '%s', using default '%s'", + element_name, class_data->default_sink_template_caps); caps = gst_caps_from_string (class_data->default_sink_template_caps); g_assert (caps != NULL); + g_error_free (err); + } else { + caps = gst_caps_from_string (template_caps); + if (!caps) { + GST_DEBUG + ("Could not parse sink template caps '%s' for element '%s', using default '%s'", + template_caps, element_name, + class_data->default_sink_template_caps); + caps = gst_caps_from_string (class_data->default_sink_template_caps); + g_assert (caps != NULL); + } } + templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps); + g_free (template_caps); + gst_element_class_add_pad_template (element_class, templ); } - templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps); - g_free (template_caps); - gst_element_class_add_pad_template (element_class, templ); err = NULL; - if (!(template_caps = - g_key_file_get_string (config, element_name, "src-template-caps", - &err))) { - GST_DEBUG - ("No src template caps specified for element '%s', using default '%s'", - element_name, class_data->default_src_template_caps); - caps = gst_caps_from_string (class_data->default_src_template_caps); - g_assert (caps != NULL); - g_error_free (err); - } else { - caps = gst_caps_from_string (template_caps); - if (!caps) { + if (class_data->type != GST_OMX_COMPONENT_TYPE_SINK) { + if (!(template_caps = + g_key_file_get_string (config, element_name, "src-template-caps", + &err))) { GST_DEBUG - ("Could not parse src template caps '%s' for element '%s', using default '%s'", - template_caps, element_name, class_data->default_src_template_caps); + ("No src template caps specified for element '%s', using default '%s'", + element_name, class_data->default_src_template_caps); caps = gst_caps_from_string (class_data->default_src_template_caps); g_assert (caps != NULL); + g_error_free (err); + } else { + caps = gst_caps_from_string (template_caps); + if (!caps) { + GST_DEBUG + ("Could not parse src template caps '%s' for element '%s', using default '%s'", + template_caps, element_name, class_data->default_src_template_caps); + caps = gst_caps_from_string (class_data->default_src_template_caps); + g_assert (caps != NULL); + } } + templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps); + g_free (template_caps); + gst_element_class_add_pad_template (element_class, templ); } - templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps); - g_free (template_caps); - gst_element_class_add_pad_template (element_class, templ); if ((hacks = g_key_file_get_string_list (config, element_name, "hacks", NULL, @@ -3046,6 +2968,8 @@ plugin_init (GstPlugin * plugin) static const gchar *env_config_name[] = { "GST_OMX_CONFIG_DIR", NULL }; GST_DEBUG_CATEGORY_INIT (gstomx_debug, "omx", 0, "gst-omx"); + GST_DEBUG_CATEGORY_INIT (gst_omx_video_debug_category, "omxvideo", 0, + "gst-omx-video"); /* Read configuration file gstomx.conf from the preferred * configuration directories */ diff --git a/omx/gstomx.h b/omx/gstomx.h index b53bc7d..eca0f73 100644 --- a/omx/gstomx.h +++ b/omx/gstomx.h @@ -49,9 +49,20 @@ #include #include +#ifdef USE_OMX_TARGET_EXYNOS #include #include #include +#endif + +#ifdef USE_OMX_TARGET_RPI +#include +#endif + +#ifdef HAVE_VIDEO_EXT +#include +#endif + #ifdef GST_OMX_STRUCT_PACKING #pragma pack() #endif @@ -107,6 +118,11 @@ G_BEGIN_DECLS */ #define GST_OMX_HACK_NO_COMPONENT_ROLE G_GUINT64_CONSTANT (0x0000000000000080) +/* If the component doesn't allow disabling the outport while + * when setting the format until the output format is known. + */ +#define GST_OMX_HACK_NO_DISABLE_OUTPORT G_GUINT64_CONSTANT (0x0000000000000100) + typedef struct _GstOMXCore GstOMXCore; typedef struct _GstOMXPort GstOMXPort; typedef enum _GstOMXPortDirection GstOMXPortDirection; @@ -300,6 +316,12 @@ typedef enum { GST_OMX_MESSAGE_BUFFER_DONE, } GstOMXMessageType; +typedef enum { + GST_OMX_COMPONENT_TYPE_SINK, + GST_OMX_COMPONENT_TYPE_SOURCE, + GST_OMX_COMPONENT_TYPE_FILTER +} GstOmxComponentType; + struct _GstOMXMessage { GstOMXMessageType type; @@ -426,6 +448,8 @@ struct _GstOMXClassData { guint32 in_port_index, out_port_index; guint64 hacks; + + GstOmxComponentType type; }; GKeyFile * gst_omx_get_configuration (void); @@ -457,8 +481,9 @@ OMX_ERRORTYPE gst_omx_component_set_parameter (GstOMXComponent * comp, OMX_I OMX_ERRORTYPE gst_omx_component_get_config (GstOMXComponent * comp, OMX_INDEXTYPE index, gpointer config); OMX_ERRORTYPE gst_omx_component_set_config (GstOMXComponent * comp, OMX_INDEXTYPE index, gpointer config); -OMX_ERRORTYPE gst_omx_component_setup_tunnel (GstOMXComponent * comp1, GstOMXPort * port1, GstOMXComponent * comp2, GstOMXPort * port2); -OMX_ERRORTYPE gst_omx_component_close_tunnel (GstOMXComponent * comp1, GstOMXPort * port1, GstOMXComponent * comp2, GstOMXPort * port2); + +OMX_ERRORTYPE gst_omx_setup_tunnel (GstOMXPort * port1, GstOMXPort * port2); +OMX_ERRORTYPE gst_omx_close_tunnel (GstOMXPort * port1, GstOMXPort * port2); OMX_ERRORTYPE gst_omx_port_get_port_definition (GstOMXPort * port, OMX_PARAM_PORTDEFINITIONTYPE * port_def); @@ -492,15 +517,15 @@ void gst_omx_set_default_role (GstOMXClassData *class_data, const g #ifdef USE_TBM -/*MFC Buffer alignment macros*/ -#define S5P_FIMV_DEC_BUF_ALIGN (8 * 1024) -#define S5P_FIMV_ENC_BUF_ALIGN (8 * 1024) -#define S5P_FIMV_NV12M_HALIGN 16 -#define S5P_FIMV_NV12M_LVALIGN 16 -#define S5P_FIMV_NV12M_CVALIGN 8 -#define S5P_FIMV_NV12MT_HALIGN 128 -#define S5P_FIMV_NV12MT_VALIGN 64 -#define S5P_FIMV_NV12M_SALIGN 2048 +/*MFC Buffer alignment macros*/ +#define S5P_FIMV_DEC_BUF_ALIGN (8 * 1024) +#define S5P_FIMV_ENC_BUF_ALIGN (8 * 1024) +#define S5P_FIMV_NV12M_HALIGN 16 +#define S5P_FIMV_NV12M_LVALIGN 16 +#define S5P_FIMV_NV12M_CVALIGN 8 +#define S5P_FIMV_NV12MT_HALIGN 128 +#define S5P_FIMV_NV12MT_VALIGN 64 +#define S5P_FIMV_NV12M_SALIGN 2048 #define S5P_FIMV_NV12MT_SALIGN 8192 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) @@ -534,7 +559,8 @@ OMX_U32 gst_omx_tbm_get_bo_fd(tbm_bo bo); OMX_PTR gst_omx_tbm_get_bo_ptr(tbm_bo bo); #endif - +/* refered by plugin_init */ +GST_DEBUG_CATEGORY_EXTERN (gst_omx_video_debug_category); G_END_DECLS diff --git a/omx/gstomxaacdec.c b/omx/gstomxaacdec.c new file mode 100644 index 0000000..66d881d --- /dev/null +++ b/omx/gstomxaacdec.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2014, Sebastian Dröge + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstomxaacdec.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_aac_dec_debug_category); +#define GST_CAT_DEFAULT gst_omx_aac_dec_debug_category + +/* prototypes */ +static gboolean gst_omx_aac_dec_set_format (GstOMXAudioDec * dec, + GstOMXPort * port, GstCaps * caps); +static gboolean gst_omx_aac_dec_is_format_change (GstOMXAudioDec * dec, + GstOMXPort * port, GstCaps * caps); +static gint gst_omx_aac_dec_get_samples_per_frame (GstOMXAudioDec * dec, + GstOMXPort * port); +static gboolean gst_omx_aac_dec_get_channel_positions (GstOMXAudioDec * dec, + GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]); + +/* class initialization */ + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_omx_aac_dec_debug_category, "omxaacdec", 0, \ + "debug category for gst-omx aac audio decoder"); + +G_DEFINE_TYPE_WITH_CODE (GstOMXAACDec, gst_omx_aac_dec, + GST_TYPE_OMX_AUDIO_DEC, DEBUG_INIT); + +static void +gst_omx_aac_dec_class_init (GstOMXAACDecClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstOMXAudioDecClass *audiodec_class = GST_OMX_AUDIO_DEC_CLASS (klass); + + audiodec_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_aac_dec_set_format); + audiodec_class->is_format_change = + GST_DEBUG_FUNCPTR (gst_omx_aac_dec_is_format_change); + audiodec_class->get_samples_per_frame = + GST_DEBUG_FUNCPTR (gst_omx_aac_dec_get_samples_per_frame); + audiodec_class->get_channel_positions = + GST_DEBUG_FUNCPTR (gst_omx_aac_dec_get_channel_positions); + + audiodec_class->cdata.default_sink_template_caps = "audio/mpeg, " + "mpegversion=(int){2, 4}, " + "stream-format=(string) { raw, adts, adif, loas }, " + "rate=(int)[8000,48000], " + "channels=(int)[1,9], " "framed=(boolean) true"; + + gst_element_class_set_static_metadata (element_class, + "OpenMAX AAC Audio Decoder", + "Codec/Decoder/Audio", + "Decode AAC audio streams", + "Sebastian Dröge "); + + gst_omx_set_default_role (&audiodec_class->cdata, "audio_decoder.aac"); +} + +static void +gst_omx_aac_dec_init (GstOMXAACDec * self) +{ + /* FIXME: Other values exist too! */ + self->spf = 1024; +} + +static gboolean +gst_omx_aac_dec_set_format (GstOMXAudioDec * dec, GstOMXPort * port, + GstCaps * caps) +{ + GstOMXAACDec *self = GST_OMX_AAC_DEC (dec); + OMX_PARAM_PORTDEFINITIONTYPE port_def; + OMX_AUDIO_PARAM_AACPROFILETYPE aac_param; + OMX_ERRORTYPE err; + GstStructure *s; + gint rate, channels, mpegversion; + const gchar *stream_format; + + gst_omx_port_get_port_definition (port, &port_def); + port_def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + err = gst_omx_port_update_port_definition (port, &port_def); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to set AAC format on component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + GST_OMX_INIT_STRUCT (&aac_param); + aac_param.nPortIndex = port->index; + + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioAac, + &aac_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to get AAC parameters from component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "mpegversion", &mpegversion) || + !gst_structure_get_int (s, "rate", &rate) || + !gst_structure_get_int (s, "channels", &channels)) { + GST_ERROR_OBJECT (self, "Incomplete caps"); + return FALSE; + } + + stream_format = gst_structure_get_string (s, "stream-format"); + if (!stream_format) { + GST_ERROR_OBJECT (self, "Incomplete caps"); + return FALSE; + } + + aac_param.nChannels = channels; + aac_param.nSampleRate = rate; + aac_param.nBitRate = 0; /* unknown */ + aac_param.nAudioBandWidth = 0; /* decoder decision */ + aac_param.eChannelMode = 0; /* FIXME */ + if (mpegversion == 2) + aac_param.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP2ADTS; + else if (strcmp (stream_format, "adts") == 0) + aac_param.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; + else if (strcmp (stream_format, "loas") == 0) + aac_param.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4LOAS; + else if (strcmp (stream_format, "adif") == 0) + aac_param.eAACStreamFormat = OMX_AUDIO_AACStreamFormatADIF; + else if (strcmp (stream_format, "raw") == 0) + aac_param.eAACStreamFormat = OMX_AUDIO_AACStreamFormatRAW; + else { + GST_ERROR_OBJECT (self, "Unexpected format: %s", stream_format); + return FALSE; + } + + err = + gst_omx_component_set_parameter (dec->dec, OMX_IndexParamAudioAac, + &aac_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Error setting AAC parameters: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_omx_aac_dec_is_format_change (GstOMXAudioDec * dec, GstOMXPort * port, + GstCaps * caps) +{ + GstOMXAACDec *self = GST_OMX_AAC_DEC (dec); + OMX_AUDIO_PARAM_AACPROFILETYPE aac_param; + OMX_ERRORTYPE err; + GstStructure *s; + gint rate, channels, mpegversion; + const gchar *stream_format; + + GST_OMX_INIT_STRUCT (&aac_param); + aac_param.nPortIndex = port->index; + + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioAac, + &aac_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to get AAC parameters from component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "mpegversion", &mpegversion) || + !gst_structure_get_int (s, "rate", &rate) || + !gst_structure_get_int (s, "channels", &channels)) { + GST_ERROR_OBJECT (self, "Incomplete caps"); + return FALSE; + } + + stream_format = gst_structure_get_string (s, "stream-format"); + if (!stream_format) { + GST_ERROR_OBJECT (self, "Incomplete caps"); + return FALSE; + } + + if (aac_param.nChannels != channels) + return TRUE; + + if (aac_param.nSampleRate != rate) + return TRUE; + + if (mpegversion == 2 + && aac_param.eAACStreamFormat != OMX_AUDIO_AACStreamFormatMP2ADTS) + return TRUE; + if (aac_param.eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4ADTS && + strcmp (stream_format, "adts") != 0) + return TRUE; + if (aac_param.eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4LOAS && + strcmp (stream_format, "loas") != 0) + return TRUE; + if (aac_param.eAACStreamFormat == OMX_AUDIO_AACStreamFormatADIF && + strcmp (stream_format, "adif") != 0) + return TRUE; + if (aac_param.eAACStreamFormat == OMX_AUDIO_AACStreamFormatRAW && + strcmp (stream_format, "raw") != 0) + return TRUE; + + return FALSE; +} + +static gint +gst_omx_aac_dec_get_samples_per_frame (GstOMXAudioDec * dec, GstOMXPort * port) +{ + return GST_OMX_AAC_DEC (dec)->spf; +} + +static gboolean +gst_omx_aac_dec_get_channel_positions (GstOMXAudioDec * dec, + GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]) +{ + OMX_AUDIO_PARAM_PCMMODETYPE pcm_param; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (&pcm_param); + pcm_param.nPortIndex = port->index; + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioPcm, + &pcm_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (dec, "Failed to get PCM parameters: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + /* FIXME: Rather arbitrary values here, based on what we do in gstfaac.c */ + switch (pcm_param.nChannels) { + case 1: + position[0] = GST_AUDIO_CHANNEL_POSITION_MONO; + break; + case 2: + position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + break; + case 3: + position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; + position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + break; + case 4: + position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; + position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + position[3] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER; + break; + case 5: + position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; + position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + position[3] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT; + position[4] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT; + break; + case 6: + position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; + position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + position[3] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT; + position[4] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT; + position[5] = GST_AUDIO_CHANNEL_POSITION_LFE1; + break; + default: + return FALSE; + } + + return TRUE; +} diff --git a/omx/gstomxaacdec.h b/omx/gstomxaacdec.h new file mode 100644 index 0000000..891589b --- /dev/null +++ b/omx/gstomxaacdec.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014, Sebastian Dröge + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_AAC_DEC_H__ +#define __GST_OMX_AAC_DEC_H__ + +#include +#include "gstomxaudiodec.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_AAC_DEC \ + (gst_omx_aac_dec_get_type()) +#define GST_OMX_AAC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_AAC_DEC,GstOMXAACDec)) +#define GST_OMX_AAC_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_AAC_DEC,GstOMXAACDecClass)) +#define GST_OMX_AAC_DEC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_AAC_DEC,GstOMXAACDecClass)) +#define GST_IS_OMX_AAC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_AAC_DEC)) +#define GST_IS_OMX_AAC_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_AAC_DEC)) + +typedef struct _GstOMXAACDec GstOMXAACDec; +typedef struct _GstOMXAACDecClass GstOMXAACDecClass; + +struct _GstOMXAACDec +{ + GstOMXAudioDec parent; + gint spf; +}; + +struct _GstOMXAACDecClass +{ + GstOMXAudioDecClass parent_class; +}; + +GType gst_omx_aac_dec_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_AAC_DEC_H__ */ + diff --git a/omx/gstomxamrdec.c b/omx/gstomxamrdec.c new file mode 100644 index 0000000..78567b6 --- /dev/null +++ b/omx/gstomxamrdec.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2014, Sebastian Dröge + * Copyright (C) 2014, LG Electronics, Inc. + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstomxamrdec.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_amr_dec_debug_category); +#define GST_CAT_DEFAULT gst_omx_amr_dec_debug_category + +/* prototypes */ +static gboolean gst_omx_amr_dec_set_format (GstOMXAudioDec * dec, + GstOMXPort * port, GstCaps * caps); +static gboolean gst_omx_amr_dec_is_format_change (GstOMXAudioDec * dec, + GstOMXPort * port, GstCaps * caps); +static gint gst_omx_amr_dec_get_samples_per_frame (GstOMXAudioDec * dec, + GstOMXPort * port); +static gboolean gst_omx_amr_dec_get_channel_positions (GstOMXAudioDec * dec, + GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]); + +/* class initialization */ + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_omx_amr_dec_debug_category, "omxamrdec", 0, \ + "debug category for gst-omx amr audio decoder"); + +G_DEFINE_TYPE_WITH_CODE (GstOMXAMRDec, gst_omx_amr_dec, + GST_TYPE_OMX_AUDIO_DEC, DEBUG_INIT); + +static void +gst_omx_amr_dec_class_init (GstOMXAMRDecClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstOMXAudioDecClass *audiodec_class = GST_OMX_AUDIO_DEC_CLASS (klass); + + audiodec_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_amr_dec_set_format); + audiodec_class->is_format_change = + GST_DEBUG_FUNCPTR (gst_omx_amr_dec_is_format_change); + audiodec_class->get_samples_per_frame = + GST_DEBUG_FUNCPTR (gst_omx_amr_dec_get_samples_per_frame); + audiodec_class->get_channel_positions = + GST_DEBUG_FUNCPTR (gst_omx_amr_dec_get_channel_positions); + + audiodec_class->cdata.default_sink_template_caps = + "audio/AMR, rate=(int)8000, channels=(int)1; " + "audio/AMR-WB, rate=(int)16000, channels=(int)1"; + + gst_element_class_set_static_metadata (element_class, + "OpenMAX AMR Audio Decoder", + "Codec/Decoder/Audio", + "Decode AMR audio streams", + "Sebastian Dröge "); + + gst_omx_set_default_role (&audiodec_class->cdata, "audio_decoder.amrnb"); +} + +static void +gst_omx_amr_dec_init (GstOMXAMRDec * self) +{ + self->spf = -1; +} + +static gboolean +gst_omx_amr_dec_set_format (GstOMXAudioDec * dec, GstOMXPort * port, + GstCaps * caps) +{ + GstOMXAMRDec *self = GST_OMX_AMR_DEC (dec); + OMX_PARAM_PORTDEFINITIONTYPE port_def; + OMX_AUDIO_PARAM_AMRTYPE amr_param; + OMX_ERRORTYPE err; + GstStructure *s; + gint rate, channels; + + gst_omx_port_get_port_definition (port, &port_def); + port_def.format.audio.eEncoding = OMX_AUDIO_CodingAMR; /* not tested for AMRWB */ + err = gst_omx_port_update_port_definition (port, &port_def); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to set AMR format on component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + GST_OMX_INIT_STRUCT (&amr_param); + amr_param.nPortIndex = port->index; + + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioAmr, + &amr_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to get AMR parameters from component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "rate", &rate) || + !gst_structure_get_int (s, "channels", &channels)) { + GST_ERROR_OBJECT (self, "Incomplete caps"); + return FALSE; + } + + self->rate = rate; + + if (rate == 8000) + self->spf = 160; /* (8000/50) */ + else if (rate == 16000) + self->spf = 320; /* (16000/50) */ + + amr_param.nChannels = channels; + amr_param.eAMRBandMode = 0; /*FIXME: It may require a specific value */ + amr_param.eAMRDTXMode = 0; + amr_param.eAMRFrameFormat = 0; + + err = + gst_omx_component_set_parameter (dec->dec, OMX_IndexParamAudioAmr, + &amr_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Error setting AMR parameters: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_omx_amr_dec_is_format_change (GstOMXAudioDec * dec, GstOMXPort * port, + GstCaps * caps) +{ + GstOMXAMRDec *self = GST_OMX_AMR_DEC (dec); + OMX_AUDIO_PARAM_AMRTYPE amr_param; + OMX_ERRORTYPE err; + GstStructure *s; + gint rate, channels; + + GST_OMX_INIT_STRUCT (&amr_param); + amr_param.nPortIndex = port->index; + + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioAmr, + &amr_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to get AMR parameters from component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "rate", &rate) || + !gst_structure_get_int (s, "channels", &channels)) { + GST_ERROR_OBJECT (self, "Incomplete caps"); + return FALSE; + } + + if (self->rate != rate) + return TRUE; + + if (amr_param.nChannels != channels) + return TRUE; + + return FALSE; +} + +static gint +gst_omx_amr_dec_get_samples_per_frame (GstOMXAudioDec * dec, GstOMXPort * port) +{ + return GST_OMX_AMR_DEC (dec)->spf; +} + +static gboolean +gst_omx_amr_dec_get_channel_positions (GstOMXAudioDec * dec, + GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]) +{ + OMX_AUDIO_PARAM_PCMMODETYPE pcm_param; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (&pcm_param); + pcm_param.nPortIndex = port->index; + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioPcm, + &pcm_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (dec, "Failed to get PCM parameters: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + + g_return_val_if_fail (pcm_param.nChannels == 1, FALSE); /* AMR supports only mono */ + + position[0] = GST_AUDIO_CHANNEL_POSITION_MONO; + + return TRUE; +} diff --git a/omx/gstomxamrdec.h b/omx/gstomxamrdec.h new file mode 100644 index 0000000..7a2df33 --- /dev/null +++ b/omx/gstomxamrdec.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014, Sebastian Dröge + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_AMR_DEC_H__ +#define __GST_OMX_AMR_DEC_H__ + +#include +#include "gstomxaudiodec.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_AMR_DEC \ + (gst_omx_amr_dec_get_type()) +#define GST_OMX_AMR_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_AMR_DEC,GstOMXAMRDec)) +#define GST_OMX_AMR_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_AMR_DEC,GstOMXAMRDecClass)) +#define GST_OMX_AMR_DEC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_AMR_DEC,GstOMXAMRDecClass)) +#define GST_IS_OMX_AMR_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_AMR_DEC)) +#define GST_IS_OMX_AMR_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_AMR_DEC)) + +typedef struct _GstOMXAMRDec GstOMXAMRDec; +typedef struct _GstOMXAMRDecClass GstOMXAMRDecClass; + +struct _GstOMXAMRDec +{ + GstOMXAudioDec parent; + gint spf; + gint rate; +}; + +struct _GstOMXAMRDecClass +{ + GstOMXAudioDecClass parent_class; +}; + +GType gst_omx_amr_dec_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_AMR_DEC_H__ */ + diff --git a/omx/gstomxanalogaudiosink.c b/omx/gstomxanalogaudiosink.c new file mode 100644 index 0000000..7c8c885 --- /dev/null +++ b/omx/gstomxanalogaudiosink.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + * Author: Josep Torra + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstomxanalogaudiosink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_analog_audio_sink_debug_category); +#define GST_CAT_DEFAULT gst_omx_analog_audio_sink_debug_category + +/* class initialization */ + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_omx_analog_audio_sink_debug_category, \ + "omxanalogaudiosink", 0, "debug category for gst-omx analog audio sink"); + +G_DEFINE_TYPE_WITH_CODE (GstOMXAnalogAudioSink, gst_omx_analog_audio_sink, + GST_TYPE_OMX_AUDIO_SINK, DEBUG_INIT); + +static void +gst_omx_analog_audio_sink_class_init (GstOMXAnalogAudioSinkClass * klass) +{ + GstOMXAudioSinkClass *audiosink_class = GST_OMX_AUDIO_SINK_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + audiosink_class->cdata.default_sink_template_caps = "audio/x-raw, " + "format = (string) " GST_AUDIO_FORMATS_ALL ", " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ] "; + audiosink_class->destination = "local"; + + gst_element_class_set_static_metadata (element_class, + "OpenMAX Analog Audio Sink", + "Sink/Audio", "Output analog audio", "Josep Torra "); + + gst_omx_set_default_role (&audiosink_class->cdata, "audio_render.local"); +} + +static void +gst_omx_analog_audio_sink_init (GstOMXAnalogAudioSink * self) +{ +} diff --git a/omx/gstomxanalogaudiosink.h b/omx/gstomxanalogaudiosink.h new file mode 100644 index 0000000..7f57048 --- /dev/null +++ b/omx/gstomxanalogaudiosink.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + * Author: Josep Torra + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_ANALOG_AUDIO_SINK_H__ +#define __GST_OMX_ANALOG_AUDIO_SINK_H__ + +#include +#include "gstomxaudiosink.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_ANALOG_AUDIO_SINK \ + (gst_omx_analog_audio_sink_get_type()) +#define GST_OMX_ANALOG_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_ANALOG_AUDIO_SINK,GstOMXAnalogAudioSink)) +#define GST_OMX_ANALOG_AUDIO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_ANALOG_AUDIO_SINK,GstOMXAnalogAudioSinkClass)) +#define GST_OMX_ANALOG_AUDIO_SINK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_ANALOG_AUDIO_SINK,GstOMXAnalogAudioSinkClass)) +#define GST_IS_OMX_ANALOG_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_ANALOG_AUDIO_SINK)) +#define GST_IS_OMX_ANALOG_AUDIO_SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_ANALOG_AUDIO_SINK)) + +typedef struct _GstOMXAnalogAudioSink GstOMXAnalogAudioSink; +typedef struct _GstOMXAnalogAudioSinkClass GstOMXAnalogAudioSinkClass; + +struct _GstOMXAnalogAudioSink +{ + GstOMXAudioSink parent; +}; + +struct _GstOMXAnalogAudioSinkClass +{ + GstOMXAudioSinkClass parent_class; +}; + +GType gst_omx_analog_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_ANALOG_AUDIO_SINK_H__ */ + diff --git a/omx/gstomxaudiodec.c b/omx/gstomxaudiodec.c new file mode 100644 index 0000000..9b84555 --- /dev/null +++ b/omx/gstomxaudiodec.c @@ -0,0 +1,1379 @@ +/* + * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. + * Author: Sebastian Dröge , Collabora Ltd. + * Copyright (C) 2013, Collabora Ltd. + * Author: Sebastian Dröge + * Copyright (C) 2014, Sebastian Dröge + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "gstomxaudiodec.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_audio_dec_debug_category); +#define GST_CAT_DEFAULT gst_omx_audio_dec_debug_category + +/* prototypes */ +static void gst_omx_audio_dec_finalize (GObject * object); + +static GstStateChangeReturn +gst_omx_audio_dec_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_omx_audio_dec_open (GstAudioDecoder * decoder); +static gboolean gst_omx_audio_dec_close (GstAudioDecoder * decoder); +static gboolean gst_omx_audio_dec_start (GstAudioDecoder * decoder); +static gboolean gst_omx_audio_dec_stop (GstAudioDecoder * decoder); +static gboolean gst_omx_audio_dec_set_format (GstAudioDecoder * decoder, + GstCaps * caps); +static void gst_omx_audio_dec_flush (GstAudioDecoder * decoder, gboolean hard); +static GstFlowReturn gst_omx_audio_dec_handle_frame (GstAudioDecoder * decoder, + GstBuffer * buffer); +static GstFlowReturn gst_omx_audio_dec_drain (GstOMXAudioDec * self); + +enum +{ + PROP_0 +}; + +/* class initialization */ + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_omx_audio_dec_debug_category, "omxaudiodec", 0, \ + "debug category for gst-omx audio decoder base class"); + + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstOMXAudioDec, gst_omx_audio_dec, + GST_TYPE_AUDIO_DECODER, DEBUG_INIT); + +static void +gst_omx_audio_dec_class_init (GstOMXAudioDecClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstAudioDecoderClass *audio_decoder_class = GST_AUDIO_DECODER_CLASS (klass); + + gobject_class->finalize = gst_omx_audio_dec_finalize; + + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_omx_audio_dec_change_state); + + audio_decoder_class->open = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_open); + audio_decoder_class->close = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_close); + audio_decoder_class->start = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_start); + audio_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_stop); + audio_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_omx_audio_dec_flush); + audio_decoder_class->set_format = + GST_DEBUG_FUNCPTR (gst_omx_audio_dec_set_format); + audio_decoder_class->handle_frame = + GST_DEBUG_FUNCPTR (gst_omx_audio_dec_handle_frame); + + klass->cdata.type = GST_OMX_COMPONENT_TYPE_FILTER; + klass->cdata.default_src_template_caps = + "audio/x-raw, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, " G_STRINGIFY (OMX_AUDIO_MAXCHANNELS) " ], " + "format = (string) " GST_AUDIO_FORMATS_ALL; +} + +static void +gst_omx_audio_dec_init (GstOMXAudioDec * self) +{ + gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (self), TRUE); + gst_audio_decoder_set_drainable (GST_AUDIO_DECODER (self), TRUE); + gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST + (self), TRUE); + GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (self)); + + g_mutex_init (&self->drain_lock); + g_cond_init (&self->drain_cond); + + self->output_adapter = gst_adapter_new (); +} + +static gboolean +gst_omx_audio_dec_open (GstAudioDecoder * decoder) +{ + GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (decoder); + GstOMXAudioDecClass *klass = GST_OMX_AUDIO_DEC_GET_CLASS (self); + gint in_port_index, out_port_index; + + GST_DEBUG_OBJECT (self, "Opening decoder"); + + self->dec = + gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name, + klass->cdata.component_name, klass->cdata.component_role, + klass->cdata.hacks); + self->started = FALSE; + + if (!self->dec) + return FALSE; + + if (gst_omx_component_get_state (self->dec, + GST_CLOCK_TIME_NONE) != OMX_StateLoaded) + return FALSE; + + in_port_index = klass->cdata.in_port_index; + out_port_index = klass->cdata.out_port_index; + + if (in_port_index == -1 || out_port_index == -1) { + OMX_PORT_PARAM_TYPE param; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (¶m); + + err = + gst_omx_component_get_parameter (self->dec, OMX_IndexParamAudioInit, + ¶m); + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)", + gst_omx_error_to_string (err), err); + /* Fallback */ + in_port_index = 0; + out_port_index = 1; + } else { + GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", + (guint) param.nPorts, (guint) param.nStartPortNumber); + in_port_index = param.nStartPortNumber + 0; + out_port_index = param.nStartPortNumber + 1; + } + } + self->dec_in_port = gst_omx_component_add_port (self->dec, in_port_index); + self->dec_out_port = gst_omx_component_add_port (self->dec, out_port_index); + + if (!self->dec_in_port || !self->dec_out_port) + return FALSE; + + GST_DEBUG_OBJECT (self, "Opened decoder"); + + return TRUE; +} + +static gboolean +gst_omx_audio_dec_shutdown (GstOMXAudioDec * self) +{ + OMX_STATETYPE state; + + GST_DEBUG_OBJECT (self, "Shutting down decoder"); + + state = gst_omx_component_get_state (self->dec, 0); + if (state > OMX_StateLoaded || state == OMX_StateInvalid) { + if (state > OMX_StateIdle) { + gst_omx_component_set_state (self->dec, OMX_StateIdle); + gst_omx_component_get_state (self->dec, 5 * GST_SECOND); + } + gst_omx_component_set_state (self->dec, OMX_StateLoaded); + gst_omx_port_deallocate_buffers (self->dec_in_port); + gst_omx_port_deallocate_buffers (self->dec_out_port); + if (state > OMX_StateLoaded) + gst_omx_component_get_state (self->dec, 5 * GST_SECOND); + } + + return TRUE; +} + +static gboolean +gst_omx_audio_dec_close (GstAudioDecoder * decoder) +{ + GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (decoder); + + GST_DEBUG_OBJECT (self, "Closing decoder"); + + if (!gst_omx_audio_dec_shutdown (self)) + return FALSE; + + self->dec_in_port = NULL; + self->dec_out_port = NULL; + if (self->dec) + gst_omx_component_free (self->dec); + self->dec = NULL; + + self->started = FALSE; + + GST_DEBUG_OBJECT (self, "Closed decoder"); + + return TRUE; +} + +static void +gst_omx_audio_dec_finalize (GObject * object) +{ + GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (object); + + g_mutex_clear (&self->drain_lock); + g_cond_clear (&self->drain_cond); + + if (self->output_adapter) + gst_object_unref (self->output_adapter); + self->output_adapter = NULL; + + G_OBJECT_CLASS (gst_omx_audio_dec_parent_class)->finalize (object); +} + +static GstStateChangeReturn +gst_omx_audio_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstOMXAudioDec *self; + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + g_return_val_if_fail (GST_IS_OMX_AUDIO_DEC (element), + GST_STATE_CHANGE_FAILURE); + self = GST_OMX_AUDIO_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + self->downstream_flow_ret = GST_FLOW_OK; + self->draining = FALSE; + self->started = FALSE; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (self->dec_in_port) + gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); + if (self->dec_out_port) + gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); + + g_mutex_lock (&self->drain_lock); + self->draining = FALSE; + g_cond_broadcast (&self->drain_cond); + g_mutex_unlock (&self->drain_lock); + break; + default: + break; + } + + ret = + GST_ELEMENT_CLASS (gst_omx_audio_dec_parent_class)->change_state + (element, transition); + + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + self->downstream_flow_ret = GST_FLOW_FLUSHING; + self->started = FALSE; + + if (!gst_omx_audio_dec_shutdown (self)) + ret = GST_STATE_CHANGE_FAILURE; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} + +static void +gst_omx_audio_dec_loop (GstOMXAudioDec * self) +{ + GstOMXAudioDecClass *klass = GST_OMX_AUDIO_DEC_GET_CLASS (self); + GstOMXPort *port = self->dec_out_port; + GstOMXBuffer *buf = NULL; + GstFlowReturn flow_ret = GST_FLOW_OK; + GstOMXAcquireBufferReturn acq_return; + OMX_ERRORTYPE err; + gint spf; + + acq_return = gst_omx_port_acquire_buffer (port, &buf); + if (acq_return == GST_OMX_ACQUIRE_BUFFER_ERROR) { + goto component_error; + } else if (acq_return == GST_OMX_ACQUIRE_BUFFER_FLUSHING) { + goto flushing; + } else if (acq_return == GST_OMX_ACQUIRE_BUFFER_EOS) { + goto eos; + } + + if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (self)) || + acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { + OMX_PARAM_PORTDEFINITIONTYPE port_def; + OMX_AUDIO_PARAM_PCMMODETYPE pcm_param; + GstAudioChannelPosition omx_position[OMX_AUDIO_MAXCHANNELS]; + GstOMXAudioDecClass *klass = GST_OMX_AUDIO_DEC_GET_CLASS (self); + gint i; + + GST_DEBUG_OBJECT (self, "Port settings have changed, updating caps"); + + /* Reallocate all buffers */ + if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE + && gst_omx_port_is_enabled (port)) { + err = gst_omx_port_set_enabled (port, FALSE); + if (err != OMX_ErrorNone) + goto reconfigure_error; + + err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND); + if (err != OMX_ErrorNone) + goto reconfigure_error; + + err = gst_omx_port_deallocate_buffers (port); + if (err != OMX_ErrorNone) + goto reconfigure_error; + + err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND); + if (err != OMX_ErrorNone) + goto reconfigure_error; + } + + /* Just update caps */ + GST_AUDIO_DECODER_STREAM_LOCK (self); + + gst_omx_port_get_port_definition (port, &port_def); + g_assert (port_def.format.audio.eEncoding == OMX_AUDIO_CodingPCM); + + GST_OMX_INIT_STRUCT (&pcm_param); + pcm_param.nPortIndex = self->dec_out_port->index; + err = + gst_omx_component_get_parameter (self->dec, OMX_IndexParamAudioPcm, + &pcm_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to get PCM parameters: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto caps_failed; + } + + g_assert (pcm_param.ePCMMode == OMX_AUDIO_PCMModeLinear); + g_assert (pcm_param.bInterleaved == OMX_TRUE); + + gst_audio_info_init (&self->info); + + for (i = 0; i < pcm_param.nChannels; i++) { + switch (pcm_param.eChannelMapping[i]) { + case OMX_AUDIO_ChannelLF: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + break; + case OMX_AUDIO_ChannelRF: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + break; + case OMX_AUDIO_ChannelCF: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; + break; + case OMX_AUDIO_ChannelLS: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT; + break; + case OMX_AUDIO_ChannelRS: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT; + break; + case OMX_AUDIO_ChannelLFE: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_LFE1; + break; + case OMX_AUDIO_ChannelCS: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER; + break; + case OMX_AUDIO_ChannelLR: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT; + break; + case OMX_AUDIO_ChannelRR: + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT; + break; + case OMX_AUDIO_ChannelNone: + default: + /* This will break the outer loop too as the + * i == pcm_param.nChannels afterwards */ + for (i = 0; i < pcm_param.nChannels; i++) + omx_position[i] = GST_AUDIO_CHANNEL_POSITION_NONE; + break; + } + } + if (pcm_param.nChannels == 1 + && omx_position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER) + omx_position[0] = GST_AUDIO_CHANNEL_POSITION_MONO; + + if (omx_position[0] == GST_AUDIO_CHANNEL_POSITION_NONE + && klass->get_channel_positions) { + GST_WARNING_OBJECT (self, + "Failed to get a valid channel layout, trying fallback"); + klass->get_channel_positions (self, self->dec_out_port, omx_position); + } + + memcpy (self->position, omx_position, sizeof (omx_position)); + gst_audio_channel_positions_to_valid_order (self->position, + pcm_param.nChannels); + self->needs_reorder = + (memcmp (self->position, omx_position, + sizeof (GstAudioChannelPosition) * pcm_param.nChannels) != 0); + if (self->needs_reorder) + gst_audio_get_channel_reorder_map (pcm_param.nChannels, self->position, + omx_position, self->reorder_map); + + gst_audio_info_set_format (&self->info, + gst_audio_format_build_integer (pcm_param.eNumData == + OMX_NumericalDataSigned, + pcm_param.eEndian == + OMX_EndianLittle ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, + pcm_param.nBitPerSample, pcm_param.nBitPerSample), + pcm_param.nSamplingRate, pcm_param.nChannels, self->position); + + GST_DEBUG_OBJECT (self, + "Setting output state: format %s, rate %u, channels %u", + gst_audio_format_to_string (self->info.finfo->format), + (guint) pcm_param.nSamplingRate, (guint) pcm_param.nChannels); + + if (!gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (self), + &self->info) + || !gst_audio_decoder_negotiate (GST_AUDIO_DECODER (self))) { + if (buf) + gst_omx_port_release_buffer (port, buf); + goto caps_failed; + } + + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + + if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { + err = gst_omx_port_set_enabled (port, TRUE); + if (err != OMX_ErrorNone) + goto reconfigure_error; + + err = gst_omx_port_allocate_buffers (port); + if (err != OMX_ErrorNone) + goto reconfigure_error; + + err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND); + if (err != OMX_ErrorNone) + goto reconfigure_error; + + err = gst_omx_port_populate (port); + if (err != OMX_ErrorNone) + goto reconfigure_error; + + err = gst_omx_port_mark_reconfigured (port); + if (err != OMX_ErrorNone) + goto reconfigure_error; + } + + /* Now get a buffer */ + if (acq_return != GST_OMX_ACQUIRE_BUFFER_OK) { + return; + } + } + + g_assert (acq_return == GST_OMX_ACQUIRE_BUFFER_OK); + if (!buf) { + g_assert ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)); + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto eos; + } + + /* This prevents a deadlock between the srcpad stream + * lock and the audiocodec stream lock, if ::reset() + * is called at the wrong time + */ + if (gst_omx_port_is_flushing (port)) { + GST_DEBUG_OBJECT (self, "Flushing"); + gst_omx_port_release_buffer (port, buf); + goto flushing; + } + + GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %" G_GUINT64_FORMAT, + (guint) buf->omx_buf->nFlags, (guint64) buf->omx_buf->nTimeStamp); + + GST_AUDIO_DECODER_STREAM_LOCK (self); + + spf = klass->get_samples_per_frame (self, self->dec_out_port); + + if (buf->omx_buf->nFilledLen > 0) { + GstBuffer *outbuf; + GstMapInfo minfo; + + GST_DEBUG_OBJECT (self, "Handling output data"); + + if (buf->omx_buf->nFilledLen % self->info.bpf != 0) { + gst_omx_port_release_buffer (port, buf); + goto invalid_buffer; + } + + outbuf = + gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (self), + buf->omx_buf->nFilledLen); + + gst_buffer_map (outbuf, &minfo, GST_MAP_WRITE); + if (self->needs_reorder) { + gint i, n_samples, c, n_channels; + gint *reorder_map = self->reorder_map; + gint16 *dest, *source; + + dest = (gint16 *) minfo.data; + source = (gint16 *) (buf->omx_buf->pBuffer + buf->omx_buf->nOffset); + n_samples = buf->omx_buf->nFilledLen / self->info.bpf; + n_channels = self->info.channels; + + for (i = 0; i < n_samples; i++) { + for (c = 0; c < n_channels; c++) { + dest[i * n_channels + reorder_map[c]] = source[i * n_channels + c]; + } + } + } else { + memcpy (minfo.data, buf->omx_buf->pBuffer + buf->omx_buf->nOffset, + buf->omx_buf->nFilledLen); + } + gst_buffer_unmap (outbuf, &minfo); + + if (spf != -1) { + gst_adapter_push (self->output_adapter, outbuf); + } else { + flow_ret = + gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), outbuf, 1); + } + } + + GST_DEBUG_OBJECT (self, "Read frame from component"); + + if (spf != -1) { + GstBuffer *outbuf; + guint avail = gst_adapter_available (self->output_adapter); + guint nframes; + + /* We take a multiple of codec frames and push + * them downstream + */ + avail /= self->info.bpf; + nframes = avail / spf; + avail = nframes * spf; + avail *= self->info.bpf; + + if (avail > 0) { + outbuf = gst_adapter_take_buffer (self->output_adapter, avail); + flow_ret = + gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), outbuf, + nframes); + } + } + + GST_DEBUG_OBJECT (self, "Finished frame: %s", gst_flow_get_name (flow_ret)); + + if (buf) { + err = gst_omx_port_release_buffer (port, buf); + if (err != OMX_ErrorNone) + goto release_error; + } + + self->downstream_flow_ret = flow_ret; + + if (flow_ret != GST_FLOW_OK) + goto flow_error; + + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + + return; + +component_error: + { + GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), + ("OpenMAX component in error state %s (0x%08x)", + gst_omx_component_get_last_error_string (self->dec), + gst_omx_component_get_last_error (self->dec))); + gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + self->downstream_flow_ret = GST_FLOW_ERROR; + self->started = FALSE; + return; + } + +flushing: + { + GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + self->downstream_flow_ret = GST_FLOW_FLUSHING; + self->started = FALSE; + return; + } + +eos: + { + spf = klass->get_samples_per_frame (self, self->dec_out_port); + if (spf != -1) { + GstBuffer *outbuf; + guint avail = gst_adapter_available (self->output_adapter); + guint nframes; + + /* On EOS we take the complete adapter content, no matter + * if it is a multiple of the codec frame size or not. + */ + avail /= self->info.bpf; + nframes = (avail + spf - 1) / spf; + avail *= self->info.bpf; + + if (avail > 0) { + outbuf = gst_adapter_take_buffer (self->output_adapter, avail); + flow_ret = + gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), outbuf, + nframes); + } + } + + g_mutex_lock (&self->drain_lock); + if (self->draining) { + GST_DEBUG_OBJECT (self, "Drained"); + self->draining = FALSE; + g_cond_broadcast (&self->drain_cond); + flow_ret = GST_FLOW_OK; + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + } else { + GST_DEBUG_OBJECT (self, "Component signalled EOS"); + flow_ret = GST_FLOW_EOS; + } + g_mutex_unlock (&self->drain_lock); + + GST_AUDIO_DECODER_STREAM_LOCK (self); + self->downstream_flow_ret = flow_ret; + + /* Here we fallback and pause the task for the EOS case */ + if (flow_ret != GST_FLOW_OK) + goto flow_error; + + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + + return; + } + +flow_error: + { + if (flow_ret == GST_FLOW_EOS) { + GST_DEBUG_OBJECT (self, "EOS"); + + gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), + gst_event_new_eos ()); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + self->started = FALSE; + } else if (flow_ret < GST_FLOW_EOS) { + GST_ELEMENT_ERROR (self, STREAM, FAILED, + ("Internal data stream error."), ("stream stopped, reason %s", + gst_flow_get_name (flow_ret))); + + gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), + gst_event_new_eos ()); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + self->started = FALSE; + } else if (flow_ret == GST_FLOW_FLUSHING) { + GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + self->started = FALSE; + } + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + return; + } + +reconfigure_error: + { + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), + ("Unable to reconfigure output port")); + gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + self->downstream_flow_ret = GST_FLOW_ERROR; + self->started = FALSE; + return; + } + +invalid_buffer: + { + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), + ("Invalid sized input buffer")); + gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED; + self->started = FALSE; + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + return; + } + +caps_failed: + { + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), ("Failed to set caps")); + gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + self->downstream_flow_ret = GST_FLOW_NOT_NEGOTIATED; + self->started = FALSE; + return; + } +release_error: + { + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), + ("Failed to relase output buffer to component: %s (0x%08x)", + gst_omx_error_to_string (err), err)); + gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (self), gst_event_new_eos ()); + gst_pad_pause_task (GST_AUDIO_DECODER_SRC_PAD (self)); + self->downstream_flow_ret = GST_FLOW_ERROR; + self->started = FALSE; + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + return; + } +} + +static gboolean +gst_omx_audio_dec_start (GstAudioDecoder * decoder) +{ + GstOMXAudioDec *self; + + self = GST_OMX_AUDIO_DEC (decoder); + + self->last_upstream_ts = 0; + self->downstream_flow_ret = GST_FLOW_OK; + + return TRUE; +} + +static gboolean +gst_omx_audio_dec_stop (GstAudioDecoder * decoder) +{ + GstOMXAudioDec *self; + + self = GST_OMX_AUDIO_DEC (decoder); + + GST_DEBUG_OBJECT (self, "Stopping decoder"); + + gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); + + gst_pad_stop_task (GST_AUDIO_DECODER_SRC_PAD (decoder)); + + if (gst_omx_component_get_state (self->dec, 0) > OMX_StateIdle) + gst_omx_component_set_state (self->dec, OMX_StateIdle); + + self->downstream_flow_ret = GST_FLOW_FLUSHING; + self->started = FALSE; + + g_mutex_lock (&self->drain_lock); + self->draining = FALSE; + g_cond_broadcast (&self->drain_cond); + g_mutex_unlock (&self->drain_lock); + + gst_adapter_flush (self->output_adapter, + gst_adapter_available (self->output_adapter)); + + gst_omx_component_get_state (self->dec, 5 * GST_SECOND); + + gst_buffer_replace (&self->codec_data, NULL); + + GST_DEBUG_OBJECT (self, "Stopped decoder"); + + return TRUE; +} + +static gboolean +gst_omx_audio_dec_set_format (GstAudioDecoder * decoder, GstCaps * caps) +{ + GstOMXAudioDec *self; + GstOMXAudioDecClass *klass; + GstStructure *s; + const GValue *codec_data; + gboolean is_format_change = FALSE; + gboolean needs_disable = FALSE; + + self = GST_OMX_AUDIO_DEC (decoder); + klass = GST_OMX_AUDIO_DEC_GET_CLASS (decoder); + + GST_DEBUG_OBJECT (self, "Setting new caps %" GST_PTR_FORMAT, caps); + + /* Check if the caps change is a real format change or if only irrelevant + * parts of the caps have changed or nothing at all. + */ + if (klass->is_format_change) + is_format_change = klass->is_format_change (self, self->dec_in_port, caps); + + needs_disable = + gst_omx_component_get_state (self->dec, + GST_CLOCK_TIME_NONE) != OMX_StateLoaded; + /* If the component is not in Loaded state and a real format change happens + * we have to disable the port and re-allocate all buffers. If no real + * format change happened we can just exit here. + */ + if (needs_disable && !is_format_change) { + GST_DEBUG_OBJECT (self, + "Already running and caps did not change the format"); + return TRUE; + } + + if (needs_disable && is_format_change) { + GstOMXPort *out_port = self->dec_out_port; + + GST_DEBUG_OBJECT (self, "Need to disable and drain decoder"); + + gst_omx_audio_dec_drain (self); + gst_omx_audio_dec_flush (decoder, FALSE); + gst_omx_port_set_flushing (out_port, 5 * GST_SECOND, TRUE); + + if (klass->cdata.hacks & GST_OMX_HACK_NO_COMPONENT_RECONFIGURE) { + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + gst_omx_audio_dec_stop (GST_AUDIO_DECODER (self)); + gst_omx_audio_dec_close (GST_AUDIO_DECODER (self)); + GST_AUDIO_DECODER_STREAM_LOCK (self); + + if (!gst_omx_audio_dec_open (GST_AUDIO_DECODER (self))) + return FALSE; + needs_disable = FALSE; + } else { + if (gst_omx_port_set_enabled (self->dec_in_port, FALSE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_set_enabled (out_port, FALSE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_buffers_released (self->dec_in_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_buffers_released (out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_deallocate_buffers (self->dec_in_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_deallocate_buffers (self->dec_out_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_enabled (self->dec_in_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_enabled (out_port, 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + } + + GST_DEBUG_OBJECT (self, "Decoder drained and disabled"); + } + + if (klass->set_format) { + if (!klass->set_format (self, self->dec_in_port, caps)) { + GST_ERROR_OBJECT (self, "Subclass failed to set the new format"); + return FALSE; + } + } + + GST_DEBUG_OBJECT (self, "Updating outport port definition"); + if (gst_omx_port_update_port_definition (self->dec_out_port, + NULL) != OMX_ErrorNone) + return FALSE; + + /* Get codec data from caps */ + gst_buffer_replace (&self->codec_data, NULL); + s = gst_caps_get_structure (caps, 0); + codec_data = gst_structure_get_value (s, "codec_data"); + if (codec_data) { + /* Vorbis and some other codecs have multiple buffers in + * the stream-header field */ + self->codec_data = gst_value_get_buffer (codec_data); + if (self->codec_data) + gst_buffer_ref (self->codec_data); + } + + GST_DEBUG_OBJECT (self, "Enabling component"); + + if (needs_disable) { + if (gst_omx_port_set_enabled (self->dec_in_port, TRUE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_allocate_buffers (self->dec_in_port) != OMX_ErrorNone) + return FALSE; + + if ((klass->cdata.hacks & GST_OMX_HACK_NO_DISABLE_OUTPORT)) { + if (gst_omx_port_set_enabled (self->dec_out_port, TRUE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_allocate_buffers (self->dec_out_port) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->dec_out_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + } + + if (gst_omx_port_wait_enabled (self->dec_in_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_mark_reconfigured (self->dec_in_port) != OMX_ErrorNone) + return FALSE; + } else { + if (!(klass->cdata.hacks & GST_OMX_HACK_NO_DISABLE_OUTPORT)) { + /* Disable output port */ + if (gst_omx_port_set_enabled (self->dec_out_port, FALSE) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->dec_out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_component_set_state (self->dec, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; + + /* Need to allocate buffers to reach Idle state */ + if (gst_omx_port_allocate_buffers (self->dec_in_port) != OMX_ErrorNone) + return FALSE; + } else { + if (gst_omx_component_set_state (self->dec, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; + + /* Need to allocate buffers to reach Idle state */ + if (gst_omx_port_allocate_buffers (self->dec_in_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_allocate_buffers (self->dec_out_port) != OMX_ErrorNone) + return FALSE; + } + + if (gst_omx_component_get_state (self->dec, + GST_CLOCK_TIME_NONE) != OMX_StateIdle) + return FALSE; + + if (gst_omx_component_set_state (self->dec, + OMX_StateExecuting) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_component_get_state (self->dec, + GST_CLOCK_TIME_NONE) != OMX_StateExecuting) + return FALSE; + } + + /* Unset flushing to allow ports to accept data again */ + gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, FALSE); + gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE); + + if (gst_omx_component_get_last_error (self->dec) != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Component in error state: %s (0x%08x)", + gst_omx_component_get_last_error_string (self->dec), + gst_omx_component_get_last_error (self->dec)); + return FALSE; + } + + self->downstream_flow_ret = GST_FLOW_OK; + + return TRUE; +} + +static void +gst_omx_audio_dec_flush (GstAudioDecoder * decoder, gboolean hard) +{ + GstOMXAudioDec *self = GST_OMX_AUDIO_DEC (decoder); + OMX_ERRORTYPE err = OMX_ErrorNone; + + GST_DEBUG_OBJECT (self, "Flushing decoder"); + + if (gst_omx_component_get_state (self->dec, 0) == OMX_StateLoaded) + return; + + /* 0) Pause the components */ + if (gst_omx_component_get_state (self->dec, 0) == OMX_StateExecuting) { + gst_omx_component_set_state (self->dec, OMX_StatePause); + gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE); + } + + /* 1) Wait until the srcpad loop is stopped, + * unlock GST_AUDIO_DECODER_STREAM_LOCK to prevent deadlocks + * caused by using this lock from inside the loop function */ + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + gst_pad_stop_task (GST_AUDIO_DECODER_SRC_PAD (decoder)); + GST_DEBUG_OBJECT (self, "Flushing -- task stopped"); + GST_AUDIO_DECODER_STREAM_LOCK (self); + + /* 2) Flush the ports */ + GST_DEBUG_OBJECT (self, "flushing ports"); + gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); + + /* 3) Resume components */ + gst_omx_component_set_state (self->dec, OMX_StateExecuting); + gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE); + + /* 4) Unset flushing to allow ports to accept data again */ + gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, FALSE); + gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE); + + err = gst_omx_port_populate (self->dec_out_port); + + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, "Failed to populate output port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + } + + /* Reset our state */ + gst_adapter_flush (self->output_adapter, + gst_adapter_available (self->output_adapter)); + self->last_upstream_ts = 0; + self->downstream_flow_ret = GST_FLOW_OK; + self->started = FALSE; + GST_DEBUG_OBJECT (self, "Flush finished"); +} + +static GstFlowReturn +gst_omx_audio_dec_handle_frame (GstAudioDecoder * decoder, GstBuffer * inbuf) +{ + GstOMXAcquireBufferReturn acq_ret = GST_OMX_ACQUIRE_BUFFER_ERROR; + GstOMXAudioDec *self; + GstOMXPort *port; + GstOMXBuffer *buf; + GstBuffer *codec_data = NULL; + guint offset = 0; + GstClockTime timestamp, duration; + OMX_ERRORTYPE err; + GstMapInfo minfo; + + self = GST_OMX_AUDIO_DEC (decoder); + + GST_DEBUG_OBJECT (self, "Handling frame"); + + if (self->downstream_flow_ret != GST_FLOW_OK) { + return self->downstream_flow_ret; + } + + if (!self->started) { + GST_DEBUG_OBJECT (self, "Starting task"); + gst_pad_start_task (GST_AUDIO_DECODER_SRC_PAD (self), + (GstTaskFunction) gst_omx_audio_dec_loop, decoder, NULL); + } + + if (inbuf == NULL) + return gst_omx_audio_dec_drain (self); + + /* Make sure to keep a reference to the input here, + * it can be unreffed from the other thread if + * finish_frame() is called */ + gst_buffer_ref (inbuf); + + timestamp = GST_BUFFER_TIMESTAMP (inbuf); + duration = GST_BUFFER_DURATION (inbuf); + + port = self->dec_in_port; + + gst_buffer_map (inbuf, &minfo, GST_MAP_READ); + + while (offset < minfo.size) { + /* Make sure to release the base class stream lock, otherwise + * _loop() can't call _finish_frame() and we might block forever + * because no input buffers are released */ + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + acq_ret = gst_omx_port_acquire_buffer (port, &buf); + + if (acq_ret == GST_OMX_ACQUIRE_BUFFER_ERROR) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto component_error; + } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_FLUSHING) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto flushing; + } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { + /* Reallocate all buffers */ + err = gst_omx_port_set_enabled (port, FALSE); + if (err != OMX_ErrorNone) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto reconfigure_error; + } + + err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND); + if (err != OMX_ErrorNone) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto reconfigure_error; + } + + err = gst_omx_port_deallocate_buffers (port); + if (err != OMX_ErrorNone) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto reconfigure_error; + } + + err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND); + if (err != OMX_ErrorNone) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto reconfigure_error; + } + + err = gst_omx_port_set_enabled (port, TRUE); + if (err != OMX_ErrorNone) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto reconfigure_error; + } + + err = gst_omx_port_allocate_buffers (port); + if (err != OMX_ErrorNone) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto reconfigure_error; + } + + err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND); + if (err != OMX_ErrorNone) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto reconfigure_error; + } + + err = gst_omx_port_mark_reconfigured (port); + if (err != OMX_ErrorNone) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + goto reconfigure_error; + } + + /* Now get a new buffer and fill it */ + GST_AUDIO_DECODER_STREAM_LOCK (self); + continue; + } + GST_AUDIO_DECODER_STREAM_LOCK (self); + + g_assert (acq_ret == GST_OMX_ACQUIRE_BUFFER_OK && buf != NULL); + + if (buf->omx_buf->nAllocLen - buf->omx_buf->nOffset <= 0) { + gst_omx_port_release_buffer (port, buf); + goto full_buffer; + } + + if (self->downstream_flow_ret != GST_FLOW_OK) { + gst_omx_port_release_buffer (port, buf); + goto flow_error; + } + + if (self->codec_data) { + GST_DEBUG_OBJECT (self, "Passing codec data to the component"); + + codec_data = self->codec_data; + + if (buf->omx_buf->nAllocLen - buf->omx_buf->nOffset < + gst_buffer_get_size (codec_data)) { + gst_omx_port_release_buffer (port, buf); + goto too_large_codec_data; + } + + buf->omx_buf->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; + buf->omx_buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; + buf->omx_buf->nFilledLen = gst_buffer_get_size (codec_data); + gst_buffer_extract (codec_data, 0, + buf->omx_buf->pBuffer + buf->omx_buf->nOffset, + buf->omx_buf->nFilledLen); + + if (GST_CLOCK_TIME_IS_VALID (timestamp)) + buf->omx_buf->nTimeStamp = + gst_util_uint64_scale (timestamp, OMX_TICKS_PER_SECOND, GST_SECOND); + else + buf->omx_buf->nTimeStamp = 0; + buf->omx_buf->nTickCount = 0; + + self->started = TRUE; + err = gst_omx_port_release_buffer (port, buf); + gst_buffer_replace (&self->codec_data, NULL); + if (err != OMX_ErrorNone) + goto release_error; + /* Acquire new buffer for the actual frame */ + continue; + } + + /* Now handle the frame */ + GST_DEBUG_OBJECT (self, "Passing frame offset %d to the component", offset); + + /* Copy the buffer content in chunks of size as requested + * by the port */ + buf->omx_buf->nFilledLen = + MIN (minfo.size - offset, + buf->omx_buf->nAllocLen - buf->omx_buf->nOffset); + gst_buffer_extract (inbuf, offset, + buf->omx_buf->pBuffer + buf->omx_buf->nOffset, + buf->omx_buf->nFilledLen); + + if (timestamp != GST_CLOCK_TIME_NONE) { + buf->omx_buf->nTimeStamp = + gst_util_uint64_scale (timestamp, OMX_TICKS_PER_SECOND, GST_SECOND); + self->last_upstream_ts = timestamp; + } else { + buf->omx_buf->nTimeStamp = 0; + } + + if (duration != GST_CLOCK_TIME_NONE && offset == 0) { + buf->omx_buf->nTickCount = + gst_util_uint64_scale (duration, OMX_TICKS_PER_SECOND, GST_SECOND); + self->last_upstream_ts += duration; + } else { + buf->omx_buf->nTickCount = 0; + } + + if (offset == 0) + buf->omx_buf->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; + + /* TODO: Set flags + * - OMX_BUFFERFLAG_DECODEONLY for buffers that are outside + * the segment + */ + + offset += buf->omx_buf->nFilledLen; + + if (offset == minfo.size) + buf->omx_buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; + + self->started = TRUE; + err = gst_omx_port_release_buffer (port, buf); + if (err != OMX_ErrorNone) + goto release_error; + } + gst_buffer_unmap (inbuf, &minfo); + gst_buffer_unref (inbuf); + + GST_DEBUG_OBJECT (self, "Passed frame to component"); + + return self->downstream_flow_ret; + +full_buffer: + { + gst_buffer_unmap (inbuf, &minfo); + gst_buffer_unref (inbuf); + + GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), + ("Got OpenMAX buffer with no free space (%p, %u/%u)", buf, + (guint) buf->omx_buf->nOffset, (guint) buf->omx_buf->nAllocLen)); + return GST_FLOW_ERROR; + } + +flow_error: + { + gst_buffer_unmap (inbuf, &minfo); + gst_buffer_unref (inbuf); + + return self->downstream_flow_ret; + } + +too_large_codec_data: + { + gst_buffer_unmap (inbuf, &minfo); + gst_buffer_unref (inbuf); + + GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), + ("codec_data larger than supported by OpenMAX port " + "(%" G_GSIZE_FORMAT " > %u)", gst_buffer_get_size (codec_data), + (guint) self->dec_in_port->port_def.nBufferSize)); + return GST_FLOW_ERROR; + } + +component_error: + { + gst_buffer_unmap (inbuf, &minfo); + gst_buffer_unref (inbuf); + + GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), + ("OpenMAX component in error state %s (0x%08x)", + gst_omx_component_get_last_error_string (self->dec), + gst_omx_component_get_last_error (self->dec))); + return GST_FLOW_ERROR; + } + +flushing: + { + gst_buffer_unmap (inbuf, &minfo); + gst_buffer_unref (inbuf); + + GST_DEBUG_OBJECT (self, "Flushing -- returning FLUSHING"); + return GST_FLOW_FLUSHING; + } +reconfigure_error: + { + gst_buffer_unmap (inbuf, &minfo); + gst_buffer_unref (inbuf); + + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), + ("Unable to reconfigure input port")); + return GST_FLOW_ERROR; + } +release_error: + { + gst_buffer_unmap (inbuf, &minfo); + gst_buffer_unref (inbuf); + + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), + ("Failed to relase input buffer to component: %s (0x%08x)", + gst_omx_error_to_string (err), err)); + + return GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_omx_audio_dec_drain (GstOMXAudioDec * self) +{ + GstOMXAudioDecClass *klass; + GstOMXBuffer *buf; + GstOMXAcquireBufferReturn acq_ret; + OMX_ERRORTYPE err; + + GST_DEBUG_OBJECT (self, "Draining component"); + + klass = GST_OMX_AUDIO_DEC_GET_CLASS (self); + + if (!self->started) { + GST_DEBUG_OBJECT (self, "Component not started yet"); + return GST_FLOW_OK; + } + self->started = FALSE; + + if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) { + GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers"); + return GST_FLOW_OK; + } + + /* Make sure to release the base class stream lock, otherwise + * _loop() can't call _finish_frame() and we might block forever + * because no input buffers are released */ + GST_AUDIO_DECODER_STREAM_UNLOCK (self); + + /* Send an EOS buffer to the component and let the base + * class drop the EOS event. We will send it later when + * the EOS buffer arrives on the output port. */ + acq_ret = gst_omx_port_acquire_buffer (self->dec_in_port, &buf); + if (acq_ret != GST_OMX_ACQUIRE_BUFFER_OK) { + GST_AUDIO_DECODER_STREAM_LOCK (self); + GST_ERROR_OBJECT (self, "Failed to acquire buffer for draining: %d", + acq_ret); + return GST_FLOW_ERROR; + } + + g_mutex_lock (&self->drain_lock); + self->draining = TRUE; + buf->omx_buf->nFilledLen = 0; + buf->omx_buf->nTimeStamp = + gst_util_uint64_scale (self->last_upstream_ts, OMX_TICKS_PER_SECOND, + GST_SECOND); + buf->omx_buf->nTickCount = 0; + buf->omx_buf->nFlags |= OMX_BUFFERFLAG_EOS; + err = gst_omx_port_release_buffer (self->dec_in_port, buf); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to drain component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + g_mutex_unlock (&self->drain_lock); + GST_AUDIO_DECODER_STREAM_LOCK (self); + return GST_FLOW_ERROR; + } + + GST_DEBUG_OBJECT (self, "Waiting until component is drained"); + + if (G_UNLIKELY (self->dec->hacks & GST_OMX_HACK_DRAIN_MAY_NOT_RETURN)) { + gint64 wait_until = g_get_monotonic_time () + G_TIME_SPAN_SECOND / 2; + + if (!g_cond_wait_until (&self->drain_cond, &self->drain_lock, wait_until)) + GST_WARNING_OBJECT (self, "Drain timed out"); + else + GST_DEBUG_OBJECT (self, "Drained component"); + + } else { + g_cond_wait (&self->drain_cond, &self->drain_lock); + GST_DEBUG_OBJECT (self, "Drained component"); + } + + g_mutex_unlock (&self->drain_lock); + GST_AUDIO_DECODER_STREAM_LOCK (self); + + gst_adapter_flush (self->output_adapter, + gst_adapter_available (self->output_adapter)); + self->started = FALSE; + + return GST_FLOW_OK; +} diff --git a/omx/gstomxaudiodec.h b/omx/gstomxaudiodec.h new file mode 100644 index 0000000..0f4adc6 --- /dev/null +++ b/omx/gstomxaudiodec.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014, Sebastian Dröge + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_AUDIO_DEC_H__ +#define __GST_OMX_AUDIO_DEC_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "gstomx.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_AUDIO_DEC \ + (gst_omx_audio_dec_get_type()) +#define GST_OMX_AUDIO_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_AUDIO_DEC,GstOMXAudioDec)) +#define GST_OMX_AUDIO_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_AUDIO_DEC,GstOMXAudioDecClass)) +#define GST_OMX_AUDIO_DEC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_AUDIO_DEC,GstOMXAudioDecClass)) +#define GST_IS_OMX_AUDIO_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_AUDIO_DEC)) +#define GST_IS_OMX_AUDIO_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_AUDIO_DEC)) + +typedef struct _GstOMXAudioDec GstOMXAudioDec; +typedef struct _GstOMXAudioDecClass GstOMXAudioDecClass; + +struct _GstOMXAudioDec +{ + GstAudioDecoder parent; + + /* < protected > */ + GstOMXComponent *dec; + GstOMXPort *dec_in_port, *dec_out_port; + + GstBufferPool *in_port_pool, *out_port_pool; + + /* < private > */ + GstAudioInfo info; + GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]; + gint reorder_map[OMX_AUDIO_MAXCHANNELS]; + gboolean needs_reorder; + GstBuffer *codec_data; + /* TRUE if the component is configured and saw + * the first buffer */ + gboolean started; + + GstClockTime last_upstream_ts; + + /* Draining state */ + GMutex drain_lock; + GCond drain_cond; + /* TRUE if EOS buffers shouldn't be forwarded */ + gboolean draining; + + GstAdapter *output_adapter; + + GstFlowReturn downstream_flow_ret; +}; + +struct _GstOMXAudioDecClass +{ + GstAudioDecoderClass parent_class; + + GstOMXClassData cdata; + + gboolean (*is_format_change) (GstOMXAudioDec * self, GstOMXPort * port, GstCaps * caps); + gboolean (*set_format) (GstOMXAudioDec * self, GstOMXPort * port, GstCaps * caps); + gint (*get_samples_per_frame) (GstOMXAudioDec * self, GstOMXPort * port); + gboolean (*get_channel_positions) (GstOMXAudioDec * self, GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]); +}; + +GType gst_omx_audio_dec_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_AUDIO_DEC_H__ */ diff --git a/omx/gstomxaudioenc.c b/omx/gstomxaudioenc.c index 9718f8f..8724f5e 100644 --- a/omx/gstomxaudioenc.c +++ b/omx/gstomxaudioenc.c @@ -37,12 +37,12 @@ static GstStateChangeReturn gst_omx_audio_enc_change_state (GstElement * element, GstStateChange transition); +static gboolean gst_omx_audio_enc_open (GstAudioEncoder * encoder); +static gboolean gst_omx_audio_enc_close (GstAudioEncoder * encoder); static gboolean gst_omx_audio_enc_start (GstAudioEncoder * encoder); static gboolean gst_omx_audio_enc_stop (GstAudioEncoder * encoder); static gboolean gst_omx_audio_enc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info); -static gboolean gst_omx_audio_enc_sink_event (GstAudioEncoder * encoder, - GstEvent * event); static GstFlowReturn gst_omx_audio_enc_handle_frame (GstAudioEncoder * encoder, GstBuffer * buffer); static void gst_omx_audio_enc_flush (GstAudioEncoder * encoder); @@ -75,6 +75,8 @@ gst_omx_audio_enc_class_init (GstOMXAudioEncClass * klass) element_class->change_state = GST_DEBUG_FUNCPTR (gst_omx_audio_enc_change_state); + audio_encoder_class->open = GST_DEBUG_FUNCPTR (gst_omx_audio_enc_open); + audio_encoder_class->close = GST_DEBUG_FUNCPTR (gst_omx_audio_enc_close); audio_encoder_class->start = GST_DEBUG_FUNCPTR (gst_omx_audio_enc_start); audio_encoder_class->stop = GST_DEBUG_FUNCPTR (gst_omx_audio_enc_stop); audio_encoder_class->flush = GST_DEBUG_FUNCPTR (gst_omx_audio_enc_flush); @@ -82,9 +84,8 @@ gst_omx_audio_enc_class_init (GstOMXAudioEncClass * klass) GST_DEBUG_FUNCPTR (gst_omx_audio_enc_set_format); audio_encoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_omx_audio_enc_handle_frame); - audio_encoder_class->sink_event = - GST_DEBUG_FUNCPTR (gst_omx_audio_enc_sink_event); + klass->cdata.type = GST_OMX_COMPONENT_TYPE_FILTER; klass->cdata.default_sink_template_caps = "audio/x-raw, " "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, " G_STRINGIFY (OMX_AUDIO_MAXCHANNELS) " ], " @@ -100,8 +101,9 @@ gst_omx_audio_enc_init (GstOMXAudioEnc * self) } static gboolean -gst_omx_audio_enc_open (GstOMXAudioEnc * self) +gst_omx_audio_enc_open (GstAudioEncoder * encoder) { + GstOMXAudioEnc *self = GST_OMX_AUDIO_ENC (encoder); GstOMXAudioEncClass *klass = GST_OMX_AUDIO_ENC_GET_CLASS (self); gint in_port_index, out_port_index; @@ -137,8 +139,8 @@ gst_omx_audio_enc_open (GstOMXAudioEnc * self) in_port_index = 0; out_port_index = 1; } else { - GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", param.nPorts, - param.nStartPortNumber); + GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", + (guint) param.nPorts, (guint) param.nStartPortNumber); in_port_index = param.nStartPortNumber + 0; out_port_index = param.nStartPortNumber + 1; } @@ -178,8 +180,10 @@ gst_omx_audio_enc_shutdown (GstOMXAudioEnc * self) } static gboolean -gst_omx_audio_enc_close (GstOMXAudioEnc * self) +gst_omx_audio_enc_close (GstAudioEncoder * encoder) { + GstOMXAudioEnc *self = GST_OMX_AUDIO_ENC (encoder); + GST_DEBUG_OBJECT (self, "Closing encoder"); if (!gst_omx_audio_enc_shutdown (self)) @@ -217,8 +221,6 @@ gst_omx_audio_enc_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: - if (!gst_omx_audio_enc_open (self)) - ret = GST_STATE_CHANGE_FAILURE; break; case GST_STATE_CHANGE_READY_TO_PAUSED: self->downstream_flow_ret = GST_FLOW_OK; @@ -243,9 +245,6 @@ gst_omx_audio_enc_change_state (GstElement * element, GstStateChange transition) break; } - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - ret = GST_ELEMENT_CLASS (gst_omx_audio_enc_parent_class)->change_state (element, transition); @@ -264,8 +263,6 @@ gst_omx_audio_enc_change_state (GstElement * element, GstStateChange transition) ret = GST_STATE_CHANGE_FAILURE; break; case GST_STATE_CHANGE_READY_TO_NULL: - if (!gst_omx_audio_enc_close (self)) - ret = GST_STATE_CHANGE_FAILURE; break; default: break; @@ -381,8 +378,8 @@ gst_omx_audio_enc_loop (GstOMXAudioEnc * self) goto eos; } - GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %lu", buf->omx_buf->nFlags, - buf->omx_buf->nTimeStamp); + GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %" G_GUINT64_FORMAT, + (guint) buf->omx_buf->nFlags, (guint64) buf->omx_buf->nTimeStamp); /* This prevents a deadlock between the srcpad stream * lock and the videocodec stream lock, if ::reset() @@ -513,6 +510,8 @@ eos: flow_ret = GST_FLOW_EOS; } g_mutex_unlock (&self->drain_lock); + + GST_AUDIO_ENCODER_STREAM_LOCK (self); self->downstream_flow_ret = flow_ret; /* Here we fallback and pause the task for the EOS case */ @@ -531,15 +530,20 @@ flow_error: gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); - } else if (flow_ret == GST_FLOW_NOT_LINKED || flow_ret < GST_FLOW_EOS) { + self->started = FALSE; + } else if (flow_ret < GST_FLOW_EOS) { GST_ELEMENT_ERROR (self, STREAM, FAILED, ("Internal data stream error."), ("stream stopped, reason %s", gst_flow_get_name (flow_ret))); gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); + self->started = FALSE; + } else if (flow_ret == GST_FLOW_FLUSHING) { + GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); + gst_pad_pause_task (GST_AUDIO_ENCODER_SRC_PAD (self)); + self->started = FALSE; } - self->started = FALSE; GST_AUDIO_ENCODER_STREAM_UNLOCK (self); return; } @@ -584,7 +588,6 @@ gst_omx_audio_enc_start (GstAudioEncoder * encoder) self = GST_OMX_AUDIO_ENC (encoder); self->last_upstream_ts = 0; - self->eos = FALSE; self->downstream_flow_ret = GST_FLOW_OK; return TRUE; @@ -609,7 +612,6 @@ gst_omx_audio_enc_stop (GstAudioEncoder * encoder) self->downstream_flow_ret = GST_FLOW_FLUSHING; self->started = FALSE; - self->eos = FALSE; g_mutex_lock (&self->drain_lock); self->draining = FALSE; @@ -664,26 +666,37 @@ gst_omx_audio_enc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info) gst_pad_stop_task (GST_AUDIO_ENCODER_SRC_PAD (encoder)); GST_AUDIO_ENCODER_STREAM_LOCK (self); - if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_wait_buffers_released (self->enc_in_port, - 5 * GST_SECOND) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_wait_buffers_released (self->enc_out_port, - 1 * GST_SECOND) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_wait_enabled (self->enc_in_port, - 1 * GST_SECOND) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_wait_enabled (self->enc_out_port, - 1 * GST_SECOND) != OMX_ErrorNone) - return FALSE; + if (klass->cdata.hacks & GST_OMX_HACK_NO_COMPONENT_RECONFIGURE) { + GST_AUDIO_ENCODER_STREAM_UNLOCK (self); + gst_omx_audio_enc_stop (GST_AUDIO_ENCODER (self)); + gst_omx_audio_enc_close (GST_AUDIO_ENCODER (self)); + GST_AUDIO_ENCODER_STREAM_LOCK (self); + + if (!gst_omx_audio_enc_open (GST_AUDIO_ENCODER (self))) + return FALSE; + needs_disable = FALSE; + } else { + if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_buffers_released (self->enc_in_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_buffers_released (self->enc_out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_enabled (self->enc_in_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_enabled (self->enc_out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + } GST_DEBUG_OBJECT (self, "Encoder drained and disabled"); } @@ -775,26 +788,51 @@ gst_omx_audio_enc_set_format (GstAudioEncoder * encoder, GstAudioInfo * info) return FALSE; if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) return FALSE; + + if ((klass->cdata.hacks & GST_OMX_HACK_NO_DISABLE_OUTPORT)) { + if (gst_omx_port_set_enabled (self->enc_out_port, TRUE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->enc_out_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + } + if (gst_omx_port_wait_enabled (self->enc_in_port, 5 * GST_SECOND) != OMX_ErrorNone) return FALSE; if (gst_omx_port_mark_reconfigured (self->enc_in_port) != OMX_ErrorNone) return FALSE; } else { - if (gst_omx_component_set_state (self->enc, OMX_StateIdle) != OMX_ErrorNone) - return FALSE; - - /* Need to allocate buffers to reach Idle state */ - if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) - return FALSE; - - /* And disable output port */ - if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) - return FALSE; - - if (gst_omx_port_wait_enabled (self->enc_out_port, - 1 * GST_SECOND) != OMX_ErrorNone) - return FALSE; + if (!(klass->cdata.hacks & GST_OMX_HACK_NO_DISABLE_OUTPORT)) { + /* Disable output port */ + if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->enc_out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_component_set_state (self->enc, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; + + /* Need to allocate buffers to reach Idle state */ + if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) + return FALSE; + } else { + if (gst_omx_component_set_state (self->enc, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; + + /* Need to allocate buffers to reach Idle state */ + if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) + return FALSE; + } if (gst_omx_component_get_state (self->enc, GST_CLOCK_TIME_NONE) != OMX_StateIdle) @@ -838,8 +876,6 @@ gst_omx_audio_enc_flush (GstAudioEncoder * encoder) GST_DEBUG_OBJECT (self, "Resetting encoder"); - gst_omx_audio_enc_drain (self); - gst_omx_port_set_flushing (self->enc_in_port, 5 * GST_SECOND, TRUE); gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); @@ -856,7 +892,7 @@ gst_omx_audio_enc_flush (GstAudioEncoder * encoder) /* Start the srcpad loop again */ self->last_upstream_ts = 0; self->downstream_flow_ret = GST_FLOW_OK; - self->eos = FALSE; + self->started = FALSE; gst_pad_start_task (GST_AUDIO_ENCODER_SRC_PAD (self), (GstTaskFunction) gst_omx_audio_enc_loop, encoder, NULL); } @@ -875,17 +911,12 @@ gst_omx_audio_enc_handle_frame (GstAudioEncoder * encoder, GstBuffer * inbuf) self = GST_OMX_AUDIO_ENC (encoder); - if (self->eos) { - GST_WARNING_OBJECT (self, "Got frame after EOS"); - return GST_FLOW_EOS; - } - if (self->downstream_flow_ret != GST_FLOW_OK) { return self->downstream_flow_ret; } if (inbuf == NULL) - return GST_FLOW_OK; + return gst_omx_audio_enc_drain (self); GST_DEBUG_OBJECT (self, "Handling frame"); @@ -1001,6 +1032,9 @@ gst_omx_audio_enc_handle_frame (GstAudioEncoder * encoder, GstBuffer * inbuf) if (duration != GST_CLOCK_TIME_NONE) { buf->omx_buf->nTickCount = gst_util_uint64_scale (buf->omx_buf->nFilledLen, duration, size); + buf->omx_buf->nTickCount = + gst_util_uint64_scale (buf->omx_buf->nTickCount, + OMX_TICKS_PER_SECOND, GST_SECOND); self->last_upstream_ts += duration; } @@ -1019,7 +1053,7 @@ full_buffer: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Got OpenMAX buffer with no free space (%p, %u/%u)", buf, - buf->omx_buf->nOffset, buf->omx_buf->nAllocLen)); + (guint) buf->omx_buf->nOffset, (guint) buf->omx_buf->nAllocLen)); return GST_FLOW_ERROR; } component_error: @@ -1051,77 +1085,6 @@ release_error: } } -static gboolean -gst_omx_audio_enc_sink_event (GstAudioEncoder * encoder, GstEvent * event) -{ - GstOMXAudioEnc *self; - GstOMXAudioEncClass *klass; - OMX_ERRORTYPE err; - - self = GST_OMX_AUDIO_ENC (encoder); - klass = GST_OMX_AUDIO_ENC_GET_CLASS (self); - - if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { - GstOMXBuffer *buf; - GstOMXAcquireBufferReturn acq_ret; - - GST_DEBUG_OBJECT (self, "Sending EOS to the component"); - - /* Don't send EOS buffer twice, this doesn't work */ - if (self->eos) { - GST_DEBUG_OBJECT (self, "Component is already EOS"); - return TRUE; - } - self->eos = TRUE; - - if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) { - GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers"); - - /* Insert a NULL into the queue to signal EOS */ - g_mutex_lock (&self->enc->lock); - g_queue_push_tail (&self->enc_out_port->pending_buffers, NULL); - g_mutex_unlock (&self->enc->lock); - g_mutex_lock (&self->enc->messages_lock); - g_cond_broadcast (&self->enc->messages_cond); - g_mutex_unlock (&self->enc->messages_lock); - return TRUE; - } - - /* Make sure to release the base class stream lock, otherwise - * _loop() can't call _finish_frame() and we might block forever - * because no input buffers are released */ - GST_AUDIO_ENCODER_STREAM_UNLOCK (self); - - /* Send an EOS buffer to the component and let the base - * class drop the EOS event. We will send it later when - * the EOS buffer arrives on the output port. */ - acq_ret = gst_omx_port_acquire_buffer (self->enc_in_port, &buf); - if (acq_ret == GST_OMX_ACQUIRE_BUFFER_OK) { - buf->omx_buf->nFilledLen = 0; - buf->omx_buf->nTimeStamp = - gst_util_uint64_scale (self->last_upstream_ts, OMX_TICKS_PER_SECOND, - GST_SECOND); - buf->omx_buf->nTickCount = 0; - buf->omx_buf->nFlags |= OMX_BUFFERFLAG_EOS; - err = gst_omx_port_release_buffer (self->enc_in_port, buf); - if (err != OMX_ErrorNone) { - GST_ERROR_OBJECT (self, "Failed to send EOS to component: %s (0x%08x)", - gst_omx_error_to_string (err), err); - } else { - GST_DEBUG_OBJECT (self, "Sent EOS to the component"); - } - } else { - GST_ERROR_OBJECT (self, "Failed to acquire buffer for EOS: %d", acq_ret); - } - - GST_AUDIO_ENCODER_STREAM_LOCK (self); - - return TRUE; - } - - return FALSE; -} - static GstFlowReturn gst_omx_audio_enc_drain (GstOMXAudioEnc * self) { @@ -1140,12 +1103,6 @@ gst_omx_audio_enc_drain (GstOMXAudioEnc * self) } self->started = FALSE; - /* Don't send EOS buffer twice, this doesn't work */ - if (self->eos) { - GST_DEBUG_OBJECT (self, "Component is EOS already"); - return GST_FLOW_OK; - } - if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) { GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers"); return GST_FLOW_OK; diff --git a/omx/gstomxaudioenc.h b/omx/gstomxaudioenc.h index f7527be..8fe5369 100644 --- a/omx/gstomxaudioenc.h +++ b/omx/gstomxaudioenc.h @@ -60,9 +60,6 @@ struct _GstOMXAudioEnc GstClockTime last_upstream_ts; - /* TRUE if upstream is EOS */ - gboolean eos; - /* Draining state */ GMutex drain_lock; GCond drain_cond; diff --git a/omx/gstomxaudiosink.c b/omx/gstomxaudiosink.c new file mode 100644 index 0000000..1739739 --- /dev/null +++ b/omx/gstomxaudiosink.c @@ -0,0 +1,1228 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + * Author: Josep Torra + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include +#include + +#include + +#include "gstomxaudiosink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_audio_sink_debug_category); +#define GST_CAT_DEFAULT gst_omx_audio_sink_debug_category + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_omx_audio_sink_debug_category, "omxaudiosink", \ + 0, "debug category for gst-omx audio sink base class"); + +#define DEFAULT_PROP_MUTE FALSE +#define DEFAULT_PROP_VOLUME 1.0 + +#define VOLUME_MAX_DOUBLE 10.0 +#define OUT_CHANNELS(num_channels) ((num_channels) > 4 ? 8: (num_channels) > 2 ? 4: (num_channels)) + +enum +{ + PROP_0, + PROP_MUTE, + PROP_VOLUME +}; + +#define gst_omx_audio_sink_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstOMXAudioSink, gst_omx_audio_sink, + GST_TYPE_AUDIO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL); + DEBUG_INIT); + +#define transform_3_4(type) \ +static inline void \ +transform_3_4_##type (gpointer psrc, gpointer pdst, guint len) \ +{ \ + g##type *src = (g##type *) psrc; \ + g##type *dst = (g##type *) pdst; \ + for (; len > 0; len--) { \ + dst[0] = src[0]; \ + dst[1] = src[1]; \ + dst[2] = src[2]; \ + dst[3] = 0; \ + src += 3; \ + dst += 4; \ + } \ +} + +#define transform_5_8(type) \ +static inline void \ +transform_5_8_##type (gpointer psrc, gpointer pdst, guint len) \ +{ \ + g##type *src = (g##type *) psrc; \ + g##type *dst = (g##type *) pdst; \ + for (; len > 0; len--) { \ + dst[0] = src[0]; \ + dst[1] = src[1]; \ + dst[2] = src[2]; \ + dst[3] = src[3]; \ + dst[4] = src[4]; \ + dst[5] = 0; \ + dst[6] = 0; \ + dst[7] = 0; \ + src += 5; \ + dst += 8; \ + } \ +} + +#define transform_6_8(type) \ +static inline void \ +transform_6_8_##type (gpointer psrc, gpointer pdst, guint len) \ +{ \ + g##type *src = (g##type *) psrc; \ + g##type *dst = (g##type *) pdst; \ + for (; len > 0; len--) { \ + dst[0] = src[0]; \ + dst[1] = src[1]; \ + dst[2] = src[2]; \ + dst[3] = src[3]; \ + dst[4] = src[4]; \ + dst[5] = src[5]; \ + dst[6] = 0; \ + dst[7] = 0; \ + src += 6; \ + dst += 8; \ + } \ +} + +#define transform_7_8(type) \ +static inline void \ +transform_7_8_##type (gpointer psrc, gpointer pdst, guint len) \ +{ \ + g##type *src = (g##type *) psrc; \ + g##type *dst = (g##type *) pdst; \ + for (; len > 0; len--) { \ + dst[0] = src[0]; \ + dst[1] = src[1]; \ + dst[2] = src[2]; \ + dst[3] = src[3]; \ + dst[4] = src[4]; \ + dst[5] = src[5]; \ + dst[6] = src[6]; \ + dst[7] = 0; \ + src += 7; \ + dst += 8; \ + } \ +} + +transform_3_4 (int16); +transform_5_8 (int16); +transform_6_8 (int16); +transform_7_8 (int16); + +transform_3_4 (int32); +transform_5_8 (int32); +transform_6_8 (int32); +transform_7_8 (int32); + +static void inline +transform (guint in_chan, guint width, gpointer psrc, gpointer pdst, guint len) +{ + guint out_chan = OUT_CHANNELS (in_chan); + if (width == 16) { + switch (out_chan) { + case 4: + if (in_chan == 3) { + transform_3_4_int16 (psrc, pdst, len); + } else { + g_assert (FALSE); + } + break; + case 8: + switch (in_chan) { + case 5: + transform_5_8_int16 (psrc, pdst, len); + break; + case 6: + transform_6_8_int16 (psrc, pdst, len); + break; + case 7: + transform_7_8_int16 (psrc, pdst, len); + break; + default: + g_assert (FALSE); + } + break; + default: + g_assert (FALSE); + } + } else if (width == 32) { + switch (out_chan) { + case 4: + if (in_chan == 3) { + transform_3_4_int32 (psrc, pdst, len); + } else { + g_assert (FALSE); + } + break; + case 8: + switch (in_chan) { + case 5: + transform_5_8_int32 (psrc, pdst, len); + break; + case 6: + transform_6_8_int32 (psrc, pdst, len); + break; + case 7: + transform_7_8_int32 (psrc, pdst, len); + break; + default: + g_assert (FALSE); + } + break; + default: + g_assert (FALSE); + } + } else { + g_assert (FALSE); + } +} + +static void +gst_omx_audio_sink_mute_set (GstOMXAudioSink * self, gboolean mute) +{ + if (self->comp) { + OMX_ERRORTYPE err; + OMX_AUDIO_CONFIG_MUTETYPE param; + + GST_OMX_INIT_STRUCT (¶m); + param.nPortIndex = self->in_port->index; + param.bMute = (mute ? OMX_TRUE : OMX_FALSE); + err = gst_omx_component_set_config (self->comp, + OMX_IndexConfigAudioMute, ¶m); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set mute to %d: %s (0x%08x)", + param.bMute, gst_omx_error_to_string (err), err); + } + } + self->mute = mute; +} + +static void +gst_omx_audio_sink_volume_set (GstOMXAudioSink * self, gdouble volume) +{ + if (self->comp) { + OMX_ERRORTYPE err; + OMX_AUDIO_CONFIG_VOLUMETYPE param; + GST_OMX_INIT_STRUCT (¶m); + param.nPortIndex = self->in_port->index; + param.bLinear = OMX_TRUE; + param.sVolume.nValue = volume * 100; + err = gst_omx_component_set_config (self->comp, + OMX_IndexConfigAudioVolume, ¶m); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set volume to %d: %s (0x%08x)", + (gint) param.sVolume.nValue, gst_omx_error_to_string (err), err); + } + } + self->volume = volume; +} + +static gboolean +gst_omx_audio_sink_open (GstAudioSink * audiosink) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); + GstOMXAudioSinkClass *klass = GST_OMX_AUDIO_SINK_GET_CLASS (self); + gint port_index; + OMX_ERRORTYPE err; + + GST_DEBUG_OBJECT (self, "Opening audio sink"); + + self->comp = + gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name, + klass->cdata.component_name, klass->cdata.component_role, + klass->cdata.hacks); + + if (!self->comp) + return FALSE; + + if (gst_omx_component_get_state (self->comp, + GST_CLOCK_TIME_NONE) != OMX_StateLoaded) + return FALSE; + + port_index = klass->cdata.in_port_index; + + if (port_index == -1) { + OMX_PORT_PARAM_TYPE param; + + GST_OMX_INIT_STRUCT (¶m); + + err = + gst_omx_component_get_parameter (self->comp, OMX_IndexParamAudioInit, + ¶m); + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)", + gst_omx_error_to_string (err), err); + /* Fallback */ + port_index = 0; + } else { + GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", + (guint) param.nPorts, (guint) param.nStartPortNumber); + port_index = param.nStartPortNumber + 0; + } + } + self->in_port = gst_omx_component_add_port (self->comp, port_index); + + port_index = klass->cdata.out_port_index; + + if (port_index == -1) { + OMX_PORT_PARAM_TYPE param; + + GST_OMX_INIT_STRUCT (¶m); + + err = + gst_omx_component_get_parameter (self->comp, OMX_IndexParamAudioInit, + ¶m); + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)", + gst_omx_error_to_string (err), err); + /* Fallback */ + port_index = 0; + } else { + GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", + (guint) param.nPorts, (guint) param.nStartPortNumber); + port_index = param.nStartPortNumber + 1; + } + } + self->out_port = gst_omx_component_add_port (self->comp, port_index); + + if (!self->in_port || !self->out_port) + return FALSE; + + err = gst_omx_port_set_enabled (self->in_port, FALSE); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to disable port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + err = gst_omx_port_set_enabled (self->out_port, FALSE); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to disable port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + GST_DEBUG_OBJECT (self, "Opened audio sink"); + + return TRUE; +} + +static gboolean +gst_omx_audio_sink_close (GstAudioSink * audiosink) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); + OMX_STATETYPE state; + + GST_DEBUG_OBJECT (self, "Closing audio sink"); + + state = gst_omx_component_get_state (self->comp, 0); + if (state > OMX_StateLoaded || state == OMX_StateInvalid) { + if (state > OMX_StateIdle) { + gst_omx_component_set_state (self->comp, OMX_StateIdle); + gst_omx_component_get_state (self->comp, 5 * GST_SECOND); + } + gst_omx_component_set_state (self->comp, OMX_StateLoaded); + gst_omx_port_deallocate_buffers (self->in_port); + if (state > OMX_StateLoaded) + gst_omx_component_get_state (self->comp, 5 * GST_SECOND); + } + + self->in_port = NULL; + self->out_port = NULL; + if (self->comp) + gst_omx_component_free (self->comp); + self->comp = NULL; + + GST_DEBUG_OBJECT (self, "Closed audio sink"); + + return TRUE; +} + +static gboolean +gst_omx_audio_sink_parse_spec (GstOMXAudioSink * self, + GstAudioRingBufferSpec * spec) +{ + self->iec61937 = FALSE; + self->endianness = GST_AUDIO_INFO_ENDIANNESS (&spec->info); + self->rate = GST_AUDIO_INFO_RATE (&spec->info); + self->channels = GST_AUDIO_INFO_CHANNELS (&spec->info); + self->width = GST_AUDIO_INFO_WIDTH (&spec->info); + self->is_signed = GST_AUDIO_INFO_IS_SIGNED (&spec->info); + self->is_float = GST_AUDIO_INFO_IS_FLOAT (&spec->info); + + switch (spec->type) { + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW: + { + guint out_channels = OUT_CHANNELS (self->channels); + + self->samples = spec->segsize / self->channels / (self->width >> 3); + if (self->channels == out_channels) { + self->buffer_size = spec->segsize; + } else { + self->buffer_size = (spec->segsize / self->channels) * out_channels; + } + break; + } + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3: + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_EAC3: + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS: + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG: + self->iec61937 = TRUE; + self->endianness = G_LITTLE_ENDIAN; + self->channels = 2; + self->width = 16; + self->is_signed = TRUE; + self->is_float = FALSE; + self->buffer_size = spec->segsize; + break; + default: + return FALSE; + } + + return TRUE; +} + +static inline void +channel_mapping (GstAudioRingBufferSpec * spec, + OMX_AUDIO_CHANNELTYPE * eChannelMapping) +{ + gint i, nchan = GST_AUDIO_INFO_CHANNELS (&spec->info); + + for (i = 0; i < nchan; i++) { + OMX_AUDIO_CHANNELTYPE pos; + + switch (GST_AUDIO_INFO_POSITION (&spec->info, i)) { + case GST_AUDIO_CHANNEL_POSITION_MONO: + case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER: + pos = OMX_AUDIO_ChannelCF; + break; + case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT: + pos = OMX_AUDIO_ChannelLF; + break; + case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT: + pos = OMX_AUDIO_ChannelRF; + break; + case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT: + pos = OMX_AUDIO_ChannelLS; + break; + case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT: + pos = OMX_AUDIO_ChannelRS; + break; + case GST_AUDIO_CHANNEL_POSITION_LFE1: + pos = OMX_AUDIO_ChannelLFE; + break; + case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER: + pos = OMX_AUDIO_ChannelCS; + break; + case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT: + pos = OMX_AUDIO_ChannelLR; + break; + case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT: + pos = OMX_AUDIO_ChannelRR; + break; + default: + pos = OMX_AUDIO_ChannelNone; + break; + } + eChannelMapping[i] = pos; + } +} + +static inline const gchar * +ch2str (OMX_AUDIO_CHANNELTYPE ch) +{ + switch (ch) { + case OMX_AUDIO_ChannelNone: + return "OMX_AUDIO_ChannelNone"; + case OMX_AUDIO_ChannelLF: + return "OMX_AUDIO_ChannelLF"; + case OMX_AUDIO_ChannelRF: + return "OMX_AUDIO_ChannelRF"; + case OMX_AUDIO_ChannelCF: + return "OMX_AUDIO_ChannelCF"; + case OMX_AUDIO_ChannelLS: + return "OMX_AUDIO_ChannelLS"; + case OMX_AUDIO_ChannelRS: + return "OMX_AUDIO_ChannelRS"; + case OMX_AUDIO_ChannelLFE: + return "OMX_AUDIO_ChannelLFE"; + case OMX_AUDIO_ChannelCS: + return "OMX_AUDIO_ChannelCS"; + case OMX_AUDIO_ChannelLR: + return "OMX_AUDIO_ChannelLR"; + case OMX_AUDIO_ChannelRR: + return "OMX_AUDIO_ChannelRR"; + default: + return "Invalid value"; + } +} + +static inline gboolean +gst_omx_audio_sink_configure_pcm (GstOMXAudioSink * self, + GstAudioRingBufferSpec * spec) +{ + OMX_AUDIO_PARAM_PCMMODETYPE param; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (¶m); + param.nPortIndex = self->in_port->index; + param.nChannels = OUT_CHANNELS (self->channels); + param.eNumData = + (self->is_signed ? OMX_NumericalDataSigned : OMX_NumericalDataUnsigned); + param.eEndian = + ((self->endianness == + G_LITTLE_ENDIAN) ? OMX_EndianLittle : OMX_EndianBig); + param.bInterleaved = OMX_TRUE; + param.nBitPerSample = self->width; + param.nSamplingRate = self->rate; + + if (self->is_float) { + /* This is cherrypicked from xbmc but it doesn't seems to be valid on my RPI. + * https://github.com/xbmc/xbmc/blob/master/xbmc/cores/AudioEngine/Sinks/AESinkPi.cpp + */ + param.ePCMMode = (OMX_AUDIO_PCMMODETYPE) 0x8000; + } else { + param.ePCMMode = OMX_AUDIO_PCMModeLinear; + } + + if (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW) { + channel_mapping (spec, ¶m.eChannelMapping[0]); + } + + GST_DEBUG_OBJECT (self, "Setting PCM parameters"); + GST_DEBUG_OBJECT (self, " nChannels: %u", (guint) param.nChannels); + GST_DEBUG_OBJECT (self, " eNumData: %s", + (param.eNumData == OMX_NumericalDataSigned ? "signed" : "unsigned")); + GST_DEBUG_OBJECT (self, " eEndian: %s", + (param.eEndian == OMX_EndianLittle ? "little endian" : "big endian")); + GST_DEBUG_OBJECT (self, " bInterleaved: %d", param.bInterleaved); + GST_DEBUG_OBJECT (self, " nBitPerSample: %u", (guint) param.nBitPerSample); + GST_DEBUG_OBJECT (self, " nSamplingRate: %u", (guint) param.nSamplingRate); + GST_DEBUG_OBJECT (self, " ePCMMode: %04x", param.ePCMMode); + GST_DEBUG_OBJECT (self, " eChannelMapping: {%s, %s, %s, %s, %s, %s, %s, %s}", + ch2str (param.eChannelMapping[0]), ch2str (param.eChannelMapping[1]), + ch2str (param.eChannelMapping[2]), ch2str (param.eChannelMapping[3]), + ch2str (param.eChannelMapping[4]), ch2str (param.eChannelMapping[5]), + ch2str (param.eChannelMapping[6]), ch2str (param.eChannelMapping[7])); + + err = + gst_omx_component_set_parameter (self->comp, OMX_IndexParamAudioPcm, + ¶m); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set PCM parameters: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_omx_audio_sink_prepare (GstAudioSink * audiosink, + GstAudioRingBufferSpec * spec) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); + OMX_PARAM_PORTDEFINITIONTYPE port_def; + OMX_ERRORTYPE err; + + if (!gst_omx_audio_sink_parse_spec (self, spec)) + goto spec_parse; + + gst_omx_port_get_port_definition (self->in_port, &port_def); + + port_def.nBufferSize = self->buffer_size; + /* Only allocate a min number of buffers for transfers from our ringbuffer to + * the hw ringbuffer as we want to keep our small */ + port_def.nBufferCountActual = MAX (port_def.nBufferCountMin, 2); + port_def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + GST_DEBUG_OBJECT (self, "Updating outport port definition"); + GST_DEBUG_OBJECT (self, " nBufferSize: %u", (guint) port_def.nBufferSize); + GST_DEBUG_OBJECT (self, " nBufferCountActual: %u", (guint) + port_def.nBufferCountActual); + GST_DEBUG_OBJECT (self, " audio.eEncoding: 0x%08x", + port_def.format.audio.eEncoding); + + err = gst_omx_port_update_port_definition (self->in_port, &port_def); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to configure port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto configuration; + } + + if (!gst_omx_audio_sink_configure_pcm (self, spec)) { + goto configuration; + } + + err = gst_omx_component_set_state (self->comp, OMX_StateIdle); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set state idle: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto activation; + } + + err = gst_omx_port_set_flushing (self->in_port, 5 * GST_SECOND, FALSE); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set port not flushing: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto activation; + } + + err = gst_omx_port_set_enabled (self->in_port, TRUE); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to enable port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto activation; + } + + GST_DEBUG_OBJECT (self, "Allocate buffers"); + err = gst_omx_port_allocate_buffers (self->in_port); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed on buffer allocation: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto activation; + } + + err = gst_omx_port_wait_enabled (self->in_port, 5 * GST_SECOND); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "port not enabled: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto activation; + } + + err = gst_omx_port_mark_reconfigured (self->in_port); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Couln't mark port as reconfigured: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto activation; + } + + err = gst_omx_component_set_state (self->comp, OMX_StatePause); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set state paused: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto activation; + } + + if (gst_omx_component_get_state (self->comp, + GST_CLOCK_TIME_NONE) != OMX_StatePause) + goto activation; + + /* Configure some parameters */ + GST_OBJECT_LOCK (self); + gst_omx_audio_sink_mute_set (self, self->mute); + gst_omx_audio_sink_volume_set (self, self->volume); + GST_OBJECT_UNLOCK (self); + +#if defined (USE_OMX_TARGET_RPI) + { + GstOMXAudioSinkClass *klass = GST_OMX_AUDIO_SINK_GET_CLASS (self); + OMX_ERRORTYPE err; + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE param; + + if (klass->destination + && strlen (klass->destination) < sizeof (param.sName)) { + GST_DEBUG_OBJECT (self, "Setting destination: %s", klass->destination); + GST_OMX_INIT_STRUCT (¶m); + strcpy ((char *) param.sName, klass->destination); + err = gst_omx_component_set_config (self->comp, + OMX_IndexConfigBrcmAudioDestination, ¶m); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to configuring destination: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto activation; + } + } + } +#endif + + return TRUE; + + /* ERRORS */ +spec_parse: + { + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (NULL), + ("Error parsing spec")); + return FALSE; + } + +configuration: + { + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (NULL), + ("Configuration failed")); + return FALSE; + } +activation: + { + GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS, (NULL), + ("Component activation failed")); + return FALSE; + } +} + +static gboolean +gst_omx_audio_sink_unprepare (GstAudioSink * audiosink) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); + OMX_ERRORTYPE err; + + if (gst_omx_component_get_state (self->comp, 0) == OMX_StateIdle) + return TRUE; + + err = gst_omx_port_set_flushing (self->in_port, 5 * GST_SECOND, TRUE); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set port flushing: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto failed; + } + + err = gst_omx_component_set_state (self->comp, OMX_StateIdle); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set state idle: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto failed; + } + + err = gst_omx_port_set_enabled (self->in_port, FALSE); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set port disabled: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto failed; + } + + err = gst_omx_port_wait_buffers_released (self->in_port, 5 * GST_SECOND); + if (err != OMX_ErrorNone) { + goto failed; + } + + err = gst_omx_port_deallocate_buffers (self->in_port); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Couldn't deallocate buffers: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto failed; + } + + err = gst_omx_port_wait_enabled (self->in_port, 1 * GST_SECOND); + if (err != OMX_ErrorNone) { + goto failed; + } + + gst_omx_component_get_state (self->comp, GST_CLOCK_TIME_NONE); + + return TRUE; + + /* ERRORS */ +failed: + { + GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), + ("OpenMAX component in error state %s (0x%08x)", + gst_omx_component_get_last_error_string (self->comp), + gst_omx_component_get_last_error (self->comp))); + return FALSE; + } +} + +static GstOMXBuffer * +gst_omx_audio_sink_acquire_buffer (GstOMXAudioSink * self) +{ + GstOMXAcquireBufferReturn acq_ret = GST_OMX_ACQUIRE_BUFFER_ERROR; + GstOMXPort *port = self->in_port; + OMX_ERRORTYPE err; + GstOMXBuffer *buf = NULL; + + while (!buf) { + acq_ret = gst_omx_port_acquire_buffer (port, &buf); + if (acq_ret == GST_OMX_ACQUIRE_BUFFER_ERROR) { + goto component_error; + } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_FLUSHING) { + GST_DEBUG_OBJECT (self, "Flushing..."); + goto flushing; + } else if (acq_ret == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { + GST_DEBUG_OBJECT (self, "Reconfigure..."); + /* Reallocate all buffers */ + err = gst_omx_port_set_enabled (port, FALSE); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set port disabled: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto reconfigure_error; + } + + err = gst_omx_port_wait_buffers_released (port, 5 * GST_SECOND); + if (err != OMX_ErrorNone) { + goto reconfigure_error; + } + + err = gst_omx_port_deallocate_buffers (port); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Couldn't deallocate buffers: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto reconfigure_error; + } + + err = gst_omx_port_wait_enabled (port, 1 * GST_SECOND); + if (err != OMX_ErrorNone) { + goto reconfigure_error; + } + + err = gst_omx_port_set_enabled (port, TRUE); + if (err != OMX_ErrorNone) { + goto reconfigure_error; + } + + err = gst_omx_port_allocate_buffers (port); + if (err != OMX_ErrorNone) { + goto reconfigure_error; + } + + err = gst_omx_port_wait_enabled (port, 5 * GST_SECOND); + if (err != OMX_ErrorNone) { + goto reconfigure_error; + } + + err = gst_omx_port_mark_reconfigured (port); + if (err != OMX_ErrorNone) { + goto reconfigure_error; + } + continue; + } + } + + return buf; + + /* ERRORS */ +component_error: + { + GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), + ("OpenMAX component in error state %s (0x%08x)", + gst_omx_component_get_last_error_string (self->comp), + gst_omx_component_get_last_error (self->comp))); + return NULL; + } +reconfigure_error: + { + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), + ("Unable to reconfigure input port")); + return NULL; + } +flushing: + { + return NULL; + } +} + +static gint +gst_omx_audio_sink_write (GstAudioSink * audiosink, gpointer data, guint length) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); + GstOMXBuffer *buf; + OMX_ERRORTYPE err; + + GST_LOG_OBJECT (self, "received audio samples buffer of %u bytes", length); + + GST_OMX_AUDIO_SINK_LOCK (self); + + if (!(buf = gst_omx_audio_sink_acquire_buffer (self))) { + goto beach; + } + + if (buf->omx_buf->nAllocLen == length) { + memcpy (buf->omx_buf->pBuffer + buf->omx_buf->nOffset, data, length); + } else { + transform (self->channels, self->width, data, + buf->omx_buf->pBuffer + buf->omx_buf->nOffset, self->samples); + } + buf->omx_buf->nFilledLen = buf->omx_buf->nAllocLen; + + err = gst_omx_port_release_buffer (self->in_port, buf); + if (err != OMX_ErrorNone) + goto release_error; + +beach: + + GST_OMX_AUDIO_SINK_UNLOCK (self); + + return length; + + /* ERRORS */ +release_error: + { + GST_OMX_AUDIO_SINK_UNLOCK (self); + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, (NULL), + ("Failed to relase input buffer to component: %s (0x%08x)", + gst_omx_error_to_string (err), err)); + return 0; + } +} + +static guint +gst_omx_audio_sink_delay (GstAudioSink * audiosink) +{ +#if defined (USE_OMX_TARGET_RPI) + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); + OMX_PARAM_U32TYPE param; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (¶m); + param.nPortIndex = self->in_port->index; + param.nU32 = 0; + err = gst_omx_component_get_config (self->comp, + OMX_IndexConfigAudioRenderingLatency, ¶m); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to get rendering latency: %s (0x%08x)", + gst_omx_error_to_string (err), err); + param.nU32 = 0; + } + + GST_DEBUG_OBJECT (self, "reported delay %u samples", (guint) param.nU32); + return param.nU32; +#else + return 0; +#endif +} + +static void +gst_omx_audio_sink_reset (GstAudioSink * audiosink) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiosink); + OMX_STATETYPE state; + + GST_DEBUG_OBJECT (self, "Flushing sink"); + + gst_omx_port_set_flushing (self->in_port, 5 * GST_SECOND, TRUE); + + GST_OMX_AUDIO_SINK_LOCK (self); + if ((state = gst_omx_component_get_state (self->comp, 0)) > OMX_StatePause) { + gst_omx_component_set_state (self->comp, OMX_StatePause); + gst_omx_component_get_state (self->comp, GST_CLOCK_TIME_NONE); + } + + gst_omx_component_set_state (self->comp, state); + gst_omx_component_get_state (self->comp, GST_CLOCK_TIME_NONE); + + gst_omx_port_set_flushing (self->in_port, 5 * GST_SECOND, FALSE); + + GST_OMX_AUDIO_SINK_UNLOCK (self); +} + +static GstBuffer * +gst_omx_audio_sink_payload (GstAudioBaseSink * audiobasesink, GstBuffer * buf) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (audiobasesink); + + if (self->iec61937) { + GstBuffer *out; + gint framesize; + GstMapInfo iinfo, oinfo; + GstAudioRingBufferSpec *spec = &audiobasesink->ringbuffer->spec; + + framesize = gst_audio_iec61937_frame_size (spec); + if (framesize <= 0) + return NULL; + + out = gst_buffer_new_and_alloc (framesize); + + gst_buffer_map (buf, &iinfo, GST_MAP_READ); + gst_buffer_map (out, &oinfo, GST_MAP_WRITE); + + if (!gst_audio_iec61937_payload (iinfo.data, iinfo.size, + oinfo.data, oinfo.size, spec, G_BIG_ENDIAN)) { + gst_buffer_unref (out); + return NULL; + } + + gst_buffer_unmap (buf, &iinfo); + gst_buffer_unmap (out, &oinfo); + + gst_buffer_copy_into (out, buf, GST_BUFFER_COPY_METADATA, 0, -1); + return out; + } + + return gst_buffer_ref (buf); +} + +static gboolean +gst_omx_audio_sink_accept_caps (GstOMXAudioSink * self, GstCaps * caps) +{ + GstPad *pad = GST_BASE_SINK (self)->sinkpad; + GstCaps *pad_caps; + GstStructure *st; + gboolean ret = FALSE; + GstAudioRingBufferSpec spec = { 0 }; + + pad_caps = gst_pad_query_caps (pad, caps); + if (!pad_caps || gst_caps_is_empty (pad_caps)) { + if (pad_caps) + gst_caps_unref (pad_caps); + ret = FALSE; + goto done; + } + gst_caps_unref (pad_caps); + + /* If we've not got fixed caps, creating a stream might fail, so let's just + * return from here with default acceptcaps behaviour */ + if (!gst_caps_is_fixed (caps)) + goto done; + + /* parse helper expects this set, so avoid nasty warning + * will be set properly later on anyway */ + spec.latency_time = GST_SECOND; + if (!gst_audio_ring_buffer_parse_caps (&spec, caps)) + goto done; + + /* Make sure input is framed (one frame per buffer) and can be payloaded */ + switch (spec.type) { + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3: + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_EAC3: + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS: + case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MPEG: + { + gboolean framed = FALSE, parsed = FALSE; + st = gst_caps_get_structure (caps, 0); + + gst_structure_get_boolean (st, "framed", &framed); + gst_structure_get_boolean (st, "parsed", &parsed); + if ((!framed && !parsed) || gst_audio_iec61937_frame_size (&spec) <= 0) + goto done; + } + default:{ + } + } + ret = TRUE; + +done: + gst_caps_replace (&spec.caps, NULL); + return ret; +} + +static gboolean +gst_omx_audio_sink_query (GstBaseSink * basesink, GstQuery * query) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (basesink); + gboolean ret; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_ACCEPT_CAPS: + { + GstCaps *caps; + + gst_query_parse_accept_caps (query, &caps); + ret = gst_omx_audio_sink_accept_caps (self, caps); + gst_query_set_accept_caps_result (query, ret); + ret = TRUE; + break; + } + default: + ret = GST_BASE_SINK_CLASS (parent_class)->query (basesink, query); + break; + } + return ret; +} + +static void +gst_omx_audio_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (object); + + switch (prop_id) { + case PROP_MUTE: + { + gboolean mute = g_value_get_boolean (value); + GST_OBJECT_LOCK (self); + if (self->mute != mute) { + gst_omx_audio_sink_mute_set (self, mute); + } + GST_OBJECT_UNLOCK (self); + break; + } + case PROP_VOLUME: + { + gdouble volume = g_value_get_double (value); + GST_OBJECT_LOCK (self); + if (volume != self->volume) { + gst_omx_audio_sink_volume_set (self, volume); + } + GST_OBJECT_UNLOCK (self); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_omx_audio_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (object); + + switch (prop_id) { + case PROP_MUTE: + GST_OBJECT_LOCK (self); + g_value_set_boolean (value, self->mute); + GST_OBJECT_UNLOCK (self); + break; + case PROP_VOLUME: + GST_OBJECT_LOCK (self); + g_value_set_double (value, self->volume); + GST_OBJECT_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_omx_audio_sink_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (element); + OMX_ERRORTYPE err; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + { + GST_DEBUG_OBJECT (self, "going to PLAYING state"); + err = gst_omx_component_set_state (self->comp, OMX_StateExecuting); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set state executing: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return GST_STATE_CHANGE_FAILURE; + } + + if (gst_omx_component_get_state (self->comp, + GST_CLOCK_TIME_NONE) != OMX_StateExecuting) { + return GST_STATE_CHANGE_FAILURE; + } + GST_DEBUG_OBJECT (self, "in PLAYING state"); + break; + } + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + { + GST_DEBUG_OBJECT (self, "going to PAUSED state"); + err = gst_omx_component_set_state (self->comp, OMX_StatePause); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Failed to set state paused: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return GST_STATE_CHANGE_FAILURE; + } + + if (gst_omx_component_get_state (self->comp, + GST_CLOCK_TIME_NONE) != OMX_StatePause) { + return GST_STATE_CHANGE_FAILURE; + } + GST_DEBUG_OBJECT (self, "in PAUSED state"); + break; + } + default: + break; + } + + return ret; +} + +static void +gst_omx_audio_sink_finalize (GObject * object) +{ + GstOMXAudioSink *self = GST_OMX_AUDIO_SINK (object); + + g_mutex_clear (&self->lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_omx_audio_sink_class_init (GstOMXAudioSinkClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass); + GstAudioBaseSinkClass *baudiosink_class = GST_AUDIO_BASE_SINK_CLASS (klass); + GstAudioSinkClass *audiosink_class = GST_AUDIO_SINK_CLASS (klass); + + gobject_class->set_property = gst_omx_audio_sink_set_property; + gobject_class->get_property = gst_omx_audio_sink_get_property; + gobject_class->finalize = gst_omx_audio_sink_finalize; + + g_object_class_install_property (gobject_class, PROP_MUTE, + g_param_spec_boolean ("mute", "Mute", "mute channel", + DEFAULT_PROP_MUTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_VOLUME, + g_param_spec_double ("volume", "Volume", "volume factor, 1.0=100%", + 0.0, VOLUME_MAX_DOUBLE, DEFAULT_PROP_VOLUME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_omx_audio_sink_change_state); + + basesink_class->query = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_query); + + baudiosink_class->payload = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_payload); + + audiosink_class->open = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_open); + audiosink_class->close = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_close); + audiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_prepare); + audiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_unprepare); + audiosink_class->write = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_write); + audiosink_class->delay = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_delay); + audiosink_class->reset = GST_DEBUG_FUNCPTR (gst_omx_audio_sink_reset); + + + klass->cdata.type = GST_OMX_COMPONENT_TYPE_SINK; +} + +static void +gst_omx_audio_sink_init (GstOMXAudioSink * self) +{ + g_mutex_init (&self->lock); + + self->mute = DEFAULT_PROP_MUTE; + self->volume = DEFAULT_PROP_VOLUME; + + /* For the Raspberry PI there's a big hw buffer and 400 ms seems a good + * size for our ringbuffer. OpenSL ES Sink also allocates a buffer of 400 ms + * in Android so I guess that this should be a sane value for OpenMax in + * general. */ + GST_AUDIO_BASE_SINK (self)->buffer_time = 400000; + gst_audio_base_sink_set_provide_clock (GST_AUDIO_BASE_SINK (self), TRUE); +} diff --git a/omx/gstomxaudiosink.h b/omx/gstomxaudiosink.h new file mode 100644 index 0000000..481b18a --- /dev/null +++ b/omx/gstomxaudiosink.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + * Author: Josep Torra + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_AUDIO_SINK_H__ +#define __GST_OMX_AUDIO_SINK_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "gstomx.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_AUDIO_SINK \ + (gst_omx_audio_sink_get_type()) +#define GST_OMX_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_AUDIO_SINK,GstOMXAudioSink)) +#define GST_OMX_AUDIO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_AUDIO_SINK,GstOMXAudioSinkClass)) +#define GST_OMX_AUDIO_SINK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_AUDIO_SINK,GstOMXAudioSinkClass)) +#define GST_IS_OMX_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_AUDIO_SINK)) +#define GST_IS_OMX_AUDIO_SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_AUDIO_SINK)) +#define GST_OMX_AUDIO_SINK_CAST(obj) ((GstOMXAudioSink *) (obj)) + +#define GST_OMX_AUDIO_SINK_GET_LOCK(obj) (&GST_OMX_AUDIO_SINK_CAST (obj)->lock) +#define GST_OMX_AUDIO_SINK_LOCK(obj) (g_mutex_lock (GST_OMX_AUDIO_SINK_GET_LOCK (obj))) +#define GST_OMX_AUDIO_SINK_UNLOCK(obj) (g_mutex_unlock (GST_OMX_AUDIO_SINK_GET_LOCK (obj))) + +#define PASSTHROUGH_CAPS \ + "audio/x-ac3, framed = (boolean) true;" \ + "audio/x-eac3, framed = (boolean) true; " \ + "audio/x-dts, framed = (boolean) true, " \ + "block-size = (int) { 512, 1024, 2048 }; " \ + "audio/mpeg, mpegversion = (int) 1, " \ + "mpegaudioversion = (int) [ 1, 2 ], parsed = (boolean) true;" + +typedef struct _GstOMXAudioSink GstOMXAudioSink; +typedef struct _GstOMXAudioSinkClass GstOMXAudioSinkClass; + +struct _GstOMXAudioSink +{ + GstAudioSink parent; + + /* < protected > */ + GstOMXComponent *comp; + GstOMXPort *in_port, *out_port; + + gboolean mute; + gdouble volume; + + gboolean iec61937; + guint endianness; + guint rate; + guint channels; + guint width; + gboolean is_signed; + gboolean is_float; + + guint buffer_size; + guint samples; + + GMutex lock; +}; + +struct _GstOMXAudioSinkClass +{ + GstAudioSinkClass parent_class; + + GstOMXClassData cdata; + const gchar * destination; +}; + +GType gst_omx_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_AUDIO_SINK_H__ */ + diff --git a/omx/gstomxbufferpool.c b/omx/gstomxbufferpool.c new file mode 100644 index 0000000..c425684 --- /dev/null +++ b/omx/gstomxbufferpool.c @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. + * Author: Sebastian Dröge , Collabora Ltd. + * Copyright (C) 2013, Collabora Ltd. + * Author: Sebastian Dröge + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstomxbufferpool.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_buffer_pool_debug_category); +#define GST_CAT_DEFAULT gst_omx_buffer_pool_debug_category + +typedef struct _GstOMXMemory GstOMXMemory; +typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator; +typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass; + +struct _GstOMXMemory +{ + GstMemory mem; + + GstOMXBuffer *buf; +}; + +struct _GstOMXMemoryAllocator +{ + GstAllocator parent; +}; + +struct _GstOMXMemoryAllocatorClass +{ + GstAllocatorClass parent_class; +}; + +#define GST_OMX_MEMORY_TYPE "openmax" + +static GstMemory * +gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_assert_not_reached (); + return NULL; +} + +static void +gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem) +{ + GstOMXMemory *omem = (GstOMXMemory *) mem; + + /* TODO: We need to remember which memories are still used + * so we can wait until everything is released before allocating + * new memory + */ + + g_slice_free (GstOMXMemory, omem); +} + +static gpointer +gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) +{ + GstOMXMemory *omem = (GstOMXMemory *) mem; + + return omem->buf->omx_buf->pBuffer + omem->mem.offset; +} + +static void +gst_omx_memory_unmap (GstMemory * mem) +{ +} + +static GstMemory * +gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size) +{ + g_assert_not_reached (); + return NULL; +} + +GType gst_omx_memory_allocator_get_type (void); +G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator, + GST_TYPE_ALLOCATOR); + +#define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type()) +#define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR)) + +static void +gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass) +{ + GstAllocatorClass *allocator_class; + + allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy; + allocator_class->free = gst_omx_memory_allocator_free; +} + +static void +gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = GST_OMX_MEMORY_TYPE; + alloc->mem_map = gst_omx_memory_map; + alloc->mem_unmap = gst_omx_memory_unmap; + alloc->mem_share = gst_omx_memory_share; + + /* default copy & is_span */ + + GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); +} + +static GstMemory * +gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags, + GstOMXBuffer * buf) +{ + GstOMXMemory *mem; + gint align; + + /* FIXME: We don't allow sharing because we need to know + * when the memory becomes unused and can only then put + * it back to the pool. Which is done in the pool's release + * function + */ + flags |= GST_MEMORY_FLAG_NO_SHARE; + + /* GStreamer uses a bitmask for the alignment while + * OMX uses the alignment itself. So we have to convert + * here */ + align = buf->port->port_def.nBufferAlignment; + if (align > 0) + align -= 1; + if (((align + 1) & align) != 0) { + GST_WARNING ("Invalid alignment that is not a power of two: %u", + (guint) buf->port->port_def.nBufferAlignment); + align = 0; + } + + mem = g_slice_new (GstOMXMemory); + /* the shared memory is always readonly */ + gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL, + buf->omx_buf->nAllocLen, align, 0, buf->omx_buf->nAllocLen); + + mem->buf = buf; + + return GST_MEMORY_CAST (mem); +} + +/* Buffer pool for the buffers of an OpenMAX port. + * + * This pool is only used if we either passed buffers from another + * pool to the OMX port or provide the OMX buffers directly to other + * elements. + * + * + * A buffer is in the pool if it is currently owned by the port, + * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside + * the pool after it was taken from the port after it was handled + * by the port, i.e. {Empty,Fill}BufferDone. + * + * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated + * by someone else and (temporarily) passed to this pool + * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of + * the buffer will be overriden, and restored in free_buffer(). Other + * buffers are just freed there. + * + * The pool always has a fixed number of minimum and maximum buffers + * and these are allocated while starting the pool and released afterwards. + * They correspond 1:1 to the OMX buffers of the port, which are allocated + * before the pool is started. + * + * Acquiring a buffer from this pool happens after the OMX buffer has + * been acquired from the port. gst_buffer_pool_acquire_buffer() is + * supposed to return the buffer that corresponds to the OMX buffer. + * + * For buffers provided to upstream, the buffer will be passed to + * the component manually when it arrives and then unreffed. If the + * buffer is released before reaching the component it will be just put + * back into the pool as if EmptyBufferDone has happened. If it was + * passed to the component, it will be back into the pool when it was + * released and EmptyBufferDone has happened. + * + * For buffers provided to downstream, the buffer will be returned + * back to the component (OMX_FillThisBuffer()) when it is released. + */ + +static GQuark gst_omx_buffer_data_quark = 0; + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_omx_buffer_pool_debug_category, "omxbufferpool", 0, \ + "debug category for gst-omx buffer pool base class"); + +G_DEFINE_TYPE_WITH_CODE (GstOMXBufferPool, gst_omx_buffer_pool, + GST_TYPE_BUFFER_POOL, DEBUG_INIT); + +static void gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, + GstBuffer * buffer); + +static gboolean +gst_omx_buffer_pool_start (GstBufferPool * bpool) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + /* Only allow to start the pool if we still are attached + * to a component and port */ + GST_OBJECT_LOCK (pool); + if (!pool->component || !pool->port) { + GST_OBJECT_UNLOCK (pool); + return FALSE; + } + GST_OBJECT_UNLOCK (pool); + + return + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool); +} + +static gboolean +gst_omx_buffer_pool_stop (GstBufferPool * bpool) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + gint i = 0; + + /* When not using the default GstBufferPool::GstAtomicQueue then + * GstBufferPool::free_buffer is not called while stopping the pool + * (because the queue is empty) */ + for (i = 0; i < pool->buffers->len; i++) + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer + (bpool, g_ptr_array_index (pool->buffers, i)); + + /* Remove any buffers that are there */ + if(pool->buffers) + g_ptr_array_set_size (pool->buffers, 0); + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = NULL; + + pool->add_videometa = FALSE; + + return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool); +} + +static const gchar ** +gst_omx_buffer_pool_get_options (GstBufferPool * bpool) +{ + static const gchar *raw_video_options[] = + { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL }; + static const gchar *options[] = { NULL }; + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + GST_OBJECT_LOCK (pool); + if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo + && pool->port->port_def.format.video.eCompressionFormat == + OMX_VIDEO_CodingUnused) { + GST_OBJECT_UNLOCK (pool); + return raw_video_options; + } + GST_OBJECT_UNLOCK (pool); + + return options; +} + +static gboolean +gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + GstCaps *caps; + + GST_OBJECT_LOCK (pool); + + if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) + goto wrong_config; + + if (caps == NULL) + goto no_caps; + + if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo + && pool->port->port_def.format.video.eCompressionFormat == + OMX_VIDEO_CodingUnused) { + GstVideoInfo info; + + /* now parse the caps from the config */ + if (!gst_video_info_from_caps (&info, caps)) + goto wrong_video_caps; + + /* enable metadata based on config of the pool */ + pool->add_videometa = + gst_buffer_pool_config_has_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + + pool->video_info = info; + } + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = gst_caps_ref (caps); + + GST_OBJECT_UNLOCK (pool); + + return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config + (bpool, config); + + /* ERRORS */ +wrong_config: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, "invalid config"); + return FALSE; + } +no_caps: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, "no caps in config"); + return FALSE; + } +wrong_video_caps: + { + GST_OBJECT_UNLOCK (pool); + GST_WARNING_OBJECT (pool, + "failed getting geometry from caps %" GST_PTR_FORMAT, caps); + return FALSE; + } +} + +static GstFlowReturn +gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + GstBuffer *buf; + GstOMXBuffer *omx_buf; + + g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR); + + omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index); + g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR); + + if (pool->other_pool) { + guint i, n; + + buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); + g_assert (pool->other_pool == buf->pool); + gst_object_replace ((GstObject **) & buf->pool, NULL); + + n = gst_buffer_n_memory (buf); + for (i = 0; i < n; i++) { + GstMemory *mem = gst_buffer_peek_memory (buf, i); + + /* FIXME: We don't allow sharing because we need to know + * when the memory becomes unused and can only then put + * it back to the pool. Which is done in the pool's release + * function + */ + GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE); + } + + if (pool->add_videometa) { + GstVideoMeta *meta; + + meta = gst_buffer_get_video_meta (buf); + if (!meta) { + gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT (&pool->video_info), + GST_VIDEO_INFO_WIDTH (&pool->video_info), + GST_VIDEO_INFO_HEIGHT (&pool->video_info)); + } + } + + pool->need_copy = FALSE; + } else { + GstMemory *mem; + const guint nstride = pool->port->port_def.format.video.nStride; + const guint nslice = pool->port->port_def.format.video.nSliceHeight; + gsize offset[GST_VIDEO_MAX_PLANES] = { 0, }; + gint stride[GST_VIDEO_MAX_PLANES] = { nstride, 0, }; + + mem = gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf); + buf = gst_buffer_new (); + gst_buffer_append_memory (buf, mem); + g_ptr_array_add (pool->buffers, buf); + + switch (GST_VIDEO_INFO_FORMAT (&pool->video_info)) { + case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_ARGB: + case GST_VIDEO_FORMAT_RGB16: + case GST_VIDEO_FORMAT_BGR16: + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_YVYU: + case GST_VIDEO_FORMAT_GRAY8: + break; + case GST_VIDEO_FORMAT_I420: + stride[1] = nstride / 2; + offset[1] = offset[0] + stride[0] * nslice; + stride[2] = nstride / 2; + offset[2] = offset[1] + (stride[1] * nslice / 2); + break; + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_NV16: + stride[1] = nstride; + offset[1] = offset[0] + stride[0] * nslice; + break; + case GST_VIDEO_FORMAT_SN12: + case GST_VIDEO_FORMAT_ST12: + offset[0] = 0; + stride[0] = pool->port->port_def.format.video.nStride; + offset[1] = + stride[0] * pool->port->port_def.format.video.nSliceHeight; + stride[1] = pool->port->port_def.format.video.nStride; + break; + default: + g_assert_not_reached (); + break; + } + + if (pool->add_videometa) { + pool->need_copy = FALSE; + } else { + GstVideoInfo info; + gboolean need_copy = FALSE; + gint i; + + gst_video_info_init (&info); + gst_video_info_set_format (&info, + GST_VIDEO_INFO_FORMAT (&pool->video_info), + GST_VIDEO_INFO_WIDTH (&pool->video_info), + GST_VIDEO_INFO_HEIGHT (&pool->video_info)); + + for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&pool->video_info); i++) { + if (info.stride[i] != stride[i] || info.offset[i] != offset[i]) { + need_copy = TRUE; + break; + } + } + + pool->need_copy = need_copy; + } + + if (pool->need_copy || pool->add_videometa) { + /* We always add the videometa. It's the job of the user + * to copy the buffer if pool->need_copy is TRUE + */ + gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_INFO_FORMAT (&pool->video_info), + GST_VIDEO_INFO_WIDTH (&pool->video_info), + GST_VIDEO_INFO_HEIGHT (&pool->video_info), + GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride); + } + } + + gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), + gst_omx_buffer_data_quark, omx_buf, NULL); + + *buffer = buf; + + pool->current_buffer_index++; + + return GST_FLOW_OK; +} + +static void +gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + /* If the buffers belong to another pool, restore them now */ + GST_OBJECT_LOCK (pool); + if (pool->other_pool) { + gst_object_replace ((GstObject **) & buffer->pool, + (GstObject *) pool->other_pool); + } + GST_OBJECT_UNLOCK (pool); + + gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer), + gst_omx_buffer_data_quark, NULL, NULL); + + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool, + buffer); +} + +static GstFlowReturn +gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, + GstBuffer ** buffer, GstBufferPoolAcquireParams * params) +{ + GstFlowReturn ret; + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + + if (pool->port->port_def.eDir == OMX_DirOutput) { + GstBuffer *buf; + + g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR); + + buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + *buffer = buf; + ret = GST_FLOW_OK; + + /* If it's our own memory we have to set the sizes */ + if (!pool->other_pool) { + GstMemory *mem = gst_buffer_peek_memory (*buffer, 0); + + g_assert (mem + && g_strcmp0 (mem->allocator->mem_type, GST_OMX_MEMORY_TYPE) == 0); + mem->size = ((GstOMXMemory *) mem)->buf->omx_buf->nFilledLen; + mem->offset = ((GstOMXMemory *) mem)->buf->omx_buf->nOffset; + } + } else { + /* Acquire any buffer that is available to be filled by upstream */ + ret = + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->acquire_buffer + (bpool, buffer, params); + } + + return ret; +} + +static void +gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); + OMX_ERRORTYPE err; + GstOMXBuffer *omx_buf; + + g_assert (pool->component && pool->port); + + if (!pool->allocating && !pool->deactivated) { + omx_buf = + gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), + gst_omx_buffer_data_quark); + if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) { + /* Release back to the port, can be filled again */ + err = gst_omx_port_release_buffer (pool->port, omx_buf); + if (err != OMX_ErrorNone) { + GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL), + ("Failed to relase output buffer to component: %s (0x%08x)", + gst_omx_error_to_string (err), err)); + } + } else if (!omx_buf->used) { + /* TODO: Implement. + * + * If not used (i.e. was not passed to the component) this should do + * the same as EmptyBufferDone. + * If it is used (i.e. was passed to the component) this should do + * nothing until EmptyBufferDone. + * + * EmptyBufferDone should release the buffer to the pool so it can + * be allocated again + * + * Needs something to call back here in EmptyBufferDone, like keeping + * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which + * would ensure that the buffer is always unused when this is called. + */ + g_assert_not_reached (); + GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer + (bpool, buffer); + } + } +} + +static void +gst_omx_buffer_pool_finalize (GObject * object) +{ + GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object); + + if (pool->element) + gst_object_unref (pool->element); + pool->element = NULL; + + if (pool->buffers) + g_ptr_array_unref (pool->buffers); + pool->buffers = NULL; + + if (pool->other_pool) + gst_object_unref (pool->other_pool); + pool->other_pool = NULL; + + if (pool->allocator) + gst_object_unref (pool->allocator); + pool->allocator = NULL; + + if (pool->caps) + gst_caps_unref (pool->caps); + pool->caps = NULL; + + G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object); +} + +static void +gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; + + gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData"); + + gobject_class->finalize = gst_omx_buffer_pool_finalize; + gstbufferpool_class->start = gst_omx_buffer_pool_start; + gstbufferpool_class->stop = gst_omx_buffer_pool_stop; + gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options; + gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config; + gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer; + gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer; + gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer; + gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer; +} + +static void +gst_omx_buffer_pool_init (GstOMXBufferPool * pool) +{ + pool->buffers = g_ptr_array_new (); + pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL); +} + +GstBufferPool * +gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, + GstOMXPort * port) +{ + GstOMXBufferPool *pool; + + pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL); + pool->element = gst_object_ref (element); + pool->component = component; + pool->port = port; + + return GST_BUFFER_POOL (pool); +} diff --git a/omx/gstomxbufferpool.h b/omx/gstomxbufferpool.h new file mode 100644 index 0000000..76f9680 --- /dev/null +++ b/omx/gstomxbufferpool.h @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * Author: Christian König + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_BUFFER_POOL_H__ +#define __GST_OMX_BUFFER_POOL_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "gstomx.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_BUFFER_POOL \ + (gst_omx_buffer_pool_get_type()) +#define GST_OMX_BUFFER_POOL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_BUFFER_POOL,GstOMXBufferPool)) +#define GST_IS_OMX_BUFFER_POOL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_BUFFER_POOL)) + +typedef struct _GstOMXBufferPool GstOMXBufferPool; +typedef struct _GstOMXBufferPoolClass GstOMXBufferPoolClass; + +struct _GstOMXBufferPool +{ + GstVideoBufferPool parent; + + GstElement *element; + + GstCaps *caps; + gboolean add_videometa; + gboolean need_copy; + GstVideoInfo video_info; + + /* Owned by element, element has to stop this pool before + * it destroys component or port */ + GstOMXComponent *component; + GstOMXPort *port; + + /* For handling OpenMAX allocated memory */ + GstAllocator *allocator; + + /* Set from outside this pool */ + /* TRUE if we're currently allocating all our buffers */ + gboolean allocating; + /* TRUE if the pool is not used anymore */ + gboolean deactivated; + + /* For populating the pool from another one */ + GstBufferPool *other_pool; + GPtrArray *buffers; + + /* Used during acquire for output ports to + * specify which buffer has to be retrieved + * and during alloc, which buffer has to be + * wrapped + */ + gint current_buffer_index; +}; + +struct _GstOMXBufferPoolClass +{ + GstVideoBufferPoolClass parent_class; +}; + +GType gst_omx_buffer_pool_get_type (void); + +GstBufferPool *gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, GstOMXPort * port); + +G_END_DECLS + +#endif /* __GST_OMX_BUFFER_POOL_H__ */ diff --git a/omx/gstomxh263enc.c b/omx/gstomxh263enc.c index e3c1a99..8385372 100644 --- a/omx/gstomxh263enc.c +++ b/omx/gstomxh263enc.c @@ -189,8 +189,9 @@ gst_omx_h263_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port, "Setting profile/level not supported by component"); } else if (err != OMX_ErrorNone) { GST_ERROR_OBJECT (self, - "Error setting profile %d and level %d: %s (0x%08x)", param.eProfile, - param.eLevel, gst_omx_error_to_string (err), err); + "Error setting profile %u and level %u: %s (0x%08x)", + (guint) param.eProfile, (guint) param.eLevel, + gst_omx_error_to_string (err), err); return FALSE; } @@ -261,7 +262,7 @@ gst_omx_h263_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, break; default: g_assert_not_reached (); - break; + return NULL; } switch (param.eLevel) { @@ -288,7 +289,7 @@ gst_omx_h263_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, break; default: g_assert_not_reached (); - break; + return NULL; } gst_caps_set_simple (caps, diff --git a/omx/gstomxh264dec.c b/omx/gstomxh264dec.c index 2581889..2132d9f 100644 --- a/omx/gstomxh264dec.c +++ b/omx/gstomxh264dec.c @@ -83,6 +83,31 @@ static gboolean gst_omx_h264_dec_is_format_change (GstOMXVideoDec * dec, GstOMXPort * port, GstVideoCodecState * state) { + GstCaps *old_caps = NULL; + GstCaps *new_caps = state->caps; + GstStructure *old_structure, *new_structure; + const gchar *old_profile, *old_level, *new_profile, *new_level; + + if (dec->input_state) { + old_caps = dec->input_state->caps; + } + + if (!old_caps) { + return FALSE; + } + + old_structure = gst_caps_get_structure (old_caps, 0); + new_structure = gst_caps_get_structure (new_caps, 0); + old_profile = gst_structure_get_string (old_structure, "profile"); + old_level = gst_structure_get_string (old_structure, "level"); + new_profile = gst_structure_get_string (new_structure, "profile"); + new_level = gst_structure_get_string (new_structure, "level"); + + if (g_strcmp0 (old_profile, new_profile) != 0 + || g_strcmp0 (old_level, new_level) != 0) { + return TRUE; + } + return FALSE; } diff --git a/omx/gstomxh264enc.c b/omx/gstomxh264enc.c index 9adac04..aa33ae5 100644 --- a/omx/gstomxh264enc.c +++ b/omx/gstomxh264enc.c @@ -26,6 +26,11 @@ #include "gstomxh264enc.h" +#ifdef USE_OMX_TARGET_RPI +#include +#include +#endif + GST_DEBUG_CATEGORY_STATIC (gst_omx_h264_enc_debug_category); #define GST_CAT_DEFAULT gst_omx_h264_enc_debug_category @@ -36,30 +41,85 @@ static GstCaps *gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, GstVideoCodecState * state); static GstFlowReturn gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, GstOMXBuffer * buf, GstVideoCodecFrame * frame); +static gboolean gst_omx_h264_enc_flush (GstVideoEncoder * enc); +static gboolean gst_omx_h264_enc_stop (GstVideoEncoder * enc); +static void gst_omx_h264_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_omx_h264_enc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); enum { - PROP_0 + PROP_0, +#ifdef USE_OMX_TARGET_RPI + PROP_INLINESPSPPSHEADERS, +#endif + PROP_PERIODICITYOFIDRFRAMES, + PROP_INTERVALOFCODINGINTRAFRAMES }; +#ifdef USE_OMX_TARGET_RPI +#define GST_OMX_H264_VIDEO_ENC_INLINE_SPS_PPS_HEADERS_DEFAULT TRUE +#endif +#define GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT (0xffffffff) +#define GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT (0xffffffff) + + /* class initialization */ #define DEBUG_INIT \ GST_DEBUG_CATEGORY_INIT (gst_omx_h264_enc_debug_category, "omxh264enc", 0, \ "debug category for gst-omx video encoder base class"); +#define parent_class gst_omx_h264_enc_parent_class G_DEFINE_TYPE_WITH_CODE (GstOMXH264Enc, gst_omx_h264_enc, GST_TYPE_OMX_VIDEO_ENC, DEBUG_INIT); static void gst_omx_h264_enc_class_init (GstOMXH264EncClass * klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstVideoEncoderClass *basevideoenc_class = GST_VIDEO_ENCODER_CLASS (klass); GstOMXVideoEncClass *videoenc_class = GST_OMX_VIDEO_ENC_CLASS (klass); videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_set_format); videoenc_class->get_caps = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_get_caps); + gobject_class->set_property = gst_omx_h264_enc_set_property; + gobject_class->get_property = gst_omx_h264_enc_get_property; + +#ifdef USE_OMX_TARGET_RPI + g_object_class_install_property (gobject_class, PROP_INLINESPSPPSHEADERS, + g_param_spec_boolean ("inline-header", + "Inline SPS/PPS headers before IDR", + "Inline SPS/PPS header before IDR", + GST_OMX_H264_VIDEO_ENC_INLINE_SPS_PPS_HEADERS_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); +#endif + + g_object_class_install_property (gobject_class, PROP_PERIODICITYOFIDRFRAMES, + g_param_spec_uint ("periodicty-idr", "Target Bitrate", + "Periodicity of IDR frames (0xffffffff=component default)", + 0, G_MAXUINT, + GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + + g_object_class_install_property (gobject_class, + PROP_INTERVALOFCODINGINTRAFRAMES, + g_param_spec_uint ("interval-intraframes", + "Interval of coding Intra frames", + "Interval of coding Intra frames (0xffffffff=component default)", 0, + G_MAXUINT, + GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + + basevideoenc_class->flush = gst_omx_h264_enc_flush; + basevideoenc_class->stop = gst_omx_h264_enc_stop; + videoenc_class->cdata.default_src_template_caps = "video/x-h264, " "width=(int) [ 16, 4096 ], " "height=(int) [ 16, 4096 ]"; videoenc_class->handle_output_frame = @@ -75,8 +135,86 @@ gst_omx_h264_enc_class_init (GstOMXH264EncClass * klass) } static void +gst_omx_h264_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOMXH264Enc *self = GST_OMX_H264_ENC (object); + + switch (prop_id) { +#ifdef USE_OMX_TARGET_RPI + case PROP_INLINESPSPPSHEADERS: + self->inline_sps_pps_headers = g_value_get_boolean (value); + break; +#endif + case PROP_PERIODICITYOFIDRFRAMES: + self->periodicty_idr = g_value_get_uint (value); + break; + case PROP_INTERVALOFCODINGINTRAFRAMES: + self->interval_intraframes = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_omx_h264_enc_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstOMXH264Enc *self = GST_OMX_H264_ENC (object); + + switch (prop_id) { +#ifdef USE_OMX_TARGET_RPI + case PROP_INLINESPSPPSHEADERS: + g_value_set_boolean (value, self->inline_sps_pps_headers); + break; +#endif + case PROP_PERIODICITYOFIDRFRAMES: + g_value_set_uint (value, self->periodicty_idr); + break; + case PROP_INTERVALOFCODINGINTRAFRAMES: + g_value_set_uint (value, self->interval_intraframes); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gst_omx_h264_enc_init (GstOMXH264Enc * self) { +#ifdef USE_OMX_TARGET_RPI + self->inline_sps_pps_headers = + GST_OMX_H264_VIDEO_ENC_INLINE_SPS_PPS_HEADERS_DEFAULT; +#endif + self->periodicty_idr = + GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT; + self->interval_intraframes = + GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT; +} + +static gboolean +gst_omx_h264_enc_flush (GstVideoEncoder * enc) +{ + GstOMXH264Enc *self = GST_OMX_H264_ENC (enc); + + g_list_free_full (self->headers, (GDestroyNotify) gst_buffer_unref); + self->headers = NULL; + + return GST_VIDEO_ENCODER_CLASS (parent_class)->flush (enc); +} + +static gboolean +gst_omx_h264_enc_stop (GstVideoEncoder * enc) +{ + GstOMXH264Enc *self = GST_OMX_H264_ENC (enc); + + g_list_free_full (self->headers, (GDestroyNotify) gst_buffer_unref); + self->headers = NULL; + + return GST_VIDEO_ENCODER_CLASS (parent_class)->stop (enc); } static gboolean @@ -87,9 +225,88 @@ gst_omx_h264_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port, GstCaps *peercaps; OMX_PARAM_PORTDEFINITIONTYPE port_def; OMX_VIDEO_PARAM_PROFILELEVELTYPE param; + OMX_VIDEO_CONFIG_AVCINTRAPERIOD config_avcintraperiod; +#ifdef USE_OMX_TARGET_RPI + OMX_CONFIG_PORTBOOLEANTYPE config_inline_header; +#endif OMX_ERRORTYPE err; const gchar *profile_string, *level_string; +#ifdef USE_OMX_TARGET_RPI + GST_OMX_INIT_STRUCT (&config_inline_header); + config_inline_header.nPortIndex = + GST_OMX_VIDEO_ENC (self)->enc_out_port->index; + err = + gst_omx_component_get_parameter (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexParamBrcmVideoAVCInlineHeaderEnable, &config_inline_header); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "can't get OMX_IndexParamBrcmVideoAVCInlineHeaderEnable %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + if (self->inline_sps_pps_headers) { + config_inline_header.bEnabled = OMX_TRUE; + } else { + config_inline_header.bEnabled = OMX_FALSE; + } + + err = + gst_omx_component_set_parameter (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexParamBrcmVideoAVCInlineHeaderEnable, &config_inline_header); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "can't set OMX_IndexParamBrcmVideoAVCInlineHeaderEnable %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } +#endif + + if (self->periodicty_idr != + GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT + || self->interval_intraframes != + GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT) { + + + GST_OMX_INIT_STRUCT (&config_avcintraperiod); + config_avcintraperiod.nPortIndex = + GST_OMX_VIDEO_ENC (self)->enc_out_port->index; + err = + gst_omx_component_get_parameter (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexConfigVideoAVCIntraPeriod, &config_avcintraperiod); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "can't get OMX_IndexConfigVideoAVCIntraPeriod %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + GST_DEBUG_OBJECT (self, "default nPFrames:%u, nIDRPeriod:%u", + (guint) config_avcintraperiod.nPFrames, + (guint) config_avcintraperiod.nIDRPeriod); + + if (self->periodicty_idr != + GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT) { + config_avcintraperiod.nIDRPeriod = self->periodicty_idr; + } + + if (self->interval_intraframes != + GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT) { + config_avcintraperiod.nPFrames = self->interval_intraframes; + } + + err = + gst_omx_component_set_parameter (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexConfigVideoAVCIntraPeriod, &config_avcintraperiod); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "can't set OMX_IndexConfigVideoAVCIntraPeriod %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + } + gst_omx_port_get_port_definition (GST_OMX_VIDEO_ENC (self)->enc_out_port, &port_def); port_def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; @@ -192,8 +409,9 @@ gst_omx_h264_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port, "Setting profile/level not supported by component"); } else if (err != OMX_ErrorNone) { GST_ERROR_OBJECT (self, - "Error setting profile %d and level %d: %s (0x%08x)", param.eProfile, - param.eLevel, gst_omx_error_to_string (err), err); + "Error setting profile %u and level %u: %s (0x%08x)", + (guint) param.eProfile, (guint) param.eLevel, + gst_omx_error_to_string (err), err); return FALSE; } @@ -258,7 +476,7 @@ gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, break; default: g_assert_not_reached (); - break; + return NULL; } switch (param.eLevel) { @@ -312,7 +530,7 @@ gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, break; default: g_assert_not_reached (); - break; + return NULL; } gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile, "level", G_TYPE_STRING, level, NULL); @@ -322,9 +540,11 @@ gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, } static GstFlowReturn -gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, +gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * enc, GstOMXPort * port, GstOMXBuffer * buf, GstVideoCodecFrame * frame) { + GstOMXH264Enc *self = GST_OMX_H264_ENC (enc); + if (buf->omx_buf->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { /* The codec data is SPS/PPS with a startcode => bytestream stream format * For bytestream stream format the SPS/PPS is only in-stream and not @@ -333,12 +553,10 @@ gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, if (buf->omx_buf->nFilledLen >= 4 && GST_READ_UINT32_BE (buf->omx_buf->pBuffer + buf->omx_buf->nOffset) == 0x00000001) { - GList *l = NULL; GstBuffer *hdrs; GstMapInfo map = GST_MAP_INFO_INIT; GST_DEBUG_OBJECT (self, "got codecconfig in byte-stream format"); - buf->omx_buf->nFlags &= ~OMX_BUFFERFLAG_CODECCONFIG; hdrs = gst_buffer_new_and_alloc (buf->omx_buf->nFilledLen); @@ -347,13 +565,20 @@ gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, buf->omx_buf->pBuffer + buf->omx_buf->nOffset, buf->omx_buf->nFilledLen); gst_buffer_unmap (hdrs, &map); - l = g_list_append (l, hdrs); - gst_video_encoder_set_headers (GST_VIDEO_ENCODER (self), l); + self->headers = g_list_append (self->headers, hdrs); + + if (frame) + gst_video_codec_frame_unref (frame); + + return GST_FLOW_OK; } + } else if (self->headers) { + gst_video_encoder_set_headers (GST_VIDEO_ENCODER (self), self->headers); + self->headers = NULL; } return GST_OMX_VIDEO_ENC_CLASS - (gst_omx_h264_enc_parent_class)->handle_output_frame (self, port, buf, + (gst_omx_h264_enc_parent_class)->handle_output_frame (enc, port, buf, frame); } diff --git a/omx/gstomxh264enc.h b/omx/gstomxh264enc.h index 5d43e5b..03326e1 100644 --- a/omx/gstomxh264enc.h +++ b/omx/gstomxh264enc.h @@ -45,6 +45,14 @@ typedef struct _GstOMXH264EncClass GstOMXH264EncClass; struct _GstOMXH264Enc { GstOMXVideoEnc parent; + +#ifdef USE_OMX_TARGET_RPI + gboolean inline_sps_pps_headers; +#endif + guint32 periodicty_idr; + guint32 interval_intraframes; + + GList *headers; }; struct _GstOMXH264EncClass diff --git a/omx/gstomxhdmiaudiosink.c b/omx/gstomxhdmiaudiosink.c new file mode 100644 index 0000000..211b719 --- /dev/null +++ b/omx/gstomxhdmiaudiosink.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + * Author: Josep Torra + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstomxhdmiaudiosink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_hdmi_audio_sink_debug_category); +#define GST_CAT_DEFAULT gst_omx_hdmi_audio_sink_debug_category + +/* class initialization */ + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_omx_hdmi_audio_sink_debug_category, \ + "omxhdmiaudiosink", 0, "debug category for gst-omx hdmi audio sink"); + +G_DEFINE_TYPE_WITH_CODE (GstOMXHdmiAudioSink, gst_omx_hdmi_audio_sink, + GST_TYPE_OMX_AUDIO_SINK, DEBUG_INIT); + +static void +gst_omx_hdmi_audio_sink_class_init (GstOMXHdmiAudioSinkClass * klass) +{ + GstOMXAudioSinkClass *audiosink_class = GST_OMX_AUDIO_SINK_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + audiosink_class->cdata.default_sink_template_caps = "audio/x-raw, " + "format = (string) " GST_AUDIO_FORMATS_ALL ", " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " + PASSTHROUGH_CAPS; + audiosink_class->destination = "hdmi"; + + gst_element_class_set_static_metadata (element_class, + "OpenMAX HDMI Audio Sink", + "Sink/Audio", + "Output audio through HDMI", "Josep Torra "); + + gst_omx_set_default_role (&audiosink_class->cdata, "audio_render.hdmi"); +} + +static void +gst_omx_hdmi_audio_sink_init (GstOMXHdmiAudioSink * self) +{ +} diff --git a/omx/gstomxhdmiaudiosink.h b/omx/gstomxhdmiaudiosink.h new file mode 100644 index 0000000..e45e56b --- /dev/null +++ b/omx/gstomxhdmiaudiosink.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014, Fluendo, S.A. + * Copyright (C) 2014, Metrological Media Innovations B.V. + * Author: Josep Torra + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_HDMI_AUDIO_SINK_H__ +#define __GST_OMX_HDMI_AUDIO_SINK_H__ + +#include +#include "gstomxaudiosink.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_HDMI_AUDIO_SINK \ + (gst_omx_hdmi_audio_sink_get_type()) +#define GST_OMX_HDMI_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_HDMI_AUDIO_SINK,GstOMXHdmiAudioSink)) +#define GST_OMX_HDMI_AUDIO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_HDMI_AUDIO_SINK,GstOMXHdmiAudioSinkClass)) +#define GST_OMX_HDMI_AUDIO_SINK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_HDMI_AUDIO_SINK,GstOMXHdmiAudioSinkClass)) +#define GST_IS_OMX_HDMI_AUDIO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_HDMI_AUDIO_SINK)) +#define GST_IS_OMX_HDMI_AUDIO_SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_HDMI_AUDIO_SINK)) + +typedef struct _GstOMXHdmiAudioSink GstOMXHdmiAudioSink; +typedef struct _GstOMXHdmiAudioSinkClass GstOMXHdmiAudioSinkClass; + +struct _GstOMXHdmiAudioSink +{ + GstOMXAudioSink parent; +}; + +struct _GstOMXHdmiAudioSinkClass +{ + GstOMXAudioSinkClass parent_class; +}; + +GType gst_omx_hdmi_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_HDMI_AUDIO_SINK_H__ */ + diff --git a/omx/gstomxmp3dec.c b/omx/gstomxmp3dec.c new file mode 100644 index 0000000..5a143d5 --- /dev/null +++ b/omx/gstomxmp3dec.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2014, Sebastian Dröge + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstomxmp3dec.h" + +GST_DEBUG_CATEGORY_STATIC (gst_omx_mp3_dec_debug_category); +#define GST_CAT_DEFAULT gst_omx_mp3_dec_debug_category + +/* prototypes */ +static gboolean gst_omx_mp3_dec_set_format (GstOMXAudioDec * dec, + GstOMXPort * port, GstCaps * caps); +static gboolean gst_omx_mp3_dec_is_format_change (GstOMXAudioDec * dec, + GstOMXPort * port, GstCaps * caps); +static gint gst_omx_mp3_dec_get_samples_per_frame (GstOMXAudioDec * dec, + GstOMXPort * port); +static gboolean gst_omx_mp3_dec_get_channel_positions (GstOMXAudioDec * dec, + GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]); + +/* class initialization */ + +#define DEBUG_INIT \ + GST_DEBUG_CATEGORY_INIT (gst_omx_mp3_dec_debug_category, "omxmp3dec", 0, \ + "debug category for gst-omx mp3 audio decoder"); + +G_DEFINE_TYPE_WITH_CODE (GstOMXMP3Dec, gst_omx_mp3_dec, + GST_TYPE_OMX_AUDIO_DEC, DEBUG_INIT); + + +static void +gst_omx_mp3_dec_class_init (GstOMXMP3DecClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstOMXAudioDecClass *audiodec_class = GST_OMX_AUDIO_DEC_CLASS (klass); + + audiodec_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_mp3_dec_set_format); + audiodec_class->is_format_change = + GST_DEBUG_FUNCPTR (gst_omx_mp3_dec_is_format_change); + audiodec_class->get_samples_per_frame = + GST_DEBUG_FUNCPTR (gst_omx_mp3_dec_get_samples_per_frame); + audiodec_class->get_channel_positions = + GST_DEBUG_FUNCPTR (gst_omx_mp3_dec_get_channel_positions); + + audiodec_class->cdata.default_sink_template_caps = "audio/mpeg, " + "mpegversion=(int)1, " + "layer=(int)3, " + "mpegaudioversion=(int)[1,3], " + "rate=(int)[8000,48000], " + "channels=(int)[1,2], " "parsed=(boolean) true"; + + gst_element_class_set_static_metadata (element_class, + "OpenMAX MP3 Audio Decoder", + "Codec/Decoder/Audio", + "Decode MP3 audio streams", + "Sebastian Dröge "); + + gst_omx_set_default_role (&audiodec_class->cdata, "audio_decoder.mp3"); +} + +static void +gst_omx_mp3_dec_init (GstOMXMP3Dec * self) +{ + self->spf = -1; +} + +static gboolean +gst_omx_mp3_dec_set_format (GstOMXAudioDec * dec, GstOMXPort * port, + GstCaps * caps) +{ + GstOMXMP3Dec *self = GST_OMX_MP3_DEC (dec); + OMX_PARAM_PORTDEFINITIONTYPE port_def; + OMX_AUDIO_PARAM_MP3TYPE mp3_param; + OMX_ERRORTYPE err; + GstStructure *s; + gint rate, channels, layer, mpegaudioversion; + + gst_omx_port_get_port_definition (port, &port_def); + port_def.format.audio.eEncoding = OMX_AUDIO_CodingMP3; + err = gst_omx_port_update_port_definition (port, &port_def); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to set MP3 format on component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + GST_OMX_INIT_STRUCT (&mp3_param); + mp3_param.nPortIndex = port->index; + + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioMp3, + &mp3_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to get MP3 parameters from component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "mpegaudioversion", &mpegaudioversion) || + !gst_structure_get_int (s, "layer", &layer) || + !gst_structure_get_int (s, "rate", &rate) || + !gst_structure_get_int (s, "channels", &channels)) { + GST_ERROR_OBJECT (self, "Incomplete caps"); + return FALSE; + } + + self->spf = (mpegaudioversion == 1 ? 1152 : 576); + + mp3_param.nChannels = channels; + mp3_param.nBitRate = 0; /* unknown */ + mp3_param.nSampleRate = rate; + mp3_param.nAudioBandWidth = 0; /* decoder decision */ + mp3_param.eChannelMode = 0; /* FIXME */ + if (mpegaudioversion == 1) + mp3_param.eFormat = OMX_AUDIO_MP3StreamFormatMP1Layer3; + else if (mpegaudioversion == 2) + mp3_param.eFormat = OMX_AUDIO_MP3StreamFormatMP2Layer3; + else + mp3_param.eFormat = OMX_AUDIO_MP3StreamFormatMP2_5Layer3; + + err = + gst_omx_component_set_parameter (dec->dec, OMX_IndexParamAudioMp3, + &mp3_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, "Error setting MP3 parameters: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + return TRUE; +} + +static gboolean +gst_omx_mp3_dec_is_format_change (GstOMXAudioDec * dec, GstOMXPort * port, + GstCaps * caps) +{ + GstOMXMP3Dec *self = GST_OMX_MP3_DEC (dec); + OMX_AUDIO_PARAM_MP3TYPE mp3_param; + OMX_ERRORTYPE err; + GstStructure *s; + gint rate, channels, layer, mpegaudioversion; + + GST_OMX_INIT_STRUCT (&mp3_param); + mp3_param.nPortIndex = port->index; + + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioMp3, + &mp3_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to get MP3 parameters from component: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "mpegaudioversion", &mpegaudioversion) || + !gst_structure_get_int (s, "layer", &layer) || + !gst_structure_get_int (s, "rate", &rate) || + !gst_structure_get_int (s, "channels", &channels)) { + GST_ERROR_OBJECT (self, "Incomplete caps"); + return FALSE; + } + + if (mp3_param.nChannels != channels) + return TRUE; + + if (mp3_param.nSampleRate != rate) + return TRUE; + + if (mpegaudioversion == 1 + && mp3_param.eFormat != OMX_AUDIO_MP3StreamFormatMP1Layer3) + return TRUE; + if (mpegaudioversion == 2 + && mp3_param.eFormat != OMX_AUDIO_MP3StreamFormatMP2Layer3) + return TRUE; + if (mpegaudioversion == 3 + && mp3_param.eFormat != OMX_AUDIO_MP3StreamFormatMP2_5Layer3) + return TRUE; + + return FALSE; +} + +static gint +gst_omx_mp3_dec_get_samples_per_frame (GstOMXAudioDec * dec, GstOMXPort * port) +{ + return GST_OMX_MP3_DEC (dec)->spf; +} + +static gboolean +gst_omx_mp3_dec_get_channel_positions (GstOMXAudioDec * dec, + GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]) +{ + OMX_AUDIO_PARAM_PCMMODETYPE pcm_param; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (&pcm_param); + pcm_param.nPortIndex = port->index; + err = + gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioPcm, + &pcm_param); + if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (dec, "Failed to get PCM parameters: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + + switch (pcm_param.nChannels) { + case 1: + position[0] = GST_AUDIO_CHANNEL_POSITION_MONO; + break; + case 2: + position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + break; + default: + return FALSE; + } + + return TRUE; +} diff --git a/omx/gstomxmp3dec.h b/omx/gstomxmp3dec.h new file mode 100644 index 0000000..4f07659 --- /dev/null +++ b/omx/gstomxmp3dec.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014, Sebastian Dröge + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_MP3_DEC_H__ +#define __GST_OMX_MP3_DEC_H__ + +#include +#include "gstomxaudiodec.h" + +G_BEGIN_DECLS + +#define GST_TYPE_OMX_MP3_DEC \ + (gst_omx_mp3_dec_get_type()) +#define GST_OMX_MP3_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_MP3_DEC,GstOMXMP3Dec)) +#define GST_OMX_MP3_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OMX_MP3_DEC,GstOMXMP3DecClass)) +#define GST_OMX_MP3_DEC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_OMX_MP3_DEC,GstOMXMP3DecClass)) +#define GST_IS_OMX_MP3_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OMX_MP3_DEC)) +#define GST_IS_OMX_MP3_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OMX_MP3_DEC)) + +typedef struct _GstOMXMP3Dec GstOMXMP3Dec; +typedef struct _GstOMXMP3DecClass GstOMXMP3DecClass; + +struct _GstOMXMP3Dec +{ + GstOMXAudioDec parent; + gint spf; +}; + +struct _GstOMXMP3DecClass +{ + GstOMXAudioDecClass parent_class; +}; + +GType gst_omx_mp3_dec_get_type (void); + +G_END_DECLS + +#endif /* __GST_OMX_MP3_DEC_H__ */ + diff --git a/omx/gstomxmpeg4videoenc.c b/omx/gstomxmpeg4videoenc.c index 15b8ce7..f5eaa0a 100644 --- a/omx/gstomxmpeg4videoenc.c +++ b/omx/gstomxmpeg4videoenc.c @@ -199,8 +199,9 @@ gst_omx_mpeg4_video_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port, "Setting profile/level not supported by component"); } else if (err != OMX_ErrorNone) { GST_ERROR_OBJECT (self, - "Error setting profile %d and level %d: %s (0x%08x)", param.eProfile, - param.eLevel, gst_omx_error_to_string (err), err); + "Error setting profile %u and level %u: %s (0x%08x)", + (guint) param.eProfile, (guint) param.eLevel, + gst_omx_error_to_string (err), err); return FALSE; } @@ -294,7 +295,7 @@ gst_omx_mpeg4_video_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, break; default: g_assert_not_reached (); - break; + return NULL; } switch (param.eLevel) { @@ -324,7 +325,7 @@ gst_omx_mpeg4_video_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, break; default: g_assert_not_reached (); - break; + return NULL; } gst_caps_set_simple (caps, diff --git a/omx/gstomxvideo.c b/omx/gstomxvideo.c new file mode 100644 index 0000000..7cd6755 --- /dev/null +++ b/omx/gstomxvideo.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. + * Author: Sebastian Dröge , Collabora Ltd. + * Copyright (C) 2013, Collabora Ltd. + * Author: Sebastian Dröge * + * Copyright 2014 Advanced Micro Devices, Inc. + * Author: Christian König + * + * 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. + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstomxvideo.h" + +GST_DEBUG_CATEGORY (gst_omx_video_debug_category); +#define GST_CAT_DEFAULT gst_omx_video_debug_category + +GstVideoFormat +gst_omx_video_get_format_from_omx (OMX_COLOR_FORMATTYPE omx_colorformat) +{ + GstVideoFormat format; + + switch (omx_colorformat) { + case OMX_COLOR_FormatL8: + format = GST_VIDEO_FORMAT_GRAY8; + break; + case OMX_COLOR_FormatYUV420Planar: + case OMX_COLOR_FormatYUV420PackedPlanar: + format = GST_VIDEO_FORMAT_I420; + break; + case OMX_COLOR_FormatYUV420SemiPlanar: + format = GST_VIDEO_FORMAT_NV12; + break; + case OMX_COLOR_FormatYUV422SemiPlanar: + format = GST_VIDEO_FORMAT_NV16; + break; + case OMX_COLOR_FormatYCbYCr: + format = GST_VIDEO_FORMAT_YUY2; + break; + case OMX_COLOR_FormatYCrYCb: + format = GST_VIDEO_FORMAT_YVYU; + break; + case OMX_COLOR_FormatCbYCrY: + format = GST_VIDEO_FORMAT_UYVY; + break; + case OMX_COLOR_Format32bitARGB8888: + /* There is a mismatch in omxil specification 4.2.1 between + * OMX_COLOR_Format32bitARGB8888 and its description + * Follow the description */ + format = GST_VIDEO_FORMAT_ABGR; + break; + case OMX_COLOR_Format32bitBGRA8888: + /* Same issue as OMX_COLOR_Format32bitARGB8888 */ + format = GST_VIDEO_FORMAT_ARGB; + break; + case OMX_COLOR_Format16bitRGB565: + format = GST_VIDEO_FORMAT_RGB16; + break; + case OMX_COLOR_Format16bitBGR565: + format = GST_VIDEO_FORMAT_BGR16; + break; + case OMX_EXT_COLOR_FormatNV12TPhysicalAddress: +#ifdef USE_MM_VIDEO_BUFFER + format = GST_VIDEO_FORMAT_SN12; +#else + format = GST_VIDEO_FORMAT_ST12; +#endif + break; + default: + format = GST_VIDEO_FORMAT_UNKNOWN; + break; + } + + return format; +} + +GList * +gst_omx_video_get_supported_colorformats (GstOMXPort * port, + GstVideoCodecState * state) +{ + GstOMXComponent *comp = port->comp; + OMX_VIDEO_PARAM_PORTFORMATTYPE param; + OMX_ERRORTYPE err; + GList *negotiation_map = NULL; + gint old_index; + GstOMXVideoNegotiationMap *m; + GstVideoFormat f; + + GST_OMX_INIT_STRUCT (¶m); + param.nPortIndex = port->index; + param.nIndex = 0; + if (!state || state->info.fps_n == 0) + param.xFramerate = 0; + else + param.xFramerate = (state->info.fps_n << 16) / (state->info.fps_d); + + old_index = -1; + do { + err = + gst_omx_component_get_parameter (comp, + OMX_IndexParamVideoPortFormat, ¶m); + + /* FIXME: Workaround for Bellagio that simply always + * returns the same value regardless of nIndex and + * never returns OMX_ErrorNoMore + */ + if (old_index == param.nIndex) + break; + + if (err == OMX_ErrorNone || err == OMX_ErrorNoMore) { + f = gst_omx_video_get_format_from_omx (param.eColorFormat); + + if (f != GST_VIDEO_FORMAT_UNKNOWN) { + m = g_slice_new (GstOMXVideoNegotiationMap); + m->format = f; + m->type = param.eColorFormat; + negotiation_map = g_list_append (negotiation_map, m); + GST_DEBUG_OBJECT (comp->parent, + "Component supports %s (%d) at index %u", + gst_video_format_to_string (f), param.eColorFormat, + (guint) param.nIndex); + } else { + GST_DEBUG_OBJECT (comp->parent, + "Component supports unsupported color format %d at index %u", + param.eColorFormat, (guint) param.nIndex); + } + } + old_index = param.nIndex++; + } while (err == OMX_ErrorNone); + + return negotiation_map; +} + +GstCaps * +gst_omx_video_get_caps_for_map (GList * map) +{ + GstCaps *caps = gst_caps_new_empty (); + GList *l; + + for (l = map; l; l = l->next) { + GstOMXVideoNegotiationMap *entry = l->data; + + gst_caps_append_structure (caps, + gst_structure_new ("video/x-raw", + "format", G_TYPE_STRING, + gst_video_format_to_string (entry->format), NULL)); + } + return caps; +} + +void +gst_omx_video_negotiation_map_free (GstOMXVideoNegotiationMap * m) +{ + g_slice_free (GstOMXVideoNegotiationMap, m); +} + +GstVideoCodecFrame * +gst_omx_video_find_nearest_frame (GstOMXBuffer * buf, GList * frames) +{ + GstVideoCodecFrame *best = NULL; + GstClockTimeDiff best_diff = G_MAXINT64; + GstClockTime timestamp; + GList *l; + + timestamp = + gst_util_uint64_scale (buf->omx_buf->nTimeStamp, GST_SECOND, + OMX_TICKS_PER_SECOND); + + for (l = frames; l; l = l->next) { + GstVideoCodecFrame *tmp = l->data; + GstClockTimeDiff diff = ABS (GST_CLOCK_DIFF (timestamp, tmp->pts)); + + if (diff < best_diff) { + best = tmp; + best_diff = diff; + + if (diff == 0) + break; + } + } + + if (best) + gst_video_codec_frame_ref (best); + + g_list_foreach (frames, (GFunc) gst_video_codec_frame_unref, NULL); + g_list_free (frames); + + return best; +} diff --git a/omx/gstomxvideo.h b/omx/gstomxvideo.h new file mode 100644 index 0000000..f146df7 --- /dev/null +++ b/omx/gstomxvideo.h @@ -0,0 +1,60 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * Author: Christian König + * + * 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. + * + * 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 + * + */ + +#ifndef __GST_OMX_VIDEO_H__ +#define __GST_OMX_VIDEO_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "gstomx.h" + +G_BEGIN_DECLS + +typedef struct +{ + GstVideoFormat format; + OMX_COLOR_FORMATTYPE type; +} GstOMXVideoNegotiationMap; + +GstVideoFormat +gst_omx_video_get_format_from_omx (OMX_COLOR_FORMATTYPE omx_colorformat); + +GList * +gst_omx_video_get_supported_colorformats (GstOMXPort * port, + GstVideoCodecState * state); + +GstCaps * gst_omx_video_get_caps_for_map(GList * map); + +void +gst_omx_video_negotiation_map_free (GstOMXVideoNegotiationMap * m); + +GstVideoCodecFrame * +gst_omx_video_find_nearest_frame (GstOMXBuffer * buf, GList * frames); + +G_END_DECLS + +#endif /* __GST_OMX_VIDEO_H__ */ diff --git a/omx/gstomxvideodec.c b/omx/gstomxvideodec.c old mode 100755 new mode 100644 index d3f341f..813962b --- a/omx/gstomxvideodec.c +++ b/omx/gstomxvideodec.c @@ -24,626 +24,36 @@ #include "config.h" #endif -//#define CODEC_DEC_OUTPUT_DUMP #include -#include -#include -#include -#if defined(CODEC_DEC_OUTPUT_DUMP) -#include /* for dump */ -#endif - -#include "gstomxvideodec.h" - -GST_DEBUG_CATEGORY_STATIC (gst_omx_video_dec_debug_category); -#define GST_CAT_DEFAULT gst_omx_video_dec_debug_category - -typedef struct _GstOMXMemory GstOMXMemory; -typedef struct _GstOMXMemoryAllocator GstOMXMemoryAllocator; -typedef struct _GstOMXMemoryAllocatorClass GstOMXMemoryAllocatorClass; - - -struct _GstOMXMemory -{ - GstMemory mem; - - GstOMXBuffer *buf; -}; - -struct _GstOMXMemoryAllocator -{ - GstAllocator parent; -}; - -struct _GstOMXMemoryAllocatorClass -{ - GstAllocatorClass parent_class; -}; - -#define GST_OMX_MEMORY_TYPE "openmax" - -static GstMemory * -gst_omx_memory_allocator_alloc_dummy (GstAllocator * allocator, gsize size, - GstAllocationParams * params) -{ - g_assert_not_reached (); - return NULL; -} - -static void -gst_omx_memory_allocator_free (GstAllocator * allocator, GstMemory * mem) -{ - GstOMXMemory *omem = (GstOMXMemory *) mem; - - /* TODO: We need to remember which memories are still used - * so we can wait until everything is released before allocating - * new memory - */ - - g_slice_free (GstOMXMemory, omem); -} - -static gpointer -gst_omx_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags) -{ - GstOMXMemory *omem = (GstOMXMemory *) mem; - - return omem->buf->omx_buf->pBuffer + omem->mem.offset; -} - -static void -gst_omx_memory_unmap (GstMemory * mem) -{ -} - -static GstMemory * -gst_omx_memory_share (GstMemory * mem, gssize offset, gssize size) -{ - g_assert_not_reached (); - return NULL; -} - -GType gst_omx_memory_allocator_get_type (void); -G_DEFINE_TYPE (GstOMXMemoryAllocator, gst_omx_memory_allocator, - GST_TYPE_ALLOCATOR); - -#define GST_TYPE_OMX_MEMORY_ALLOCATOR (gst_omx_memory_allocator_get_type()) -#define GST_IS_OMX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OMX_MEMORY_ALLOCATOR)) - -static void -gst_omx_memory_allocator_class_init (GstOMXMemoryAllocatorClass * klass) -{ - GstAllocatorClass *allocator_class; - - allocator_class = (GstAllocatorClass *) klass; - - allocator_class->alloc = gst_omx_memory_allocator_alloc_dummy; - allocator_class->free = gst_omx_memory_allocator_free; -} - -static void -gst_omx_memory_allocator_init (GstOMXMemoryAllocator * allocator) -{ - GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); - - alloc->mem_type = GST_OMX_MEMORY_TYPE; - alloc->mem_map = gst_omx_memory_map; - alloc->mem_unmap = gst_omx_memory_unmap; - alloc->mem_share = gst_omx_memory_share; - - /* default copy & is_span */ - - GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); -} - -static GstMemory * -gst_omx_memory_allocator_alloc (GstAllocator * allocator, GstMemoryFlags flags, - GstOMXBuffer * buf) -{ - GstOMXMemory *mem; - - /* FIXME: We don't allow sharing because we need to know - * when the memory becomes unused and can only then put - * it back to the pool. Which is done in the pool's release - * function - */ - flags |= GST_MEMORY_FLAG_NO_SHARE; - - mem = g_slice_new (GstOMXMemory); - /* the shared memory is always readonly */ - gst_memory_init (GST_MEMORY_CAST (mem), flags, allocator, NULL, - buf->omx_buf->nAllocLen, buf->port->port_def.nBufferAlignment, - 0, buf->omx_buf->nAllocLen); - - mem->buf = buf; - - return GST_MEMORY_CAST (mem); -} - -/* Buffer pool for the buffers of an OpenMAX port. - * - * This pool is only used if we either passed buffers from another - * pool to the OMX port or provide the OMX buffers directly to other - * elements. - * - * - * A buffer is in the pool if it is currently owned by the port, - * i.e. after OMX_{Fill,Empty}ThisBuffer(). A buffer is outside - * the pool after it was taken from the port after it was handled - * by the port, i.e. {Empty,Fill}BufferDone. - * - * Buffers can be allocated by us (OMX_AllocateBuffer()) or allocated - * by someone else and (temporarily) passed to this pool - * (OMX_UseBuffer(), OMX_UseEGLImage()). In the latter case the pool of - * the buffer will be overriden, and restored in free_buffer(). Other - * buffers are just freed there. - * - * The pool always has a fixed number of minimum and maximum buffers - * and these are allocated while starting the pool and released afterwards. - * They correspond 1:1 to the OMX buffers of the port, which are allocated - * before the pool is started. - * - * Acquiring a buffer from this pool happens after the OMX buffer has - * been acquired from the port. gst_buffer_pool_acquire_buffer() is - * supposed to return the buffer that corresponds to the OMX buffer. - * - * For buffers provided to upstream, the buffer will be passed to - * the component manually when it arrives and then unreffed. If the - * buffer is released before reaching the component it will be just put - * back into the pool as if EmptyBufferDone has happened. If it was - * passed to the component, it will be back into the pool when it was - * released and EmptyBufferDone has happened. - * - * For buffers provided to downstream, the buffer will be returned - * back to the component (OMX_FillThisBuffer()) when it is released. - */ - -static GQuark gst_omx_buffer_data_quark = 0; - -#define GST_OMX_BUFFER_POOL(pool) ((GstOMXBufferPool *) pool) -typedef struct _GstOMXBufferPool GstOMXBufferPool; -typedef struct _GstOMXBufferPoolClass GstOMXBufferPoolClass; - -struct _GstOMXBufferPool -{ - GstVideoBufferPool parent; - - GstElement *element; - - GstCaps *caps; - gboolean add_videometa; - GstVideoInfo video_info; - - /* Owned by element, element has to stop this pool before - * it destroys component or port */ - GstOMXComponent *component; - GstOMXPort *port; - - GstAllocator *allocator; - - /* Set from outside this pool */ - /* TRUE if we're currently allocating all our buffers */ - gboolean allocating; - - /* TRUE if the pool is not used anymore */ - gboolean deactivated; - - /* For populating the pool from another one */ - GstBufferPool *other_pool; - GPtrArray *buffers; - - /* Used during acquire for output ports to - * specify which buffer has to be retrieved - * and during alloc, which buffer has to be - * wrapped - */ - gint current_buffer_index; -}; - -struct _GstOMXBufferPoolClass -{ - GstVideoBufferPoolClass parent_class; -}; - -GType gst_omx_buffer_pool_get_type (void); - -G_DEFINE_TYPE (GstOMXBufferPool, gst_omx_buffer_pool, GST_TYPE_BUFFER_POOL); - -static gboolean -gst_omx_buffer_pool_start (GstBufferPool * bpool) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - /* Only allow to start the pool if we still are attached - * to a component and port */ - GST_OBJECT_LOCK (pool); - if (!pool->component || !pool->port) { - GST_OBJECT_UNLOCK (pool); - return FALSE; - } - GST_OBJECT_UNLOCK (pool); - - return - GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->start (bpool); -} - -static gboolean -gst_omx_buffer_pool_stop (GstBufferPool * bpool) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - /* Remove any buffers that are there */ - if(pool->buffers) - g_ptr_array_set_size (pool->buffers, 0); - - if (pool->caps) - gst_caps_unref (pool->caps); - pool->caps = NULL; - - pool->add_videometa = FALSE; - - return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->stop (bpool); -} - -static const gchar ** -gst_omx_buffer_pool_get_options (GstBufferPool * bpool) -{ - static const gchar *raw_video_options[] = - { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL }; - static const gchar *options[] = { NULL }; - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - GST_OBJECT_LOCK (pool); - if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo - && pool->port->port_def.format.video.eCompressionFormat == - OMX_VIDEO_CodingUnused) { - GST_OBJECT_UNLOCK (pool); - return raw_video_options; - } - GST_OBJECT_UNLOCK (pool); - - return options; -} - -static gboolean -gst_omx_buffer_pool_set_config (GstBufferPool * bpool, GstStructure * config) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - GstCaps *caps; - - GST_OBJECT_LOCK (pool); - - if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)) - goto wrong_config; - - if (caps == NULL) - goto no_caps; - - if (pool->port && pool->port->port_def.eDomain == OMX_PortDomainVideo - && pool->port->port_def.format.video.eCompressionFormat == - OMX_VIDEO_CodingUnused) { - GstVideoInfo info; - - /* now parse the caps from the config */ - if (!gst_video_info_from_caps (&info, caps)) - goto wrong_video_caps; - - /* enable metadata based on config of the pool */ - pool->add_videometa = - gst_buffer_pool_config_has_option (config, - GST_BUFFER_POOL_OPTION_VIDEO_META); - - pool->video_info = info; - } - - if (pool->caps) - gst_caps_unref (pool->caps); - pool->caps = gst_caps_ref (caps); - - GST_OBJECT_UNLOCK (pool); - - return GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->set_config - (bpool, config); - - /* ERRORS */ -wrong_config: - { - GST_OBJECT_UNLOCK (pool); - GST_WARNING_OBJECT (pool, "invalid config"); - return FALSE; - } -no_caps: - { - GST_OBJECT_UNLOCK (pool); - GST_WARNING_OBJECT (pool, "no caps in config"); - return FALSE; - } -wrong_video_caps: - { - GST_OBJECT_UNLOCK (pool); - GST_WARNING_OBJECT (pool, - "failed getting geometry from caps %" GST_PTR_FORMAT, caps); - return FALSE; - } -} - -static GstFlowReturn -gst_omx_buffer_pool_alloc_buffer (GstBufferPool * bpool, - GstBuffer ** buffer, GstBufferPoolAcquireParams * params) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - GstBuffer *buf; - GstOMXBuffer *omx_buf; - - g_return_val_if_fail (pool->allocating, GST_FLOW_ERROR); - - omx_buf = g_ptr_array_index (pool->port->buffers, pool->current_buffer_index); - g_return_val_if_fail (omx_buf != NULL, GST_FLOW_ERROR); - - if (pool->other_pool) { - guint i, n; - - buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); - g_assert (pool->other_pool == buf->pool); - gst_object_replace ((GstObject **) & buf->pool, NULL); - - n = gst_buffer_n_memory (buf); - for (i = 0; i < n; i++) { - GstMemory *mem = gst_buffer_peek_memory (buf, i); - - /* FIXME: We don't allow sharing because we need to know - * when the memory becomes unused and can only then put - * it back to the pool. Which is done in the pool's release - * function - */ - GST_MINI_OBJECT_FLAG_SET (mem, GST_MEMORY_FLAG_NO_SHARE); - } - - if (pool->add_videometa) { - GstVideoMeta *meta; - - meta = gst_buffer_get_video_meta (buf); - if (!meta) { - gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE, - GST_VIDEO_INFO_FORMAT (&pool->video_info), - GST_VIDEO_INFO_WIDTH (&pool->video_info), - GST_VIDEO_INFO_HEIGHT (&pool->video_info)); - } - } - } else { - GstMemory *mem; - - mem = gst_omx_memory_allocator_alloc (pool->allocator, 0, omx_buf); - buf = gst_buffer_new (); - gst_buffer_append_memory (buf, mem); - g_ptr_array_add (pool->buffers, buf); - - if (pool->add_videometa) { - gsize offset[4] = { 0, }; - gint stride[4] = { 0, }; - - switch (pool->video_info.finfo->format) { - case GST_VIDEO_FORMAT_I420: - offset[0] = 0; - stride[0] = pool->port->port_def.format.video.nStride; - offset[1] = - stride[0] * pool->port->port_def.format.video.nSliceHeight; - stride[1] = pool->port->port_def.format.video.nStride / 2; - offset[2] = - offset[1] + - stride[1] * (pool->port->port_def.format.video.nSliceHeight / 2); - stride[2] = pool->port->port_def.format.video.nStride / 2; - break; - case GST_VIDEO_FORMAT_NV12: - case GST_VIDEO_FORMAT_SN12: - case GST_VIDEO_FORMAT_ST12: - offset[0] = 0; - stride[0] = pool->port->port_def.format.video.nStride; - offset[1] = - stride[0] * pool->port->port_def.format.video.nSliceHeight; - stride[1] = pool->port->port_def.format.video.nStride; - break; - default: - g_assert_not_reached (); - break; - } - - gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE, - GST_VIDEO_INFO_FORMAT (&pool->video_info), - GST_VIDEO_INFO_WIDTH (&pool->video_info), - GST_VIDEO_INFO_HEIGHT (&pool->video_info), - GST_VIDEO_INFO_N_PLANES (&pool->video_info), offset, stride); - } - } - - gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buf), - gst_omx_buffer_data_quark, omx_buf, NULL); - - *buffer = buf; - - pool->current_buffer_index++; - - return GST_FLOW_OK; -} - -static void -gst_omx_buffer_pool_free_buffer (GstBufferPool * bpool, GstBuffer * buffer) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - /* If the buffers belong to another pool, restore them now */ - GST_OBJECT_LOCK (pool); - if (pool->other_pool) { - gst_object_replace ((GstObject **) & buffer->pool, - (GstObject *) pool->other_pool); - } - GST_OBJECT_UNLOCK (pool); - gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (buffer), - gst_omx_buffer_data_quark, NULL, NULL); - - GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->free_buffer (bpool, - buffer); -} - -static GstFlowReturn -gst_omx_buffer_pool_acquire_buffer (GstBufferPool * bpool, - GstBuffer ** buffer, GstBufferPoolAcquireParams * params) -{ - GstFlowReturn ret; - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - - if (pool->port->port_def.eDir == OMX_DirOutput) { - GstBuffer *buf; - - g_return_val_if_fail (pool->current_buffer_index != -1, GST_FLOW_ERROR); - - buf = g_ptr_array_index (pool->buffers, pool->current_buffer_index); - g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); - *buffer = buf; - ret = GST_FLOW_OK; - - /* If it's our own memory we have to set the sizes */ - if (!pool->other_pool) { - GstMemory *mem = gst_buffer_peek_memory (*buffer, 0); - - g_assert (mem - && g_strcmp0 (mem->allocator->mem_type, GST_OMX_MEMORY_TYPE) == 0); - mem->size = ((GstOMXMemory *) mem)->buf->omx_buf->nFilledLen; - mem->offset = ((GstOMXMemory *) mem)->buf->omx_buf->nOffset; - } - } else { - /* Acquire any buffer that is available to be filled by upstream */ - ret = - GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->acquire_buffer - (bpool, buffer, params); - } - - return ret; -} - -static void -gst_omx_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (bpool); - OMX_ERRORTYPE err; - GstOMXBuffer *omx_buf; - - g_assert (pool->component && pool->port); - - if (!pool->allocating && !pool->deactivated) { - omx_buf = - gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (buffer), - gst_omx_buffer_data_quark); - if (pool->port->port_def.eDir == OMX_DirOutput && !omx_buf->used) { - /* Release back to the port, can be filled again */ - err = gst_omx_port_release_buffer (pool->port, omx_buf); - if (err != OMX_ErrorNone) { - GST_ELEMENT_ERROR (pool->element, LIBRARY, SETTINGS, (NULL), - ("Failed to relase output buffer to component: %s (0x%08x)", - gst_omx_error_to_string (err), err)); - } - } else if (!omx_buf->used) { - /* TODO: Implement. - * - * If not used (i.e. was not passed to the component) this should do - * the same as EmptyBufferDone. - * If it is used (i.e. was passed to the component) this should do - * nothing until EmptyBufferDone. - * - * EmptyBufferDone should release the buffer to the pool so it can - * be allocated again - * - * Needs something to call back here in EmptyBufferDone, like keeping - * a ref on the buffer in GstOMXBuffer until EmptyBufferDone... which - * would ensure that the buffer is always unused when this is called. - */ - g_assert_not_reached (); - GST_BUFFER_POOL_CLASS (gst_omx_buffer_pool_parent_class)->release_buffer - (bpool, buffer); - } - } -} - -static void -gst_omx_buffer_pool_finalize (GObject * object) -{ - GstOMXBufferPool *pool = GST_OMX_BUFFER_POOL (object); - - if (pool->element) - gst_object_unref (pool->element); - pool->element = NULL; - - if (pool->buffers) - g_ptr_array_unref (pool->buffers); - pool->buffers = NULL; - - if (pool->other_pool) - gst_object_unref (pool->other_pool); - pool->other_pool = NULL; - - if (pool->allocator) - gst_object_unref (pool->allocator); - pool->allocator = NULL; - - if (pool->caps) - gst_caps_unref (pool->caps); - pool->caps = NULL; - - G_OBJECT_CLASS (gst_omx_buffer_pool_parent_class)->finalize (object); -} - -static void -gst_omx_buffer_pool_class_init (GstOMXBufferPoolClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass; - - gst_omx_buffer_data_quark = g_quark_from_static_string ("GstOMXBufferData"); - - gobject_class->finalize = gst_omx_buffer_pool_finalize; - gstbufferpool_class->start = gst_omx_buffer_pool_start; - gstbufferpool_class->stop = gst_omx_buffer_pool_stop; - gstbufferpool_class->get_options = gst_omx_buffer_pool_get_options; - gstbufferpool_class->set_config = gst_omx_buffer_pool_set_config; - gstbufferpool_class->alloc_buffer = gst_omx_buffer_pool_alloc_buffer; - gstbufferpool_class->free_buffer = gst_omx_buffer_pool_free_buffer; - gstbufferpool_class->acquire_buffer = gst_omx_buffer_pool_acquire_buffer; - gstbufferpool_class->release_buffer = gst_omx_buffer_pool_release_buffer; -} +#if defined (USE_OMX_TARGET_RPI) && defined(__GNUC__) +#ifndef __VCCOREVER__ +#define __VCCOREVER__ 0x04000000 +#endif -static void -gst_omx_buffer_pool_init (GstOMXBufferPool * pool) -{ - pool->buffers = g_ptr_array_new (); - pool->allocator = g_object_new (gst_omx_memory_allocator_get_type (), NULL); -} +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#pragma GCC optimize ("gnu89-inline") +#endif -static GstBufferPool * -gst_omx_buffer_pool_new (GstElement * element, GstOMXComponent * component, - GstOMXPort * port) -{ - GstOMXBufferPool *pool; +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) +#include +#include +#endif - pool = g_object_new (gst_omx_buffer_pool_get_type (), NULL); - pool->element = gst_object_ref (element); - pool->component = component; - pool->port = port; +#if defined (USE_OMX_TARGET_RPI) && defined(__GNUC__) +#pragma GCC reset_options +#pragma GCC diagnostic pop +#endif - return GST_BUFFER_POOL (pool); -} +#include -typedef struct _BufferIdentification BufferIdentification; -struct _BufferIdentification -{ - guint64 timestamp; -}; +#include "gstomxbufferpool.h" +#include "gstomxvideo.h" +#include "gstomxvideodec.h" -static void -buffer_identification_free (BufferIdentification * id) -{ - g_slice_free (BufferIdentification, id); -} +GST_DEBUG_CATEGORY_STATIC (gst_omx_video_dec_debug_category); +#define GST_CAT_DEFAULT gst_omx_video_dec_debug_category /* prototypes */ static void gst_omx_video_dec_finalize (GObject * object); @@ -658,16 +68,14 @@ static gboolean gst_omx_video_dec_start (GstVideoDecoder * decoder); static gboolean gst_omx_video_dec_stop (GstVideoDecoder * decoder); static gboolean gst_omx_video_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state); -static gboolean gst_omx_video_dec_reset (GstVideoDecoder * decoder, - gboolean hard); +static gboolean gst_omx_video_dec_flush (GstVideoDecoder * decoder); static GstFlowReturn gst_omx_video_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame); static GstFlowReturn gst_omx_video_dec_finish (GstVideoDecoder * decoder); static gboolean gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query); -static GstFlowReturn gst_omx_video_dec_drain (GstOMXVideoDec * self, - gboolean is_eos); +static GstFlowReturn gst_omx_video_dec_drain (GstOMXVideoDec * self); static OMX_ERRORTYPE gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self); @@ -705,7 +113,7 @@ gst_omx_video_dec_class_init (GstOMXVideoDecClass * klass) video_decoder_class->close = GST_DEBUG_FUNCPTR (gst_omx_video_dec_close); video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_omx_video_dec_start); video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_omx_video_dec_stop); - video_decoder_class->reset = GST_DEBUG_FUNCPTR (gst_omx_video_dec_reset); + video_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_omx_video_dec_flush); video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_video_dec_set_format); video_decoder_class->handle_frame = @@ -714,7 +122,13 @@ gst_omx_video_dec_class_init (GstOMXVideoDecClass * klass) video_decoder_class->decide_allocation = GST_DEBUG_FUNCPTR (gst_omx_video_dec_decide_allocation); - klass->cdata.default_src_template_caps = "video/x-raw, " + klass->cdata.type = GST_OMX_COMPONENT_TYPE_FILTER; + klass->cdata.default_src_template_caps = +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE, + "RGBA") "; " +#endif + "video/x-raw, " "width = " GST_VIDEO_SIZE_RANGE ", " "height = " GST_VIDEO_SIZE_RANGE ", " "framerate = " GST_VIDEO_FPS_RANGE; } @@ -723,6 +137,9 @@ static void gst_omx_video_dec_init (GstOMXVideoDec * self) { gst_video_decoder_set_packetized (GST_VIDEO_DECODER (self), TRUE); + gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST + (self), TRUE); + GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (self)); g_mutex_init (&self->drain_lock); g_cond_init (&self->drain_cond); @@ -774,8 +191,8 @@ gst_omx_video_dec_open (GstVideoDecoder * decoder) in_port_index = 0; out_port_index = 1; } else { - GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", param.nPorts, - param.nStartPortNumber); + GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", + (guint) param.nPorts, (guint) param.nStartPortNumber); in_port_index = param.nStartPortNumber + 0; out_port_index = param.nStartPortNumber + 1; } @@ -794,9 +211,55 @@ gst_omx_video_dec_open (GstVideoDecoder * decoder) return FALSE; } #endif - GST_DEBUG_OBJECT (self, "Opened decoder"); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + GST_DEBUG_OBJECT (self, "Opening EGL renderer"); + self->egl_render = + gst_omx_component_new (GST_OBJECT_CAST (self), klass->cdata.core_name, + "OMX.broadcom.egl_render", NULL, klass->cdata.hacks); + + if (!self->egl_render) + return FALSE; + + if (gst_omx_component_get_state (self->egl_render, + GST_CLOCK_TIME_NONE) != OMX_StateLoaded) + return FALSE; + + { + OMX_PORT_PARAM_TYPE param; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (¶m); + + err = + gst_omx_component_get_parameter (self->egl_render, + OMX_IndexParamVideoInit, ¶m); + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, "Couldn't get port information: %s (0x%08x)", + gst_omx_error_to_string (err), err); + /* Fallback */ + in_port_index = 0; + out_port_index = 1; + } else { + GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", param.nPorts, + param.nStartPortNumber); + in_port_index = param.nStartPortNumber + 0; + out_port_index = param.nStartPortNumber + 1; + } + } + + self->egl_in_port = + gst_omx_component_add_port (self->egl_render, in_port_index); + self->egl_out_port = + gst_omx_component_add_port (self->egl_render, out_port_index); + + if (!self->egl_in_port || !self->egl_out_port) + return FALSE; + + GST_DEBUG_OBJECT (self, "Opened EGL renderer"); +#endif + return TRUE; } @@ -807,6 +270,31 @@ gst_omx_video_dec_shutdown (GstOMXVideoDec * self) GST_DEBUG_OBJECT (self, "Shutting down decoder"); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + state = gst_omx_component_get_state (self->egl_render, 0); + if (state > OMX_StateLoaded || state == OMX_StateInvalid) { + if (state > OMX_StateIdle) { + gst_omx_component_set_state (self->egl_render, OMX_StateIdle); + gst_omx_component_set_state (self->dec, OMX_StateIdle); + gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND); + gst_omx_component_get_state (self->dec, 1 * GST_SECOND); + } + gst_omx_component_set_state (self->egl_render, OMX_StateLoaded); + gst_omx_component_set_state (self->dec, OMX_StateLoaded); + + gst_omx_port_deallocate_buffers (self->dec_in_port); + gst_omx_video_dec_deallocate_output_buffers (self); + gst_omx_close_tunnel (self->dec_out_port, self->egl_in_port); + if (state > OMX_StateLoaded) { + gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND); + gst_omx_component_get_state (self->dec, 1 * GST_SECOND); + } + } + + /* Otherwise we didn't use EGL and just fall back to + * shutting down the decoder */ +#endif + state = gst_omx_component_get_state (self->dec, 0); if (state > OMX_StateLoaded || state == OMX_StateInvalid) { if (state > OMX_StateIdle) { @@ -853,6 +341,14 @@ gst_omx_video_dec_close (GstVideoDecoder * decoder) gst_omx_component_free (self->dec); self->dec = NULL; +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + self->egl_in_port = NULL; + self->egl_out_port = NULL; + if (self->egl_render) + gst_omx_component_free (self->egl_render); + self->egl_render = NULL; +#endif + self->started = FALSE; GST_DEBUG_OBJECT (self, "Closed decoder"); @@ -896,6 +392,12 @@ gst_omx_video_dec_change_state (GstElement * element, GstStateChange transition) gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); if (self->dec_out_port) gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (self->egl_in_port) + gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, TRUE); + if (self->egl_out_port) + gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, TRUE); +#endif g_mutex_lock (&self->drain_lock); self->draining = FALSE; @@ -903,163 +405,37 @@ gst_omx_video_dec_change_state (GstElement * element, GstStateChange transition) g_mutex_unlock (&self->drain_lock); break; default: - break; - } - - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - ret = - GST_ELEMENT_CLASS (gst_omx_video_dec_parent_class)->change_state - (element, transition); - - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - self->downstream_flow_ret = GST_FLOW_FLUSHING; - self->started = FALSE; - - if (!gst_omx_video_dec_shutdown (self)) - ret = GST_STATE_CHANGE_FAILURE; - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} - -#define MAX_FRAME_DIST_TICKS (5 * OMX_TICKS_PER_SECOND) -#define MAX_FRAME_DIST_FRAMES (100) - -static GstVideoCodecFrame * -_find_nearest_frame (GstOMXVideoDec * self, GstOMXBuffer * buf) -{ - GList *l, *best_l = NULL; - GList *finish_frames = NULL; - GstVideoCodecFrame *best = NULL; - guint64 best_timestamp = 0; - guint64 best_diff = G_MAXUINT64; - BufferIdentification *best_id = NULL; - GList *frames; - - frames = gst_video_decoder_get_frames (GST_VIDEO_DECODER (self)); - - for (l = frames; l; l = l->next) { - GstVideoCodecFrame *tmp = l->data; - BufferIdentification *id = gst_video_codec_frame_get_user_data (tmp); - guint64 timestamp, diff; - - /* This happens for frames that were just added but - * which were not passed to the component yet. Ignore - * them here! - */ - if (!id) - continue; - - timestamp = id->timestamp; - - if (timestamp > buf->omx_buf->nTimeStamp) - diff = timestamp - buf->omx_buf->nTimeStamp; - else - diff = buf->omx_buf->nTimeStamp - timestamp; - - if (best == NULL || diff < best_diff) { - best = tmp; - best_timestamp = timestamp; - best_diff = diff; - best_l = l; - best_id = id; - - /* For frames without timestamp we simply take the first frame */ - if ((buf->omx_buf->nTimeStamp == 0 && timestamp == 0) || diff == 0) - break; - } - } - - if (best_id) { - for (l = frames; l && l != best_l; l = l->next) { - GstVideoCodecFrame *tmp = l->data; - BufferIdentification *id = gst_video_codec_frame_get_user_data (tmp); - guint64 diff_ticks, diff_frames; - - /* This happens for frames that were just added but - * which were not passed to the component yet. Ignore - * them here! - */ - if (!id) - continue; - - if (id->timestamp > best_timestamp) - break; - - if (id->timestamp == 0 || best_timestamp == 0) - diff_ticks = 0; - else - diff_ticks = best_timestamp - id->timestamp; - diff_frames = best->system_frame_number - tmp->system_frame_number; - - if (diff_ticks > MAX_FRAME_DIST_TICKS - || diff_frames > MAX_FRAME_DIST_FRAMES) { - finish_frames = - g_list_prepend (finish_frames, gst_video_codec_frame_ref (tmp)); - } - } - } - - if (finish_frames) { - g_warning ("Too old frames, bug in decoder -- please file a bug"); - for (l = finish_frames; l; l = l->next) { - gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), l->data); - } - } - - if (best) - gst_video_codec_frame_ref (best); - - g_list_foreach (frames, (GFunc) gst_video_codec_frame_unref, NULL); - g_list_free (frames); - - return best; -} - -#ifdef CODEC_DEC_OUTPUT_DUMP /* for decoder output dump */ -static inline void -decoder_output_dump(GstOMXVideoDec *self, MMVideoBuffer *outbuf) -{ - char *temp = (char *)outbuf->data[0]; - int i = 0; - char filename[100]={0}; - FILE *fp = NULL; - int ret =0; + break; + } - GST_ERROR_OBJECT (self, "codec dec output dump start. w = %d, h = %d", outbuf->width[0], outbuf->height[0]); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; - sprintf(filename, "/opt/usr/dec_output_dump_%d_%d.yuv", outbuf->stride_width[0], outbuf->height[0]); - fp = fopen(filename, "ab"); + ret = + GST_ELEMENT_CLASS (gst_omx_video_dec_parent_class)->change_state + (element, transition); - for (i = 0; i < outbuf->height[0]; i++) { - ret = fwrite(temp, outbuf->width[0], 1, fp); - temp += outbuf->stride_width[0]; - } + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; - temp = (char *)outbuf->data[1]; + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + self->downstream_flow_ret = GST_FLOW_FLUSHING; + self->started = FALSE; - for(i = 0; i < outbuf->height[1]; i++) { - ret = fwrite(temp, outbuf->width[1], 1, fp); - temp += outbuf->stride_width[1]; + if (!gst_omx_video_dec_shutdown (self)) + ret = GST_STATE_CHANGE_FAILURE; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; } - GST_ERROR_OBJECT (self,"codec dec output dumped!! ret = %d", ret); - fclose(fp); + return ret; } -#endif static gboolean gst_omx_video_dec_fill_buffer (GstOMXVideoDec * self, @@ -1074,8 +450,9 @@ gst_omx_video_dec_fill_buffer (GstOMXVideoDec * self, if (vinfo->width != port_def->format.video.nFrameWidth || vinfo->height != port_def->format.video.nFrameHeight) { - GST_ERROR_OBJECT (self, "Resolution do not match. port: %dx%d vinfo: %dx%d", - port_def->format.video.nFrameWidth, port_def->format.video.nFrameHeight, + GST_ERROR_OBJECT (self, "Resolution do not match: port=%ux%u vinfo=%dx%d", + (guint) port_def->format.video.nFrameWidth, + (guint) port_def->format.video.nFrameHeight, vinfo->width, vinfo->height); goto done; } @@ -1085,7 +462,11 @@ gst_omx_video_dec_fill_buffer (GstOMXVideoDec * self, if (gst_buffer_get_size (outbuf) == inbuf->omx_buf->nFilledLen) { GstMapInfo map = GST_MAP_INFO_INIT; - gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + if (!gst_buffer_map (outbuf, &map, GST_MAP_WRITE)) { + GST_ERROR_OBJECT (self, "Failed to map output buffer"); + goto done; + } + memcpy (map.data, inbuf->omx_buf->pBuffer + inbuf->omx_buf->nOffset, inbuf->omx_buf->nFilledLen); @@ -1096,100 +477,59 @@ gst_omx_video_dec_fill_buffer (GstOMXVideoDec * self, #endif //ENS:a /* Different strides */ - - switch (vinfo->finfo->format) { - case GST_VIDEO_FORMAT_I420:{ - gint i, j, height, width; - guint8 *src, *dest; - gint src_stride, dest_stride; - - gst_video_frame_map (&frame, vinfo, outbuf, GST_MAP_WRITE); - for (i = 0; i < 3; i++) { - if (i == 0) { - src_stride = port_def->format.video.nStride; - dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, i); - - /* XXX: Try this if no stride was set */ - if (src_stride == 0) - src_stride = dest_stride; - } else { - src_stride = port_def->format.video.nStride / 2; - dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, i); - - /* XXX: Try this if no stride was set */ - if (src_stride == 0) - src_stride = dest_stride; - } - - src = inbuf->omx_buf->pBuffer + inbuf->omx_buf->nOffset; - if (i > 0) - src += - port_def->format.video.nSliceHeight * - port_def->format.video.nStride; - if (i == 2) - src += - (port_def->format.video.nSliceHeight / 2) * - (port_def->format.video.nStride / 2); - - dest = GST_VIDEO_FRAME_COMP_DATA (&frame, i); - height = GST_VIDEO_FRAME_COMP_HEIGHT (&frame, i); - width = GST_VIDEO_FRAME_COMP_WIDTH (&frame, i); - - for (j = 0; j < height; j++) { - memcpy (dest, src, width); - src += src_stride; - dest += dest_stride; - } - } - gst_video_frame_unmap (&frame); - ret = TRUE; - break; - } - case GST_VIDEO_FORMAT_NV12:{ - gint i, j, height, width; - guint8 *src, *dest; - gint src_stride, dest_stride; - - gst_video_frame_map (&frame, vinfo, outbuf, GST_MAP_WRITE); - for (i = 0; i < 2; i++) { - if (i == 0) { - src_stride = port_def->format.video.nStride; - dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, i); - - /* XXX: Try this if no stride was set */ - if (src_stride == 0) - src_stride = dest_stride; - } else { - src_stride = port_def->format.video.nStride; - dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, i); - - /* XXX: Try this if no stride was set */ - if (src_stride == 0) - src_stride = dest_stride; - } - - src = inbuf->omx_buf->pBuffer + inbuf->omx_buf->nOffset; - if (i == 1) - src += - port_def->format.video.nSliceHeight * - port_def->format.video.nStride; - - dest = GST_VIDEO_FRAME_COMP_DATA (&frame, i); - height = GST_VIDEO_FRAME_COMP_HEIGHT (&frame, i); - width = GST_VIDEO_FRAME_COMP_WIDTH (&frame, i) * (i == 0 ? 1 : 2); - - for (j = 0; j < height; j++) { - memcpy (dest, src, width); - src += src_stride; - dest += dest_stride; - } - } - gst_video_frame_unmap (&frame); - ret = TRUE; - break; - } - case GST_VIDEO_FORMAT_SN12: - case GST_VIDEO_FORMAT_ST12:{ + if (gst_video_frame_map (&frame, vinfo, outbuf, GST_MAP_WRITE)) { + const guint nstride = port_def->format.video.nStride; + const guint nslice = port_def->format.video.nSliceHeight; + guint src_stride[GST_VIDEO_MAX_PLANES] = { nstride, 0, }; + guint src_size[GST_VIDEO_MAX_PLANES] = { nstride * nslice, 0, }; + gint dst_width[GST_VIDEO_MAX_PLANES] = { 0, }; + gint dst_height[GST_VIDEO_MAX_PLANES] = + { GST_VIDEO_INFO_HEIGHT (vinfo), 0, }; + const guint8 *src; + guint p; + + switch (GST_VIDEO_INFO_FORMAT (vinfo)) { + case GST_VIDEO_FORMAT_ABGR: + case GST_VIDEO_FORMAT_ARGB: + dst_width[0] = GST_VIDEO_INFO_WIDTH (vinfo) * 4; + break; + case GST_VIDEO_FORMAT_RGB16: + case GST_VIDEO_FORMAT_BGR16: + case GST_VIDEO_FORMAT_YUY2: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_YVYU: + dst_width[0] = GST_VIDEO_INFO_WIDTH (vinfo) * 2; + break; + case GST_VIDEO_FORMAT_GRAY8: + dst_width[0] = GST_VIDEO_INFO_WIDTH (vinfo); + break; + case GST_VIDEO_FORMAT_I420: + dst_width[0] = GST_VIDEO_INFO_WIDTH (vinfo); + src_stride[1] = nstride / 2; + src_size[1] = (src_stride[1] * nslice) / 2; + dst_width[1] = GST_VIDEO_INFO_WIDTH (vinfo) / 2; + dst_height[1] = GST_VIDEO_INFO_HEIGHT (vinfo) / 2; + src_stride[2] = nstride / 2; + src_size[2] = (src_stride[1] * nslice) / 2; + dst_width[2] = GST_VIDEO_INFO_WIDTH (vinfo) / 2; + dst_height[2] = GST_VIDEO_INFO_HEIGHT (vinfo) / 2; + break; + case GST_VIDEO_FORMAT_NV12: + dst_width[0] = GST_VIDEO_INFO_WIDTH (vinfo); + src_stride[1] = nstride; + src_size[1] = src_stride[1] * nslice / 2; + dst_width[1] = GST_VIDEO_INFO_WIDTH (vinfo); + dst_height[1] = GST_VIDEO_INFO_HEIGHT (vinfo) / 2; + break; + case GST_VIDEO_FORMAT_NV16: + dst_width[0] = GST_VIDEO_INFO_WIDTH (vinfo); + src_stride[1] = nstride; + src_size[1] = src_stride[1] * nslice; + dst_width[1] = GST_VIDEO_INFO_WIDTH (vinfo); + dst_height[1] = GST_VIDEO_INFO_HEIGHT (vinfo); + break; + case GST_VIDEO_FORMAT_SN12: + case GST_VIDEO_FORMAT_ST12:{ GstMemory *mem_imgb = NULL; void *imgb_data = NULL; #ifdef USE_MM_VIDEO_BUFFER @@ -1207,7 +547,7 @@ gst_omx_video_dec_fill_buffer (GstOMXVideoDec * self, GST_WARNING_OBJECT (self, "dec output buf has TBM_BO buf_share_method"); } #ifdef CODEC_DEC_OUTPUT_DUMP - decoder_output_dump(self, out_imgb); + decoder_output_dump(self, out_imgb); #endif #else SCMN_IMGB *out_imgb = NULL; @@ -1240,12 +580,34 @@ gst_omx_video_dec_fill_buffer (GstOMXVideoDec * self, ret = TRUE; break; } - default: - GST_ERROR_OBJECT (self, "Unsupported format"); - goto done; - break; - } + default: + g_assert_not_reached (); + break; + } + + src = inbuf->omx_buf->pBuffer + inbuf->omx_buf->nOffset; + for (p = 0; p < GST_VIDEO_INFO_N_PLANES (vinfo); p++) { + const guint8 *data; + guint8 *dst; + guint h; + + dst = GST_VIDEO_FRAME_PLANE_DATA (&frame, p); + data = src; + for (h = 0; h < dst_height[p]; h++) { + memcpy (dst, data, dst_width[p]); + dst += GST_VIDEO_INFO_PLANE_STRIDE (vinfo, p); + data += src_stride[p]; + } + src += src_size[p]; + } + + gst_video_frame_unmap (&frame); + ret = TRUE; + } else { + GST_ERROR_OBJECT (self, "Can't map output buffer to frame"); + goto done; + } done: if (ret) { @@ -1276,18 +638,29 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self) GstVideoCodecState *state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (self)); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + port = self->eglimage ? self->egl_out_port : self->dec_out_port; +#else port = self->dec_out_port; +#endif pool = gst_video_decoder_get_buffer_pool (GST_VIDEO_DECODER (self)); - /* FIXME: Enable this once there's a way to request downstream to - * release all our buffers, e.g. - * http://cgit.freedesktop.org/~wtay/gstreamer/log/?h=release-pool */ if (pool) { GstAllocator *allocator; config = gst_buffer_pool_get_config (pool); - gst_buffer_pool_config_get_params (config, &caps, NULL, &min, &max); - gst_buffer_pool_config_get_allocator (config, &allocator, NULL); + if (!gst_buffer_pool_config_get_params (config, &caps, NULL, &min, &max)) { + GST_ERROR_OBJECT (self, "Can't get buffer pool params"); + gst_structure_free (config); + err = OMX_ErrorUndefined; + goto done; + } + if (!gst_buffer_pool_config_get_allocator (config, &allocator, NULL)) { + GST_ERROR_OBJECT (self, "Can't get buffer pool allocator"); + gst_structure_free (config); + err = OMX_ErrorUndefined; + goto done; + } /* Need at least 2 buffers for anything meaningful */ min = MAX (MAX (min, port->port_def.nBufferCountMin), 4); @@ -1302,9 +675,15 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self) add_videometa = gst_buffer_pool_config_has_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); + gst_structure_free (config); - /* TODO: Implement something here */ +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + eglimage = self->eglimage && (allocator + && g_strcmp0 (allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0); +#else + /* TODO: Implement something that works for other targets too */ eglimage = FALSE; +#endif caps = caps ? gst_caps_ref (caps) : NULL; GST_DEBUG_OBJECT (self, "Trying to use pool %p with caps %" GST_PTR_FORMAT @@ -1316,14 +695,150 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self) GST_DEBUG_OBJECT (self, "No pool available, not negotiated yet"); } - min = max = port->port_def.nBufferCountMin; - if (caps){ - GST_LOG_OBJECT(self,"gst-omx: Creating our own outport buffer pool. min:[%d], max:[%d]",min,max); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + /* Will retry without EGLImage */ + if (self->eglimage && !eglimage) { + GST_DEBUG_OBJECT (self, + "Wanted to use EGLImage but downstream doesn't support it"); + err = OMX_ErrorUndefined; + goto done; + } +#endif + + if (caps) self->out_port_pool = gst_omx_buffer_pool_new (GST_ELEMENT_CAST (self), self->dec, port); - } - /* TODO: Implement EGLImage handling and usage of other downstream buffers */ +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (eglimage) { + GList *buffers = NULL; + GList *images = NULL; + gint i; + GstBufferPoolAcquireParams params = { 0, }; + EGLDisplay egl_display = EGL_NO_DISPLAY; + + GST_DEBUG_OBJECT (self, "Trying to allocate %d EGLImages", min); + + for (i = 0; i < min; i++) { + GstBuffer *buffer; + GstMemory *mem; + + if (gst_buffer_pool_acquire_buffer (pool, &buffer, ¶ms) != GST_FLOW_OK + || gst_buffer_n_memory (buffer) != 1 + || !(mem = gst_buffer_peek_memory (buffer, 0)) + || g_strcmp0 (mem->allocator->mem_type, + GST_EGL_IMAGE_MEMORY_TYPE) != 0) { + GST_INFO_OBJECT (self, "Failed to allocated %d-th EGLImage", i); + g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); + g_list_free (images); + buffers = NULL; + images = NULL; + /* TODO: For non-RPi targets we want to use the normal memory code below */ + /* Retry without EGLImage */ + err = OMX_ErrorUndefined; + goto done; + } + + buffers = g_list_append (buffers, buffer); + gst_egl_image_memory_set_orientation (mem, + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_FLIP); + images = g_list_append (images, gst_egl_image_memory_get_image (mem)); + if (egl_display == EGL_NO_DISPLAY) + egl_display = gst_egl_image_memory_get_display (mem); + } + + GST_DEBUG_OBJECT (self, "Allocated %d EGLImages successfully", min); + + /* Everything went fine? */ + if (eglimage) { + GST_DEBUG_OBJECT (self, "Setting EGLDisplay"); + self->egl_out_port->port_def.format.video.pNativeWindow = egl_display; + err = + gst_omx_port_update_port_definition (self->egl_out_port, + &self->egl_out_port->port_def); + if (err != OMX_ErrorNone) { + GST_INFO_OBJECT (self, + "Failed to set EGLDisplay on port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); + g_list_free (images); + /* TODO: For non-RPi targets we want to use the normal memory code below */ + /* Retry without EGLImage */ + goto done; + } else { + GList *l; + + if (min != port->port_def.nBufferCountActual) { + err = gst_omx_port_update_port_definition (port, NULL); + if (err == OMX_ErrorNone) { + port->port_def.nBufferCountActual = min; + err = gst_omx_port_update_port_definition (port, &port->port_def); + } + + if (err != OMX_ErrorNone) { + GST_INFO_OBJECT (self, + "Failed to configure %u output buffers: %s (0x%08x)", min, + gst_omx_error_to_string (err), err); + g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); + g_list_free (images); + /* TODO: For non-RPi targets we want to use the normal memory code below */ + /* Retry without EGLImage */ + + goto done; + } + } + + if (!gst_omx_port_is_enabled (port)) { + err = gst_omx_port_set_enabled (port, TRUE); + if (err != OMX_ErrorNone) { + GST_INFO_OBJECT (self, + "Failed to enable port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); + g_list_free (images); + /* TODO: For non-RPi targets we want to use the normal memory code below */ + /* Retry without EGLImage */ + goto done; + } + } + + err = gst_omx_port_use_eglimages (port, images); + g_list_free (images); + + if (err != OMX_ErrorNone) { + GST_INFO_OBJECT (self, + "Failed to pass EGLImages to port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); + /* TODO: For non-RPi targets we want to use the normal memory code below */ + /* Retry without EGLImage */ + goto done; + } + + err = gst_omx_port_wait_enabled (port, 2 * GST_SECOND); + if (err != OMX_ErrorNone) { + GST_INFO_OBJECT (self, + "Failed to wait until port is enabled: %s (0x%08x)", + gst_omx_error_to_string (err), err); + g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); + /* TODO: For non-RPi targets we want to use the normal memory code below */ + /* Retry without EGLImage */ + goto done; + } + + GST_DEBUG_OBJECT (self, "Populating internal buffer pool"); + GST_OMX_BUFFER_POOL (self->out_port_pool)->other_pool = + GST_BUFFER_POOL (gst_object_ref (pool)); + for (l = buffers; l; l = l->next) { + g_ptr_array_add (GST_OMX_BUFFER_POOL (self->out_port_pool)->buffers, + l->data); + } + g_list_free (buffers); + /* All good and done, set caps below */ + } + } + } +#endif /* If not using EGLImage or trying to use EGLImage failed */ if (!eglimage) { @@ -1366,8 +881,15 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self) min); min = port->port_def.nBufferCountMin; - if (!was_enabled) - gst_omx_port_set_enabled (port, FALSE); + if (!was_enabled) { + err = gst_omx_port_set_enabled (port, FALSE); + if (err != OMX_ErrorNone) { + GST_INFO_OBJECT (self, + "Failed to disable port again: %s (0x%08x)", + gst_omx_error_to_string (err), err); + goto done; + } + } if (min != port->port_def.nBufferCountActual) { err = gst_omx_port_update_port_definition (port, NULL); @@ -1390,7 +912,7 @@ gst_omx_video_dec_allocate_output_buffers (GstOMXVideoDec * self) err = gst_omx_port_allocate_buffers (port); #endif /* Can't provide buffers downstream in this case */ - //gst_caps_replace (&caps, NULL); + gst_caps_replace (&caps, NULL); } if (err != OMX_ErrorNone) { @@ -1467,19 +989,349 @@ gst_omx_video_dec_deallocate_output_buffers (GstOMXVideoDec * self) if (self->out_port_pool) { gst_buffer_pool_set_active (self->out_port_pool, FALSE); +#if 0 + gst_buffer_pool_wait_released (self->out_port_pool); +#endif GST_OMX_BUFFER_POOL (self->out_port_pool)->deactivated = TRUE; gst_object_unref (self->out_port_pool); self->out_port_pool = NULL; } +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + err = + gst_omx_port_deallocate_buffers (self-> + eglimage ? self->egl_out_port : self->dec_out_port); +#else err = gst_omx_port_deallocate_buffers (self->dec_out_port); +#endif + + return err; +} + +static OMX_ERRORTYPE +gst_omx_video_dec_reconfigure_output_port (GstOMXVideoDec * self) +{ + GstOMXPort *port; + OMX_ERRORTYPE err; + GstVideoCodecState *state; + OMX_PARAM_PORTDEFINITIONTYPE port_def; + GstVideoFormat format; + + /* At this point the decoder output port is disabled */ + +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + { + OMX_STATETYPE egl_state; + + if (self->eglimage) { + /* Nothing to do here, we could however fall back to non-EGLImage in theory */ + port = self->egl_out_port; + err = OMX_ErrorNone; + goto enable_port; + } else { + /* Set up egl_render */ + + self->eglimage = TRUE; + + gst_omx_port_get_port_definition (self->dec_out_port, &port_def); + GST_VIDEO_DECODER_STREAM_LOCK (self); + state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self), + GST_VIDEO_FORMAT_RGBA, port_def.format.video.nFrameWidth, + port_def.format.video.nFrameHeight, self->input_state); + + /* at this point state->caps is NULL */ + if (state->caps) + gst_caps_unref (state->caps); + state->caps = gst_video_info_to_caps (&state->info); + gst_caps_set_features (state->caps, 0, + gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE, NULL)); + + /* try to negotiate with caps feature */ + if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) { + + GST_DEBUG_OBJECT (self, + "Failed to negotiate with feature %s", + GST_CAPS_FEATURE_MEMORY_EGL_IMAGE); + + if (state->caps) + gst_caps_replace (&state->caps, NULL); + + /* fallback: try to use EGLImage even if it is not in the caps feature */ + if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) { + gst_video_codec_state_unref (state); + GST_ERROR_OBJECT (self, "Failed to negotiate RGBA for EGLImage"); + GST_VIDEO_DECODER_STREAM_UNLOCK (self); + goto no_egl; + } + } + + gst_video_codec_state_unref (state); + GST_VIDEO_DECODER_STREAM_UNLOCK (self); + + /* Now link it all together */ + + err = gst_omx_port_set_enabled (self->egl_in_port, FALSE); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_wait_enabled (self->egl_in_port, 1 * GST_SECOND); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_set_enabled (self->egl_out_port, FALSE); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_wait_enabled (self->egl_out_port, 1 * GST_SECOND); + if (err != OMX_ErrorNone) + goto no_egl; + + { +#define OMX_IndexParamBrcmVideoEGLRenderDiscardMode 0x7f0000db + OMX_CONFIG_PORTBOOLEANTYPE discardMode; + memset (&discardMode, 0, sizeof (discardMode)); + discardMode.nSize = sizeof (discardMode); + discardMode.nPortIndex = 220; + discardMode.nVersion.nVersion = OMX_VERSION; + discardMode.bEnabled = OMX_FALSE; + if (gst_omx_component_set_parameter (self->egl_render, + OMX_IndexParamBrcmVideoEGLRenderDiscardMode, + &discardMode) != OMX_ErrorNone) + goto no_egl; +#undef OMX_IndexParamBrcmVideoEGLRenderDiscardMode + } + + err = gst_omx_setup_tunnel (self->dec_out_port, self->egl_in_port); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_set_enabled (self->egl_in_port, TRUE); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_component_set_state (self->egl_render, OMX_StateIdle); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_wait_enabled (self->egl_in_port, 1 * GST_SECOND); + if (err != OMX_ErrorNone) + goto no_egl; + + if (gst_omx_component_get_state (self->egl_render, + GST_CLOCK_TIME_NONE) != OMX_StateIdle) + goto no_egl; + + err = gst_omx_video_dec_allocate_output_buffers (self); + if (err != OMX_ErrorNone) + goto no_egl; + + if (gst_omx_component_set_state (self->egl_render, + OMX_StateExecuting) != OMX_ErrorNone) + goto no_egl; + + if (gst_omx_component_get_state (self->egl_render, + GST_CLOCK_TIME_NONE) != OMX_StateExecuting) + goto no_egl; + + err = + gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE); + if (err != OMX_ErrorNone) + goto no_egl; + + err = + gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, FALSE); + if (err != OMX_ErrorNone) + goto no_egl; + + err = + gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, FALSE); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_populate (self->egl_out_port); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_set_enabled (self->dec_out_port, TRUE); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_wait_enabled (self->dec_out_port, 1 * GST_SECOND); + if (err != OMX_ErrorNone) + goto no_egl; + + + err = gst_omx_port_mark_reconfigured (self->dec_out_port); + if (err != OMX_ErrorNone) + goto no_egl; + + err = gst_omx_port_mark_reconfigured (self->egl_out_port); + if (err != OMX_ErrorNone) + goto no_egl; + + goto done; + } + + no_egl: + + gst_omx_port_set_enabled (self->dec_out_port, FALSE); + gst_omx_port_wait_enabled (self->dec_out_port, 1 * GST_SECOND); + egl_state = gst_omx_component_get_state (self->egl_render, 0); + if (egl_state > OMX_StateLoaded || egl_state == OMX_StateInvalid) { + if (egl_state > OMX_StateIdle) { + gst_omx_component_set_state (self->egl_render, OMX_StateIdle); + gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND); + } + gst_omx_component_set_state (self->egl_render, OMX_StateLoaded); + + gst_omx_video_dec_deallocate_output_buffers (self); + gst_omx_close_tunnel (self->dec_out_port, self->egl_in_port); + + if (egl_state > OMX_StateLoaded) { + gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND); + } + } + + /* After this egl_render should be deactivated + * and the decoder's output port disabled */ + self->eglimage = FALSE; + } +#endif + port = self->dec_out_port; + + /* Update caps */ + GST_VIDEO_DECODER_STREAM_LOCK (self); + + gst_omx_port_get_port_definition (port, &port_def); + g_assert (port_def.format.video.eCompressionFormat == OMX_VIDEO_CodingUnused); + + format = + gst_omx_video_get_format_from_omx (port_def.format.video.eColorFormat); + + if (format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_ERROR_OBJECT (self, "Unsupported color format: %d", + port_def.format.video.eColorFormat); + GST_VIDEO_DECODER_STREAM_UNLOCK (self); + err = OMX_ErrorUndefined; + goto done; + } + + GST_DEBUG_OBJECT (self, + "Setting output state: format %s (%d), width %u, height %u", + gst_video_format_to_string (format), + port_def.format.video.eColorFormat, + (guint) port_def.format.video.nFrameWidth, + (guint) port_def.format.video.nFrameHeight); + + state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self), + format, port_def.format.video.nFrameWidth, + port_def.format.video.nFrameHeight, self->input_state); + + if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) { + gst_video_codec_state_unref (state); + GST_ERROR_OBJECT (self, "Failed to negotiate"); + err = OMX_ErrorUndefined; + GST_VIDEO_DECODER_STREAM_UNLOCK (self); + goto done; + } + + gst_video_codec_state_unref (state); + + GST_VIDEO_DECODER_STREAM_UNLOCK (self); + +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) +enable_port: +#endif + err = gst_omx_video_dec_allocate_output_buffers (self); + if (err != OMX_ErrorNone) + goto done; + + err = gst_omx_port_populate (port); + if (err != OMX_ErrorNone) + goto done; + + err = gst_omx_port_mark_reconfigured (port); + if (err != OMX_ErrorNone) + goto done; + +done: + + return err; +} + +static void +gst_omx_video_dec_clean_older_frames (GstOMXVideoDec * self, + GstOMXBuffer * buf, GList * frames) +{ + GList *l; + GstClockTime timestamp; + + timestamp = gst_util_uint64_scale (buf->omx_buf->nTimeStamp, GST_SECOND, + OMX_TICKS_PER_SECOND); + + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + /* We could release all frames stored with pts < timestamp since the + * decoder will likely output frames in display order */ + for (l = frames; l; l = l->next) { + GstVideoCodecFrame *tmp = l->data; + + if (tmp->pts < timestamp) { + gst_video_decoder_release_frame (GST_VIDEO_DECODER (self), tmp); + GST_LOG_OBJECT (self, + "discarding ghost frame %p (#%d) PTS:%" GST_TIME_FORMAT " DTS:%" + GST_TIME_FORMAT, tmp, tmp->system_frame_number, + GST_TIME_ARGS (tmp->pts), GST_TIME_ARGS (tmp->dts)); + } else { + gst_video_codec_frame_unref (tmp); + } + } + } else { + /* We will release all frames with invalid timestamp because we don't even + * know if they will be output some day. */ + for (l = frames; l; l = l->next) { + GstVideoCodecFrame *tmp = l->data; + + if (!GST_CLOCK_TIME_IS_VALID (tmp->pts)) { + gst_video_decoder_release_frame (GST_VIDEO_DECODER (self), tmp); + GST_LOG_OBJECT (self, + "discarding frame %p (#%d) with invalid PTS:%" GST_TIME_FORMAT + " DTS:%" GST_TIME_FORMAT, tmp, tmp->system_frame_number, + GST_TIME_ARGS (tmp->pts), GST_TIME_ARGS (tmp->dts)); + } else { + gst_video_codec_frame_unref (tmp); + } + } + } + + g_list_free (frames); +} + +static GstBuffer * +copy_frame (const GstVideoInfo * info, GstBuffer * outbuf) +{ + GstVideoInfo out_info, tmp_info; + GstBuffer *tmpbuf; + GstVideoFrame out_frame, tmp_frame; - return err; + out_info = *info; + tmp_info = *info; + + tmpbuf = gst_buffer_new_and_alloc (out_info.size); + + gst_video_frame_map (&out_frame, &out_info, outbuf, GST_MAP_READ); + gst_video_frame_map (&tmp_frame, &tmp_info, tmpbuf, GST_MAP_WRITE); + gst_video_frame_copy (&tmp_frame, &out_frame); + gst_video_frame_unmap (&out_frame); + gst_video_frame_unmap (&tmp_frame); + + gst_buffer_unref (outbuf); + + return tmpbuf; } static void gst_omx_video_dec_loop (GstOMXVideoDec * self) { - GstOMXPort *port = self->dec_out_port; + GstOMXPort *port; GstOMXBuffer *buf = NULL; GstVideoCodecFrame *frame; GstFlowReturn flow_ret = GST_FLOW_OK; @@ -1487,6 +1339,12 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) GstClockTimeDiff deadline; OMX_ERRORTYPE err; +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + port = self->eglimage ? self->egl_out_port : self->dec_out_port; +#else + port = self->dec_out_port; +#endif + acq_return = gst_omx_port_acquire_buffer (port, &buf); if (acq_return == GST_OMX_ACQUIRE_BUFFER_ERROR) { goto component_error; @@ -1524,79 +1382,55 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) goto reconfigure_error; } - GST_VIDEO_DECODER_STREAM_LOCK (self); + if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { + /* We have the possibility to reconfigure everything now */ + err = gst_omx_video_dec_reconfigure_output_port (self); + if (err != OMX_ErrorNone) + goto reconfigure_error; + } else { + /* Just update caps */ + GST_VIDEO_DECODER_STREAM_LOCK (self); - gst_omx_port_get_port_definition (port, &port_def); - g_assert (port_def.format.video.eCompressionFormat == - OMX_VIDEO_CodingUnused); + gst_omx_port_get_port_definition (port, &port_def); + g_assert (port_def.format.video.eCompressionFormat == + OMX_VIDEO_CodingUnused); - switch (port_def.format.video.eColorFormat) { - case OMX_COLOR_FormatYUV420Planar: - case OMX_COLOR_FormatYUV420PackedPlanar: - GST_ERROR_OBJECT (self, "Output is I420 (%d)", - port_def.format.video.eColorFormat); - format = GST_VIDEO_FORMAT_I420; - break; - case OMX_COLOR_FormatYUV420SemiPlanar: - GST_ERROR_OBJECT (self, "Output is NV12 (%d)", - port_def.format.video.eColorFormat); - format = GST_VIDEO_FORMAT_NV12; - break; - case OMX_EXT_COLOR_FormatNV12TPhysicalAddress: -#ifdef USE_MM_VIDEO_BUFFER - GST_LOG_OBJECT (self, "Output is SN12 (%d)", - port_def.format.video.eColorFormat); - format = GST_VIDEO_FORMAT_SN12; -#else - GST_LOG_OBJECT (self, "Output is ST12 (%d)", - port_def.format.video.eColorFormat); - format = GST_VIDEO_FORMAT_ST12; -#endif - break; - default: + format = + gst_omx_video_get_format_from_omx (port_def.format. + video.eColorFormat); + + if (format == GST_VIDEO_FORMAT_UNKNOWN) { GST_ERROR_OBJECT (self, "Unsupported color format: %d", port_def.format.video.eColorFormat); if (buf) - gst_omx_port_release_buffer (self->dec_out_port, buf); + gst_omx_port_release_buffer (port, buf); GST_VIDEO_DECODER_STREAM_UNLOCK (self); goto caps_failed; - break; - } - - GST_DEBUG_OBJECT (self, - "Setting output state: format %s, width %lu, height %lu", - gst_video_format_to_string (format), - port_def.format.video.nFrameWidth, port_def.format.video.nFrameHeight); - - state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self), - format, port_def.format.video.nFrameWidth, - port_def.format.video.nFrameHeight, self->input_state); - - /* Take framerate and pixel-aspect-ratio from sinkpad caps */ + } - if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) { - if (buf) - gst_omx_port_release_buffer (self->dec_out_port, buf); - gst_video_codec_state_unref (state); - goto caps_failed; - } + GST_DEBUG_OBJECT (self, + "Setting output state: format %s (%d), width %u, height %u", + gst_video_format_to_string (format), + port_def.format.video.eColorFormat, + (guint) port_def.format.video.nFrameWidth, + (guint) port_def.format.video.nFrameHeight); - gst_video_codec_state_unref (state); + state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (self), + format, port_def.format.video.nFrameWidth, + port_def.format.video.nFrameHeight, self->input_state); - GST_VIDEO_DECODER_STREAM_UNLOCK (self); + /* Take framerate and pixel-aspect-ratio from sinkpad caps */ - if (acq_return == GST_OMX_ACQUIRE_BUFFER_RECONFIGURE) { - err = gst_omx_video_dec_allocate_output_buffers (self); - if (err != OMX_ErrorNone) - goto reconfigure_error; + if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (self))) { + if (buf) + gst_omx_port_release_buffer (port, buf); + gst_video_codec_state_unref (state); + goto caps_failed; + } - err = gst_omx_port_populate (port); - if (err != OMX_ErrorNone) - goto reconfigure_error; + gst_video_codec_state_unref (state); - err = gst_omx_port_mark_reconfigured (port); - if (err != OMX_ErrorNone) - goto reconfigure_error; + GST_VIDEO_DECODER_STREAM_UNLOCK (self); } /* Now get a buffer */ @@ -1611,17 +1445,27 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) * lock and the videocodec stream lock, if ::reset() * is called at the wrong time */ - if (gst_omx_port_is_flushing (self->dec_out_port)) { - GST_ERROR_OBJECT (self, "Flushing"); - gst_omx_port_release_buffer (self->dec_out_port, buf); + if (gst_omx_port_is_flushing (port)) { + GST_DEBUG_OBJECT (self, "Flushing"); + gst_omx_port_release_buffer (port, buf); goto flushing; } - GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08lx %" G_GUINT64_FORMAT, - buf->omx_buf->nFlags, (guint64) buf->omx_buf->nTimeStamp); + GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %" G_GUINT64_FORMAT, + (guint) buf->omx_buf->nFlags, (guint64) buf->omx_buf->nTimeStamp); GST_VIDEO_DECODER_STREAM_LOCK (self); - frame = _find_nearest_frame (self, buf); + frame = gst_omx_video_find_nearest_frame (buf, + gst_video_decoder_get_frames (GST_VIDEO_DECODER (self))); + + /* So we have a timestamped OMX buffer and get, or not, corresponding frame. + * Assuming decoder output frames in display order, frames preceding this + * frame could be discarded as they seems useless due to e.g interlaced + * stream, corrupted input data... + * In any cases, not likely to be seen again. so drop it before they pile up + * and use all the memory. */ + gst_omx_video_dec_clean_older_frames (self, buf, + gst_video_decoder_get_frames (GST_VIDEO_DECODER (self))); if (frame && (deadline = gst_video_decoder_get_max_decode_time @@ -1631,8 +1475,8 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) GST_TIME_ARGS (-deadline)); flow_ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); frame = NULL; - } else if (!frame && buf->omx_buf->nFilledLen > 0) { - GstBuffer *outbuf; + } else if (!frame && (buf->omx_buf->nFilledLen > 0 || buf->eglimage)) { + GstBuffer *outbuf = NULL; /* This sometimes happens at EOS or if the input is not properly framed, * let's handle it gracefully by allocating a new buffer for the current @@ -1662,6 +1506,12 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) gst_omx_port_release_buffer (port, buf); goto invalid_buffer; } + + if (GST_OMX_BUFFER_POOL (self->out_port_pool)->need_copy) + outbuf = + copy_frame (&GST_OMX_BUFFER_POOL (self->out_port_pool)->video_info, + outbuf); + buf = NULL; } else { outbuf = @@ -1674,9 +1524,10 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) } flow_ret = gst_pad_push (GST_VIDEO_DECODER_SRC_PAD (self), outbuf); - } else if (buf->omx_buf->nFilledLen > 0) { + } else if (buf->omx_buf->nFilledLen > 0 || buf->eglimage) { if (self->out_port_pool) { gint i, n; + GstBuffer *outbuf; GstBufferPoolAcquireParams params = { 0, }; n = port->buffers->len; @@ -1691,7 +1542,7 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) GST_OMX_BUFFER_POOL (self->out_port_pool)->current_buffer_index = i; flow_ret = gst_buffer_pool_acquire_buffer (self->out_port_pool, - &frame->output_buffer, ¶ms); + &outbuf, ¶ms); if (flow_ret != GST_FLOW_OK) { flow_ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); @@ -1699,14 +1550,14 @@ gst_omx_video_dec_loop (GstOMXVideoDec * self) gst_omx_port_release_buffer (port, buf); goto invalid_buffer; } - if(!gst_omx_video_dec_fill_buffer (self, buf, frame->output_buffer)) { - gst_buffer_replace (&frame->output_buffer, NULL); - flow_ret = - gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); - frame = NULL; - gst_omx_port_release_buffer (port, buf); - goto invalid_buffer; - } + + if (GST_OMX_BUFFER_POOL (self->out_port_pool)->need_copy) + outbuf = + copy_frame (&GST_OMX_BUFFER_POOL (self->out_port_pool)->video_info, + outbuf); + + frame->output_buffer = outbuf; + flow_ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame); frame = NULL; @@ -1782,6 +1633,13 @@ eos: { g_mutex_lock (&self->drain_lock); if (self->draining) { + GstQuery *query = gst_query_new_drain (); + + /* Drain the pipeline to reclaim all memories back to the pool */ + if (!gst_pad_peer_query (GST_VIDEO_DECODER_SRC_PAD (self), query)) + GST_DEBUG_OBJECT (self, "drain query failed"); + gst_query_unref (query); + GST_DEBUG_OBJECT (self, "Drained"); self->draining = FALSE; g_cond_broadcast (&self->drain_cond); @@ -1792,6 +1650,8 @@ eos: flow_ret = GST_FLOW_EOS; } g_mutex_unlock (&self->drain_lock); + + GST_VIDEO_DECODER_STREAM_LOCK (self); self->downstream_flow_ret = flow_ret; /* Here we fallback and pause the task for the EOS case */ @@ -1811,7 +1671,8 @@ flow_error: gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); - } else if (flow_ret == GST_FLOW_NOT_LINKED || flow_ret < GST_FLOW_EOS) { + self->started = FALSE; + } else if (flow_ret < GST_FLOW_EOS) { GST_ELEMENT_ERROR (self, STREAM, FAILED, ("Internal data stream error."), ("stream stopped, reason %s", gst_flow_get_name (flow_ret))); @@ -1819,8 +1680,12 @@ flow_error: gst_pad_push_event (GST_VIDEO_DECODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); + self->started = FALSE; + } else if (flow_ret == GST_FLOW_FLUSHING) { + GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); + gst_pad_pause_task (GST_VIDEO_DECODER_SRC_PAD (self)); + self->started = FALSE; } - self->started = FALSE; GST_VIDEO_DECODER_STREAM_UNLOCK (self); return; } @@ -1880,7 +1745,6 @@ gst_omx_video_dec_start (GstVideoDecoder * decoder) self = GST_OMX_VIDEO_DEC (decoder); self->last_upstream_ts = 0; - self->eos = FALSE; self->downstream_flow_ret = GST_FLOW_OK; return TRUE; @@ -1898,14 +1762,22 @@ gst_omx_video_dec_stop (GstVideoDecoder * decoder) gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, TRUE); +#endif + gst_pad_stop_task (GST_VIDEO_DECODER_SRC_PAD (decoder)); if (gst_omx_component_get_state (self->dec, 0) > OMX_StateIdle) gst_omx_component_set_state (self->dec, OMX_StateIdle); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (gst_omx_component_get_state (self->egl_render, 0) > OMX_StateIdle) + gst_omx_component_set_state (self->egl_render, OMX_StateIdle); +#endif self->downstream_flow_ret = GST_FLOW_FLUSHING; self->started = FALSE; - self->eos = FALSE; g_mutex_lock (&self->drain_lock); self->draining = FALSE; @@ -1913,6 +1785,9 @@ gst_omx_video_dec_stop (GstVideoDecoder * decoder) g_mutex_unlock (&self->drain_lock); gst_omx_component_get_state (self->dec, 5 * GST_SECOND); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + gst_omx_component_get_state (self->egl_render, 1 * GST_SECOND); +#endif gst_buffer_replace (&self->codec_data, NULL); @@ -1925,100 +1800,6 @@ gst_omx_video_dec_stop (GstVideoDecoder * decoder) return TRUE; } -typedef struct -{ - GstVideoFormat format; - OMX_COLOR_FORMATTYPE type; -} VideoNegotiationMap; - -static void -video_negotiation_map_free (VideoNegotiationMap * m) -{ - g_slice_free (VideoNegotiationMap, m); -} - -static GList * -gst_omx_video_dec_get_supported_colorformats (GstOMXVideoDec * self) -{ - GstOMXPort *port = self->dec_out_port; - GstVideoCodecState *state = self->input_state; - OMX_VIDEO_PARAM_PORTFORMATTYPE param; - OMX_ERRORTYPE err; - GList *negotiation_map = NULL; - gint old_index; - - GST_OMX_INIT_STRUCT (¶m); - param.nPortIndex = port->index; - param.nIndex = 0; - if (!state || state->info.fps_n == 0) - param.xFramerate = 0; - else - param.xFramerate = (state->info.fps_n << 16) / (state->info.fps_d); - - old_index = -1; - do { - VideoNegotiationMap *m; - - err = - gst_omx_component_get_parameter (self->dec, - OMX_IndexParamVideoPortFormat, ¶m); - - /* FIXME: Workaround for Bellagio that simply always - * returns the same value regardless of nIndex and - * never returns OMX_ErrorNoMore - */ - if (old_index == param.nIndex) - break; - - if (err == OMX_ErrorNone || err == OMX_ErrorNoMore) { - switch (param.eColorFormat) { - case OMX_COLOR_FormatYUV420Planar: - case OMX_COLOR_FormatYUV420PackedPlanar: - m = g_slice_new (VideoNegotiationMap); - m->format = GST_VIDEO_FORMAT_I420; - m->type = param.eColorFormat; - negotiation_map = g_list_append (negotiation_map, m); - GST_DEBUG_OBJECT (self, "Component supports I420 (%d) at index %lu", - param.eColorFormat, param.nIndex); - break; - case OMX_COLOR_FormatYUV420SemiPlanar: - m = g_slice_new (VideoNegotiationMap); - m->format = GST_VIDEO_FORMAT_NV12; - m->type = param.eColorFormat; - negotiation_map = g_list_append (negotiation_map, m); - GST_DEBUG_OBJECT (self, "Component supports NV12 (%d) at index %lu", - param.eColorFormat, param.nIndex); - break; - case OMX_EXT_COLOR_FormatNV12TPhysicalAddress: - m = g_slice_new (VideoNegotiationMap); -#ifdef USE_MM_VIDEO_BUFFER - m->format = GST_VIDEO_FORMAT_SN12; - m->type = param.eColorFormat; - negotiation_map = g_list_append (negotiation_map, m); - GST_DEBUG_OBJECT (self, "Component supports SN12 (%d) at index %d", - param.eColorFormat, param.nIndex); -#else - m->format = GST_VIDEO_FORMAT_ST12; - m->type = param.eColorFormat; - negotiation_map = g_list_append (negotiation_map, m); - GST_DEBUG_OBJECT (self, "Component supports ST12 (%d) at index %d", - param.eColorFormat, param.nIndex); -#endif - break; - - default: - GST_DEBUG_OBJECT (self, - "Component supports unsupported color format %d at index %lu", - param.eColorFormat, param.nIndex); - break; - } - } - old_index = param.nIndex++; - } while (err == OMX_ErrorNone); - - return negotiation_map; -} - static gboolean gst_omx_video_dec_negotiate (GstOMXVideoDec * self) { @@ -2030,8 +1811,6 @@ gst_omx_video_dec_negotiate (GstOMXVideoDec * self) GstVideoFormat format; GstStructure *s; const gchar *format_str; - gchar *format_tmp; - int i; #ifdef USE_TBM EnableGemBuffersParams gemBuffers; #endif @@ -2046,16 +1825,11 @@ gst_omx_video_dec_negotiate (GstOMXVideoDec * self) GST_DEBUG_OBJECT (self, "Allowed downstream caps: %" GST_PTR_FORMAT, intersection); - negotiation_map = gst_omx_video_dec_get_supported_colorformats (self); - comp_supported_caps = gst_caps_new_empty (); - for (l = negotiation_map; l; l = l->next) { - VideoNegotiationMap *map = l->data; + negotiation_map = + gst_omx_video_get_supported_colorformats (self->dec_out_port, + self->input_state); - gst_caps_append_structure (comp_supported_caps, - gst_structure_new ("video/x-raw", - "format", G_TYPE_STRING, - gst_video_format_to_string (map->format), NULL)); - } + comp_supported_caps = gst_omx_video_get_caps_for_map (negotiation_map); if (!gst_caps_is_empty (comp_supported_caps)) { GstCaps *tmp; @@ -2070,38 +1844,23 @@ gst_omx_video_dec_negotiate (GstOMXVideoDec * self) gst_caps_unref (intersection); GST_ERROR_OBJECT (self, "Empty caps"); g_list_free_full (negotiation_map, - (GDestroyNotify) video_negotiation_map_free); + (GDestroyNotify) gst_omx_video_negotiation_map_free); return FALSE; } - for(i=0; idec_out_port->index; for (l = negotiation_map; l; l = l->next) { - VideoNegotiationMap *m = l->data; + GstOMXVideoNegotiationMap *m = l->data; if (m->format == format) { param.eColorFormat = m->type; @@ -2123,7 +1882,7 @@ gst_omx_video_dec_negotiate (GstOMXVideoDec * self) /* We must find something here */ g_assert (l != NULL); g_list_free_full (negotiation_map, - (GDestroyNotify) video_negotiation_map_free); + (GDestroyNotify) gst_omx_video_negotiation_map_free); err = gst_omx_component_set_parameter (self->dec, @@ -2148,6 +1907,7 @@ gst_omx_video_dec_negotiate (GstOMXVideoDec * self) gst_omx_error_to_string (err), err); } #endif + gst_caps_unref (intersection); return (err == OMX_ErrorNone); } @@ -2200,17 +1960,18 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder, } if (needs_disable && is_format_change) { - GST_DEBUG_OBJECT (self, "Need to disable and drain decoder"); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + GstOMXPort *out_port = + self->eglimage ? self->egl_out_port : self->dec_out_port; +#else + GstOMXPort *out_port = self->dec_out_port; +#endif - gst_omx_video_dec_drain (self, FALSE); - gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); + GST_DEBUG_OBJECT (self, "Need to disable and drain decoder"); - /* Wait until the srcpad loop is finished, - * unlock GST_VIDEO_DECODER_STREAM_LOCK to prevent deadlocks - * caused by using this lock from inside the loop function */ - GST_VIDEO_DECODER_STREAM_UNLOCK (self); - gst_pad_stop_task (GST_VIDEO_DECODER_SRC_PAD (decoder)); - GST_VIDEO_DECODER_STREAM_LOCK (self); + gst_omx_video_dec_drain (self); + gst_omx_video_dec_flush (decoder); + gst_omx_port_set_flushing (out_port, 5 * GST_SECOND, TRUE); if (klass->cdata.hacks & GST_OMX_HACK_NO_COMPONENT_RECONFIGURE) { GST_VIDEO_DECODER_STREAM_UNLOCK (self); @@ -2222,14 +1983,23 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder, return FALSE; needs_disable = FALSE; } else { +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (self->eglimage) { + gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, TRUE); + } +#endif + if (gst_omx_port_set_enabled (self->dec_in_port, FALSE) != OMX_ErrorNone) return FALSE; - if (gst_omx_port_set_enabled (self->dec_out_port, FALSE) != OMX_ErrorNone) + if (gst_omx_port_set_enabled (out_port, FALSE) != OMX_ErrorNone) return FALSE; if (gst_omx_port_wait_buffers_released (self->dec_in_port, 5 * GST_SECOND) != OMX_ErrorNone) return FALSE; - if (gst_omx_port_wait_buffers_released (self->dec_out_port, + if (gst_omx_port_wait_buffers_released (out_port, 1 * GST_SECOND) != OMX_ErrorNone) return FALSE; if (gst_omx_port_deallocate_buffers (self->dec_in_port) != OMX_ErrorNone) @@ -2239,9 +2009,40 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder, if (gst_omx_port_wait_enabled (self->dec_in_port, 1 * GST_SECOND) != OMX_ErrorNone) return FALSE; - if (gst_omx_port_wait_enabled (self->dec_out_port, - 1 * GST_SECOND) != OMX_ErrorNone) + if (gst_omx_port_wait_enabled (out_port, 1 * GST_SECOND) != OMX_ErrorNone) return FALSE; + +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (self->eglimage) { + OMX_STATETYPE egl_state; + + egl_state = gst_omx_component_get_state (self->egl_render, 0); + if (egl_state > OMX_StateLoaded || egl_state == OMX_StateInvalid) { + + if (egl_state > OMX_StateIdle) { + gst_omx_component_set_state (self->egl_render, OMX_StateIdle); + gst_omx_component_set_state (self->dec, OMX_StateIdle); + egl_state = gst_omx_component_get_state (self->egl_render, + 5 * GST_SECOND); + gst_omx_component_get_state (self->dec, 1 * GST_SECOND); + } + gst_omx_component_set_state (self->egl_render, OMX_StateLoaded); + gst_omx_component_set_state (self->dec, OMX_StateLoaded); + + gst_omx_close_tunnel (self->dec_out_port, self->egl_in_port); + + if (egl_state > OMX_StateLoaded) { + gst_omx_component_get_state (self->egl_render, 5 * GST_SECOND); + } + + gst_omx_component_set_state (self->dec, OMX_StateIdle); + + gst_omx_component_set_state (self->dec, OMX_StateExecuting); + gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE); + } + self->eglimage = FALSE; + } +#endif } if (self->input_state) gst_video_codec_state_unref (self->input_state); @@ -2291,53 +2092,62 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder, return FALSE; #endif + if ((klass->cdata.hacks & GST_OMX_HACK_NO_DISABLE_OUTPORT)) { + if (gst_omx_port_set_enabled (self->dec_out_port, TRUE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_allocate_buffers (self->dec_out_port) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->dec_out_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + } + if (gst_omx_port_wait_enabled (self->dec_in_port, 5 * GST_SECOND) != OMX_ErrorNone) return FALSE; - if (gst_omx_port_mark_reconfigured (self->dec_in_port) != OMX_ErrorNone) return FALSE; } else { if (!gst_omx_video_dec_negotiate (self)) GST_LOG_OBJECT (self, "Negotiation failed, will get output format later"); - if (gst_omx_component_set_state (self->dec, OMX_StateIdle) != OMX_ErrorNone) - return FALSE; + if (!(klass->cdata.hacks & GST_OMX_HACK_NO_DISABLE_OUTPORT)) { + /* Disable output port */ + if (gst_omx_port_set_enabled (self->dec_out_port, FALSE) != OMX_ErrorNone) + return FALSE; - /* Need to allocate buffers to reach Idle state */ -#ifdef USE_TBM - if(gst_omx_port_tbm_allocate_dec_buffers(self->hTBMBufMgr,self->dec_in_port,0) != OMX_ErrorNone) - return FALSE; -#else - if (gst_omx_port_allocate_buffers (self->dec_in_port) != OMX_ErrorNone) - return FALSE; -#endif + if (gst_omx_port_wait_enabled (self->dec_out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_component_set_state (self->dec, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; + + /* Need to allocate buffers to reach Idle state */ + if (gst_omx_port_allocate_buffers (self->dec_in_port) != OMX_ErrorNone) + return FALSE; + } else { + if (gst_omx_component_set_state (self->dec, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; + + /* Need to allocate buffers to reach Idle state */ + if (gst_omx_port_allocate_buffers (self->dec_in_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_allocate_buffers (self->dec_out_port) != OMX_ErrorNone) + return FALSE; + } -#ifdef USE_TBM - if(gst_omx_port_tbm_allocate_dec_buffers(self->hTBMBufMgr,self->dec_out_port, - self->dec_in_port->port_def.format.video.eCompressionFormat) != OMX_ErrorNone) - return FALSE; -#else - if (gst_omx_port_allocate_buffers (self->dec_out_port) != OMX_ErrorNone) - return FALSE; -#endif -#if 0 - GST_ERROR_OBJECT (self, "\n6. DISABLE OUTPUT PORT\n"); - /* And disable output port */ - if (gst_omx_port_set_enabled (self->dec_out_port, FALSE) != OMX_ErrorNone) - return FALSE; - GST_ERROR_OBJECT (self, "\n7. DISABLE OUTPUT PORT WAIT\n"); - if (gst_omx_port_wait_enabled (self->dec_out_port, - 1 * GST_SECOND) != OMX_ErrorNone) - return FALSE; -#endif if (gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE) != OMX_StateIdle) return FALSE; + if (gst_omx_component_set_state (self->dec, OMX_StateExecuting) != OMX_ErrorNone) return FALSE; - GST_ERROR_OBJECT (self, "\n9. GET COMPONENT STATE\n"); + if (gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE) != OMX_StateExecuting) return FALSE; @@ -2354,50 +2164,92 @@ gst_omx_video_dec_set_format (GstVideoDecoder * decoder, return FALSE; } - /* Start the srcpad loop again */ - GST_DEBUG_OBJECT (self, "Starting task again"); - self->downstream_flow_ret = GST_FLOW_OK; - gst_pad_start_task (GST_VIDEO_DECODER_SRC_PAD (self), - (GstTaskFunction) gst_omx_video_dec_loop, decoder, NULL); - return TRUE; } static gboolean -gst_omx_video_dec_reset (GstVideoDecoder * decoder, gboolean hard) +gst_omx_video_dec_flush (GstVideoDecoder * decoder) { - GstOMXVideoDec *self; - - self = GST_OMX_VIDEO_DEC (decoder); + GstOMXVideoDec *self = GST_OMX_VIDEO_DEC (decoder); + OMX_ERRORTYPE err = OMX_ErrorNone; - /* FIXME: Handle different values of hard */ + GST_DEBUG_OBJECT (self, "Flushing decoder"); - GST_DEBUG_OBJECT (self, "Resetting decoder"); + if (gst_omx_component_get_state (self->dec, 0) == OMX_StateLoaded) + return TRUE; - gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); - gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); + /* 0) Pause the components */ + if (gst_omx_component_get_state (self->dec, 0) == OMX_StateExecuting) { + gst_omx_component_set_state (self->dec, OMX_StatePause); + gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE); + } +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (self->eglimage) { + if (gst_omx_component_get_state (self->egl_render, 0) == OMX_StateExecuting) { + gst_omx_component_set_state (self->egl_render, OMX_StatePause); + gst_omx_component_get_state (self->egl_render, GST_CLOCK_TIME_NONE); + } + } +#endif - /* Wait until the srcpad loop is finished, + /* 1) Wait until the srcpad loop is stopped, * unlock GST_VIDEO_DECODER_STREAM_LOCK to prevent deadlocks * caused by using this lock from inside the loop function */ GST_VIDEO_DECODER_STREAM_UNLOCK (self); - GST_PAD_STREAM_LOCK (GST_VIDEO_DECODER_SRC_PAD (self)); - GST_PAD_STREAM_UNLOCK (GST_VIDEO_DECODER_SRC_PAD (self)); + gst_pad_stop_task (GST_VIDEO_DECODER_SRC_PAD (decoder)); + GST_DEBUG_OBJECT (self, "Flushing -- task stopped"); GST_VIDEO_DECODER_STREAM_LOCK (self); + /* 2) Flush the ports */ + GST_DEBUG_OBJECT (self, "flushing ports"); + gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, TRUE); + +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (self->eglimage) { + gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, TRUE); + gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, TRUE); + } +#endif + + /* 3) Resume components */ + gst_omx_component_set_state (self->dec, OMX_StateExecuting); + gst_omx_component_get_state (self->dec, GST_CLOCK_TIME_NONE); +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (self->eglimage) { + gst_omx_component_set_state (self->egl_render, OMX_StateExecuting); + gst_omx_component_get_state (self->egl_render, GST_CLOCK_TIME_NONE); + } +#endif + + /* 4) Unset flushing to allow ports to accept data again */ gst_omx_port_set_flushing (self->dec_in_port, 5 * GST_SECOND, FALSE); gst_omx_port_set_flushing (self->dec_out_port, 5 * GST_SECOND, FALSE); - gst_omx_port_populate (self->dec_out_port); - /* Start the srcpad loop again */ +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + if (self->eglimage) { + gst_omx_port_set_flushing (self->egl_in_port, 5 * GST_SECOND, FALSE); + gst_omx_port_set_flushing (self->egl_out_port, 5 * GST_SECOND, FALSE); + err = gst_omx_port_populate (self->egl_out_port); + gst_omx_port_mark_reconfigured (self->egl_out_port); + } else { + err = gst_omx_port_populate (self->dec_out_port); + } +#else + err = gst_omx_port_populate (self->dec_out_port); +#endif + + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, "Failed to populate output port: %s (0x%08x)", + gst_omx_error_to_string (err), err); + } + + /* Reset our state */ self->last_upstream_ts = 0; - self->eos = FALSE; self->downstream_flow_ret = GST_FLOW_OK; - gst_pad_start_task (GST_VIDEO_DECODER_SRC_PAD (self), - (GstTaskFunction) gst_omx_video_dec_loop, decoder, NULL); - - GST_DEBUG_OBJECT (self, "Reset decoder"); + self->started = FALSE; + GST_DEBUG_OBJECT (self, "Flush finished"); return TRUE; } @@ -2421,15 +2273,14 @@ gst_omx_video_dec_handle_frame (GstVideoDecoder * decoder, GST_DEBUG_OBJECT (self, "Handling frame"); - if (self->eos) { - GST_WARNING_OBJECT (self, "Got frame after EOS"); - gst_video_codec_frame_unref (frame); - return GST_FLOW_EOS; - } - - if (!self->started && !GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (frame)) { - gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); - return GST_FLOW_OK; + if (!self->started) { + if (!GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (frame)) { + gst_video_decoder_drop_frame (GST_VIDEO_DECODER (self), frame); + return GST_FLOW_OK; + } + GST_DEBUG_OBJECT (self, "Starting task"); + gst_pad_start_task (GST_VIDEO_DECODER_SRC_PAD (self), + (GstTaskFunction) gst_omx_video_dec_loop, decoder, NULL); } timestamp = frame->pts; @@ -2619,22 +2470,14 @@ gst_omx_video_dec_handle_frame (GstVideoDecoder * decoder, if (duration != GST_CLOCK_TIME_NONE && offset == 0) { buf->omx_buf->nTickCount = - gst_util_uint64_scale (buf->omx_buf->nFilledLen, duration, size); + gst_util_uint64_scale (duration, OMX_TICKS_PER_SECOND, GST_SECOND); self->last_upstream_ts += duration; } else { buf->omx_buf->nTickCount = 0; } - if (offset == 0) { - BufferIdentification *id = g_slice_new0 (BufferIdentification); - - if (GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (frame)) - buf->omx_buf->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; - - id->timestamp = buf->omx_buf->nTimeStamp; - gst_video_codec_frame_set_user_data (frame, id, - (GDestroyNotify) buffer_identification_free); - } + if (offset == 0 && GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (frame)) + buf->omx_buf->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; /* TODO: Set flags * - OMX_BUFFERFLAG_DECODEONLY for buffers that are outside @@ -2663,7 +2506,7 @@ full_buffer: gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Got OpenMAX buffer with no free space (%p, %u/%u)", buf, - buf->omx_buf->nOffset, buf->omx_buf->nAllocLen)); + (guint) buf->omx_buf->nOffset, (guint) buf->omx_buf->nAllocLen)); return GST_FLOW_ERROR; } @@ -2678,9 +2521,9 @@ too_large_codec_data: { gst_video_codec_frame_unref (frame); GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), - ("codec_data larger than supported by OpenMAX port (%u > %u)", - gst_buffer_get_size (codec_data), - self->dec_in_port->port_def.nBufferSize)); + ("codec_data larger than supported by OpenMAX port " + "(%" G_GSIZE_FORMAT " > %u)", gst_buffer_get_size (codec_data), + (guint) self->dec_in_port->port_def.nBufferSize)); return GST_FLOW_ERROR; } @@ -2724,11 +2567,11 @@ gst_omx_video_dec_finish (GstVideoDecoder * decoder) self = GST_OMX_VIDEO_DEC (decoder); - return gst_omx_video_dec_drain (self, TRUE); + return gst_omx_video_dec_drain (self); } static GstFlowReturn -gst_omx_video_dec_drain (GstOMXVideoDec * self, gboolean is_eos) +gst_omx_video_dec_drain (GstOMXVideoDec * self) { GstOMXVideoDecClass *klass; GstOMXBuffer *buf; @@ -2745,14 +2588,6 @@ gst_omx_video_dec_drain (GstOMXVideoDec * self, gboolean is_eos) } self->started = FALSE; - /* Don't send EOS buffer twice, this doesn't work */ - if (self->eos) { - GST_DEBUG_OBJECT (self, "Component is EOS already"); - return GST_FLOW_OK; - } - if (is_eos) - self->eos = TRUE; - if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) { GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers"); return GST_FLOW_OK; @@ -2786,6 +2621,7 @@ gst_omx_video_dec_drain (GstOMXVideoDec * self, gboolean is_eos) if (err != OMX_ErrorNone) { GST_ERROR_OBJECT (self, "Failed to drain component: %s (0x%08x)", gst_omx_error_to_string (err), err); + g_mutex_unlock (&self->drain_lock); GST_VIDEO_DECODER_STREAM_LOCK (self); return GST_FLOW_ERROR; } @@ -2819,6 +2655,50 @@ gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query) GstBufferPool *pool; GstStructure *config; +#if defined (USE_OMX_TARGET_RPI) && defined (HAVE_GST_GL) + { + GstCaps *caps; + gint i, n; + GstVideoInfo info; + + gst_query_parse_allocation (query, &caps, NULL); + if (caps && gst_video_info_from_caps (&info, caps) + && info.finfo->format == GST_VIDEO_FORMAT_RGBA) { + gboolean found = FALSE; + GstCapsFeatures *feature = gst_caps_get_features (caps, 0); + /* Prefer an EGLImage allocator if available and we want to use it */ + n = gst_query_get_n_allocation_params (query); + for (i = 0; i < n; i++) { + GstAllocator *allocator; + GstAllocationParams params; + + gst_query_parse_nth_allocation_param (query, i, &allocator, ¶ms); + if (allocator) { + if (g_strcmp0 (allocator->mem_type, GST_EGL_IMAGE_MEMORY_TYPE) == 0) { + found = TRUE; + gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms); + while (gst_query_get_n_allocation_params (query) > 1) + gst_query_remove_nth_allocation_param (query, 1); + } + + gst_object_unref (allocator); + + if (found) + break; + } + } + + /* if try to negotiate with caps feature memory:EGLImage + * and if allocator is not of type memory EGLImage then fails */ + if (feature + && gst_caps_features_contains (feature, + GST_CAPS_FEATURE_MEMORY_EGL_IMAGE) && !found) { + return FALSE; + } + } + } +#endif + if (!GST_VIDEO_DECODER_CLASS (gst_omx_video_dec_parent_class)->decide_allocation (bdec, query)) return FALSE; @@ -2837,4 +2717,3 @@ gst_omx_video_dec_decide_allocation (GstVideoDecoder * bdec, GstQuery * query) return TRUE; } - diff --git a/omx/gstomxvideodec.h b/omx/gstomxvideodec.h index 2a0c9b9..7e047be 100644 --- a/omx/gstomxvideodec.h +++ b/omx/gstomxvideodec.h @@ -21,6 +21,10 @@ #ifndef __GST_OMX_VIDEO_DEC_H__ #define __GST_OMX_VIDEO_DEC_H__ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include @@ -68,7 +72,7 @@ struct _GstOMXVideoDec /* < protected > */ GstOMXComponent *dec; GstOMXPort *dec_in_port, *dec_out_port; - + GstBufferPool *in_port_pool, *out_port_pool; /* < private > */ @@ -86,14 +90,16 @@ struct _GstOMXVideoDec /* TRUE if EOS buffers shouldn't be forwarded */ gboolean draining; - /* TRUE if upstream is EOS */ - gboolean eos; - GstFlowReturn downstream_flow_ret; #ifdef USE_TBM gint drm_fd; tbm_bufmgr hTBMBufMgr; #endif +#ifdef USE_OMX_TARGET_RPI + GstOMXComponent *egl_render; + GstOMXPort *egl_in_port, *egl_out_port; + gboolean eglimage; +#endif }; struct _GstOMXVideoDecClass diff --git a/omx/gstomxvideoenc.c b/omx/gstomxvideoenc.c old mode 100755 new mode 100644 index 60963c3..941f03d --- a/omx/gstomxvideoenc.c +++ b/omx/gstomxvideoenc.c @@ -26,18 +26,18 @@ #include #include +#include "gstomxvideo.h" #include "gstomxvideoenc.h" -//#define CODEC_ENC_INPUT_DUMP +#ifdef USE_OMX_TARGET_RPI +#include +#include +#endif + GST_DEBUG_CATEGORY_STATIC (gst_omx_video_enc_debug_category); #define GST_CAT_DEFAULT gst_omx_video_enc_debug_category #define GST_TYPE_OMX_VIDEO_ENC_CONTROL_RATE (gst_omx_video_enc_control_rate_get_type ()) - -#ifdef CODEC_ENC_INPUT_DUMP -#include -#endif - static GType gst_omx_video_enc_control_rate_get_type (void) { @@ -61,18 +61,6 @@ gst_omx_video_enc_control_rate_get_type (void) return qtype; } -typedef struct _BufferIdentification BufferIdentification; -struct _BufferIdentification -{ - guint64 timestamp; -}; - -static void -buffer_identification_free (BufferIdentification * id) -{ - g_slice_free (BufferIdentification, id); -} - /* prototypes */ static void gst_omx_video_enc_finalize (GObject * object); static void gst_omx_video_enc_set_property (GObject * object, guint prop_id, @@ -91,8 +79,7 @@ static gboolean gst_omx_video_enc_start (GstVideoEncoder * encoder); static gboolean gst_omx_video_enc_stop (GstVideoEncoder * encoder); static gboolean gst_omx_video_enc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state); -static gboolean gst_omx_video_enc_reset (GstVideoEncoder * encoder, - gboolean hard); +static gboolean gst_omx_video_enc_flush (GstVideoEncoder * encoder); static GstFlowReturn gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame); static gboolean gst_omx_video_enc_finish (GstVideoEncoder * encoder); @@ -101,8 +88,7 @@ static gboolean gst_omx_video_enc_propose_allocation (GstVideoEncoder * encoder, static GstCaps *gst_omx_video_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter); -static GstFlowReturn gst_omx_video_enc_drain (GstOMXVideoEnc * self, - gboolean at_eos); +static GstFlowReturn gst_omx_video_enc_drain (GstOMXVideoEnc * self); static GstFlowReturn gst_omx_video_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, GstOMXBuffer * buf, GstVideoCodecFrame * frame); @@ -188,7 +174,7 @@ gst_omx_video_enc_class_init (GstOMXVideoEncClass * klass) video_encoder_class->close = GST_DEBUG_FUNCPTR (gst_omx_video_enc_close); video_encoder_class->start = GST_DEBUG_FUNCPTR (gst_omx_video_enc_start); video_encoder_class->stop = GST_DEBUG_FUNCPTR (gst_omx_video_enc_stop); - video_encoder_class->reset = GST_DEBUG_FUNCPTR (gst_omx_video_enc_reset); + video_encoder_class->flush = GST_DEBUG_FUNCPTR (gst_omx_video_enc_flush); video_encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_video_enc_set_format); video_encoder_class->handle_frame = @@ -198,6 +184,7 @@ gst_omx_video_enc_class_init (GstOMXVideoEncClass * klass) GST_DEBUG_FUNCPTR (gst_omx_video_enc_propose_allocation); video_encoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_omx_video_enc_getcaps); + klass->cdata.type = GST_OMX_COMPONENT_TYPE_FILTER; klass->cdata.default_sink_template_caps = "video/x-raw, " "width = " GST_VIDEO_SIZE_RANGE ", " "height = " GST_VIDEO_SIZE_RANGE ", " "framerate = " GST_VIDEO_FPS_RANGE; @@ -223,36 +210,6 @@ gst_omx_video_enc_init (GstOMXVideoEnc * self) #endif } -#ifdef CODEC_ENC_INPUT_DUMP -static inline void -gst_omx_video_enc_input_dump (MMVideoBuffer *inbuf) -{ - char *temp = (char *)inbuf->data[0]; - int i = 0; - char filename[100]={0}; - FILE *fp = NULL; - - GST_WARNING ("codec enc input dump start. w = %d, h = %d", inbuf->width[0], inbuf->height[0]); - - sprintf(filename, "/opt/usr/media/Videos/enc_input_dump_%d_%d.yuv", inbuf->width[0], inbuf->height[0]); - fp = fopen(filename, "ab"); - - for (i = 0; i < inbuf->height[0]; i++) { - fwrite(temp, inbuf->width[0], 1, fp); - temp += inbuf->stride_width[0]; - } - - temp = (char*)inbuf->data[0] + inbuf->stride_width[0] * inbuf->stride_height[0]; - - for(i = 0; i < inbuf->height[1] ; i++) { - fwrite(temp, inbuf->width[1], 1, fp); - temp += inbuf->stride_width[1]; - } - GST_WARNING ("codec encoder input dumped!!"); - fclose(fp); -} -#endif - static gboolean gst_omx_video_enc_open (GstVideoEncoder * encoder) { @@ -292,8 +249,8 @@ gst_omx_video_enc_open (GstVideoEncoder * encoder) in_port_index = 0; out_port_index = 1; } else { - GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", param.nPorts, - param.nStartPortNumber); + GST_DEBUG_OBJECT (self, "Detected %u ports, starting at %u", + (guint) param.nPorts, (guint) param.nStartPortNumber); in_port_index = param.nStartPortNumber + 0; out_port_index = param.nStartPortNumber + 1; } @@ -596,100 +553,6 @@ gst_omx_video_enc_change_state (GstElement * element, GstStateChange transition) return ret; } -#define MAX_FRAME_DIST_TICKS (5 * OMX_TICKS_PER_SECOND) -#define MAX_FRAME_DIST_FRAMES (100) - -static GstVideoCodecFrame * -_find_nearest_frame (GstOMXVideoEnc * self, GstOMXBuffer * buf) -{ - GList *l, *best_l = NULL; - GList *finish_frames = NULL; - GstVideoCodecFrame *best = NULL; - guint64 best_timestamp = 0; - guint64 best_diff = G_MAXUINT64; - BufferIdentification *best_id = NULL; - GList *frames; - - frames = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (self)); - - for (l = frames; l; l = l->next) { - GstVideoCodecFrame *tmp = l->data; - BufferIdentification *id = gst_video_codec_frame_get_user_data (tmp); - guint64 timestamp, diff; - - /* This happens for frames that were just added but - * which were not passed to the component yet. Ignore - * them here! - */ - if (!id) - continue; - - timestamp = id->timestamp; - - if (timestamp > buf->omx_buf->nTimeStamp) - diff = timestamp - buf->omx_buf->nTimeStamp; - else - diff = buf->omx_buf->nTimeStamp - timestamp; - - if (best == NULL || diff < best_diff) { - best = tmp; - best_timestamp = timestamp; - best_diff = diff; - best_l = l; - best_id = id; - - /* For frames without timestamp we simply take the first frame */ - if ((buf->omx_buf->nTimeStamp == 0 && timestamp == 0) || diff == 0) - break; - } - } - - if (best_id) { - for (l = frames; l && l != best_l; l = l->next) { - GstVideoCodecFrame *tmp = l->data; - BufferIdentification *id = gst_video_codec_frame_get_user_data (tmp); - guint64 diff_ticks, diff_frames; - - /* This happens for frames that were just added but - * which were not passed to the component yet. Ignore - * them here! - */ - if (!id) - continue; - - if (id->timestamp > best_timestamp) - break; - - if (id->timestamp == 0 || best_timestamp == 0) - diff_ticks = 0; - else - diff_ticks = best_timestamp - id->timestamp; - diff_frames = best->system_frame_number - tmp->system_frame_number; - - if (diff_ticks > MAX_FRAME_DIST_TICKS - || diff_frames > MAX_FRAME_DIST_FRAMES) { - finish_frames = - g_list_prepend (finish_frames, gst_video_codec_frame_ref (tmp)); - } - } - } - - if (finish_frames) { - g_warning ("Too old frames, bug in encoder -- please file a bug"); - for (l = finish_frames; l; l = l->next) { - gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (self), l->data); - } - } - - if (best) - gst_video_codec_frame_ref (best); - - g_list_foreach (frames, (GFunc) gst_video_codec_frame_unref, NULL); - g_list_free (frames); - - return best; -} - static GstFlowReturn gst_omx_video_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, GstOMXBuffer * buf, GstVideoCodecFrame * frame) @@ -718,6 +581,7 @@ gst_omx_video_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), caps, self->input_state); state->codec_data = codec_data; + gst_video_codec_state_unref (state); if (!gst_video_encoder_negotiate (GST_VIDEO_ENCODER (self))) { gst_video_codec_frame_unref (frame); return GST_FLOW_NOT_NEGOTIATED; @@ -887,7 +751,7 @@ gst_omx_video_enc_loop (GstOMXVideoEnc * self) g_assert (acq_return == GST_OMX_ACQUIRE_BUFFER_OK); /* This prevents a deadlock between the srcpad stream - * lock and the videocodec stream lock, if ::reset() + * lock and the videocodec stream lock, if ::flush() * is called at the wrong time */ if (gst_omx_port_is_flushing (self->enc_out_port)) { @@ -896,11 +760,12 @@ gst_omx_video_enc_loop (GstOMXVideoEnc * self) goto flushing; } - GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %lu", buf->omx_buf->nFlags, - buf->omx_buf->nTimeStamp); + GST_DEBUG_OBJECT (self, "Handling buffer: 0x%08x %" G_GUINT64_FORMAT, + (guint) buf->omx_buf->nFlags, (guint64) buf->omx_buf->nTimeStamp); GST_VIDEO_ENCODER_STREAM_LOCK (self); - frame = _find_nearest_frame (self, buf); + frame = gst_omx_video_find_nearest_frame (buf, + gst_video_encoder_get_frames (GST_VIDEO_ENCODER (self))); g_assert (klass->handle_output_frame); flow_ret = klass->handle_output_frame (self, self->enc_out_port, buf, frame); @@ -957,6 +822,8 @@ eos: flow_ret = GST_FLOW_EOS; } g_mutex_unlock (&self->drain_lock); + + GST_VIDEO_ENCODER_STREAM_LOCK (self); self->downstream_flow_ret = flow_ret; /* Here we fallback and pause the task for the EOS case */ @@ -975,15 +842,20 @@ flow_error: gst_pad_push_event (GST_VIDEO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_ENCODER_SRC_PAD (self)); - } else if (flow_ret == GST_FLOW_NOT_LINKED || flow_ret < GST_FLOW_EOS) { + self->started = FALSE; + } else if (flow_ret < GST_FLOW_EOS) { GST_ELEMENT_ERROR (self, STREAM, FAILED, ("Internal data stream error."), ("stream stopped, reason %s", gst_flow_get_name (flow_ret))); gst_pad_push_event (GST_VIDEO_ENCODER_SRC_PAD (self), gst_event_new_eos ()); gst_pad_pause_task (GST_VIDEO_ENCODER_SRC_PAD (self)); + self->started = FALSE; + } else if (flow_ret == GST_FLOW_FLUSHING) { + GST_DEBUG_OBJECT (self, "Flushing -- stopping task"); + gst_pad_pause_task (GST_VIDEO_ENCODER_SRC_PAD (self)); + self->started = FALSE; } - self->started = FALSE; GST_VIDEO_ENCODER_STREAM_UNLOCK (self); return; } @@ -1028,7 +900,6 @@ gst_omx_video_enc_start (GstVideoEncoder * encoder) self = GST_OMX_VIDEO_ENC (encoder); self->last_upstream_ts = 0; - self->eos = FALSE; self->downstream_flow_ret = GST_FLOW_OK; return TRUE; @@ -1053,7 +924,6 @@ gst_omx_video_enc_stop (GstVideoEncoder * encoder) self->downstream_flow_ret = GST_FLOW_FLUSHING; self->started = FALSE; - self->eos = FALSE; if (self->input_state) gst_video_codec_state_unref (self->input_state); @@ -1069,99 +939,6 @@ gst_omx_video_enc_stop (GstVideoEncoder * encoder) return TRUE; } -typedef struct -{ - GstVideoFormat format; - OMX_COLOR_FORMATTYPE type; -} VideoNegotiationMap; - -static void -video_negotiation_map_free (VideoNegotiationMap * m) -{ - g_slice_free (VideoNegotiationMap, m); -} - -static GList * -gst_omx_video_enc_get_supported_colorformats (GstOMXVideoEnc * self) -{ - GstOMXPort *port = self->enc_in_port; - GstVideoCodecState *state = self->input_state; - OMX_VIDEO_PARAM_PORTFORMATTYPE param; - OMX_ERRORTYPE err; - GList *negotiation_map = NULL; - gint old_index; - - GST_OMX_INIT_STRUCT (¶m); - param.nPortIndex = port->index; - param.nIndex = 0; - if (!state || state->info.fps_n == 0) - param.xFramerate = 0; - else - param.xFramerate = (state->info.fps_n << 16) / (state->info.fps_d); - - old_index = -1; - do { - VideoNegotiationMap *m; - - err = - gst_omx_component_get_parameter (self->enc, - OMX_IndexParamVideoPortFormat, ¶m); - - /* FIXME: Workaround for Bellagio that simply always - * returns the same value regardless of nIndex and - * never returns OMX_ErrorNoMore - */ - if (old_index == param.nIndex) - break; - - if (err == OMX_ErrorNone || err == OMX_ErrorNoMore) { - switch (param.eColorFormat) { - case OMX_COLOR_FormatYUV420Planar: - case OMX_COLOR_FormatYUV420PackedPlanar: - m = g_slice_new (VideoNegotiationMap); - m->format = GST_VIDEO_FORMAT_I420; - m->type = param.eColorFormat; - negotiation_map = g_list_append (negotiation_map, m); - GST_DEBUG_OBJECT (self, "Component supports I420 (%d) at index %d", - param.eColorFormat, param.nIndex); - break; - case OMX_COLOR_FormatYUV420SemiPlanar: - m = g_slice_new (VideoNegotiationMap); - m->format = GST_VIDEO_FORMAT_NV12; - m->type = param.eColorFormat; - negotiation_map = g_list_append (negotiation_map, m); - GST_DEBUG_OBJECT (self, "Component supports NV12 (%d) at index %d", - param.eColorFormat, param.nIndex); - break; - case OMX_EXT_COLOR_FormatNV12LPhysicalAddress: - m = g_slice_new (VideoNegotiationMap); - m->format = GST_VIDEO_FORMAT_SN12; - m->type = param.eColorFormat; - negotiation_map = g_list_append (negotiation_map, m); - GST_DEBUG_OBJECT (self, "Component supports SN12 (%d) at index %d", - param.eColorFormat, param.nIndex); - break; - case OMX_EXT_COLOR_FormatNV12TPhysicalAddress: - m = g_slice_new (VideoNegotiationMap); - m->format = GST_VIDEO_FORMAT_ST12; - m->type = param.eColorFormat; - negotiation_map = g_list_append (negotiation_map, m); - GST_DEBUG_OBJECT (self, "Component supports ST12 (%d) at index %d", - param.eColorFormat, param.nIndex); - break; - default: - GST_DEBUG_OBJECT (self, - "Component supports unsupported color format %d at index %d", - param.eColorFormat, param.nIndex); - break; - } - } - old_index = param.nIndex++; - } while (err == OMX_ErrorNone); - - return negotiation_map; -} - static gboolean gst_omx_video_enc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state) @@ -1190,7 +967,7 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, */ if (needs_disable) { GST_DEBUG_OBJECT (self, "Need to disable and drain encoder"); - gst_omx_video_enc_drain (self, FALSE); + gst_omx_video_enc_drain (self); gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); /* Wait until the srcpad loop is finished, @@ -1200,31 +977,44 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, gst_pad_stop_task (GST_VIDEO_ENCODER_SRC_PAD (encoder)); GST_VIDEO_ENCODER_STREAM_LOCK (self); - if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_wait_buffers_released (self->enc_in_port, - 5 * GST_SECOND) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_wait_buffers_released (self->enc_out_port, - 1 * GST_SECOND) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_wait_enabled (self->enc_in_port, - 1 * GST_SECOND) != OMX_ErrorNone) - return FALSE; - if (gst_omx_port_wait_enabled (self->enc_out_port, - 1 * GST_SECOND) != OMX_ErrorNone) - return FALSE; + if (klass->cdata.hacks & GST_OMX_HACK_NO_COMPONENT_RECONFIGURE) { + GST_VIDEO_ENCODER_STREAM_UNLOCK (self); + gst_omx_video_enc_stop (GST_VIDEO_ENCODER (self)); + gst_omx_video_enc_close (GST_VIDEO_ENCODER (self)); + GST_VIDEO_ENCODER_STREAM_LOCK (self); + + if (!gst_omx_video_enc_open (GST_VIDEO_ENCODER (self))) + return FALSE; + needs_disable = FALSE; + } else { + if (gst_omx_port_set_enabled (self->enc_in_port, FALSE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_buffers_released (self->enc_in_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_buffers_released (self->enc_out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_deallocate_buffers (self->enc_in_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_deallocate_buffers (self->enc_out_port) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_enabled (self->enc_in_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_wait_enabled (self->enc_out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + } GST_DEBUG_OBJECT (self, "Encoder drained and disabled"); } - negotiation_map = gst_omx_video_enc_get_supported_colorformats (self); + negotiation_map = + gst_omx_video_get_supported_colorformats (self->enc_in_port, + self->input_state); if (!negotiation_map) { /* Fallback */ switch (info->finfo->format) { @@ -1242,7 +1032,7 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, } } else { for (l = negotiation_map; l; l = l->next) { - VideoNegotiationMap *m = l->data; + GstOMXVideoNegotiationMap *m = l->data; if (m->format == info->finfo->format) { port_def.format.video.eColorFormat = m->type; @@ -1250,7 +1040,7 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, } } g_list_free_full (negotiation_map, - (GDestroyNotify) video_negotiation_map_free); + (GDestroyNotify) gst_omx_video_negotiation_map_free); } port_def.format.video.nFrameWidth = info->width; @@ -1307,6 +1097,44 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, &port_def) != OMX_ErrorNone) return FALSE; +#ifdef USE_OMX_TARGET_RPI + /* aspect ratio */ + { + OMX_ERRORTYPE err; + OMX_CONFIG_POINTTYPE aspect_ratio_param; + + GST_OMX_INIT_STRUCT (&aspect_ratio_param); + aspect_ratio_param.nPortIndex = self->enc_out_port->index; + + err = gst_omx_component_get_parameter (self->enc, + OMX_IndexParamBrcmPixelAspectRatio, &aspect_ratio_param); + + if (err == OMX_ErrorNone) { + + aspect_ratio_param.nX = info->par_n; + aspect_ratio_param.nY = info->par_d; + + err = + gst_omx_component_set_parameter (self->enc, + OMX_IndexParamBrcmPixelAspectRatio, &aspect_ratio_param); + + if (err == OMX_ErrorUnsupportedIndex) { + GST_WARNING_OBJECT (self, + "Setting aspect ratio parameters not supported by the component"); + } else if (err == OMX_ErrorUnsupportedSetting) { + GST_WARNING_OBJECT (self, + "Setting aspect ratio %u %u not supported by the component", + aspect_ratio_param.nX, aspect_ratio_param.nY); + } else if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Failed to set aspect ratio: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } + } + } +#endif // USE_OMX_TARGET_RPI + if (klass->set_format) { if (!klass->set_format (self, self->enc_in_port, state)) { GST_ERROR_OBJECT (self, "Subclass failed to set the new format"); @@ -1319,6 +1147,21 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, NULL) != OMX_ErrorNone) return FALSE; + if (self->target_bitrate != 0xffffffff) { + OMX_VIDEO_PARAM_BITRATETYPE config; + OMX_ERRORTYPE err; + + GST_OMX_INIT_STRUCT (&config); + config.nPortIndex = self->enc_out_port->index; + config.nTargetBitrate = self->target_bitrate; + config.eControlRate = self->control_rate; + err = gst_omx_component_set_parameter (self->enc, + OMX_IndexParamVideoBitrate, &config); + if (err != OMX_ErrorNone) + GST_ERROR_OBJECT (self, "Failed to set bitrate parameter: %s (0x%08x)", + gst_omx_error_to_string (err), err); + } + GST_DEBUG_OBJECT (self, "Enabling component"); if (needs_disable) { if (gst_omx_port_set_enabled (self->enc_in_port, TRUE) != OMX_ErrorNone) @@ -1331,39 +1174,63 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) return FALSE; #endif + if ((klass->cdata.hacks & GST_OMX_HACK_NO_DISABLE_OUTPORT)) { + if (gst_omx_port_set_enabled (self->enc_out_port, TRUE) != OMX_ErrorNone) + return FALSE; + if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->enc_out_port, + 5 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + } + if (gst_omx_port_wait_enabled (self->enc_in_port, 5 * GST_SECOND) != OMX_ErrorNone) return FALSE; if (gst_omx_port_mark_reconfigured (self->enc_in_port) != OMX_ErrorNone) return FALSE; } else { - if (gst_omx_component_set_state (self->enc, OMX_StateIdle) != OMX_ErrorNone) - return FALSE; + if (!(klass->cdata.hacks & GST_OMX_HACK_NO_DISABLE_OUTPORT)) { + /* Disable output port */ + if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_port_wait_enabled (self->enc_out_port, + 1 * GST_SECOND) != OMX_ErrorNone) + return FALSE; + + if (gst_omx_component_set_state (self->enc, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; + + /* Need to allocate buffers to reach Idle state */ + + if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) + return FALSE; + } else { + if (gst_omx_component_set_state (self->enc, + OMX_StateIdle) != OMX_ErrorNone) + return FALSE; - /* Need to allocate buffers to reach Idle state */ + /* Need to allocate buffers to reach Idle state */ #ifdef USE_TBM if(gst_omx_port_tbm_allocate_enc_buffers(self->hTBMBufMgr,self->enc_in_port, self->enc_in_port->port_def.format.video.eCompressionFormat) != OMX_ErrorNone) return FALSE; -#else - if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) - return FALSE; +#else + if (gst_omx_port_allocate_buffers (self->enc_in_port) != OMX_ErrorNone) + return FALSE; #endif -#ifdef EXYNOS_SPECIFIC - /*Specific for exynos processors*/ - /*if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone)*/ +#ifdef USE_TBM if(gst_omx_port_tbm_allocate_enc_buffers(self->hTBMBufMgr,self->enc_out_port, - self->enc_in_port->port_def.format.video.eCompressionFormat) != OMX_ErrorNone) -#else - /* And disable output port */ - if (gst_omx_port_set_enabled (self->enc_out_port, FALSE) != OMX_ErrorNone) - return FALSE; - - if (gst_omx_port_wait_enabled (self->enc_out_port, - 1 * GST_SECOND) != OMX_ErrorNone) + self->enc_out_port->port_def.format.video.eCompressionFormat) != OMX_ErrorNone) +#else + if (gst_omx_port_allocate_buffers (self->enc_out_port) != OMX_ErrorNone) #endif - return FALSE; + return FALSE; + } if (gst_omx_component_get_state (self->enc, GST_CLOCK_TIME_NONE) != OMX_StateIdle) @@ -1403,13 +1270,16 @@ gst_omx_video_enc_set_format (GstVideoEncoder * encoder, } static gboolean -gst_omx_video_enc_reset (GstVideoEncoder * encoder, gboolean hard) +gst_omx_video_enc_flush (GstVideoEncoder * encoder) { GstOMXVideoEnc *self; self = GST_OMX_VIDEO_ENC (encoder); - GST_DEBUG_OBJECT (self, "Resetting encoder"); + GST_DEBUG_OBJECT (self, "Flushing encoder"); + + if (gst_omx_component_get_state (self->enc, 0) == OMX_StateLoaded) + return TRUE; gst_omx_port_set_flushing (self->enc_in_port, 5 * GST_SECOND, TRUE); gst_omx_port_set_flushing (self->enc_out_port, 5 * GST_SECOND, TRUE); @@ -1428,7 +1298,6 @@ gst_omx_video_enc_reset (GstVideoEncoder * encoder, gboolean hard) /* Start the srcpad loop again */ self->last_upstream_ts = 0; - self->eos = FALSE; self->downstream_flow_ret = GST_FLOW_OK; gst_pad_start_task (GST_VIDEO_ENCODER_SRC_PAD (self), (GstTaskFunction) gst_omx_video_enc_loop, encoder, NULL); @@ -1671,12 +1540,6 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, GST_DEBUG_OBJECT (self, "Handling frame"); - if (self->eos) { - GST_WARNING_OBJECT (self, "Got frame after EOS"); - gst_video_codec_frame_unref (frame); - return GST_FLOW_EOS; - } - if (self->downstream_flow_ret != GST_FLOW_OK) { gst_video_codec_frame_unref (frame); return self->downstream_flow_ret; @@ -1685,7 +1548,6 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, port = self->enc_in_port; while (acq_ret != GST_OMX_ACQUIRE_BUFFER_OK) { - BufferIdentification *id; GstClockTime timestamp, duration; /* Make sure to release the base class stream lock, otherwise @@ -1776,6 +1638,18 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, GST_DEBUG_OBJECT (self, "Handling frame"); if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) { +#ifdef USE_OMX_TARGET_RPI + OMX_CONFIG_BOOLEANTYPE config; + + GST_OMX_INIT_STRUCT (&config); + config.bEnabled = OMX_TRUE; + + GST_DEBUG_OBJECT (self, "Forcing a keyframe (iframe on the RPi)"); + + err = + gst_omx_component_set_config (self->enc, + OMX_IndexConfigBrcmVideoRequestIFrame, &config); +#else OMX_CONFIG_INTRAREFRESHVOPTYPE config; GST_OMX_INIT_STRUCT (&config); @@ -1786,6 +1660,7 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, err = gst_omx_component_set_config (self->enc, OMX_IndexConfigVideoIntraVOPRefresh, &config); +#endif if (err != OMX_ErrorNone) GST_ERROR_OBJECT (self, "Failed to force a keyframe: %s (0x%08x)", gst_omx_error_to_string (err), err); @@ -1808,16 +1683,12 @@ gst_omx_video_enc_handle_frame (GstVideoEncoder * encoder, duration = frame->duration; if (duration != GST_CLOCK_TIME_NONE) { buf->omx_buf->nTickCount = - gst_util_uint64_scale (buf->omx_buf->nFilledLen, duration, - gst_buffer_get_size (frame->input_buffer)); + gst_util_uint64_scale (duration, OMX_TICKS_PER_SECOND, GST_SECOND); self->last_upstream_ts += duration; + } else { + buf->omx_buf->nTickCount = 0; } - id = g_slice_new0 (BufferIdentification); - id->timestamp = buf->omx_buf->nTimeStamp; - gst_video_codec_frame_set_user_data (frame, id, - (GDestroyNotify) buffer_identification_free); - self->started = TRUE; err = gst_omx_port_release_buffer (port, buf); if (err != OMX_ErrorNone) @@ -1834,7 +1705,7 @@ full_buffer: { GST_ELEMENT_ERROR (self, LIBRARY, FAILED, (NULL), ("Got OpenMAX buffer with no free space (%p, %u/%u)", buf, - buf->omx_buf->nOffset, buf->omx_buf->nAllocLen)); + (guint) buf->omx_buf->nOffset, (guint) buf->omx_buf->nAllocLen)); gst_video_codec_frame_unref (frame); return GST_FLOW_ERROR; } @@ -1892,11 +1763,11 @@ gst_omx_video_enc_finish (GstVideoEncoder * encoder) self = GST_OMX_VIDEO_ENC (encoder); - return gst_omx_video_enc_drain (self, TRUE); + return gst_omx_video_enc_drain (self); } static GstFlowReturn -gst_omx_video_enc_drain (GstOMXVideoEnc * self, gboolean at_eos) +gst_omx_video_enc_drain (GstOMXVideoEnc * self) { GstOMXVideoEncClass *klass; GstOMXBuffer *buf; @@ -1913,14 +1784,6 @@ gst_omx_video_enc_drain (GstOMXVideoEnc * self, gboolean at_eos) } self->started = FALSE; - /* Don't send EOS buffer twice, this doesn't work */ - if (self->eos) { - GST_DEBUG_OBJECT (self, "Component is EOS already"); - return GST_FLOW_OK; - } - if (at_eos) - self->eos = TRUE; - if ((klass->cdata.hacks & GST_OMX_HACK_NO_EMPTY_EOS_BUFFER)) { GST_WARNING_OBJECT (self, "Component does not support empty EOS buffers"); return GST_FLOW_OK; @@ -1983,22 +1846,18 @@ static GstCaps * gst_omx_video_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter) { GstOMXVideoEnc *self = GST_OMX_VIDEO_ENC (encoder); - GList *negotiation_map = NULL, *l; + GList *negotiation_map = NULL; GstCaps *comp_supported_caps; if (!self->enc) return gst_video_encoder_proxy_getcaps (encoder, NULL, filter); - negotiation_map = gst_omx_video_enc_get_supported_colorformats (self); - comp_supported_caps = gst_caps_new_empty (); - for (l = negotiation_map; l; l = l->next) { - VideoNegotiationMap *map = l->data; - - gst_caps_append_structure (comp_supported_caps, - gst_structure_new ("video/x-raw", - "format", G_TYPE_STRING, - gst_video_format_to_string (map->format), NULL)); - } + negotiation_map = + gst_omx_video_get_supported_colorformats (self->enc_in_port, + self->input_state); + comp_supported_caps = gst_omx_video_get_caps_for_map (negotiation_map); + g_list_free_full (negotiation_map, + (GDestroyNotify) gst_omx_video_negotiation_map_free); if (!gst_caps_is_empty (comp_supported_caps)) { GstCaps *ret = diff --git a/omx/gstomxvideoenc.h b/omx/gstomxvideoenc.h old mode 100755 new mode 100644 index 9ac7f17..8266d84 --- a/omx/gstomxvideoenc.h +++ b/omx/gstomxvideoenc.h @@ -68,9 +68,6 @@ struct _GstOMXVideoEnc /* TRUE if EOS buffers shouldn't be forwarded */ gboolean draining; - /* TRUE if upstream is EOS */ - gboolean eos; - /* properties */ guint32 control_rate; guint32 target_bitrate; diff --git a/omx/gstomxvp8dec.h b/omx/gstomxvp8dec.h index 4c4d40c..f99f4ce 100644 --- a/omx/gstomxvp8dec.h +++ b/omx/gstomxvp8dec.h @@ -27,7 +27,7 @@ G_BEGIN_DECLS #define GST_TYPE_OMX_VP8_DEC \ - (gst_omx_VP8_dec_get_type()) + (gst_omx_vp8_dec_get_type()) #define GST_OMX_VP8_DEC(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OMX_VP8_DEC,GstOMXVP8Dec)) #define GST_OMX_VP8_DEC_CLASS(klass) \ diff --git a/packaging/common.tar.bz2 b/packaging/common.tar.bz2 deleted file mode 100644 index a18ecc8436bc17aed8388b01d15c551c2886d53a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95459 zcmV(|K+(TKT4*^jL0KkKSu1sg+X3N{|NsC0{`>#`|NsC0|NsC0|Njsq10))%fn=N+ z1Q5-DL?~hL-KXBpYy;hZ^mo^5JYE;qcW^#i)wOH~Kzy^<@&vy2uX|S3-DDtF-QBMf zvit7WI`PjQ;db;7thzKB0cLtVc>p|xF8FcY+n;;q0PTRM+V48|a>w3)U=1VZ(|dPy zKE8Z13J0gqb@iYM2aq0sJySux_nVu+^gRcC*8AT@ZMSo#`<{nG(f4-t_isIXecg|f z;UQ28DBHL_4w9-+5eGxtU4nqFu#~Ip8Yl*U3_uYmBvO>wr2*EVSJo7u5&*U|6skP? zxdMRmVgwL^i)~*_m90{H?iv6A^aF4fYtiHnufFZv2D&E4ySp#E_V>FtAjht1*y1z0r()?)$mVZumQ2Y<2h5 z8Fcn?_nvw8Jw1Ehd+%d*2A^x)(RZ$Q#o8Z3*7_&gf$n0pBKa-!a_86H*S-#qy89F) z1CI^gG<~X{Tidp~wE9A}9nsS7dha~hCwuPa!+QGr+WX&g#@)`%)0STOe9Y~P`en1a_t(pJ-uvG9RWEt~ z00Yn;2QAmWy}o_z>(_Ig=`U^XY45ka_qShdy}Q8u);2j4#fe)r@O8WTgZOf+PubK}<-mBej zz5oH;dTGAV()-?Btkgcn54!Sqp*?$Z+*_v;-)5;PxxL))2>TZ8+idIuZ?<$0YKG!29e1|wSVu6D@-O_inj)ofoy6xWX@DKn1sG%Vn zXzb)n-|B`aVZG&Iv&wP!F+`#9wE(np15yH&aavAy%46aWEI0PgPe zJ$t*Kd+VON<$dj~z+3L?nZ0}6-rEdEZ$pl}`Y`Ve_P28S^UXrhwB-)Hb8_Lm&VE000Dn zL_!DvnKBxfN_jNVriM~)MLkSU)iPfDtIa3GJ2E6Hj_;N000Bj13&>1L=XZAfFn%?fQ?haWT)vgp3qE! zYI!q7HY$Iq>JHq*}0iZMhJCFa@<4{b=pql?Z`LFeCwXDzeMIZ5D#h_Ae z?1u=8K!4GP|7m}>XKhY@+^So?BOmVZ{^ltpzrp)Ef76p)Znqz_s($D|25pKasj4E0 ztJI*F7)=mAqcB!LC)gkWB=ddYI+ z-v;*ZZsxBUCEGJN+riA%ImZ>(_}>39ss0Jkt-c`nN*jP^LWHG%vVYJrnP~A86UWIY zX{9I%DTWeBhC$Hi0g7ezl)ATRtN4Eyd*2pGJ)UBfDZ8vaokG0K#oqRG_q~hE-c{}? zQwxc_;&Hjq4y*YAe53JAW}j}VU#Io83`+ozbhkuJ~o!mG>DF+O2h8z%e4##Z|(~+F1 zjhd!|rxJ2j8;U=+I-Rdx~hHJCbCGDX5>rJi+ityMI65MlyiBww@Jqe1}H z0K0`siX{1G%ca`~d9mKv^1!n;lG|r@w2A4xQW#;6F!2S~1kVTuAv(yZfecC%1dzc3 zM2Zms5P?XI0vRCT5e*5Pzt4X}ZjRsfxIl{it{(!0PS&s0d>|e!+8~l^ot?kE#};(m zsujP}KkUoM^fX1+J?7Yd$IC_6A{3)JHfXVHqmms*&H3|earD7SkkXh=95Uw4JIGnY@g=BR(yH&n1akPMuu zQ-L{VTG)?-4cMj5H;=9Z*9SO%n>#<2YHf4EQelqY6OoKgw~za_crunRF|0*&cIl4x zuS7IzhCMV+#5l~?aT{VoWPmue4iaMhb;XEgd1u1o{(P&|$ z84r?2J6Q_v&4>o74|*M1zu%O?5(y?|K5HkN2(4#b8P^9soCn{8*KBt#s;cW`OegVX zCgU>C?R z-Nk1o8gJ#@D{XF4nsSd^!|@Lxvto*2AtoFsP02Dzq!l}1ky~v znS`}x{=-?_$5R2ZXw$GNi)u_4W^k)Ru}WqI7Xhh7TO)>0M54Z~=Vk+~1y^BDQ6^($ zt`rMxF{dmhzs&tvf6{I$BFs4{ zu+g6g3{z320Z^^U8=lC6)aKjqkZHH$vhBE9n%c%R>xqsvzkXKwtzI*1_I-F69-=3~ zL@F{UNCP7xu22a8s004F{~QDAf(_uha-Mq}TR3YVL*?kG=Bjm~qz-*}yBfd3MN8sD zniq9{(GlVXN52z(L)|Nm4fwV6{3hh=!^p_yyY4h=ne;L)Bce~|dxGdW%rSJ4B?xC6 zeWzWN+wgxysK*WkepGV-J29xn;acfLedd+vt`;@*d*ihlgsL5c+^4%TI8MZ0@H{m5 zI-Y;E{W4tmw2WtU!y(pwNT2i4L&LWia^26PQH*OX|!%#=P<#m)^};;t_{T z!gtTa)=a$`>NQv)QmRxU^fz1@WFL%tygK2UytU?19n!(t$++FjH0O^MV8EXSxLXL5 z8g21qDfB~ah0uXBI3_Dh{~i62d!OT3qN?9F5u7C9*iCz!|6DY8SwwB1j9zY`JbTI0 zH(Ay5p86V88GJOxQp2K9`H<9iXZ#E7`6zEPjn!DjeIOykd$u^oVL(L9eP~-dL1cz22JBZcONoijb?YDh5S#&9;o)qlao|>~9 zmJ!e7zfPG;!=Q}6)hH-U51@&p;`CMjHxun&3esu#IW<}C&pAd#$RxH_&+{;SZsE3D(2WZ-Ms8ICP zjnZ3)F&v^g2%JMK;zDMzK@c&7kPl9pX&o%Oe z0%b29GfFb=P2q0FPbAp+-W#@%Z$E7i%36FJusx~aKC_|a*WE{T5rrl(_sUA>5k^Rb zcg~Z_^F_(yY^Csp;55L5%HYY!i>FHU& z;ziLm!04E^4qDb&s`0zY^r1gtIt0Bw*09ceScf zJ1J~zBLJKBbITQI0YU+|l3xgc@zBC*!OU)f1avUs0sB-9?mq{3ZhXZrGq07tp!$cb zp4OGaEo#d0WbHO)KnXBOtAXEni#VI65aS?>ymUOO@yEI*Q`*K-(%5CQ0;a*8uvzPE zaf}_r7eJ!+&{);Pq(i1UWljhE=}?s;=q=d{gFnG2b*h_YEle+Vf*?3wOXDdJ4Jrga zw`~d|lXx*Fggnmi4|WZI2;Z$cKAdr_2@HmpZ+9HW%~Y6KX1XKfYS}ebZ=qWbezbOtUxpMuV=g@O{v3j2+BxJ^p^1OfxGk?B@oc?y ziBm&UL~09nFE8ua?=JiU!ic4;tb;LHF*}Q+s_dAlDOG;U3+LZ4=+tP04{I@9yWNA{ z1cd@-JhO+lyB6nm?xWU?!#ehHpWrZ11k>*K5VT4GiBd$RJ(n-VNyGT5E0z=E-4rV4 z{PKIfA1g5t4S0+{1qFGtlgu&qk#u3w{SY>1jda4+O(TuyjYqBw6CFBfWJvZL#K>}8 z?l;~NH8XiJvPG0JKr$?c@!}|A=jyhjrVC~<%4q)3B zXK8Zj&_ZzJ;jGB(hakHA7US_%a7`PpQc20`@Az`{ob=VCLo;Frr;DE6p|NnU3J5b8 zLLE7DUY!n6XyO8rZQk)BS85#~fSQJhURciqG^WgxB@i^AEltv5-b-(4eic`oC|DduvxJ4Joy--n-{AM zG*jC5b8}HhCgQ<2C}%o2^JAygo|3ndOsuv!?%mVM=I|Eb!JJMLHr=4(H%n(21cu}> z@3)mRd#zK}v|`pjbIN$-%A}VTeN+9i&BtkU<_d&co)Nh-O8YE~6FiQnHYun^4?A(c z6SN3LILK$&!zWxotxnB~(A9PbqzqptS-8k*HX3L+icPTAOPVp}0ymn0xJo1w1G6KG zSWK8el7Ol^RUYdwSYN9gd2Qu>D8uo7A0U`+&wT5Lv2bCS7}20ENegc2$ETI|`MmvL zcR>X`1VCh0?uumqLvU8dW6MFozPMCQ4RLNB40=_Kn`%Pat_=kQ!@H0}K|u$D6=i3* zYYg{Pqehta+->Z0uDyj(=HQcmGdddfdO{p=vBDBMTnb)b=96N1DzJ#AU%68j&Z2f#-(oAM)0q=oGU?KxjhQW~8m0s;jZWGAoVWUg2 zW#ItM#78u0dqdUwK$NEcM=8uwh^24F!5J8BgEbomY4f@X8`12RhXCWf^}=*HC*woR zM2;hThqpBMr(1fqNH&afpxP|EF$X4@#-X=FrPI7QVTR~tn+-;#F)*CaKEH3}f3aS& zo};+ENuB~kP((xoMeFrOv}cm+OfWfN^kLNVPKG!4svON7p8YvY?9}QZc}|RyCpueX zc;ifx54_%MTCY{>SOqG)+;jtY8{nYW_{uslKryPr4eqUtDJ`$uR4cPHq}SM zB!X*x^v6J*Jg7{dXAnD)!!~*E)Iogk8yGT~icWt;{Nj>-q9PjlK4jDkR$@KigU#VC zv+dCm5Re@X$OUZohP=)F**OP$&Et%V9R@OB>zFhh8@f%DW)9(E4}zibQ&=O8uK zOu8^^S*49FWg7+h!7 z*;H-Ut2TSm6wb2CDSUTyEyR5J{2j5&F-~pxLN>WUOjz=(sA6d`=1fqbQ#wJW%V2l; zfE^B<0_f$XWYJ`*I#5C#Sf$BV$KOVNH*oPhHPL8sP{cI8Vt7tMwa<2!V zkHp*cS9=n(w1l%Y+;Ho^Rz}3d4Xjj=O(TepEh`E?*0BBO10)8zKcVA&v^JCSe7hk{ zA-3VvL8&Ts9RTQb&!-K86!LWLY8qs8QpTAdNITG!adS*n+vvXgT!Tg|Zs=!Va3$#ht^G z-p|=+?2u|!_t`5duQG|MvE`hjI_TJV;k%ky%M)H+?Vc{)Ho>P1q%6^Xja#L?P;9x+ zRP{{!zYMqJV*WBdN@8JWmJin##Ks^I= zH4>Cr8a%FRi=mQNZLT)B;BTGqhQf;@3%LHB67W_%*0be{kP(CGXQL`^qA_ZyQK{4d zQ80nABq$VOXt?g6+eV)gKGvjPt^ECx27Hg%g5j}-{~f6BKVHl zh)PzPtuK^}W)L)>z+Ue!exD6bD>m~tJ@>cO{>9|d)o7sia1IdF?DC(aPQ6}#8* z!qgo|WHhYU`93qsh$?zKkEAli;dBRP8GG;W^X#K106wd8uw()oh`# zru|e@)oa-kG>tVm*+`Il4WA^EbB-wvMV71Z?$NUgyN3B`#)Tmxk>Q>#)*-l-q{lVS zPakF^Z13_zcCEiWOYnYQ9Z-;R^Fgjg@g&B~?_zj9m`;26=S1NxQq;DgrBIv+Q1{bw zLpI{rHG62JGRAn1ZA4AQF8!)`uXOdGRvpHd-v`4Q!Vt>Zoa4$ZhrPu8Rx?$4mLs@qwnjL`!`(Rgnv zj@qd2h%6)X{038R4myP3p%^kB=L3GA>2M^=UEYsE9qtCf+~sFi)^4oUmX<7Vslm&*^T*Kap%mFIe>e&1;`xLBfM zK2C0EHy-YZO-ZZ0#%{&Vx%%FTWy1fiAdklpSp`$4x<0SH;wCZa$sDgn5EGK&A_gn# z>@&r_u815K7j##zijQPir5UbtfiEKt_Pjf5l#t!i9tioG+DLh9%>k)RVgf3pIy}ev z-~C>|_4Y^lp>0v-->|>JJ|8o}f>4|v#ix9HP?Q%#dHKloH#H~RY^z7B#)A7dZn|#;Zu-V`9 zX0NyP8GVzd$9yxsy<% z>$2@JYrIcJJ#_d^Y|C3ykfJi6B2c$6%DuEX6P2Rd` zf9?9*+ih2VZv=={;Dr&SNwfAs65mW?Lh4m6=b+OFN! zZkc5N%`RrIWb@TggdV>|G&Wt7#Tzc3n;mVzv$W-{)0XLH=mFcp(A=aDj~%14jU}Zf zFL!f*n#{~G`k(bn%vkHeBWLAv950jH>Fy(jYe(vjoAZ7)*>TQA4$eVC%D$Mp2^meU zO$^sO6Xv(gsyT&XY*wA4J3IFuPkmqLzvr-w{p_x?;FL!yM2~y&IR=KeJ{Cb?#`vQ$ zv*vvY@Ab*Itnz+z;;q=<&A!|sTR2%|BzR7B#|A#+Q3CZ+DydvU^Dmh#uA3xT50T4Y zqSiom!q_1u!-wbmFT6j;NsYXHwKGFf1n6%%FH+k)Fjdjf2OEgNov~M}c$~+F58u6DD*fLS=>;?xt#c<3f{o^mI-8>hG*7^Vhr+6F|^n^Rxa`NTI>Z4nQD; zBsCu+zm@i^BrcJv?I*@NA?ne^?bap&bwWuOmTj?6gVP!$$W$IjMK+o3@kdTS6pmXp)#JksSYdGQ{V&1k ztBT8j!|FAw@}O(#=w2p@uPhS}4KWxI7^{ zzq(;nSH&)`d>S(9+W2{y_dZ^Sy4oOC;T({S{4aaQ88p-}tv9aQ%zE6f2)tvh&7(nx zuw_=v*|D1j>9{1!kwUJDu|}%BJhkkkxoqmZ_%Oyv=E1}`!`MyD$YinP_b)cRS2IJY z=FW31nXh-p&-=f11wQENOqBR#`ym4Y9l(VMSNXBjbVFnIfs_RUtXB)9kk|LOPJbPb zB{lVXhw0t4*xUgiA@+z5EvkqJ#0+6tRf;4CwM$|Ig+{2MR6%~T_W0Rzk}u?j7Wuns zj}84cn>Za)Paaf;h=_K%zDB};0~8VfDuJ!wv@D9&Sq2F>r5P(%up-ha!B#W6Blu^mOJYpq5L@;)L z8GrU6h1ZWY?#LIrUAxf2){1L+N_Nl6N0W5{nE{a|6g#0xRLB(wfPU=6pz2?23S=Ns(giAn$a$ngP^O_8L_5n;`eg^xI$^6`kGAil zSxci`0rSr8%2jU{LlCI>AoK}U(IJdM3k0BiEV{j2*z%B}u<2yt>H0478Vt}PlLx!w}JVIa#kQbp6(J+D>erih*4Nw-gc3H6_A<;wECvQxP zAQ{;=JC0Oxxv!!@nXFAREPQ$0az)0vwJ>{Jtf{GnH2@{;Q7ccp) z-r*s-$|6AI7~A^>V-9jS&2oJK(QvpH)K7{V?(tpD$_~*-gf0YYZW%wH^5{5={Fl2A zuJxD{BuRBW#>K|(Y^~pt!Z-kg1AFppZ3xS;h|-C?APMY9uK*|sAvp<$pFm2 zLVyCQaDe%xJUyq|{U74veJDGAzxT8pcZ)t4xkpNknyRWkv8F)gzRohgcan6?%U~k* zV#zwe{(iNsXlWyq!Qk6&5zGlAkYip1B22MHIAtpwKu7P26G}t!+;#l1+1D@F?Qc0H zSjTCLL+^v2IuPUzBy@*@6WHm-VTLu=TWfRHTWz;HNA5{5z;Kjty~|l3q-F~5#o*;D zQA<4zNfJ!eNj+a%83`s4tj@HI(y7LjO_S(RklzOom>?E)g80`4?uk|2F8H-`d_C<- zpA|XHqHd!K_bp{EveY`YtfJXbm?qf13X59OUv0&-QGDc$)^wgoQ+=5;H+UjrJU023 z@WYULCL9jWYH`-~_ASeY=S&iT63*cd(H}O%+FBrKmjrQwa}0wFGbaw}jVS5~_%qoi zlWfs}_#ic)tOGFifumY$1Wc$J28Ad^!h#YB8fHc)f@uksVns3#I%&gDnHgk-WML;Z zph9Fo(ljiM6$Hjri$*FU4r4)0P?QE#B_=EnIgzY?G`fNzrT54(29P3M!;GqAxHFXw zK;#A2MjhL*B>T*QwtX!c8xz23!Zyn-8gPy$9C0%Sh(u7rF(WY~EX*amb)|ISV=*y6 z^&mB)AgC-auP|MaqA7#4Z8S;9NFF&x#;B8R5>KI8&Va@Gu#GdZgiVQ@wr~_>QZ&%u zM&nG0cBg#|X|>qwbY~&sDdmJ_h$G3<;KC&!z;vl)ri8&cswLHRO=0}K4LK_2%@rAf z>RSJ=kCM6o#JqDwXfa?(w!3R2J2I%zV73ILzf9zgMpDUv*$;w5_(&HD=iA56bgvTA z`q4Nuq>F-J6o|=JQ^h5krgw**^T-G@06K`@a;Ma7tuuz;Ujp4o(4AYhFSjHY@xNI z@)6$Gk*=7Z+NZzG`T3*tx5T=;N2|K`f_!RI*QFiYZ3WOxe4uY6&B6!=`p>TyK~G>( z;V^ruCljZ9_Q_dBf&!rz!+V+2+3fRqdGS}k^b4`dXgh=saLmrk%Pob&@_c(+>HhA0 zwOnvP7wTbCx!hzluy(&v%>GuliJ1(j_*~M1aqQsw8I&fDaJ zi|9y5WFkZ)J;%qA7_>F9*ZPPQ6_viG$mqR9h}lr*eyL70!j$Nq185>v-B4`tDR;QK z=uVn}HRy;rNwK19Aa)2G5Uj$uXG+pK&Jq>rm${ z_ddc-fIxsnR){C3&)|h;g8&(mVZQwm> z(oPx%5QZb$#e9jY^f2EYKEaJhKEJQ+`u=&gRH~F>GqX0z?Dc;s9*v{3@?$^I`2w~m zV@k(vk%1+TP{ae2Zt{lwyv(YT$i_<;mWl!q6`sdB6dmI~@ICpC{@b=4DLWbF_HL?&=d_(aawRLZY% zl&tQ2HZfM|`8M?VJA3-~Ys1?%R%nRMhOHH>$1MQ|bm_9$$*y01+iNf?Vrb6$k;Nic z@S6uty}g>v$ze(dZ9Yxy&ZlP<#SF|)ysZcgd~N#&|8($L3*8-DVR}rkod}l>8~$j?t`kaQ38PoY@s9Wp zXH+&qJaMg$L~`up`cGFh5M|2-9PBa(2F$(uX=yUwDwu;asb`D+!aYrl4L7%P3XC-& zdVR6&EYRyVdA$4;o`b<`r73Q6YG>X?5hgu+i>840FNu!pbCVzAX{cns(0!IdjL0Wb zWX2$X2e&vT^w6`>p;%j))M$sesfnR?FSbt{5p-$(65oEpnpyD(HYO{5g?7Zvo~Tsg zd&v6}L$krtpi1Lcvj6KgwR!Y>-U(mb*W1CZJ_~5(*ly3)On4?M3CGhPu`y01KeC#1 z-n?xGAUh9NJmI*nCeaou*nE`qPhN+9SQWznv2o+AFA1t7iHLJ8`oEvEzEP)`p}mp*9>k!(eL;VvRu?{%BaVscFZ(|VGiybatkdxdE(J!cd4ho zK1b05VUfGpcHJ*}ZCnHeAN?bDSKQD~8^lxn`TJYezokY~_%Ji6N|* zOZ8N5L;(mL-NeqA4@E`Zwlx9HUe0HjXQN5>?(lfonC^O+y>%X#nraNezLdO+XmR&t z7_L4}&cm9g(nAVit*4u6-5$-)oiK^|{XSP!k(w*M^%DTDTIUHecw_Z8vzw5o$Kh|N zBFxwbX(6+n3^8o2pzvRZr(fC$Fd8dW;pVcQQ&vX2^}?u}pS4swHn??Ad~BwBk*J>i zAF{=uK|g7ypY2>M)d9#07W}DO)8i?)W}tM0;AjxNNO7^o*a7A=(SZmqfkCb=oD!R5NYg zqDh3JohJo2cYeO=P2{lhs;;Aaz~b-YIP)U*{#j#~H9hXkM~~U&?><^xD7)(|2mI5= zyLtD`x3??r?)@)CptX=S9Nk3B8!}^2#UuRjJRe(_>tPr|0n>Tz=;3}Qcuy`&*7P^k zheJ|X*XV`ACS?;ux!?seHa0$h!oI{cbi2?!PGRb&C_j5@u2a#Z9@<#hZeBv_UTa7fR1%B%8R?C;=t?W72l<~!%N8JCnUyFlw+sC7c1 z(sFZ)?*B8gzJdAAhM!vb2jeUO_0Y3JV+>78FLgEqYJ>10IYdV2-?9N{5YE;>wg}ee+GRlBZ|q6#7vIsSwO%| zy*5U|IYBWLQo$h=b0VGaPo@BMdH*tg>A%4IpT2(XKK`f5+3_1VDdq^6dVV~V{2xke z#JJ_EJ$;uhUWEYj=+c#YlGRSd_zvw^fL@@6IqN?PbNXuctFE_Mn=z)0eVT;C3ZGiM zOhzdj&)Wv4hpxTwhWz+s*{fX@-xE8-KcLdT`On8E}H8} z_4knA%rv`Ps+3Mt(}zWSm*YWDa7Wc^HVqk2t=Ph9Sn5n~PL7uBIPJv!OI3NS)rU4W z>Fy|m?gpg+MHb833SZKF`8|b`b~R#)pxYqps)-qa(=auatrm(fq6~B(#YQemtuoh# zr^yNJvo1->=?Vf34P6$LB@UN(nL{%&C!)FpS5GA6o{mZ-z^c7Pq_sB2lt`Jh6klU3 zc*3M+?KSSF#_g?k{;Y779`&Q5n@Y%jhpJ?@-9|dxE>Kyi8m8FQ(aq$ZkZL)@m5@OQ z=j=E2#svG=Je-jbQ4v8zMFsJsu(nd;88PMKZg^01Eub3x=|$P|IE{hkbAS9p=tMmf z_$i8Htto~X`-g$HjSRji%rsTaKLP};H*9SumQArt?iWSJ80){S$as}T>N%0UgmqJo zy%jCVv>S;$pEnvyaE8#B)RQK$lQ450@Q#bK?0r5Af)O*cTeE$-_xI)9XD{&g^Naew zFzni5LSYHShwPR+x6~ZV+8p$1Luua<# z*n^K4FQtNB(~yK5)m2qhR3X*_*9kHFQ`_kd?F?scCa47Pm>8e(Q=$N^2MTv~^lcHK z`u*3~B068{rB;?FD=p&g2>pnZ(nvdD9zg#g_)XX45g;an^GO`d& zbFx3h;FwI9LL2Lr8fhW-*l`rT_(7}s-8+x(dE@2fiflexdapmDx>YG3*Oqe~ZWDX> zBL~KwIU%i4Zw*Y@Q=NWXTl@_M!LBIEkg03JGIrLpmby6gLE4FaC_U8aB>p8P8lI;iPxn-J%k6G< zecr(8ms*6|;2@0tsWQa$cHdYTLa`|cwMrClyQvT>_D1GsA!=T$@{GE!>_WxiaK0W!& z^>0;c7Ki>oY6mv@e<>XbOTMuLS_Fpa#vKnrqT78KOP|L)_7rjB0j=2l(d7h%NnGAg=_+sZWkv~8 zSw=@7DbUp8TJtgR=>1fdSodH+eknp4?VuU1_}--zf?uKcjY9)i)71#`>MGP#e;=gS zN@$qYFF9yPyt;@@(nc-KF|Lcy)O`OmGf%v0Q!_fCXL6F*t4HGI$ZG4C3kR)-f*~~U zL(uQnE>@;j^y`%>#LvwPDKv4u7PUTKl=2tkA(rjtcd5w}TH1^Cd^d3i40v$Z-%?Pb zBvS;o#H7yD86t@ylgcv&{y#nQv1xNQkFHyf12Sa6nV?{)*QNd((^`sznYJK?kp{3- z!DU88JcaX7SMH(ngsv*lPi59Y#h7~QrzXDJ5K(AP3~&mYMF-F7#L-)|s^~+oZYaRb zacnaiU!fQ^3e49*GswczsZUhCf1MIu=d=9%cGa$1oc}s=D{A*k8i&A3AAS`b^daL9JwmbBb{Pq5&N8G zW-_BKUH%+px4SblKS-u8Bj>xBu1_?UbWDt-PkTLDpYzE;7xI?Q7M$t(hIS@aN18A6* z{U1IV@!!v*Uxfda9px1CGaxL=F(oRC3>>n_qlPj4qBq9!J>&d*S{^MG4$Q!z)Q(t7 z#OT1rj3+R|yU6nhu8B$#aPxK9X=&;>fy$jKL&kjN1j-4MX87*ld|jLEciiBnwm${4 z(+I}LnC)^~T8wOOGB!pw>}}(24R7|! zQx9)Vy?uI{`tKXz)VG%($zT#pP{LM10f(LbBCpu7&3qAHTbpEg(0I(Hju0rOrf=>~E_j%fGsOuPmHphJN zvstY1QpUK)u;OT!wi@R*e$yzMKk{~|>9=mw=WEtaHsrDJ%~|uM7IsSRozroN37XjqMzUt@EFjYoye#)q6HrtKtU!p2juSpM+J0xxBS(_J-Wh~5GR@Ctydu>l*&m{4(L$8A_SVk8@+H*e&<06#Ay>ZP~R_6X*BCUU+N5b2Tkfxk9k(Ky+1= zd9u1C;|#_hM_V&DLmg7oZ_}^c=X1<72X^f3 zoR*qq`Y*3G*gpGH@V%qXzFJtHY~R$36g0g1Yq}kNiPbuhu8ZR~c}4@F5od%rM$<&$ zBZ7|H)12iS)Mu#o>am2$kaUy?XNz2SSGxllTB_-T@4f$lm&|}992Ri&?j1yp&I*j#(6%gsfxi-@)u~y z$}*eV1sI|!sfS!-%@H=_&7fM$N0>x5hpK+`=?AhpM>zL#TupdW%XK&YU929pVku2Z z)iN+(>ctM;YZ`Es4iQWxXplA!*Bdnr$6H;R$R1qq@njmQaP{qjDRL>P{4yRdtmOi)m-u zUxp8D+skTrx$0c%th>i{6Wbc&^Ie!C-%;F1@VAtEZ;N6d^!i7kthjQ!*-j_R9|yf2 z>BSc?eJ~!PIPjJ-O@nA2nM*nSM+ommb3WY+l}tq24Oa9$J9b9Vts3+}xW^gvjWIfH zYI?e38Wx%ii`zqA)9!%QQq@8NFGdy3tP^q3br`3q+}%OCyH!KnAH%~W+4%VWCV?iZ@OEx^9NHPf!KN??pw*_FSbgx5J#m6e zxTRg!dH2#!Q_?C?wN}-t_+=Y4`_$#fqcwxeQ8FP=S*Mx@RyH9c<G%QJr(=w*vs~@M!uki%z0b`{(pZOO;d}BtM7`-a z8ZobRQ_Eg%W@x`a6MuhYa0mD8j4_OFuJ@w4a+Iftu-0tnmh4So1?sZ$Wa(Eof#u3yUGHxZqS2h%IX+@9*+$fb)TO5xbL;iFsgia zx#0CqzCkcfd{4dNl1N)211-F26ZsWcrlSH0^?i27Gee{ zIrDq+x;Ij7xX3j*ebrUa*__Uq^zoO+G_3LE)tZu7ChB^lf z@h-{vtakNZ$XY6MC-OidP>9qJLh50?G`llE=zVAdrZU;A$>lbk(a_Hv+6hI|zLLfa{blG%SeD0tI-(xR!Hk(}Cb zpC&(C`hycgLu00hV;(`Sapmgr>fd)qR|F~weL6%_XNGv38R8h~iK{-`@0AJa=(b)# z!&|m#*iEQvCORNHYs~Uu$u=-b-pA4gyx)z7_hYq{KC%h6Zz6qUTTPNjq;!U6WWa#6 zJ2!uKNql@wu^VRdrm8(3@BB4`ioe0fqE4_}AzdKDaL(7AQ2;l$7wBThxCLg0kaz2B z**-ySYJ(-lBJL;Tz034>7nf8yJq+#B%tU*xwwe=)-T7<@%9)g>hw1H~;P^&3ag}9f zq@Mq0CZDJ9->*f!LJ$>?r;y~^5k_|R^9=YBA3;eDdEaG&X|+~`*HyD-d068=GNrDn zLyMOva@XygnVn$7+~as=&g@|7xie*m@>~0qm??E}H|39H7IekzaJnS2Gw7oKyqG&! zcXFK`>Zl`Lb(O{!lBMsky2{}vrnuvamVAb*KCgoSi(>U25(a8zWH5;w#oMv?sTsJd zq~eJ5BSb9U57WijGP;VXxyYibRJ(by(cuVJqZ_4tJVAp^ghz?%shGvzWi;$MYOBl1 zZu>%WIhwSAk{OS|BsyROm=XpFJ2)iBSYTgTzpDsCMckA%G?fofRmzAg_E8w~K;N%9 zy5XgsnWCh$qOWU|BR*Jo|T7n<}hQ!KU&rGx8im2@kErF+DuI~u)r3&B@C*1Q$=v9 zYb)H6Y<6D_DxKYmj+^p$5Sf-Xye+?zuU{VhVsV_W(-lXqV5^3+JR3lQ=|A>ARgWT^ z2*s68nE{|rV9%PX{FCkIIX=$ELm!+Ov%J=G+CRHZwy>V?d*UKR4{gY0O8Ku=o3`Wh ze>Gn?`ecvdnDa+kew`eC$0lYJU(KGpyXn7lFPmGJzt4={y6PwD!0Th*o-VD|69bGh zj&g_n8b8ov*FN{I_{h43-bj*iTmJdpyc;RLdoqD?CVvVI%RRTeOH#8of z=SM$9MOZ7dwS=Pyn=q!<(N}u1X-dfNlnLmSSX1|MX{v>%kJ(jp7&&2?6b&v`Pw#`* zZx3M4q8m3(X1*hz95xSI;|Zg(&2b@ZKeyEzr#*dyS}V}@tC6ET{3(efq&9d@K@iw? z@w0Q$F2^>Bg2C)t6BCocLf^STBKlnNPia+hx33#G4|{EaPhBHWUb z#II%}=JUcAv)JMwI^hRvtt0EtP3m-SyVo-S$xmzP4z1cD2q&Vrwrb4y7Cx1#y?u|4 zcr-3(<(BYU&}uIwp3YBPbXY-A13NfZXY0ZSia)Ei*ck?`rrj9rPOwZRG&4mYNs+~b zPa(yg2M^Vrs}?jq;>brn5VLmhht6Du2aNp%i!e0=^BlHqQu+DEF+VSw?V5OF`6G;_ zUKrPbxfLg;ZJ(-mGN^W_qS(r%H+Uqj?3MpC*|{;R*4)|L9o^A#V1)y*LI~U~Y}Q*l zJM_lOGTm$G*KUoo+m%@}Gar4;nT~4M&+oL9TJ@+ zl1q4wlFC=Mm(zS~vipT;vZF#dqRh;q<1x*XQBm@mb2ez13q{%Pp~S;z$*nRgrD!Rt zwDPi6#E|F4X`{)ss+#h_c{IXCD=Y^~8=DPToP`+}JgI3_Qg}Ty#|d{%Lr&=_xd*Dq z>7{A4p4nBKj3Ta;A9eeo8~$847JXj_<+|BX;o+ZE^lHap3eGuWJ%MD(yCi7y_@Q5! z>_>4}7ppWoa$!U{Y;$=|xIzHqRY3AhMIWbp3TA6NIZEJua?{M{>yhS=GlkJfx zAPm#ZjqjQ3EToJ*Jk~Y&#Y*!YhQ8K~J8>p2_R@aAj=KQ@r zEy*R86pg3Q-Bz0@C&RU%nn{_R2kabhme@Sl_W7SYR0$}M96*s#&*)WZC@G}^5t=>L zRzp3_V@SDY9k8|5n4rB>-XmvuIM*rm$FrQ6m0HJS9R3XbL!*z=#_m7^mzGiZb@ND8n~oKTwfXo zo;qq2cr^jipi4)R+lZE`E@a7^bkU{v_QrxuD1Sr6tP@5C>GCuR{Z_f!W;AtWcRdnX z@P5y1zR=0v?Q+}bi%Az-!8@PKyrz@o&mkLM>MZSNE{uJgZ269YyX#+OETpO#`{Czj zetPSl?w?_o54__a+Bz}hPIc9f0`_4YL4}2tXlN2~GKEg;r3&ZAXA~%$?6>BfI=wUw zjLgz?svh=e>nzF$fZDsR49@8YSNsw=9R_-?;U z;VC^YDjSwGqrZ!^nmVhu3>0;?7IM>7#_1FgYS)7`P?R_fQS#T|wlpU??TKYZxZ5QX zhDHqvk3WKNY8ifrcw@&W#b}0f?lk8?3499(Q~GeCaYUJrVZOR3p|o_7+TUf|UFAkK z;S4y{O63^6d9zDY@v?=c5USl1>gVp$>}hi4N@-Wo9ASnTQ!{{sj8W| zDm3pqR*j}|J-lBwE|XvCGRMA4p}wii=P!|eCk`z4wXm<hZf_MtAzfve<6IvxovCK{URKxLPBxQ=R5aP@S2em^qie6N>uRqq z)&xqDpMo3F9DL8-=8GSvX{BCY?6iMWVm)>0)^4zEd_KpF<5Sw_G_`u^c96RyJ2PH< z^}@x+RB3x)?^&ee{vVW;7eHaa?N@Smtjej}E3`tU;aVxepOg7=8*@qgw%d6K72lBy zpvm%;GAT7p+gkDvU#{M5R8vH<^v@4W`El7yBD226DSN4M8Xo!WC3bps!$nE!DU+zX zY?$<^JmXVRbx#@&uc4@7RMU-r7W-tk>Z^{tT1_=h9kPuwIPlZ9!g^mxQ?0n$8^!3e z!7=wH!FK1gd1;Fp^9Ss7Q6CEiiqTx8tggGIX^aJq=;_WH8N`yA&l@E8amDwK(}Kq% zS6pq{3{=-h!(`~Mei$%SJmWAi7S1*{m|uMT`s`~HdDoi8iUf_ZXjjJuXkV%rOmzp| zpm0lO>b-jB8|}VDbu|rayPkKFD`zgz9kQ^AY}ycgTk_s`ct-4;e`s6K-J6h~$Co_f z`wM4x#XD$A=VzJJRGayO88Mv&Z2A3Mg@k5ftA2|pX%zC^BCMsEvhief=C-V!#kT0j z3Aelzy%egNk%>WNd`(B4$nAx&T~KLl>#sI_`Lbc$QVQ{nPmecD}!W#zV8L=7y7EP%x!A}>DS}2Hcf9wYuCaX-B-v-rxUtr zq1(D+z24V`_AOC5-L6erynq~@Roqo!zH&-fMb-58Mb3$13b386s#r%g5vDMpe00Ct z7tdr9CmiwTpCG-wkD=umzYz1^O_bLLfK_;he_xxAL(-px$FnyVG|9fNSLZixR{N>; zX$Kfp7j;K~>Xia>pnBls@`M^57`!P%%?#;R1SrX^ndZRdVmnBXMh9Hpm@00MdcykWnG@o5&kt=T|aWRMEp&Di1BfdRt7#-$O zvk}H>#u0CP>j}GN=f$<`JgrvysM~cl;lf{LxzKM>f_Lhv@Z0>GLrYtjN~|iKBQZl= zax9m=JSTc9RZ-WOIiQPL1$#6<>KlH`du-aN$+RV0OIJF21;Tq{aXad#J+pU2p)uUs zo9>J;B{zFiR(mBh7JPDq#Y^+(qW&v0U*U{=PhFncJFt3MeU8LOW{>ws(9Phs8kF{C zhYLMZ$ZVHzdyk9F7ViEdc)eBYs)My$->kStHyVoFwD5H_n=m2xpy37+Xex z%aFMdeieac@TE+Wt^HB=R-y6ONqJY_zF7XeUx)n5>4)P~Y9&71tEhzr365VDuZJJYPU&Bw-mZsf zUl~QSqE$J`ZajBqL8Fh&wW-F7sk;ngMj4D4suQ20t7T)t(Lv*B!jIK0&hGBy-Q@45 zDLQQrcdIC7M0oWxQqi(zobrsU$Cd5pFxD{WQ#hyfl^3_Au#0I{+>p)rkEn&VkJI;6 z+oq~8RaG1!@^_@uEs>s1m|3ZZYc!ic)?KAhcQqkl=~6}Y{MNzxx^mf?Rn>YoK6{{z zCH_s(vc2sG) zKfx30APOT{<(*I?Q^2FWcy&q|Wka4^)#$TB)e7s67Pe-ulm`HcHDn;rgqGfH(q)-Q!IJ#yIi`L zVCJPvZUhw2`H`D5=&cdtq^-5k!9m(h_xV$_T zZYSpWRbDHfjFiV=I_FX3i^H{RyYcw_qs4EN69=*NIgxZq5q9#ouZ`ueMw$__RKeQ3 z2;1e6e4kV-jSy}CoBF?fa)=zrD_d1Cob%yF0zv~rQuOCkOT4fW)o&Sah?^sxvPol! zbW5?;Ya-X#ac?P8Z`Vx%+OC1Br!T0O>J^0#I;6xb)8|?h;W{yc`kLzVuPfcoYyTlY z$u={)KKHS!z5_`!VNps)nE?Ok!wqQ=(EtZJFTow?22blQqNVvvU)a zxYsRPbIEXW^!v>d57$~5nC{W34Jr4?*w+5i@z~VsJ=i*@DhqZ~Cm)q9KO>K2 z&?s-KpLV%dPCmrOj(NZM({?g6PBF2ab!g|eG>PTeBlE|y_Y{WZgiRZ(qJt7jhOAG^ z;UTh}$_P|r2ZR;+X_L%(oS_O{5RqQ!v6fCBsW!NLq8n`;m_6#z$jn#kgKdnq=kRa+ zQ}XEVXwo^h7GI_AgPdZ5az8{ql?27Id8SzWupIAB>;O@k&4$Pb7|{1Qb~NMf#aalvBK(Wr)=sFpgWoT~md$#VKZFd&hiLgIT3>Tp{TpE_@Pk6;e3Ub}fArl!UA@Q69B;0}+e@lUIh;b9hY1?b@F6JSU@00w`o#?Q@@b*m_!YRhO1xR0Dyj|G z4aUnlpC>~fdZiLLVSF0d-3aS(^s8~mB<6xmXr9-soKD-UGtOM4C5S>Qqmk-~Y8X9{ z;cc4;*LFAP;Sh$*(8wnNFqSySBiuVC0vv{ zj+n17v66RI-xudSyKx0{a6hc|i4lgFgPlzV-%Nt+ znU{p?%FePR_k#y}!`RErKAzEUu|mwzm2YaZDA;k-_{p@IHO`W}gwLhojIqkf3~v%4 zqcD2L-FDgK#kbn}>!;ux7NT3uZ8>2uJvb<8;K{sb;oq$B?2ASt;};NB;we3>!N#qb&u7`@ z`z_T?Y^;9w$`%FihdiHCk03zldqe5he1lxhu0wvsadO#Iu;RlayexWY5RD%N+4Q=& z%9y{h=){lW{g7`HR{lZh=cBp3-HzO{D4Mny*cPiV9-fXVN%nXB(rrBJ3Uv5fgjL1O z$tVkqbjuGyqwyoxuzie zqKsdCbMvh-vV^IkY44X@rng3vaz0o*XG58l8(5lGxobbAoGiB$1lx(@x8JY$*)6AO zS=ScbO+(J$_QCGG$hn6n8avIL{koF$H}kubAnZQIza zeBANS|EpFHw~FrL$`_>gi1X{Joa4#~ZN|_3YIW?F}>H zeV>2Uc#-K_lkfL@F}ka)?=v02%tRs^e+(jN=-kLO-XW3yL+N78yfUWu-1o(~5cT7 za4))dNkvcOhwHR*r92;94{{+`$N>@eUN+uQkY~*?RTJ43`1hy0Z zZQqnO;hj1hmmj(vk_47&2K#?=T}yAQ>fF=VR^9)EL(6S^7Ho&39enqdlxBA?wIsMZ zPN}t#e8SYRq`P!%bTZ|{r;c6F(tDMIk5y!J?5C|<0>t-Cw>s-Y9&5@8gC;GPmfHRJ zY{zb1AK65q9%uDJfzjz`9*d+yR$=kU^8w$UKIPrTMTQo0=Z@N_$4n$s*S89KX9d@r z)-kvJyJmlWh~9fZLkTcXXBHCSdsEqTdR zW=WJRxSdki*WeWG5$Hg~(o@X;ar-{lcRm`YlIYDlbHfR#GB;U4PZm0hIvMb~3p6cj zqEe}8w6Yt(=he|xKODQuWE11j2e&-wzIZP^IPjvHXm{_$?4nQBL7liekrhbYR)ng( z+*-uLe^2nnvunI}X>O-F-nq~2OHrhDC$RIC%uAhLrj0$FXzP;D+O_-jU9S5c_mrw$ zMj(w2LQz)dx?K}kLZ5P@R7~VNk}Ws;qJ5}o4f zdK7$_q%G8gfWVK|SErd;$cjmH)^iTQ}V(rDe zIO(0Aq7W(K+l`XU-1{u|Agt2L*n9Tf14BsDI#6$; zy!~0RbC`qWd;C>jU4me%8?ClfmBK8~L^}tj-(cTkHhEE7=;a#5I`jVzJ%lE1bh z33s8%uF+G#bMcm*F7IXKzoJxI&vVaU(AUl5h1As7Rvh00eY6jF!b1RtRur`?WQ0$% zrQ*E>LwXGVu6i7tjmvGv#9e*H1~_Z+ki%O)*X`B@&%_Le&*D&Jn*BIO*;PA@c3qA)X+dFctIam>rw3G637!cYPO}-?i@FyXN^keTI!S9bE<;ws6unl^;rU&i?|(G3Q6xP)m=qpTq8d zf7EY19*!R#yVwKW!}`yGxB0{%xQHzskNZ;HMoIWf1tg36-O@uO9Kc`n`u)c{{Xb^y zKO7%n@41ynjtv_o@PZw1og~E6VK^Lqx_86n__B$c-V?jucj}^TDiA-P=c1pkzf>r0 z*>^doM7`8eAgW(q$d|iO`Ya8_ndE%Vkr9TkJ>X-nHy%9_uzoeM8)R=s@U z+E;u6l-jU63)&%0et3~Q`Z#gNulsuR^xfyxF3+Qvbsh$nF5D{gY}Fid^;-G1KUH~Z z;BR*H_yL;e@e=i6nIFp4qo0OQ+%f zHS9m;MRi+;yhh)5b@TY9Pamsp+EXp`Li2~DZ;>27dF6@VkU04t97aDc#m@$2gdzL7 zDwvU_kPpZ9>S<;Bd>_&O9#5NRKKFOEPr1St-7bnTkF1lTUvHy zIqCQ_%7YoU@uv#wEBZMLAlcF9DDXGm+WT@kY+-y_=)`>grV&EL;RsUSr`PWJed?Kv z*7M%{id|iuoWF0IVYZ)-N5}L7^q~ER53epgC(^lM+Ldh+@jQ}I%H|64M=+T>aVM?d zvhn?-M0h+{wvy!{od!}cGMqNjZUi4C+lCY5T?=*^0==P9}GTfEIteP z3wUbk>csUL{te%DjJvEkryfC75LC11uOG z1EAK3;4~l0ItU6RrDzD2imIxjsF)fiAeKMPiID+y!v>{#W&2RHFYw@EP{=y5W!h3f5d=g@uPYcRT&E1zjh6FV z!di+UAFsK~;~xLv^z-!5E*J`$=GD-BeiQhb%Xq%s3nm)&L3!g;!^CtQ!R-DP-K+Lz zhw5&F$cT7+Sj4~2^5~OpKB9mf(ohaDjq`LP=hAP>uE*oPh+=A}XiMMZKY#YC^-t#D z!tcLjrh}w^aE`Xb*P9-a>#yUNS##I*9C)rY<)=k{i!JC_Jzc|=cS2>;xwoGo`#@3& zwV;9jfc4a74*@`BSbce)^d1r0ltrEzRhO(LT8%FBq@CH_#OUu#I&bN5kEmiAc(f41 zC$5s%i3z;Xj=A;2EjY@o*3dD6(ojf+s-MiMZ(?rK(9SYRIOB`bCU!tR-)5zPT_|vF zHD$D%GNlRC!(A)CxU0@#L00Vwd*W^baD6|^Qx=AG$Cj3X>~;3P!=|dJGGsgFTYe{Rht58>#Z}Q69FHJG1T&wP z-{P`aqdpi*U%g{$`>dNT?{p?CO%HXd$PxWJ_d=5yHD8CvD;YM%(E5?m zdjvQ69AYpMAp|=NS;lW&=k4zN!`@5D&!|Os)``j@Rlr!(`puV-Ip`Pu2HI zeSmiq=*12r13$dSeg4d#V-ol*!IEeo*fBQbQLdv>V@U(c!vNT@Aq}lF?vQ~e@X4`z z?BJLbyObdPoKNscsFff%V=QB#Y0TuY4XViFK898-7@*KI`tSpmxEzo!B~zvp5iCF< z0I@|4k3ZAN^>q9@?{Cw)$@w<=yfkqo=kEDvmz?puZEoh&YY@!ZP~6>wKePMsJRjLl z5T6g#%@g7DYegEIzLzl~VhDh#qEZ5Ah$stIGFD(1P8PK8(@)P7S(FnEO-v;5IMtY; zoD6_u!*O9S1hB+XTd}ICDs3Ys6cQH+Vlq@r8{=7_*`p{Hq@V?2 zMF}cEhM-_0KX$o2v;6YV>apHf>%lnDHYA^jJW5c1=z=?!{;3GjLJs3~dZ&V4==R-B z?_DrRFjt5LX!7Le6gc7|O)P0P8Ic$^QgetDhvtgr{+=-k*Ex_Kw}Q(=bh0-I(xBo1 z=H@}FTUg|Xf7j;*yAp=vddW=&LtnuLNzmRLwHj51G(2<0`M0E80vB(Qm#!(YAr}OKxu?OD9rEBYPMrYtu^+^7wMay=j$lV&KAHw zIC<_^kRut%Rq(8UEBomcW-c-q7-ppX$5$qxzU3RQce_maBUgBnyLbx1a<}~#HOPmx{8c<{(AcOYr8P1B9bJxOB;Rz zdbS!eG9x~YCzP>^vv|tf!UPG88^|HBg*O~zBieuRC;;Gi8%MExi#!C<4a4Pzw7x?+IGnri{Dgy`wa`_ z9f8Ap2Jq#tm@157N0ZG7o-rPPa@4a7dUM!%^jF=>jf@XW0{7;P7V=eAp3`8B!U5GP zBFQ3)ea8Xd8no=p-a@E2J7q1ex-PUN0!0}j>bitWTpU{VAZ-Dt%q0)(xY3#|L1YBr z(cW$X^S`S&F1kUr#ZaFNr*P9F1B>j=Ji78b>O-5cBcV(NG0bHFM-u-%k*bO)qKl5; zHU3WhhZ~~#GBPr**sBIt7(vFM=SX$P##WbIK=B~-$m5DT%W-;D?$O4KZn!qy2+BF+#vzZklAkp${aFG!}f4{~aQ{>^O z*eH0}&v|;;k29UpDUGVLFzKrY%34tm?oXoCFDZ!W2-*fW7G>hj@Zu8&5pX--iFbCw z$oPW;o{CIUPkwZ!c(WU4d&SCu5y}XK)Z8)kA|o3c?rp|1x=uKXpXq;A^S3l@XDIsr z6gGVU9V|%6J&C+}PolUoeR6Te8`a(Zw>I*{?#3}PN2+T2D^}z1dFN%qT zN#UUQHLn22wZwkIYtQ4uTdmm@1e1{ddL~uMCKJ=l)VM-E5r=36ViRGcfx$awsV{IP zrk^!>w)nKG@#~{lYbcNOLeVEOw8=5b36i2Iuuk%;0D=L&r3qGfVy zhNbOS4!5$oomD2~G4m!KF-6wFaW~||Mu_9guD)jPh|D{=OKs186u;q^#wzK&Fvzil zV>gwV0}-()<{?O6IDt+IN-`n1&84uwq^&S`At5G0WQ=Hy{CTD^O2ZpsM#GJeX6mT> z3^KSdaz2~TY{H&g!wkn9!BluvbHIgkZ^5uM#4Oz(!+*kFUgE@VzOm4YH-p^JHW zkK$Nnjx_@DOlzZ!kg@GNKFxTeN*(&COlkMgPXhR%Z*C4-sMh!#9r-vX(U2vt^;Nv0V~xwP=>KA+DG+-iAxfd=i;lGliTHUVd-H`*NFld(eKHV? zJ*UX+qQm=$3rqzA&-uR?N}tL4qRrC5h{E;IY3{q_C=DgF8}oc-lb(?`>hK?V?hW5z z{Ple}++Z7X@>lu8#D4+DeIAV+t?yJy^YJygpNC53=Ga&MAB*XRwT#dmrLRNEr&5(qEoz3WI1RQqw*cLYLZ5He5l&{q3B>~@73zrj3SG&i4=yuJ6 zBHF6JhF-l>i;gN!Y(jDU($|=Ang27hYKKg@an8jJG%#sH&g>rLBXFCi$<_1l_1VSE zPgYU>nR0P)-*vl>@`t{Ct-~If)>;BL8-AkjQpV8N#(+|qQ1LlI~4~!4N1Gs~xnkQL7)n(1G zpFUPa_Jl!UVB;bdQ)U_SWL+3^$RP4>Y`;HJ29J(k)C57=I$?T%m(Jt}MrN5rzkS4W z2>s1AEiLVTLr*xR;y&S}1&bZ$b|^5vQ4iW}3Gtt(h+u8iX}0!{Q?sk7g&bXws7*RU zZ7rov1#ki8=*Y9UoaDk!sx6vZJK_z80~Fjww-@&ZK&IHbHKS!;^Yf5kN-ggsA7QU= zWELl2a|Jn*Ga41{+-#;FM98p7wF~>Fpg}SGa!b}{cEdvYo-^|VAtVbX z=yRSwvn4DYpTNv-zwUE86bf!p1B@6a zB53M0v$%PtBVH^xXiU<>emyt4VR33n9N^}4LQQ|3Q^Q)QjtM#SXl5a-5zFg5GyT5* zcK6|kPA3ryMN%=6L6Sr-=;71JjH1!|zh&*qnY;YoW1Ikw>-H!po^1r z63$TLVGu0c+xC8MmVK?lb+gc0IITlNzYrXVNtb# z;|#g(<(4XCbh21Hhnf9;rk}o>SEBd_^dH0qkmC(oGvQB{cayQ0-6l6`c0B^|F17w0 zjO>?*FmqJs;}~mN0m^x;(tCnMz9IDvR1Dd3O0k(&GmGo&?M&-P8`!p7aF_xC2B*+b z-dn7(LFofVibKyb&>`vMx5NA^ER%9(6vObP7(lSaJvN~=oIM8J$$ZRC6UvfUFtDld zA*N*u17Egf+5pH{zJOu-?*_r9X#5oYEerAeF3d?93!klVptw!wVaGA4oC>$O1R&_G zjV+yYBC_+zVftEaHkD*BYABwo z;I&amX$MvnJyXUTdNRzo@P3aRx@T*AZ6leGe9OaZnGi<7%7jw7U*ooA5Lf=YoWwXe zrV8F?1p@#E26Ft^I&S=snz<`NA(eMPIz?=LvInxt7_mUV-#|CSIeTp{mL26pPs37- z{y*9LXL+W)O<|SxpFL#B{*`)blLy=TJL=yVQS`j2tE!`S zW##t|2^d&(ZS9X{W`*(VsCRbzB24_mQJp$jOn)?=*6`oPf;KtQo9j$PQqJuUQ=gSb z;G1;C(V+(#VjsD~nQG010O3JdZjN#3E!qC3+s%jimlvu&wUaf75^Pfrj3}>@R3*lb zeKRx=+mi-8Pim`1P=&`m5r*l<8G1)!m+SiX+^MHP$cYsmXu>dPz;|?xEpISBm4X<$ zyxALN*^u`dJFe625I4tYS)PcF$Ev(Bk*^#Q+)lmS)C8*qF)Us3m5H#FiC$KzW@fzV zV!{IZ+C2H`C-7l@aL8r*0uHD<6P{sAhf{|>=N^;nOz^0tG^TB1I{X}t1@33u==6zs1rTygr${~Z;#Cx`tIfl|BaA$5j%*N=thk0pHtu4kaZZug2e z^GgBl(_@RkFrHp33QTdF&ikhl?KXOiHhk{~(VA~R}?bO7y(5vlsHHZ`u4-U zE^{e7^z)MbJ)EW(el4}Qn>1Bjy_1q1&}t#UOjxt$i~~%cm`6I#$o_P8A}AedN90YY z{m08*Usvu-b?Vxr)zBZMEPiJvujYgs!YUU%Y(0H551#(S>J1Mhf6*yz4QLs|hbI#O zsw44BlIOY*d20Bv|CPVh)Oxy%2$qevnOXacDNM>e8B*n`^~a-4G7r-y=iNE^Hhmh- zo#X;G^t~qO7uLK}ca`bYi z7MOdC|7XVSt8Doy*E=ARTK+0NCSj4f!^d@bW$87MsW-T(W$&dP8nfEe)#yhvL_N*& z>W!KJ*~xC}EYm>)&s&w3F;vuH?)BwSX<=JhnV86dnuxLI%ussC$UCVy=*RpF3JntW zKfee<3KrXM?)v^)_8fufJjb9929CO~V*3~hu_ls3G1b#a+2Vh{$fufK4qg(vss8u! z;bb*t(*o+b_Wn+-d;Ets;)CWR6@N4r+3I*UePhlc2K@=52f`qPY9UPd1OTX5fTsN) z!I#a+=k3nhOi8oBwQ)XKyJqWaYE+fJ&uhj~s;H6%OqGyGN~1HLkE-9IOi|&^4%Oe7 ze>*h3m-l{T~^w18q40fhu-r9U^sN2>fC>85<1 z%VTQ}Q$WQ!)N^B4Q;7oo9=VN0xj4m^%Eoa+)mtN&8j#NUz}{-}P&ZPW8}Gk=X3CJ?!wu-hTQBe3*i9kcKT)mWp8u6gV$n z;$R1PV;BYlJp}mta4#Z?4iQq~LCOW;Xal-BQxU*pmY+$x#WdGhY)G|qU+6;-3^+M- zi7RXq4QTl2j1=;AAR{=N0#29pxsf_-aLjbLh6KqaL@|Y9kuj0~h5dt9KQ{0L+7XQ{ z?A-@~aZ{QV>_yVSe8BW(VWDR8HSA+W>8dELNZI$h+*U0uBkq=%LoOn&4x#m$9Q_Q z;Al2ty<+JHa-}LUJ2*IYbDI$3h^BB|Fdy!pwx|b5=a7j> zb05&Yo^pfizFo0uKYf32HYdaUV-7kT4l8s_GX&avQDm|p+`4!kDlnV+)}mI{<|#V!clBE`u`+!)NDSOeY5u3cs3;nU0+qMCiOMU?9_N1 zn8E5jjZ~Fnd=2xSAIIjtIDgR|Uqk;3+Yo4Kc7y~;HeZb-hI26F5j~dqDyqj*`_59L zO;fc^InKP(E$rmqvQ2wV_N640VYV6GD_mK%A}u0?0>!~atfbw)Qp$vj9n0tM{gES6 zeEUmtaQyaat$~=hH8|QfE!Onl(9{Y!M`M0pRR4qa`lBCQqaUmHQR@Cu^DKO=RS9O& zjT&PZ=zq6gJ3k#wX=sT$ea^wbOZjqm^de27s!HX6(WhgD?e|1Roc7e&7~T2Z&f2Zs zl~?d+lnY@#z4PzX`dfo$wyp8cdgk&`!c=h6-=2!-3~NKlln1NE=_Hh=I7K9qRz=3L z8TUUIU-s7lQ?zy~4i83<%)}?nuQZv7Qr~%_(X++k;Z7#FLD*)+2Hfwv&1JNu`)5J= z4vuN=vu?(bS5%X7<36}1^#Lb9?)2}g3;a5es*{g`@z8iaAUOZp8ztm@ArbHqL=H-(9>%bjyWOB~TLBJ2}%yGM)rz1Sm1(WBK>*psFZt-Fw#P`-TX z3|@x1Tl0DHer}@5?>_r&#J;fiQ7;pWY!vYTa?08HT<2PKQ;XqDCSheByIMS$)SG6? z$j+<5c{R~hn>lJJwRt|tOF#ADW;GL4((>}k0;^xss=3T|=7$_^k`>+_({)(K96n3F zU3Xo57n1YrgqGUJrS0DiS#s@FG^lDRBYCdM+MDRAfr`c-4VtWmgVD}al4}{2)>50R zqCZ7v-J7F@Q+)p)$X^YIQfAJ0cR^^~IYWwiiWXZJ<@XnjQM~k@K8D4pEvI!u9}k<8 zo3XE9GHAb#_&%0V4}h8TmVOm=pBu6gFZYg3XYvV67deUwk;L({=Yx5K#$$}~eO5rs z=Vc-LBKN?{p{6ktoZCZ5)e*RT9(T(1WMH41H~t^x54B>4bbxHNC6ZG?nx9Dlx!h9` zQ5e`nEEP}@i=02(U)paTU!UGj(q?-;AAd#lPgJUf8gI3Kq7CN!c@yYz3{&>RVUwi@ ze_XNuB~gSMf8YIv8$YKcf5s>F)HyC(-eMebgz$NPD7eA?ukd98pD4 zMEkku=>D7Z{fvH(vi@BC=(3yk6H-*&#x^@qicE^RBdu3#fKlT98vM-Ozi<0(^La$k z8Mf}+c%sU0Z(2>KrkKGJK~;QA+;wp}1%%GhAfq(i5qKlH4*> zjW()~Sc%-&;rC3BV0x|}S4y<1_obHSdVm=SgAO1CwgFnt+uE z88AWxLRm;;!U$n#0yHMTq2nAw$IA5{zgC<#=+-&sU5ipYVm)u@*>I?QGuthijav~h zhqtU2Er-R(P7j+KwOoD4{4r+xkMzLokiVrD8MGiM@qSA2ip7n1PY(c3)8bDhgag{E z-x#9B&o%jP;Sr`Ln3`fLiunQ8)8_OC

i69&#c4G@m5Joc$wMd__e;MW5BJsxpcF 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

-KyQKexNd1LgPs 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�xZ69K|V2fj$z*?QLH-3=5Gr=EG(caI%<uoPC~v!}4I^|)Mrl4Ae2K2WGq>+Do01#S#NPl| zY@;liJc)6#QECIHr~u_ZB72syo`Vm3HcX@xQ4$b@q*Vz(O$0`aXg_g(M-DXpt=p&u z(4(1#ikgQHoIcYE)0hNP3|mv$Cq#b@9>qaD{k-LQ9$_4NddQr^+9L=EbT>bog@^5l zuOLUs6*Mj7-}Eq%NCZYKB@rwY5fI9S2o$715itS-@yMo4DlSK@Pm{Si;y@B+IN${U z%&{+gzkUTW2g`+fgS_&Bldycj6jd}4B_#;egrluNKe0hOFjH^2P>1X8px1tzmkR+* zDnAkTjnGx>fF=9o6#Ny)vD5X}ujToDn@mYl!VZ!7Vu%P$D#S#y6IJ)@WXUjTzWX&& zOcvA;DTzv@Xkuk{5Ojn6e|761d>7fo{-gO1#?WODF<*hv0s%JuRKfaphN%J}gSepZ z+##Gozyy<4iWsL<6f+Nw1NH?fV86uuI&?`f3kwJLWL%I52$5nCU}{}Q$KU1-|F_}w z-25Ke;@Qh&|4cCYa+btX?}ESN^w0-}b{h0Se|y#Hk_OpEGn203B1@txlhRZd9FmSW zIOjCw5QPLjC W$Uy$lLcofD{}*yaI8cx)b%oozGdUpu diff --git a/packaging/gitmodules.sh b/packaging/gitmodules.sh deleted file mode 100755 index 1de5a67..0000000 --- a/packaging/gitmodules.sh +++ /dev/null @@ -1,25 +0,0 @@ -#! /bin/sh -# @author: Philippe Coval -# @description: manage git submodules with git-build-package-rpm - -set -x -set -e - -cat .gitmodules || return 1 - - -git submodule status | awk '{ print $2 }' | while read dir ; do - name=$(basename "$dir" ) - echo "name=" - echo "dir=$dir" - git submodule init - git submodule update - - tar cjvf "./packaging/${name}.tar.bz2" "${dir}" - - cat<