From 6cd9932780125c6f35809d2e3f17dec7d7cd184c Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Tue, 16 Dec 2014 11:20:42 +0000 Subject: [PATCH] From Julen Garcia, "Here there is a small plugin I use to play video files. It is based on GStreamer http://gstreamer.freedesktop.org and I have used the FFmpeg plugin as inspiration." From Robert Osfield, fixed handled of row widths so that they are padded to a 4 byte boundary as certain row widths were being rendered incorrectly. git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14604 16af8721-9629-0410-8352-f15c8da7e697 --- CMakeLists.txt | 2 + CMakeModules/FindGLIB.cmake | 136 +++++++++++ CMakeModules/FindGStreamer.cmake | 174 ++++++++++++++ src/osgPlugins/CMakeLists.txt | 4 + src/osgPlugins/gstreamer/CMakeLists.txt | 24 ++ .../gstreamer/GStreamerImageStream.cpp | 221 ++++++++++++++++++ .../gstreamer/GStreamerImageStream.hpp | 53 +++++ .../gstreamer/ReaderWriterGStreamer.cpp | 77 ++++++ 8 files changed, 691 insertions(+) create mode 100644 CMakeModules/FindGLIB.cmake create mode 100644 CMakeModules/FindGStreamer.cmake create mode 100644 src/osgPlugins/gstreamer/CMakeLists.txt create mode 100644 src/osgPlugins/gstreamer/GStreamerImageStream.cpp create mode 100644 src/osgPlugins/gstreamer/GStreamerImageStream.hpp create mode 100644 src/osgPlugins/gstreamer/ReaderWriterGStreamer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 13fbadd6f..556e02322 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,6 +624,8 @@ ELSE() FIND_PACKAGE(LibVNCServer) FIND_PACKAGE(OurDCMTK) FIND_PACKAGE(FFmpeg) + FIND_PACKAGE(GStreamer COMPONENTS gstreamer-app gstreamer-pbutils) + FIND_PACKAGE(GLIB COMPONENTS gobject) FIND_PACKAGE(DirectShow) FIND_PACKAGE(SDL) FIND_PACKAGE(Poppler-glib) diff --git a/CMakeModules/FindGLIB.cmake b/CMakeModules/FindGLIB.cmake new file mode 100644 index 000000000..dce2225a5 --- /dev/null +++ b/CMakeModules/FindGLIB.cmake @@ -0,0 +1,136 @@ +# - Try to find Glib and its components (gio, gobject etc) +# Once done, this will define +# +# GLIB_FOUND - system has Glib +# GLIB_INCLUDE_DIRS - the Glib include directories +# GLIB_LIBRARIES - link these to use Glib +# +# Optionally, the COMPONENTS keyword can be passed to find_package() +# and Glib components can be looked for. Currently, the following +# components can be used, and they define the following variables if +# found: +# +# gio: GLIB_GIO_LIBRARIES +# gobject: GLIB_GOBJECT_LIBRARIES +# gmodule: GLIB_GMODULE_LIBRARIES +# gthread: GLIB_GTHREAD_LIBRARIES +# +# Note that the respective _INCLUDE_DIR variables are not set, since +# all headers are in the same directory as GLIB_INCLUDE_DIRS. +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS 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 ITS +# 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. + +if (WIN32) + find_library(GLIB_LIBRARIES + NAMES glib-2.0 + PATHS C:/gstreamer/1.0/x86_64/lib + ) + +else () + find_package(PkgConfig) + pkg_check_modules(PC_GLIB QUIET glib-2.0) + + find_library(GLIB_LIBRARIES + NAMES glib-2.0 + HINTS ${PC_GLIB_LIBDIR} + ${PC_GLIB_LIBRARY_DIRS} + ) +endif () + +# Files in glib's main include path may include glibconfig.h, which, +# for some odd reason, is normally in $LIBDIR/glib-2.0/include. +get_filename_component(_GLIB_LIBRARY_DIR ${GLIB_LIBRARIES} PATH) + +find_path(GLIBCONFIG_INCLUDE_DIR + NAMES glibconfig.h + HINTS ${PC_LIBDIR} ${PC_LIBRARY_DIRS} ${_GLIB_LIBRARY_DIR} + PATH_SUFFIXES glib-2.0/include +) + +if (WIN32) + find_path(GLIB_INCLUDE_DIR + NAMES glib.h + PATHS C:/gstreamer/1.0/x86_64/include + PATH_SUFFIXES glib-2.0 + ) +else() + find_path(GLIB_INCLUDE_DIR + NAMES glib.h + HINTS ${PC_GLIB_INCLUDEDIR} + ${PC_GLIB_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0 + ) +endif() + +set(GLIB_INCLUDE_DIRS ${GLIB_INCLUDE_DIR} ${GLIBCONFIG_INCLUDE_DIR}) + +# Version detection +file(READ "${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h" GLIBCONFIG_H_CONTENTS) +string(REGEX MATCH "#define GLIB_MAJOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") +set(GLIB_VERSION_MAJOR "${CMAKE_MATCH_1}") +string(REGEX MATCH "#define GLIB_MINOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") +set(GLIB_VERSION_MINOR "${CMAKE_MATCH_1}") +string(REGEX MATCH "#define GLIB_MICRO_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") +set(GLIB_VERSION_MICRO "${CMAKE_MATCH_1}") +set(GLIB_VERSION "${GLIB_VERSION_MAJOR}.${GLIB_VERSION_MINOR}.${GLIB_VERSION_MICRO}") + +# Additional Glib components. We only look for libraries, as not all of them +# have corresponding headers and all headers are installed alongside the main +# glib ones. +foreach (_component ${GLIB_FIND_COMPONENTS}) + if (${_component} STREQUAL "gio") + find_library(GLIB_GIO_LIBRARIES NAMES gio-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GIO_LIBRARIES) + elseif (${_component} STREQUAL "gobject") + find_library(GLIB_GOBJECT_LIBRARIES NAMES gobject-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GOBJECT_LIBRARIES) + elseif (${_component} STREQUAL "gmodule") + find_library(GLIB_GMODULE_LIBRARIES NAMES gmodule-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GMODULE_LIBRARIES) + elseif (${_component} STREQUAL "gthread") + find_library(GLIB_GTHREAD_LIBRARIES NAMES gthread-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GTHREAD_LIBRARIES) + elseif (${_component} STREQUAL "gio-unix") + # gio-unix is compiled as part of the gio library, but the include paths + # are separate from the shared glib ones. Since this is currently only used + # by WebKitGTK+ we don't go to extraordinary measures beyond pkg-config. + pkg_check_modules(GIO_UNIX QUIET gio-unix-2.0) + endif () +endforeach () + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLIB REQUIRED_VARS GLIB_INCLUDE_DIRS GLIB_LIBRARIES ${ADDITIONAL_REQUIRED_VARS} + VERSION_VAR GLIB_VERSION) + +mark_as_advanced( + GLIBCONFIG_INCLUDE_DIR + GLIB_GIO_LIBRARIES + GLIB_GIO_UNIX_LIBRARIES + GLIB_GMODULE_LIBRARIES + GLIB_GOBJECT_LIBRARIES + GLIB_GTHREAD_LIBRARIES + GLIB_INCLUDE_DIR + GLIB_INCLUDE_DIRS + GLIB_LIBRARIES +) diff --git a/CMakeModules/FindGStreamer.cmake b/CMakeModules/FindGStreamer.cmake new file mode 100644 index 000000000..e07789d0f --- /dev/null +++ b/CMakeModules/FindGStreamer.cmake @@ -0,0 +1,174 @@ +# - Try to find GStreamer and its plugins +# Once done, this will define +# +# GSTREAMER_FOUND - system has GStreamer +# GSTREAMER_INCLUDE_DIRS - the GStreamer include directories +# GSTREAMER_LIBRARIES - link these to use GStreamer +# +# Additionally, gstreamer-base is always looked for and required, and +# the following related variables are defined: +# +# GSTREAMER_BASE_INCLUDE_DIRS - gstreamer-base's include directory +# GSTREAMER_BASE_LIBRARIES - link to these to use gstreamer-base +# +# Optionally, the COMPONENTS keyword can be passed to find_package() +# and GStreamer plugins can be looked for. Currently, the following +# plugins can be searched, and they define the following variables if +# found: +# +# gstreamer-app: GSTREAMER_APP_INCLUDE_DIRS and GSTREAMER_APP_LIBRARIES +# gstreamer-audio: GSTREAMER_AUDIO_INCLUDE_DIRS and GSTREAMER_AUDIO_LIBRARIES +# gstreamer-fft: GSTREAMER_FFT_INCLUDE_DIRS and GSTREAMER_FFT_LIBRARIES +# gstreamer-pbutils: GSTREAMER_PBUTILS_INCLUDE_DIRS and GSTREAMER_PBUTILS_LIBRARIES +# gstreamer-video: GSTREAMER_VIDEO_INCLUDE_DIRS and GSTREAMER_VIDEO_LIBRARIES +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS 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 ITS +# 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. + +# Helper macro to find a GStreamer plugin (or GStreamer itself) +# _component_prefix is prepended to the _INCLUDE_DIRS and _LIBRARIES variables (eg. "GSTREAMER_AUDIO") +# _pkgconfig_name is the component's pkg-config name (eg. "gstreamer-1.0", or "gstreamer-video-1.0"). +# _header is the component's header, relative to the gstreamer-1.0 directory (eg. "gst/gst.h"). +# _library is the component's library name (eg. "gstreamer-1.0" or "gstvideo-1.0") +#macro(FIND_GSTREAMER_COMPONENT _component_prefix _pkgconfig_name _header _library) +# pkg_check_modules(PC_${_component_prefix} QUIET ${_pkgconfig_name}) +# +# find_path(${_component_prefix}_INCLUDE_DIRS +# NAMES ${_header} +# HINTS ${PC_${_component_prefix}_INCLUDE_DIRS} ${PC_${_component_prefix}_INCLUDEDIR} +# PATH_SUFFIXES gstreamer-1.0 +# ) +# +# find_library(${_component_prefix}_LIBRARIES +# NAMES ${_library} +# HINTS ${PC_${_component_prefix}_LIBRARY_DIRS} ${PC_${_component_prefix}_LIBDIR} +# ) +#endmacro() + +if (WIN32) + macro(FIND_GSTREAMER_COMPONENT _component_prefix _pkgconfig_name _header _library) + find_path(${_component_prefix}_INCLUDE_DIRS + NAMES ${_header} + PATHS C:/gstreamer/1.0/x86_64/include + PATH_SUFFIXES gstreamer-1.0 + ) + + find_library(${_component_prefix}_LIBRARIES + NAMES ${_library} + PATHS C:/gstreamer/1.0/x86_64/lib + ) + endmacro() +else () + + find_package(PkgConfig) + + macro(FIND_GSTREAMER_COMPONENT _component_prefix _pkgconfig_name _header _library) + pkg_check_modules(PC_${_component_prefix} QUIET ${_pkgconfig_name}) + + find_path(${_component_prefix}_INCLUDE_DIRS + NAMES ${_header} + HINTS ${PC_${_component_prefix}_INCLUDE_DIRS} ${PC_${_component_prefix}_INCLUDEDIR} + PATH_SUFFIXES gstreamer-1.0 + ) + + find_library(${_component_prefix}_LIBRARIES + NAMES ${_library} + HINTS ${PC_${_component_prefix}_LIBRARY_DIRS} ${PC_${_component_prefix}_LIBDIR} + ) + endmacro() +endif () + +# ------------------------ +# 1. Find GStreamer itself +# ------------------------ + +# 1.1. Find headers and libraries +FIND_GSTREAMER_COMPONENT(GSTREAMER gstreamer-1.0 gst/gst.h gstreamer-1.0) +#FIND_GSTREAMER_COMPONENT(GSTREAMER_BASE gstreamer-base-1.0 gst/gst.h gstbase-1.0) + + +# 1.2. Check GStreamer version +if (GSTREAMER_INCLUDE_DIRS) + if (EXISTS "${GSTREAMER_INCLUDE_DIRS}/gst/gstversion.h") + file(READ "${GSTREAMER_INCLUDE_DIRS}/gst/gstversion.h" GSTREAMER_VERSION_CONTENTS) + + string(REGEX MATCH "#define +GST_VERSION_MAJOR +\\(([0-9]+)\\)" _dummy "${GSTREAMER_VERSION_CONTENTS}") + set(GSTREAMER_VERSION_MAJOR "${CMAKE_MATCH_1}") + + string(REGEX MATCH "#define +GST_VERSION_MINOR +\\(([0-9]+)\\)" _dummy "${GSTREAMER_VERSION_CONTENTS}") + set(GSTREAMER_VERSION_MINOR "${CMAKE_MATCH_1}") + + string(REGEX MATCH "#define +GST_VERSION_MICRO +\\(([0-9]+)\\)" _dummy "${GSTREAMER_VERSION_CONTENTS}") + set(GSTREAMER_VERSION_MICRO "${CMAKE_MATCH_1}") + + set(GSTREAMER_VERSION "${GSTREAMER_VERSION_MAJOR}.${GSTREAMER_VERSION_MINOR}.${GSTREAMER_VERSION_MICRO}") + endif () +endif () + + +if ("${GStreamer_FIND_VERSION}" VERSION_GREATER "${GSTREAMER_VERSION}") + message(FATAL_ERROR "Required version (" ${GStreamer_FIND_VERSION} ") is higher than found version (" ${GSTREAMER_VERSION} ")") +endif () + +# ------------------------- +# 2. Find GStreamer plugins +# ------------------------- + +FIND_GSTREAMER_COMPONENT(GSTREAMER_APP gstreamer-app-1.0 gst/app/gstappsink.h gstapp-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_AUDIO gstreamer-audio-1.0 gst/audio/audio.h gstaudio-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_FFT gstreamer-fft-1.0 gst/fft/gstfft.h gstfft-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_PBUTILS gstreamer-pbutils-1.0 gst/pbutils/pbutils.h gstpbutils-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_VIDEO gstreamer-video-1.0 gst/video/video.h gstvideo-1.0) + +# ------------------------------------------------ +# 3. Process the COMPONENTS passed to FIND_PACKAGE +# ------------------------------------------------ +set(_GSTREAMER_REQUIRED_VARS GSTREAMER_INCLUDE_DIRS GSTREAMER_LIBRARIES GSTREAMER_VERSION GSTREAMER_BASE_INCLUDE_DIRS GSTREAMER_BASE_LIBRARIES) + +foreach (_component ${GStreamer_FIND_COMPONENTS}) + set(_gst_component "GSTREAMER_${_component}") + string(TOUPPER ${_gst_component} _UPPER_NAME) + + list(APPEND _GSTREAMER_REQUIRED_VARS ${_UPPER_NAME}_INCLUDE_DIRS ${_UPPER_NAME}_LIBRARIES) +endforeach () + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GStreamer REQUIRED_VARS _GSTREAMER_REQUIRED_VARS + VERSION_VAR GSTREAMER_VERSION) + +mark_as_advanced( + GSTREAMER_APP_INCLUDE_DIRS + GSTREAMER_APP_LIBRARIES + GSTREAMER_AUDIO_INCLUDE_DIRS + GSTREAMER_AUDIO_LIBRARIES + GSTREAMER_BASE_INCLUDE_DIRS + GSTREAMER_BASE_LIBRARIES + GSTREAMER_FFT_INCLUDE_DIRS + GSTREAMER_FFT_LIBRARIES + GSTREAMER_INCLUDE_DIRS + GSTREAMER_LIBRARIES + GSTREAMER_PBUTILS_INCLUDE_DIRS + GSTREAMER_PBUTILS_LIBRARIES + GSTREAMER_VIDEO_INCLUDE_DIRS + GSTREAMER_VIDEO_LIBRARIES +) \ No newline at end of file diff --git a/src/osgPlugins/CMakeLists.txt b/src/osgPlugins/CMakeLists.txt index 66c3f055c..c6db5a5e9 100644 --- a/src/osgPlugins/CMakeLists.txt +++ b/src/osgPlugins/CMakeLists.txt @@ -200,6 +200,10 @@ IF(FFMPEG_FOUND AND OSG_CPP_EXCEPTIONS_AVAILABLE) ADD_SUBDIRECTORY(ffmpeg) ENDIF() +IF(GSTREAMER_FOUND AND GLIB_FOUND) + ADD_SUBDIRECTORY(gstreamer) +ENDIF() + IF(DIRECTSHOW_FOUND) ADD_SUBDIRECTORY(directshow) ENDIF() diff --git a/src/osgPlugins/gstreamer/CMakeLists.txt b/src/osgPlugins/gstreamer/CMakeLists.txt new file mode 100644 index 000000000..8cfb5622e --- /dev/null +++ b/src/osgPlugins/gstreamer/CMakeLists.txt @@ -0,0 +1,24 @@ +INCLUDE_DIRECTORIES( + ${GSTREAMER_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS} +) + +SET(TARGET_EXTERNAL_LIBRARIES + ${GSTREAMER_LIBRARIES} + ${GSTREAMER_APP_LIBRARIES} + ${GSTREAMER_PBUTILS_LIBRARIES} + ${GLIB_LIBRARIES} + ${GLIB_GOBJECT_LIBRARIES} +) + +SET(TARGET_SRC + GStreamerImageStream.cpp + ReaderWriterGStreamer.cpp +) + +SET(TARGET_H + GStreamerImageStream.hpp +) + +#### end var setup ### +SETUP_PLUGIN(gstreamer) diff --git a/src/osgPlugins/gstreamer/GStreamerImageStream.cpp b/src/osgPlugins/gstreamer/GStreamerImageStream.cpp new file mode 100644 index 000000000..fd1469f2c --- /dev/null +++ b/src/osgPlugins/gstreamer/GStreamerImageStream.cpp @@ -0,0 +1,221 @@ +#include "GStreamerImageStream.hpp" + +namespace osgGStreamer { + +GStreamerImageStream::GStreamerImageStream() +{ + setOrigin(osg::Image::TOP_LEFT); + + loop = g_main_loop_new(NULL, FALSE); +} + +GStreamerImageStream::GStreamerImageStream(const GStreamerImageStream & image, const osg::CopyOp & copyop) : + osg::ImageStream(image, copyop) +{ + // TODO: probably incorrect or incomplete +} + +GStreamerImageStream::~GStreamerImageStream() +{ + OSG_INFO<<"Destructing GStreamerImageStream..."< 0 ) + has_audio_stream = true; + + gst_discoverer_info_unref(info); + g_free(uri); + } + + // build pipeline + + gchar *audio_pipe = ""; + + if( has_audio_stream ) + audio_pipe = "deco. ! queue ! audioconvert ! autoaudiosink"; + + gchar *string = g_strdup_printf("filesrc location=%s ! \ + decodebin name=deco \ + deco. ! queue ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink emit-signals=true \ + %s", filename.c_str(), audio_pipe); + + pipeline = gst_parse_launch(string, &error); + + if( pipeline == NULL ) + { + g_printerr("Error: %s\n", error->message); + return false; + } + + g_free(string); + g_error_free(error); + + // bus + + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); + + gst_bus_add_watch(bus, (GstBusFunc)on_message, this); + + gst_object_unref(bus); + + + // sink + + GstElement *sink = gst_bin_get_by_name(GST_BIN(pipeline), "sink"); + + g_signal_connect(sink, "new-sample", G_CALLBACK(on_new_sample), this); + g_signal_connect(sink, "new-preroll", G_CALLBACK(on_new_preroll), this); + + gst_object_unref(sink); + + gst_element_set_state(pipeline, GST_STATE_PAUSED); + gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); // wait until the state changed + + //setPixelBufferObject(new osg::PixelBufferObject(this)); // can help with the performance + setImage(width, height, 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, internal_buffer, osg::Image::NO_DELETE); + + start(); + + return true; +} + +//** Controls ** + +void GStreamerImageStream::play() +{ + gst_element_set_state(pipeline, GST_STATE_PLAYING); +} + +void GStreamerImageStream::pause() +{ + gst_element_set_state(pipeline, GST_STATE_PAUSED); +} + +void GStreamerImageStream::rewind() +{ + gst_element_seek_simple(pipeline, GST_FORMAT_TIME, GstSeekFlags(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), 0); +} + +void GStreamerImageStream::seek(double time) +{ + gst_element_seek_simple(pipeline, GST_FORMAT_TIME, GstSeekFlags(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), time * GST_MSECOND); +} + +int GStreamerImageStream::s() const +{ + return width; +} + +int GStreamerImageStream::t() const +{ + return height; +} + +//** Callback implementations ** + +GstFlowReturn GStreamerImageStream::on_new_sample(GstAppSink *appsink, GStreamerImageStream *user_data) +{ + // get the buffer from appsink + + GstSample *sample = gst_app_sink_pull_sample(appsink); + GstBuffer *buffer = gst_sample_get_buffer(sample); + + // upload data + + GstMapInfo info; + gst_buffer_map(buffer, &info, GST_MAP_READ); + gst_buffer_extract(buffer, 0, user_data->internal_buffer, info.size); + + OSG_NOTICE<<"on_new_sample("<<(user_data->width)<<", "<<(user_data->height)<<")"<setImage(user_data->width, user_data->height, 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, user_data->internal_buffer, osg::Image::NO_DELETE, 4); + + // clean resources + + gst_buffer_unmap(buffer, &info); + gst_sample_unref(sample); + + return GST_FLOW_OK; +} + +GstFlowReturn GStreamerImageStream::on_new_preroll(GstAppSink *appsink, GStreamerImageStream *user_data) +{ + // get the sample from appsink + + GstSample *sample = gst_app_sink_pull_preroll(appsink); + + // get sample info + + GstCaps *caps = gst_sample_get_caps(sample); + GstStructure *structure = gst_caps_get_structure(caps, 0); + + int width; + int height; + + gst_structure_get_int(structure, "width", &width); + gst_structure_get_int(structure, "height", &height); + + user_data->width = width; + user_data->height = height; + + int row_width = width*3; + if ((row_width%4)!=0) + { + OSG_NOTICE<<"Rounding up row width from "<internal_buffer = (unsigned char*)malloc(sizeof(unsigned char)*row_width*height); + + OSG_NOTICE<<"on_new_preroll("<<(user_data->width)<<", "<<(user_data->height)<<")"<rewind(); + } + + return TRUE; +} + +void GStreamerImageStream::run() +{ + g_main_loop_run(loop); +} + +} // namespace osgGStreamer diff --git a/src/osgPlugins/gstreamer/GStreamerImageStream.hpp b/src/osgPlugins/gstreamer/GStreamerImageStream.hpp new file mode 100644 index 000000000..bd89c45e5 --- /dev/null +++ b/src/osgPlugins/gstreamer/GStreamerImageStream.hpp @@ -0,0 +1,53 @@ +#ifndef HEADER_GUARD_OSGGSTREAMER_GSTREAMER_IMAGE_STREAM_H +#define HEADER_GUARD_OSGGSTREAMER_GSTREAMER_IMAGE_STREAM_H + +#include +#include + +#include + +#include + +namespace osgGStreamer { + + class GStreamerImageStream : public osg::ImageStream, public OpenThreads::Thread + { + public: + + GStreamerImageStream(); + GStreamerImageStream(const GStreamerImageStream & image, const osg::CopyOp & copyop = osg::CopyOp::SHALLOW_COPY); + + META_Object(osgGStreamer, GStreamerImageStream); + + bool open(const std::string &filename); + + virtual void play(); + virtual void pause(); + virtual void rewind(); + virtual void seek(double time); + + virtual int s() const; + virtual int t() const; + + private: + + virtual ~GStreamerImageStream(); + + static gboolean on_message(GstBus *bus, GstMessage *message, GStreamerImageStream *user_data); + + static GstFlowReturn on_new_sample(GstAppSink *appsink, GStreamerImageStream *user_data); + static GstFlowReturn on_new_preroll(GstAppSink *appsink, GStreamerImageStream *user_data); + + virtual void run(); + + GMainLoop *loop; + GstElement *pipeline; + + unsigned char *internal_buffer; + + int width; + int height; + }; +} + +#endif // HEADER_GUARD_OSGGSTREAMER_GSTREAMER_IMAGE_STREAM_H diff --git a/src/osgPlugins/gstreamer/ReaderWriterGStreamer.cpp b/src/osgPlugins/gstreamer/ReaderWriterGStreamer.cpp new file mode 100644 index 000000000..7983b7499 --- /dev/null +++ b/src/osgPlugins/gstreamer/ReaderWriterGStreamer.cpp @@ -0,0 +1,77 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * 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 + * OpenSceneGraph Public License for more details. +*/ + +#include "GStreamerImageStream.hpp" + +#include +#include +#include + + +class ReaderWriterGStreamer : public osgDB::ReaderWriter +{ +public: + + ReaderWriterGStreamer() + { + supportsExtension("avi", ""); + supportsExtension("flv", "Flash video"); + supportsExtension("mov", "Quicktime"); + supportsExtension("ogg", "Theora movie format"); + supportsExtension("mpg", "Mpeg movie format"); + supportsExtension("mpv", "Mpeg movie format"); + supportsExtension("wmv", "Windows Media Video format"); + supportsExtension("mkv", "Matroska"); + supportsExtension("mjpeg", "Motion JPEG"); + supportsExtension("mp4", "MPEG-4"); + supportsExtension("m4v", "MPEG-4"); + supportsExtension("sav", "Unknown"); + supportsExtension("3gp", "3G multi-media format"); + supportsExtension("sdp", "Session Description Protocol"); + supportsExtension("m2ts", "MPEG-2 Transport Stream"); + + gst_init(NULL, NULL); + } + + virtual ~ReaderWriterGStreamer() + { + } + + virtual const char * className() const + { + return "ReaderWriterGStreamer"; + } + + virtual ReadResult readImage(const std::string & filename, const osgDB::ReaderWriter::Options* options) const + { + const std::string ext = osgDB::getLowerCaseFileExtension(filename); + + if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; + + const std::string path = osgDB::containsServerAddress(filename) ? + filename : + osgDB::findDataFile(filename, options); + + if (path.empty()) return ReadResult::FILE_NOT_FOUND; + + osg::ref_ptr imageStream = new osgGStreamer::GStreamerImageStream(); + + if (!imageStream->open(filename)) return ReadResult::FILE_NOT_HANDLED; + + return imageStream.release(); + } +}; + + + +REGISTER_OSGPLUGIN(gstreamer, ReaderWriterGStreamer)