aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/media_plugins/gstreamer010
diff options
context:
space:
mode:
authorArmin Weatherwax2010-06-14 12:04:49 +0200
committerArmin Weatherwax2010-09-23 15:38:25 +0200
commit35df5441d3e2789663532c948731aff3a1e04728 (patch)
treeac7674289784a5f96106ea507637055a8dada78a /linden/indra/media_plugins/gstreamer010
parentChanged version to Experimental 2010.09.18 (diff)
downloadmeta-impy-35df5441d3e2789663532c948731aff3a1e04728.zip
meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.gz
meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.bz2
meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.xz
llmediaplugins first step
Diffstat (limited to '')
-rw-r--r--linden/indra/media_plugins/gstreamer010/CMakeLists.txt71
-rw-r--r--linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h (renamed from linden/indra/llmedia/llmediaimplregister.h)47
-rw-r--r--linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp171
-rw-r--r--linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h78
-rw-r--r--linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc51
-rw-r--r--linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc5
-rw-r--r--linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h53
-rw-r--r--linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp (renamed from linden/indra/llmedia/llmediaimplgstreamervidplug.cpp)252
-rw-r--r--linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h (renamed from linden/indra/llmedia/llmediaimplgstreamervidplug.h)13
-rw-r--r--linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp1202
-rwxr-xr-xlinden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp~1219
11 files changed, 3048 insertions, 114 deletions
diff --git a/linden/indra/media_plugins/gstreamer010/CMakeLists.txt b/linden/indra/media_plugins/gstreamer010/CMakeLists.txt
new file mode 100644
index 0000000..a3a32d8
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/CMakeLists.txt
@@ -0,0 +1,71 @@
1# -*- cmake -*-
2
3project(media_plugin_gstreamer010)
4
5include(00-Common)
6include(LLCommon)
7include(LLImage)
8include(LLPlugin)
9include(LLMath)
10include(LLRender)
11include(LLWindow)
12include(Linking)
13include(PluginAPI)
14include(MediaPluginBase)
15include(FindOpenGL)
16
17include(GStreamer010Plugin)
18
19include_directories(
20 ${LLPLUGIN_INCLUDE_DIRS}
21 ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
22 ${LLCOMMON_INCLUDE_DIRS}
23 ${LLMATH_INCLUDE_DIRS}
24 ${LLIMAGE_INCLUDE_DIRS}
25 ${LLRENDER_INCLUDE_DIRS}
26 ${LLWINDOW_INCLUDE_DIRS}
27 ${GSTREAMER010_INCLUDE_DIRS}
28 ${GSTREAMER010_PLUGINS_BASE_INCLUDE_DIRS}
29)
30
31### media_plugin_gstreamer010
32
33set(media_plugin_gstreamer010_SOURCE_FILES
34 media_plugin_gstreamer010.cpp
35 llmediaimplgstreamer_syms.cpp
36 llmediaimplgstreamervidplug.cpp
37 )
38
39set(media_plugin_gstreamer010_HEADER_FILES
40 llmediaimplgstreamervidplug.h
41 llmediaimplgstreamer_syms.h
42 llmediaimplgstreamertriviallogging.h
43 )
44
45#awfixme if (${CXX_VERSION_NUMBER} MATCHES "4[23].")
46 # Work around a bad interaction between broken gstreamer headers and
47 # g++ 4.3's increased strictness.
48 set_source_files_properties(llmediaimplgstreamervidplug.cpp PROPERTIES
49 COMPILE_FLAGS -Wno-write-strings)
50#awfixme endif (${CXX_VERSION_NUMBER} MATCHES "4[23].")
51
52add_library(media_plugin_gstreamer010
53 SHARED
54 ${media_plugin_gstreamer010_SOURCE_FILES}
55)
56
57target_link_libraries(media_plugin_gstreamer010
58 ${LLPLUGIN_LIBRARIES}
59 ${MEDIA_PLUGIN_BASE_LIBRARIES}
60 ${LLCOMMON_LIBRARIES}
61 ${PLUGIN_API_WINDOWS_LIBRARIES}
62 ${GSTREAMER010_LIBRARIES}
63)
64
65add_dependencies(media_plugin_gstreamer010
66 ${LLPLUGIN_LIBRARIES}
67 ${MEDIA_PLUGIN_BASE_LIBRARIES}
68 ${LLCOMMON_LIBRARIES}
69)
70
71
diff --git a/linden/indra/llmedia/llmediaimplregister.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h
index 5191274..ef41736 100644
--- a/linden/indra/llmedia/llmediaimplregister.h
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h
@@ -1,6 +1,7 @@
1/** 1/**
2 * @file llmediaimplregister.h 2 * @file llmediaimplgstreamer.h
3 * @brief Allow impls to register themselves with the impl factory 3 * @author Tofu Linden
4 * @brief implementation that supports media playback via GStreamer.
4 * 5 *
5 * $LicenseInfo:firstyear=2007&license=viewergpl$ 6 * $LicenseInfo:firstyear=2007&license=viewergpl$
6 * 7 *
@@ -30,31 +31,27 @@
30 * $/LicenseInfo$ 31 * $/LicenseInfo$
31 */ 32 */
32 33
33#ifndef LLIMEDIAIMPLREGISTER_H 34// header guard
34#define LLIMEDIAIMPLREGISTER_H 35#ifndef llmediaimplgstreamer_h
36#define llmediaimplgstreamer_h
35 37
36#include <string> 38#if LL_GSTREAMER010_ENABLED
37 39
38#include "llmediaimplfactory.h" 40extern "C" {
41#include <stdio.h>
42#include <gst/gst.h>
39 43
40/////////////////////////////////////////////////////////////////////////////// 44#include "apr_pools.h"
41// 45#include "apr_dso.h"
42class LLMediaImplRegister 46}
43{
44 public:
45 LLMediaImplRegister( std::string impl_name, LLMediaImplMaker* impl_maker ) :
46 mImplName( impl_name )
47 {
48 LLMediaImplFactory::getInstance()->registerImpl( impl_name, impl_maker );
49 };
50 47
51 std::string getImplName()
52 {
53 return mImplName;
54 };
55 48
56 private: 49extern "C" {
57 std::string mImplName; 50gboolean llmediaimplgstreamer_bus_callback (GstBus *bus,
58}; 51 GstMessage *message,
52 gpointer data);
53}
59 54
60#endif // LLIMEDIAIMPLREGISTER_H 55#endif // LL_GSTREAMER010_ENABLED
56
57#endif // llmediaimplgstreamer_h
diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp
new file mode 100644
index 0000000..cc52232
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp
@@ -0,0 +1,171 @@
1/**
2 * @file llmediaimplgstreamer_syms.cpp
3 * @brief dynamic GStreamer symbol-grabbing code
4 *
5 * $LicenseInfo:firstyear=2007&license=viewergpl$
6 *
7 * Copyright (c) 2007-2009, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 */
32
33#if LL_GSTREAMER010_ENABLED
34
35#include <string>
36
37extern "C" {
38#include <gst/gst.h>
39
40#include "apr_pools.h"
41#include "apr_dso.h"
42}
43
44#include "llmediaimplgstreamertriviallogging.h"
45
46#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) RTN (*ll##GSTSYM)(__VA_ARGS__) = NULL
47#include "llmediaimplgstreamer_syms_raw.inc"
48#include "llmediaimplgstreamer_syms_rawv.inc"
49#undef LL_GST_SYM
50
51// a couple of stubs for disgusting reasons
52GstDebugCategory*
53ll_gst_debug_category_new(gchar *name, guint color, gchar *description)
54{
55 static GstDebugCategory dummy;
56 return &dummy;
57}
58void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname)
59{
60}
61
62static bool sSymsGrabbed = false;
63static apr_pool_t *sSymGSTDSOMemoryPool = NULL;
64static apr_dso_handle_t *sSymGSTDSOHandleG = NULL;
65static apr_dso_handle_t *sSymGSTDSOHandleV = NULL;
66
67
68bool grab_gst_syms(std::string gst_dso_name,
69 std::string gst_dso_name_vid)
70{
71 if (sSymsGrabbed)
72 {
73 // already have grabbed good syms
74 return TRUE;
75 }
76
77 bool sym_error = false;
78 bool rtn = false;
79 apr_status_t rv;
80 apr_dso_handle_t *sSymGSTDSOHandle = NULL;
81
82#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##GSTSYM, sSymGSTDSOHandle, #GSTSYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #GSTSYM); if (REQ) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #GSTSYM, (void*)ll##GSTSYM);}while(0)
83
84 //attempt to load the shared libraries
85 apr_pool_create(&sSymGSTDSOMemoryPool, NULL);
86
87 if ( APR_SUCCESS == (rv = apr_dso_load(&sSymGSTDSOHandle,
88 gst_dso_name.c_str(),
89 sSymGSTDSOMemoryPool) ))
90 {
91 INFOMSG("Found DSO: %s", gst_dso_name.c_str());
92#include "llmediaimplgstreamer_syms_raw.inc"
93
94 if ( sSymGSTDSOHandle )
95 {
96 sSymGSTDSOHandleG = sSymGSTDSOHandle;
97 sSymGSTDSOHandle = NULL;
98 }
99
100 if ( APR_SUCCESS ==
101 (rv = apr_dso_load(&sSymGSTDSOHandle,
102 gst_dso_name_vid.c_str(),
103 sSymGSTDSOMemoryPool) ))
104 {
105 INFOMSG("Found DSO: %s", gst_dso_name_vid.c_str());
106#include "llmediaimplgstreamer_syms_rawv.inc"
107 rtn = !sym_error;
108 }
109 else
110 {
111 INFOMSG("Couldn't load DSO: %s", gst_dso_name_vid.c_str());
112 rtn = false; // failure
113 }
114 }
115 else
116 {
117 INFOMSG("Couldn't load DSO: %s", gst_dso_name.c_str());
118 rtn = false; // failure
119 }
120
121 if (sym_error)
122 {
123 WARNMSG("Failed to find necessary symbols in GStreamer libraries.");
124 }
125
126 if ( sSymGSTDSOHandle )
127 {
128 sSymGSTDSOHandleV = sSymGSTDSOHandle;
129 sSymGSTDSOHandle = NULL;
130 }
131#undef LL_GST_SYM
132
133 sSymsGrabbed = !!rtn;
134 return rtn;
135}
136
137
138void ungrab_gst_syms()
139{
140 // should be safe to call regardless of whether we've
141 // actually grabbed syms.
142
143 if ( sSymGSTDSOHandleG )
144 {
145 apr_dso_unload(sSymGSTDSOHandleG);
146 sSymGSTDSOHandleG = NULL;
147 }
148
149 if ( sSymGSTDSOHandleV )
150 {
151 apr_dso_unload(sSymGSTDSOHandleV);
152 sSymGSTDSOHandleV = NULL;
153 }
154
155 if ( sSymGSTDSOMemoryPool )
156 {
157 apr_pool_destroy(sSymGSTDSOMemoryPool);
158 sSymGSTDSOMemoryPool = NULL;
159 }
160
161 // NULL-out all of the symbols we'd grabbed
162#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{ll##GSTSYM = NULL;}while(0)
163#include "llmediaimplgstreamer_syms_raw.inc"
164#include "llmediaimplgstreamer_syms_rawv.inc"
165#undef LL_GST_SYM
166
167 sSymsGrabbed = false;
168}
169
170
171#endif // LL_GSTREAMER010_ENABLED
diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h
new file mode 100644
index 0000000..ee7473d
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h
@@ -0,0 +1,78 @@
1/**
2 * @file llmediaimplgstreamer_syms.h
3 * @brief dynamic GStreamer symbol-grabbing code
4 *
5 * $LicenseInfo:firstyear=2007&license=viewergpl$
6 *
7 * Copyright (c) 2007-2009, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 */
32
33#include "linden_common.h"
34
35#if LL_GSTREAMER010_ENABLED
36
37extern "C" {
38#include <gst/gst.h>
39}
40
41bool grab_gst_syms(std::string gst_dso_name,
42 std::string gst_dso_name_vid);
43void ungrab_gst_syms();
44
45#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) extern RTN (*ll##GSTSYM)(__VA_ARGS__)
46#include "llmediaimplgstreamer_syms_raw.inc"
47#include "llmediaimplgstreamer_syms_rawv.inc"
48#undef LL_GST_SYM
49
50// regrettable hacks to give us better runtime compatibility with older systems
51#define llg_return_if_fail(COND) do{if (!(COND)) return;}while(0)
52#define llg_return_val_if_fail(COND,V) do{if (!(COND)) return V;}while(0)
53
54// regrettable hacks because GStreamer was not designed for runtime loading
55#undef GST_TYPE_MESSAGE
56#define GST_TYPE_MESSAGE (llgst_message_get_type())
57#undef GST_TYPE_OBJECT
58#define GST_TYPE_OBJECT (llgst_object_get_type())
59#undef GST_TYPE_PIPELINE
60#define GST_TYPE_PIPELINE (llgst_pipeline_get_type())
61#undef GST_TYPE_ELEMENT
62#define GST_TYPE_ELEMENT (llgst_element_get_type())
63#undef GST_TYPE_VIDEO_SINK
64#define GST_TYPE_VIDEO_SINK (llgst_video_sink_get_type())
65// more regrettable hacks to stub-out these .h-exposed GStreamer internals
66void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname);
67#undef _gst_debug_register_funcptr
68#define _gst_debug_register_funcptr ll_gst_debug_register_funcptr
69GstDebugCategory* ll_gst_debug_category_new(gchar *name, guint color, gchar *description);
70#undef _gst_debug_category_new
71#define _gst_debug_category_new ll_gst_debug_category_new
72#undef __gst_debug_enabled
73#define __gst_debug_enabled (0)
74
75// more hacks
76#define LLGST_MESSAGE_TYPE_NAME(M) (llgst_message_type_get_name(GST_MESSAGE_TYPE(M)))
77
78#endif // LL_GSTREAMER010_ENABLED
diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc
new file mode 100644
index 0000000..b33e593
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc
@@ -0,0 +1,51 @@
1
2// required symbols to grab
3LL_GST_SYM(true, gst_pad_peer_accept_caps, gboolean, GstPad *pad, GstCaps *caps);
4LL_GST_SYM(true, gst_buffer_new, GstBuffer*, void);
5LL_GST_SYM(true, gst_buffer_set_caps, void, GstBuffer*, GstCaps *);
6LL_GST_SYM(true, gst_structure_set_value, void, GstStructure *, const gchar *, const GValue*);
7LL_GST_SYM(true, gst_init_check, gboolean, int *argc, char **argv[], GError ** err);
8LL_GST_SYM(true, gst_message_get_type, GType, void);
9LL_GST_SYM(true, gst_message_type_get_name, const gchar*, GstMessageType type);
10LL_GST_SYM(true, gst_message_parse_error, void, GstMessage *message, GError **gerror, gchar **debug);
11LL_GST_SYM(true, gst_message_parse_warning, void, GstMessage *message, GError **gerror, gchar **debug);
12LL_GST_SYM(true, gst_message_parse_state_changed, void, GstMessage *message, GstState *oldstate, GstState *newstate, GstState *pending);
13LL_GST_SYM(true, gst_element_set_state, GstStateChangeReturn, GstElement *element, GstState state);
14LL_GST_SYM(true, gst_object_unref, void, gpointer object);
15LL_GST_SYM(true, gst_object_get_type, GType, void);
16LL_GST_SYM(true, gst_pipeline_get_type, GType, void);
17LL_GST_SYM(true, gst_pipeline_get_bus, GstBus*, GstPipeline *pipeline);
18LL_GST_SYM(true, gst_bus_add_watch, guint, GstBus * bus, GstBusFunc func, gpointer user_data);
19LL_GST_SYM(true, gst_element_factory_make, GstElement*, const gchar *factoryname, const gchar *name);
20LL_GST_SYM(true, gst_element_get_type, GType, void);
21LL_GST_SYM(true, gst_static_pad_template_get, GstPadTemplate*, GstStaticPadTemplate *pad_template);
22LL_GST_SYM(true, gst_element_class_add_pad_template, void, GstElementClass *klass, GstPadTemplate *temp);
23LL_GST_SYM(true, gst_element_class_set_details, void, GstElementClass *klass, const GstElementDetails *details);
24LL_GST_SYM(true, gst_caps_unref, void, GstCaps* caps);
25LL_GST_SYM(true, gst_caps_ref, GstCaps *, GstCaps* caps);
26//LL_GST_SYM(true, gst_caps_is_empty, gboolean, const GstCaps *caps);
27LL_GST_SYM(true, gst_caps_from_string, GstCaps *, const gchar *string);
28LL_GST_SYM(true, gst_caps_replace, void, GstCaps **caps, GstCaps *newcaps);
29LL_GST_SYM(true, gst_caps_get_structure, GstStructure *, const GstCaps *caps, guint index);
30LL_GST_SYM(true, gst_caps_copy, GstCaps *, const GstCaps * caps);
31//LL_GST_SYM(true, gst_caps_intersect, GstCaps *, const GstCaps *caps1, const GstCaps *caps2);
32LL_GST_SYM(true, gst_element_register, gboolean, GstPlugin *plugin, const gchar *name, guint rank, GType type);
33LL_GST_SYM(true, _gst_plugin_register_static, void, GstPluginDesc *desc);
34LL_GST_SYM(true, gst_structure_get_int, gboolean, const GstStructure *structure, const gchar *fieldname, gint *value);
35LL_GST_SYM(true, gst_structure_get_value, G_CONST_RETURN GValue *, const GstStructure *structure, const gchar *fieldname);
36LL_GST_SYM(true, gst_value_get_fraction_numerator, gint, const GValue *value);
37LL_GST_SYM(true, gst_value_get_fraction_denominator, gint, const GValue *value);
38LL_GST_SYM(true, gst_structure_get_name, G_CONST_RETURN gchar *, const GstStructure *structure);
39LL_GST_SYM(true, gst_element_seek, bool, GstElement *, gdouble, GstFormat, GstSeekFlags, GstSeekType, gint64, GstSeekType, gint64);
40
41// optional symbols to grab
42LL_GST_SYM(false, gst_registry_fork_set_enabled, void, gboolean enabled);
43LL_GST_SYM(false, gst_segtrap_set_enabled, void, gboolean enabled);
44LL_GST_SYM(false, gst_message_parse_buffering, void, GstMessage *message, gint *percent);
45LL_GST_SYM(false, gst_message_parse_info, void, GstMessage *message, GError **gerror, gchar **debug);
46LL_GST_SYM(false, gst_element_query_position, gboolean, GstElement *element, GstFormat *format, gint64 *cur);
47LL_GST_SYM(false, gst_version, void, guint *major, guint *minor, guint *micro, guint *nano);
48
49// GStreamer 'internal' symbols which may not be visible in some runtimes but are still used in expanded GStreamer header macros - yuck! We'll substitute our own stubs for these.
50//LL_GST_SYM(true, _gst_debug_register_funcptr, void, GstDebugFuncPtr func, gchar* ptrname);
51//LL_GST_SYM(true, _gst_debug_category_new, GstDebugCategory *, gchar *name, guint color, gchar *description);
diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc
new file mode 100644
index 0000000..14fbcb4
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc
@@ -0,0 +1,5 @@
1
2// required symbols to grab
3LL_GST_SYM(true, gst_video_sink_get_type, GType, void);
4
5// optional symbols to grab
diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h
new file mode 100644
index 0000000..e31d4a3
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h
@@ -0,0 +1,53 @@
1/**
2 * @file llmediaimplgstreamertriviallogging.h
3 * @brief minimal logging utilities.
4 *
5 * $LicenseInfo:firstyear=2009&license=viewergpl$
6 *
7 * Copyright (c) 2009, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 */
32
33#ifndef __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__
34#define __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__
35
36#include <cstdio>
37
38/////////////////////////////////////////////////////////////////////////
39// Debug/Info/Warning macros.
40#define MSGMODULEFOO "(media plugin)"
41#define STDERRMSG(...) do{\
42 fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\
43 fprintf(stderr, __VA_ARGS__);\
44 fputc('\n',stderr);\
45 }while(0)
46#define NULLMSG(...) do{}while(0)
47
48#define DEBUGMSG NULLMSG
49#define INFOMSG STDERRMSG
50#define WARNMSG STDERRMSG
51/////////////////////////////////////////////////////////////////////////
52
53#endif /* __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ */
diff --git a/linden/indra/llmedia/llmediaimplgstreamervidplug.cpp b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
index f2c0e88..25e96d4 100644
--- a/linden/indra/llmedia/llmediaimplgstreamervidplug.cpp
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
@@ -1,5 +1,5 @@
1/** 1/**
2 * @file llmediaimplgstreamervidplug.h 2 * @file llmediaimplgstreamervidplug.cpp
3 * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl 3 * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
4 * 4 *
5 * $LicenseInfo:firstyear=2007&license=viewergpl$ 5 * $LicenseInfo:firstyear=2007&license=viewergpl$
@@ -30,7 +30,7 @@
30 * $/LicenseInfo$ 30 * $/LicenseInfo$
31 */ 31 */
32 32
33///#if LL_GSTREAMER_ENABLED 33#if LL_GSTREAMER010_ENABLED
34 34
35#include "linden_common.h" 35#include "linden_common.h"
36 36
@@ -38,30 +38,21 @@
38#include <gst/video/video.h> 38#include <gst/video/video.h>
39#include <gst/video/gstvideosink.h> 39#include <gst/video/gstvideosink.h>
40 40
41#include "llthread.h" 41#include "llmediaimplgstreamer_syms.h"
42#include "llmediaimplgstreamertriviallogging.h"
42 43
43#include "llmediaimplgstreamervidplug.h" 44#include "llmediaimplgstreamervidplug.h"
44 45
46
45GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); 47GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug);
46#define GST_CAT_DEFAULT gst_slvideo_debug 48#define GST_CAT_DEFAULT gst_slvideo_debug
47 49
48/* Filter signals and args */
49enum
50{
51 /* FILL ME */
52 LAST_SIGNAL
53};
54
55enum
56{
57 ARG_0
58};
59 50
60#define SLV_SIZECAPS ", width=(int){1,2,4,8,16,32,64,128,256,512,1024}, height=(int){1,2,4,8,16,32,64,128,256,512,1024} " 51#define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] "
61#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS ";" GST_VIDEO_CAPS_BGRx SLV_SIZECAPS 52#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS
62 53
63static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ( 54static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE (
64 "sink", 55 (gchar*)"sink",
65 GST_PAD_SINK, 56 GST_PAD_SINK,
66 GST_PAD_ALWAYS, 57 GST_PAD_ALWAYS,
67 GST_STATIC_CAPS (SLV_ALLCAPS) 58 GST_STATIC_CAPS (SLV_ALLCAPS)
@@ -87,9 +78,9 @@ gst_slvideo_base_init (gpointer gclass)
87 }; 78 };
88 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); 79 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
89 80
90 gst_element_class_add_pad_template (element_class, 81 llgst_element_class_add_pad_template (element_class,
91 gst_static_pad_template_get (&sink_factory)); 82 llgst_static_pad_template_get (&sink_factory));
92 gst_element_class_set_details (element_class, &element_details); 83 llgst_element_class_set_details (element_class, &element_details);
93} 84}
94 85
95 86
@@ -100,7 +91,7 @@ gst_slvideo_finalize (GObject * object)
100 slvideo = GST_SLVIDEO (object); 91 slvideo = GST_SLVIDEO (object);
101 if (slvideo->caps) 92 if (slvideo->caps)
102 { 93 {
103 gst_caps_unref(slvideo->caps); 94 llgst_caps_unref(slvideo->caps);
104 } 95 }
105 96
106 G_OBJECT_CLASS(parent_class)->finalize (object); 97 G_OBJECT_CLASS(parent_class)->finalize (object);
@@ -111,7 +102,7 @@ static GstFlowReturn
111gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) 102gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf)
112{ 103{
113 GstSLVideo *slvideo; 104 GstSLVideo *slvideo;
114 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); 105 llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
115 106
116 slvideo = GST_SLVIDEO(bsink); 107 slvideo = GST_SLVIDEO(bsink);
117 108
@@ -204,7 +195,7 @@ gst_slvideo_get_caps (GstBaseSink * bsink)
204 GstSLVideo *slvideo; 195 GstSLVideo *slvideo;
205 slvideo = GST_SLVIDEO(bsink); 196 slvideo = GST_SLVIDEO(bsink);
206 197
207 return gst_caps_ref (slvideo->caps); 198 return llgst_caps_ref (slvideo->caps);
208} 199}
209 200
210 201
@@ -214,42 +205,37 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
214{ 205{
215 GstSLVideo *filter; 206 GstSLVideo *filter;
216 GstStructure *structure; 207 GstStructure *structure;
217 GstCaps *intersection;
218 208
219 GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); 209 GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
220 210
221 filter = GST_SLVIDEO(bsink); 211 filter = GST_SLVIDEO(bsink);
222 212
223 intersection = gst_caps_intersect (filter->caps, caps); 213 int width, height;
224 if (gst_caps_is_empty (intersection))
225 {
226 // no overlap between our caps and requested caps
227 return FALSE;
228 }
229 gst_caps_unref(intersection);
230
231 int width = 0;
232 int height = 0;
233 gboolean ret; 214 gboolean ret;
234 const GValue *fps; 215 const GValue *fps;
235 const GValue *par; 216 const GValue *par;
236 structure = gst_caps_get_structure (caps, 0); 217 structure = llgst_caps_get_structure (caps, 0);
237 ret = gst_structure_get_int (structure, "width", &width); 218 ret = llgst_structure_get_int (structure, "width", &width);
238 ret = ret && gst_structure_get_int (structure, "height", &height); 219 ret = ret && llgst_structure_get_int (structure, "height", &height);
239 fps = gst_structure_get_value (structure, "framerate"); 220 fps = llgst_structure_get_value (structure, "framerate");
240 ret = ret && (fps != NULL); 221 ret = ret && (fps != NULL);
241 par = gst_structure_get_value (structure, "pixel-aspect-ratio"); 222 par = llgst_structure_get_value (structure, "pixel-aspect-ratio");
242 if (!ret) 223 if (!ret)
243 return FALSE; 224 return FALSE;
244 225
226 INFOMSG("** filter caps set with width=%d, height=%d", width, height);
227
228 GST_OBJECT_LOCK(filter);
229
245 filter->width = width; 230 filter->width = width;
246 filter->height = height; 231 filter->height = height;
247 filter->fps_n = gst_value_get_fraction_numerator(fps); 232
248 filter->fps_d = gst_value_get_fraction_denominator(fps); 233 filter->fps_n = llgst_value_get_fraction_numerator(fps);
234 filter->fps_d = llgst_value_get_fraction_denominator(fps);
249 if (par) 235 if (par)
250 { 236 {
251 filter->par_n = gst_value_get_fraction_numerator(par); 237 filter->par_n = llgst_value_get_fraction_numerator(par);
252 filter->par_d = gst_value_get_fraction_denominator(par); 238 filter->par_d = llgst_value_get_fraction_denominator(par);
253 } 239 }
254 else 240 else
255 { 241 {
@@ -259,16 +245,18 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
259 GST_VIDEO_SINK_WIDTH(filter) = width; 245 GST_VIDEO_SINK_WIDTH(filter) = width;
260 GST_VIDEO_SINK_HEIGHT(filter) = height; 246 GST_VIDEO_SINK_HEIGHT(filter) = height;
261 247
248 // crufty lump - we *always* accept *only* RGBX now.
249 /*
262 filter->format = SLV_PF_UNKNOWN; 250 filter->format = SLV_PF_UNKNOWN;
263 if (0 == strcmp(gst_structure_get_name(structure), 251 if (0 == strcmp(llgst_structure_get_name(structure),
264 "video/x-raw-rgb")) 252 "video/x-raw-rgb"))
265 { 253 {
266 int red_mask; 254 int red_mask;
267 int green_mask; 255 int green_mask;
268 int blue_mask; 256 int blue_mask;
269 gst_structure_get_int(structure, "red_mask", &red_mask); 257 llgst_structure_get_int(structure, "red_mask", &red_mask);
270 gst_structure_get_int(structure, "green_mask", &green_mask); 258 llgst_structure_get_int(structure, "green_mask", &green_mask);
271 gst_structure_get_int(structure, "blue_mask", &blue_mask); 259 llgst_structure_get_int(structure, "blue_mask", &blue_mask);
272 if ((unsigned int)red_mask == 0xFF000000 && 260 if ((unsigned int)red_mask == 0xFF000000 &&
273 (unsigned int)green_mask == 0x00FF0000 && 261 (unsigned int)green_mask == 0x00FF0000 &&
274 (unsigned int)blue_mask == 0x0000FF00) 262 (unsigned int)blue_mask == 0x0000FF00)
@@ -282,8 +270,12 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
282 filter->format = SLV_PF_BGRX; 270 filter->format = SLV_PF_BGRX;
283 //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n"); 271 //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n");
284 } 272 }
285 } 273 }*/
286 274
275 filter->format = SLV_PF_RGBX;
276
277 GST_OBJECT_UNLOCK(filter);
278
287 return TRUE; 279 return TRUE;
288} 280}
289 281
@@ -317,6 +309,97 @@ gst_slvideo_stop (GstBaseSink * bsink)
317} 309}
318 310
319 311
312static GstFlowReturn
313gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
314 GstCaps * caps, GstBuffer ** buf)
315{
316 gint width, height;
317 GstStructure *structure = NULL;
318 GstSLVideo *slvideo;
319 slvideo = GST_SLVIDEO(bsink);
320
321 // caps == requested caps
322 // we can ignore these and reverse-negotiate our preferred dimensions with
323 // the peer if we like - we need to do this to obey dynamic resize requests
324 // flowing in from the app.
325 structure = llgst_caps_get_structure (caps, 0);
326 if (!llgst_structure_get_int(structure, "width", &width) ||
327 !llgst_structure_get_int(structure, "height", &height))
328 {
329 GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps);
330 return GST_FLOW_NOT_NEGOTIATED;
331 }
332
333 GstBuffer *newbuf = llgst_buffer_new();
334 bool made_bufferdata_ptr = false;
335#define MAXDEPTHHACK 4
336
337 GST_OBJECT_LOCK(slvideo);
338 if (slvideo->resize_forced)
339 {
340 gint slwantwidth, slwantheight;
341 slwantwidth = slvideo->resize_try_width;
342 slwantheight = slvideo->resize_try_height;
343
344 if (slwantwidth != width ||
345 slwantheight != height)
346 {
347 // don't like requested caps, we will issue our own suggestion - copy
348 // the requested caps but substitute our own width and height and see
349 // if our peer is happy with that.
350
351 GstCaps *desired_caps;
352 GstStructure *desired_struct;
353 desired_caps = llgst_caps_copy (caps);
354 desired_struct = llgst_caps_get_structure (desired_caps, 0);
355
356 GValue value = {0};
357 g_value_init(&value, G_TYPE_INT);
358 g_value_set_int(&value, slwantwidth);
359 llgst_structure_set_value (desired_struct, "width", &value);
360 g_value_unset(&value);
361 g_value_init(&value, G_TYPE_INT);
362 g_value_set_int(&value, slwantheight);
363 llgst_structure_set_value (desired_struct, "height", &value);
364
365 if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo),
366 desired_caps))
367 {
368 // todo: re-use buffers from a pool?
369 // todo: set MALLOCDATA to null, set DATA to point straight to shm?
370
371 // peer likes our cap suggestion
372 DEBUGMSG("peer loves us :)");
373 GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK;
374 GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
375 GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
376 llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps);
377
378 made_bufferdata_ptr = true;
379 } else {
380 // peer hates our cap suggestion
381 INFOMSG("peer hates us :(");
382 llgst_caps_unref(desired_caps);
383 }
384 }
385 }
386
387 if (!made_bufferdata_ptr) // need to fallback to malloc at original size
388 {
389 GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK;
390 GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
391 GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
392 llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps);
393 }
394
395 GST_OBJECT_UNLOCK(slvideo);
396
397 *buf = GST_BUFFER_CAST(newbuf);
398
399 return GST_FLOW_OK;
400}
401
402
320/* initialize the plugin's class */ 403/* initialize the plugin's class */
321static void 404static void
322gst_slvideo_class_init (GstSLVideoClass * klass) 405gst_slvideo_class_init (GstSLVideoClass * klass)
@@ -338,7 +421,7 @@ gst_slvideo_class_init (GstSLVideoClass * klass)
338#define LLGST_DEBUG_FUNCPTR(p) (p) 421#define LLGST_DEBUG_FUNCPTR(p) (p)
339 gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps); 422 gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps);
340 gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps); 423 gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps);
341 //gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc); 424 gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc);
342 //gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times); 425 //gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times);
343 gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame); 426 gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
344 gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame); 427 gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
@@ -351,20 +434,6 @@ gst_slvideo_class_init (GstSLVideoClass * klass)
351} 434}
352 435
353 436
354static void
355gst_slvideo_update_caps (GstSLVideo * slvideo)
356{
357 GstCaps *caps;
358
359 // GStreamer will automatically convert colourspace if necessary.
360 // GStreamer will automatically resize media to one of these enumerated
361 // powers-of-two that we ask for (yay GStreamer!)
362 caps = gst_caps_from_string (SLV_ALLCAPS);
363
364 gst_caps_replace (&slvideo->caps, caps);
365}
366
367
368/* initialize the new element 437/* initialize the new element
369 * instantiate pads and add them to element 438 * instantiate pads and add them to element
370 * set functions 439 * set functions
@@ -374,6 +443,7 @@ static void
374gst_slvideo_init (GstSLVideo * filter, 443gst_slvideo_init (GstSLVideo * filter,
375 GstSLVideoClass * gclass) 444 GstSLVideoClass * gclass)
376{ 445{
446 filter->caps = NULL;
377 filter->width = -1; 447 filter->width = -1;
378 filter->height = -1; 448 filter->height = -1;
379 449
@@ -385,19 +455,24 @@ gst_slvideo_init (GstSLVideo * filter,
385 filter->retained_frame_width = filter->width; 455 filter->retained_frame_width = filter->width;
386 filter->retained_frame_height = filter->height; 456 filter->retained_frame_height = filter->height;
387 filter->retained_frame_format = SLV_PF_UNKNOWN; 457 filter->retained_frame_format = SLV_PF_UNKNOWN;
458 GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS);
459 llgst_caps_replace (&filter->caps, caps);
460 filter->resize_forced = false;
461 filter->resize_try_width = -1;
462 filter->resize_try_height = -1;
388 GST_OBJECT_UNLOCK(filter); 463 GST_OBJECT_UNLOCK(filter);
389
390 gst_slvideo_update_caps(filter);
391} 464}
392 465
393static void 466static void
394gst_slvideo_set_property (GObject * object, guint prop_id, 467gst_slvideo_set_property (GObject * object, guint prop_id,
395 const GValue * value, GParamSpec * pspec) 468 const GValue * value, GParamSpec * pspec)
396{ 469{
397 g_return_if_fail (GST_IS_SLVIDEO (object)); 470 llg_return_if_fail (GST_IS_SLVIDEO (object));
398 471
399 if (prop_id) { 472 switch (prop_id) {
473 default:
400 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 474 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
475 break;
401 } 476 }
402} 477}
403 478
@@ -405,10 +480,12 @@ static void
405gst_slvideo_get_property (GObject * object, guint prop_id, 480gst_slvideo_get_property (GObject * object, guint prop_id,
406 GValue * value, GParamSpec * pspec) 481 GValue * value, GParamSpec * pspec)
407{ 482{
408 g_return_if_fail (GST_IS_SLVIDEO (object)); 483 llg_return_if_fail (GST_IS_SLVIDEO (object));
409 484
410 if (prop_id) { 485 switch (prop_id) {
486 default:
411 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 487 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
488 break;
412 } 489 }
413} 490}
414 491
@@ -421,28 +498,35 @@ gst_slvideo_get_property (GObject * object, guint prop_id,
421static gboolean 498static gboolean
422plugin_init (GstPlugin * plugin) 499plugin_init (GstPlugin * plugin)
423{ 500{
424 //fprintf(stderr, "\n\n\nPLUGIN INIT\n\n\n"); 501 DEBUGMSG("\n\n\nPLUGIN INIT\n\n\n");
425 502
426 GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin", 503 GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin",
427 0, (gchar*)"Second Life Video Sink"); 504 0, (gchar*)"Second Life Video Sink");
428 505
429 return gst_element_register (plugin, "private-slvideo", 506 return llgst_element_register (plugin, (gchar*)"private-slvideo",
430 GST_RANK_NONE, GST_TYPE_SLVIDEO); 507 GST_RANK_NONE, GST_TYPE_SLVIDEO);
431} 508}
432 509
433 510/* this is the structure that gstreamer looks for to register plugins
511 */
512/* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since
513 some g++ versions buggily avoid __attribute__((constructor)) functions -
514 so we provide an explicit plugin init function.
515 */
434void gst_slvideo_init_class (void) 516void gst_slvideo_init_class (void)
435{ 517{
436 gst_plugin_register_static( GST_VERSION_MAJOR, 518#define PACKAGE "packagehack"
437 GST_VERSION_MINOR, 519 // this macro quietly refers to PACKAGE internally
438 (const gchar *)"private-slvideoplugin", 520 static GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
439 (gchar *)"SL Video sink plugin", 521 GST_VERSION_MINOR,
440 plugin_init, 522 (gchar*)"private-slvideoplugin",
441 (const gchar *)"0.1", 523 (gchar*)"SL Video sink plugin",
442 GST_LICENSE_UNKNOWN, 524 plugin_init, (gchar*)"0.1", (gchar*)GST_LICENSE_UNKNOWN,
443 (const gchar *)"Second Life", 525 (gchar*)"Second Life",
444 (const gchar *)"Second Life", 526 (gchar*)"http://www.secondlife.com/");
445 (const gchar *)"http://www.secondlife.com/" ); 527#undef PACKAGE
528 ll_gst_plugin_register_static (&gst_plugin_desc);
529 DEBUGMSG(stderr, "\n\n\nCLASS INIT\n\n\n");
446} 530}
447 531
448///#endif // LL_GSTREAMER_ENABLED 532#endif // LL_GSTREAMER010_ENABLED
diff --git a/linden/indra/llmedia/llmediaimplgstreamervidplug.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h
index 3a984a9..f6d55b8 100644
--- a/linden/indra/llmedia/llmediaimplgstreamervidplug.h
+++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h
@@ -33,13 +33,12 @@
33#ifndef __GST_SLVIDEO_H__ 33#ifndef __GST_SLVIDEO_H__
34#define __GST_SLVIDEO_H__ 34#define __GST_SLVIDEO_H__
35 35
36///#if LL_GSTREAMER_ENABLED 36#if LL_GSTREAMER010_ENABLED
37 37
38extern "C" { 38extern "C" {
39#include <gst/gst.h> 39#include <gst/gst.h>
40#include <gst/video/video.h> 40#include <gst/video/video.h>
41#include <gst/video/gstvideosink.h> 41#include <gst/video/gstvideosink.h>
42#include <glib/gthread.h>
43} 42}
44 43
45G_BEGIN_DECLS 44G_BEGIN_DECLS
@@ -85,9 +84,13 @@ struct _GstSLVideo
85 // when the retained frame is updated.) 84 // when the retained frame is updated.)
86 bool retained_frame_ready; // new frame ready since flag last reset. (*TODO: could get the writer to wait on a semaphore instead of having the reader poll, potentially making dropped frames somewhat cheaper.) 85 bool retained_frame_ready; // new frame ready since flag last reset. (*TODO: could get the writer to wait on a semaphore instead of having the reader poll, potentially making dropped frames somewhat cheaper.)
87 unsigned char* retained_frame_data; 86 unsigned char* retained_frame_data;
88 int retained_frame_allocbytes; 87 int retained_frame_allocbytes;
89 int retained_frame_width, retained_frame_height; 88 int retained_frame_width, retained_frame_height;
90 SLVPixelFormat retained_frame_format; 89 SLVPixelFormat retained_frame_format;
90 // sticky resize info
91 bool resize_forced;
92 int resize_try_width;
93 int resize_try_height;
91}; 94};
92 95
93struct _GstSLVideoClass 96struct _GstSLVideoClass
@@ -101,6 +104,6 @@ void gst_slvideo_init_class (void);
101 104
102G_END_DECLS 105G_END_DECLS
103 106
104///#endif // LL_GSTREAMER_ENABLED 107#endif // LL_GSTREAMER010_ENABLED
105 108
106#endif /* __GST_SLVIDEO_H__ */ 109#endif /* __GST_SLVIDEO_H__ */
diff --git a/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp b/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp
new file mode 100644
index 0000000..77b7c13
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp
@@ -0,0 +1,1202 @@
1/**
2 * @file media_plugin_gstreamer010.cpp
3 * @brief GStreamer-0.10 plugin for LLMedia API plugin system
4 *
5 * $LicenseInfo:firstyear=2007&license=viewergpl$
6 *
7 * Copyright (c) 2007-2009, Linden Research, Inc.
8 *
9 * Second Life Viewer Source Code
10 * The source code in this file ("Source Code") is provided by Linden Lab
11 * to you under the terms of the GNU General Public License, version 2.0
12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 *
17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at
21 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
22 *
23 * By copying, modifying or distributing this software, you acknowledge
24 * that you have read and understood your obligations described above,
25 * and agree to abide by those obligations.
26 *
27 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
28 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
29 * COMPLETENESS OR PERFORMANCE.
30 * $/LicenseInfo$
31 */
32
33#include "linden_common.h"
34
35#include "llgl.h"
36
37#include "llplugininstance.h"
38#include "llpluginmessage.h"
39#include "llpluginmessageclasses.h"
40#include "media_plugin_base.h"
41
42#if LL_GSTREAMER010_ENABLED
43
44extern "C" {
45#include <gst/gst.h>
46}
47
48#include "llmediaimplgstreamer.h"
49#include "llmediaimplgstreamertriviallogging.h"
50
51#include "llmediaimplgstreamervidplug.h"
52
53#include "llmediaimplgstreamer_syms.h"
54
55//////////////////////////////////////////////////////////////////////////////
56//
57class MediaPluginGStreamer010 : public MediaPluginBase
58{
59public:
60 MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
61 ~MediaPluginGStreamer010();
62
63 /* virtual */ void receiveMessage(const char *message_string);
64
65 static bool startup();
66 static bool closedown();
67
68 gboolean processGSTEvents(GstBus *bus,
69 GstMessage *message);
70
71private:
72 std::string getVersion();
73 bool navigateTo( const std::string urlIn );
74 bool seek( double time_sec );
75 bool setVolume( float volume );
76
77 // misc
78 bool pause();
79 bool stop();
80 bool play(double rate);
81 bool getTimePos(double &sec_out);
82
83 static const double MIN_LOOP_SEC = 1.0F;
84
85 bool mIsLooping;
86
87 enum ECommand {
88 COMMAND_NONE,
89 COMMAND_STOP,
90 COMMAND_PLAY,
91 COMMAND_FAST_FORWARD,
92 COMMAND_FAST_REWIND,
93 COMMAND_PAUSE,
94 COMMAND_SEEK,
95 };
96 ECommand mCommand;
97
98private:
99 bool unload();
100 bool load();
101
102 bool update(int milliseconds);
103 void mouseDown( int x, int y );
104 void mouseUp( int x, int y );
105 void mouseMove( int x, int y );
106
107 bool sizeChanged();
108
109 static bool mDoneInit;
110
111 guint mBusWatchID;
112
113 float mVolume;
114
115 int mDepth;
116
117 // media natural size
118 int mNaturalWidth;
119 int mNaturalHeight;
120 int mNaturalRowbytes;
121 // previous media natural size so we can detect changes
122 int mPreviousNaturalWidth;
123 int mPreviousNaturalHeight;
124 // desired render size from host
125 int mWidth;
126 int mHeight;
127 // padded texture size we need to write into
128 int mTextureWidth;
129 int mTextureHeight;
130
131 int mTextureFormatPrimary;
132 int mTextureFormatType;
133
134 bool mSeekWanted;
135 double mSeekDestination;
136
137 // Very GStreamer-specific
138 GMainLoop *mPump; // event pump for this media
139 GstElement *mPlaybin;
140 GstSLVideo *mVideoSink;
141};
142
143//static
144bool MediaPluginGStreamer010::mDoneInit = false;
145
146MediaPluginGStreamer010::MediaPluginGStreamer010(
147 LLPluginInstance::sendMessageFunction host_send_func,
148 void *host_user_data ) :
149 MediaPluginBase(host_send_func, host_user_data),
150 mBusWatchID ( 0 ),
151 mNaturalRowbytes ( 4 ),
152 mTextureFormatPrimary ( GL_RGBA ),
153 mTextureFormatType ( GL_UNSIGNED_INT_8_8_8_8_REV ),
154 mSeekWanted(false),
155 mSeekDestination(0.0),
156 mPump ( NULL ),
157 mPlaybin ( NULL ),
158 mVideoSink ( NULL ),
159 mCommand ( COMMAND_NONE )
160{
161 std::ostringstream str;
162 INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(getpid()));
163}
164
165///////////////////////////////////////////////////////////////////////////////
166//
167//#define LL_GST_REPORT_STATE_CHANGES
168#ifdef LL_GST_REPORT_STATE_CHANGES
169static char* get_gst_state_name(GstState state)
170{
171 switch (state) {
172 case GST_STATE_VOID_PENDING: return "VOID_PENDING";
173 case GST_STATE_NULL: return "NULL";
174 case GST_STATE_READY: return "READY";
175 case GST_STATE_PAUSED: return "PAUSED";
176 case GST_STATE_PLAYING: return "PLAYING";
177 }
178 return "(unknown)";
179}
180#endif // LL_GST_REPORT_STATE_CHANGES
181
182gboolean
183MediaPluginGStreamer010::processGSTEvents(GstBus *bus,
184 GstMessage *message)
185{
186 if (!message)
187 return TRUE; // shield against GStreamer bug
188
189 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED &&
190 GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
191 {
192 DEBUGMSG("Got GST message type: %s",
193 LLGST_MESSAGE_TYPE_NAME (message));
194 }
195 else
196 {
197 DEBUGMSG("Got GST message type: %s",
198 LLGST_MESSAGE_TYPE_NAME (message));
199 }
200
201 switch (GST_MESSAGE_TYPE (message)) {
202 case GST_MESSAGE_BUFFERING: {
203 // NEEDS GST 0.10.11+
204 if (llgst_message_parse_buffering)
205 {
206 gint percent = 0;
207 llgst_message_parse_buffering(message, &percent);
208 DEBUGMSG("GST buffering: %d%%", percent);
209 }
210 break;
211 }
212 case GST_MESSAGE_STATE_CHANGED: {
213 GstState old_state;
214 GstState new_state;
215 GstState pending_state;
216 llgst_message_parse_state_changed(message,
217 &old_state,
218 &new_state,
219 &pending_state);
220#ifdef LL_GST_REPORT_STATE_CHANGES
221 // not generally very useful, and rather spammy.
222 DEBUGMSG("state change (old,<new>,pending): %s,<%s>,%s",
223 get_gst_state_name(old_state),
224 get_gst_state_name(new_state),
225 get_gst_state_name(pending_state));
226#endif // LL_GST_REPORT_STATE_CHANGES
227
228 switch (new_state) {
229 case GST_STATE_VOID_PENDING:
230 break;
231 case GST_STATE_NULL:
232 break;
233 case GST_STATE_READY:
234 setStatus(STATUS_LOADED);
235 break;
236 case GST_STATE_PAUSED:
237 setStatus(STATUS_PAUSED);
238 break;
239 case GST_STATE_PLAYING:
240 setStatus(STATUS_PLAYING);
241 break;
242 }
243 break;
244 }
245 case GST_MESSAGE_ERROR: {
246 GError *err = NULL;
247 gchar *debug = NULL;
248
249 llgst_message_parse_error (message, &err, &debug);
250 WARNMSG("GST error: %s", err?err->message:"(unknown)");
251 if (err)
252 g_error_free (err);
253 g_free (debug);
254
255 mCommand = COMMAND_STOP;
256
257 setStatus(STATUS_ERROR);
258
259 break;
260 }
261 case GST_MESSAGE_INFO: {
262 if (llgst_message_parse_info)
263 {
264 GError *err = NULL;
265 gchar *debug = NULL;
266
267 llgst_message_parse_info (message, &err, &debug);
268 INFOMSG("GST info: %s", err?err->message:"(unknown)");
269 if (err)
270 g_error_free (err);
271 g_free (debug);
272 }
273 break;
274 }
275 case GST_MESSAGE_WARNING: {
276 GError *err = NULL;
277 gchar *debug = NULL;
278
279 llgst_message_parse_warning (message, &err, &debug);
280 WARNMSG("GST warning: %s", err?err->message:"(unknown)");
281 if (err)
282 g_error_free (err);
283 g_free (debug);
284
285 break;
286 }
287 case GST_MESSAGE_EOS:
288 /* end-of-stream */
289 DEBUGMSG("GST end-of-stream.");
290 if (mIsLooping)
291 {
292 DEBUGMSG("looping media...");
293 double eos_pos_sec = 0.0F;
294 bool got_eos_position = getTimePos(eos_pos_sec);
295
296 if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC)
297 {
298 // if we know that the movie is really short, don't
299 // loop it else it can easily become a time-hog
300 // because of GStreamer spin-up overhead
301 DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec);
302 // inject a COMMAND_PAUSE
303 mCommand = COMMAND_PAUSE;
304 }
305 else
306 {
307#undef LLGST_LOOP_BY_SEEKING
308// loop with a stop-start instead of a seek, because it actually seems rather
309// faster than seeking on remote streams.
310#ifdef LLGST_LOOP_BY_SEEKING
311 // first, try looping by an explicit rewind
312 bool seeksuccess = seek(0.0);
313 if (seeksuccess)
314 {
315 play(1.0);
316 }
317 else
318#endif // LLGST_LOOP_BY_SEEKING
319 { // use clumsy stop-start to loop
320 DEBUGMSG("didn't loop by rewinding - stopping and starting instead...");
321 stop();
322 play(1.0);
323 }
324 }
325 }
326 else // not a looping media
327 {
328 // inject a COMMAND_STOP
329 mCommand = COMMAND_STOP;
330 }
331 break;
332 default:
333 /* unhandled message */
334 break;
335 }
336
337 /* we want to be notified again the next time there is a message
338 * on the bus, so return true (false means we want to stop watching
339 * for messages on the bus and our callback should not be called again)
340 */
341 return TRUE;
342}
343
344extern "C" {
345gboolean
346llmediaimplgstreamer_bus_callback (GstBus *bus,
347 GstMessage *message,
348 gpointer data)
349{
350 MediaPluginGStreamer010 *impl = (MediaPluginGStreamer010*)data;
351 return impl->processGSTEvents(bus, message);
352}
353} // extern "C"
354
355
356
357bool
358MediaPluginGStreamer010::navigateTo ( const std::string urlIn )
359{
360 if (!mDoneInit)
361 return false; // error
362
363 setStatus(STATUS_LOADING);
364
365 DEBUGMSG("Setting media URI: %s", urlIn.c_str());
366
367 mSeekWanted = false;
368
369 if (NULL == mPump ||
370 NULL == mPlaybin)
371 {
372 setStatus(STATUS_ERROR);
373 return false; // error
374 }
375
376 // set URI
377 g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
378 //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL);
379
380 // navigateTo implicitly plays, too.
381 play(1.0);
382
383 return true;
384}
385
386
387bool
388MediaPluginGStreamer010::update(int milliseconds)
389{
390 if (!mDoneInit)
391 return false; // error
392
393 DEBUGMSG("updating media...");
394
395 // sanity check
396 if (NULL == mPump ||
397 NULL == mPlaybin)
398 {
399 DEBUGMSG("dead media...");
400 return false;
401 }
402
403 // see if there's an outstanding seek wanted
404 if (mSeekWanted &&
405 // bleh, GST has to be happy that the movie is really truly playing
406 // or it may quietly ignore the seek (with rtsp:// at least).
407 (GST_STATE(mPlaybin) == GST_STATE_PLAYING))
408 {
409 seek(mSeekDestination);
410 mSeekWanted = false;
411 }
412
413 // *TODO: time-limit - but there isn't a lot we can do here, most
414 // time is spent in gstreamer's own opaque worker-threads. maybe
415 // we can do something sneaky like only unlock the video object
416 // for 'milliseconds' and otherwise hold the lock.
417 while (g_main_context_pending(g_main_loop_get_context(mPump)))
418 {
419 g_main_context_iteration(g_main_loop_get_context(mPump), FALSE);
420 }
421
422 // check for availability of a new frame
423
424 if (mVideoSink)
425 {
426 GST_OBJECT_LOCK(mVideoSink);
427 if (mVideoSink->retained_frame_ready)
428 {
429 DEBUGMSG("NEW FRAME READY");
430
431 if (mVideoSink->retained_frame_width != mNaturalWidth ||
432 mVideoSink->retained_frame_height != mNaturalHeight)
433 // *TODO: also check for change in format
434 {
435 // just resize container, don't consume frame
436 int neww = mVideoSink->retained_frame_width;
437 int newh = mVideoSink->retained_frame_height;
438
439 int newd = 4;
440 mTextureFormatPrimary = GL_RGBA;
441 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
442
443 /*
444 int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format];
445 if (SLV_PF_BGRX == mVideoSink->retained_frame_format)
446 {
447 mTextureFormatPrimary = GL_BGRA;
448 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
449 }
450 else
451 {
452 mTextureFormatPrimary = GL_RGBA;
453 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
454 }
455 */
456
457 GST_OBJECT_UNLOCK(mVideoSink);
458
459 mNaturalRowbytes = neww * newd;
460 DEBUGMSG("video container resized to %dx%d",
461 neww, newh);
462
463 mDepth = newd;
464 mNaturalWidth = neww;
465 mNaturalHeight = newh;
466 sizeChanged();
467 return true;
468 }
469
470 if (mPixels &&
471 mNaturalHeight <= mHeight &&
472 mNaturalWidth <= mWidth &&
473 !mTextureSegmentName.empty())
474 {
475
476 // we're gonna totally consume this frame - reset 'ready' flag
477 mVideoSink->retained_frame_ready = FALSE;
478 int destination_rowbytes = mWidth * mDepth;
479 for (int row=0; row<mNaturalHeight; ++row)
480 {
481 memcpy(&mPixels
482 [destination_rowbytes * row],
483 &mVideoSink->retained_frame_data
484 [mNaturalRowbytes * row],
485 mNaturalRowbytes);
486 }
487
488 GST_OBJECT_UNLOCK(mVideoSink);
489 DEBUGMSG("NEW FRAME REALLY TRULY CONSUMED, TELLING HOST");
490
491 setDirty(0,0,mNaturalWidth,mNaturalHeight);
492 }
493 else
494 {
495 // new frame ready, but we're not ready to
496 // consume it.
497
498 GST_OBJECT_UNLOCK(mVideoSink);
499
500 DEBUGMSG("NEW FRAME not consumed, still waiting for a shm segment and/or shm resize");
501 }
502
503 return true;
504 }
505 else
506 {
507 // nothing to do yet.
508 GST_OBJECT_UNLOCK(mVideoSink);
509 return true;
510 }
511 }
512
513 return true;
514}
515
516
517void
518MediaPluginGStreamer010::mouseDown( int x, int y )
519{
520 // do nothing
521}
522
523void
524MediaPluginGStreamer010::mouseUp( int x, int y )
525{
526 // do nothing
527}
528
529void
530MediaPluginGStreamer010::mouseMove( int x, int y )
531{
532 // do nothing
533}
534
535
536bool
537MediaPluginGStreamer010::pause()
538{
539 DEBUGMSG("pausing media...");
540 // todo: error-check this?
541 llgst_element_set_state(mPlaybin, GST_STATE_PAUSED);
542 return true;
543}
544
545bool
546MediaPluginGStreamer010::stop()
547{
548 DEBUGMSG("stopping media...");
549 // todo: error-check this?
550 llgst_element_set_state(mPlaybin, GST_STATE_READY);
551 return true;
552}
553
554bool
555MediaPluginGStreamer010::play(double rate)
556{
557 // NOTE: we don't actually support non-natural rate.
558
559 DEBUGMSG("playing media... rate=%f", rate);
560 // todo: error-check this?
561 llgst_element_set_state(mPlaybin, GST_STATE_PLAYING);
562 return true;
563}
564
565bool
566MediaPluginGStreamer010::setVolume( float volume )
567{
568 // we try to only update volume as conservatively as
569 // possible, as many gst-plugins-base versions up to at least
570 // November 2008 have critical race-conditions in setting volume - sigh
571 if (mVolume == volume)
572 return true; // nothing to do, everything's fine
573
574 mVolume = volume;
575 if (mDoneInit && mPlaybin)
576 {
577 g_object_set(mPlaybin, "volume", mVolume, NULL);
578 return true;
579 }
580
581 return false;
582}
583
584bool
585MediaPluginGStreamer010::seek(double time_sec)
586{
587 bool success = false;
588 if (mDoneInit && mPlaybin)
589 {
590 success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME,
591 GstSeekFlags(GST_SEEK_FLAG_FLUSH |
592 GST_SEEK_FLAG_KEY_UNIT),
593 GST_SEEK_TYPE_SET, gint64(time_sec*GST_SECOND),
594 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
595 }
596 DEBUGMSG("MEDIA SEEK REQUEST to %fsec result was %d",
597 float(time_sec), int(success));
598 return success;
599}
600
601bool
602MediaPluginGStreamer010::getTimePos(double &sec_out)
603{
604 bool got_position = false;
605 if (mPlaybin)
606 {
607 gint64 pos;
608 GstFormat timefmt = GST_FORMAT_TIME;
609 got_position =
610 llgst_element_query_position &&
611 llgst_element_query_position(mPlaybin,
612 &timefmt,
613 &pos);
614 got_position = got_position
615 && (timefmt == GST_FORMAT_TIME);
616 // GStreamer may have other ideas, but we consider the current position
617 // undefined if not PLAYING or PAUSED
618 got_position = got_position &&
619 (GST_STATE(mPlaybin) == GST_STATE_PLAYING ||
620 GST_STATE(mPlaybin) == GST_STATE_PAUSED);
621 if (got_position && !GST_CLOCK_TIME_IS_VALID(pos))
622 {
623 if (GST_STATE(mPlaybin) == GST_STATE_PLAYING)
624 {
625 // if we're playing then we treat an invalid clock time
626 // as 0, for complicated reasons (insert reason here)
627 pos = 0;
628 }
629 else
630 {
631 got_position = false;
632 }
633
634 }
635 // If all the preconditions succeeded... we can trust the result.
636 if (got_position)
637 {
638 sec_out = double(pos) / double(GST_SECOND); // gst to sec
639 }
640 }
641 return got_position;
642}
643
644bool
645MediaPluginGStreamer010::load()
646{
647 if (!mDoneInit)
648 return false; // error
649
650 setStatus(STATUS_LOADING);
651
652 DEBUGMSG("setting up media...");
653
654 mIsLooping = false;
655 mVolume = 0.1234567; // minor hack to force an initial volume update
656
657 // Create a pumpable main-loop for this media
658 mPump = g_main_loop_new (NULL, FALSE);
659 if (!mPump)
660 {
661 setStatus(STATUS_ERROR);
662 return false; // error
663 }
664
665 // instantiate a playbin element to do the hard work
666 mPlaybin = llgst_element_factory_make ("playbin", "play");
667 if (!mPlaybin)
668 {
669 setStatus(STATUS_ERROR);
670 return false; // error
671 }
672
673 // get playbin's bus
674 GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
675 if (!bus)
676 {
677 setStatus(STATUS_ERROR);
678 return false; // error
679 }
680 mBusWatchID = llgst_bus_add_watch (bus,
681 llmediaimplgstreamer_bus_callback,
682 this);
683 llgst_object_unref (bus);
684
685 if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
686 // instantiate a custom video sink
687 mVideoSink =
688 GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo"));
689 if (!mVideoSink)
690 {
691 WARNMSG("Could not instantiate private-slvideo element.");
692 // todo: cleanup.
693 setStatus(STATUS_ERROR);
694 return false; // error
695 }
696
697 // connect the pieces
698 g_object_set(mPlaybin, "video-sink", mVideoSink, NULL);
699 }
700
701 return true;
702}
703
704bool
705MediaPluginGStreamer010::unload ()
706{
707 if (!mDoneInit)
708 return false; // error
709
710 DEBUGMSG("unloading media...");
711
712 // stop getting callbacks for this bus
713 g_source_remove(mBusWatchID);
714 mBusWatchID = 0;
715
716 if (mPlaybin)
717 {
718 llgst_element_set_state (mPlaybin, GST_STATE_NULL);
719 llgst_object_unref (GST_OBJECT (mPlaybin));
720 mPlaybin = NULL;
721 }
722
723 if (mPump)
724 {
725 g_main_loop_quit(mPump);
726 mPump = NULL;
727 }
728
729 mVideoSink = NULL;
730
731 setStatus(STATUS_NONE);
732
733 return true;
734}
735
736
737//static
738bool
739MediaPluginGStreamer010::startup()
740{
741 // first - check if GStreamer is explicitly disabled
742 if (NULL != getenv("LL_DISABLE_GSTREAMER"))
743 return false;
744
745 // only do global GStreamer initialization once.
746 if (!mDoneInit)
747 {
748 g_thread_init(NULL);
749
750 // Init the glib type system - we need it.
751 g_type_init();
752
753 // Get symbols!
754#if LL_DARWIN
755 if (! grab_gst_syms("libgstreamer-0.10.dylib",
756 "libgstvideo-0.10.dylib") )
757#elseif LL_WINDOWS
758 if (! grab_gst_syms("libgstreamer-0.10.dll",
759 "libgstvideo-0.10.dll") )
760#else // linux or other ELFy unixoid
761 if (! grab_gst_syms("libgstreamer-0.10.so.0",
762 "libgstvideo-0.10.so.0") )
763#endif
764 {
765 WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled.");
766 return false;
767 }
768
769 if (llgst_segtrap_set_enabled)
770 {
771 llgst_segtrap_set_enabled(FALSE);
772 }
773 else
774 {
775 WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught.");
776 }
777
778#if LL_LINUX
779 // Gstreamer tries a fork during init, waitpid-ing on it,
780 // which conflicts with any installed SIGCHLD handler...
781 struct sigaction tmpact, oldact;
782 if (llgst_registry_fork_set_enabled) {
783 // if we can disable SIGCHLD-using forking behaviour,
784 // do it.
785 llgst_registry_fork_set_enabled(false);
786 }
787 else {
788 // else temporarily install default SIGCHLD handler
789 // while GStreamer initialises
790 tmpact.sa_handler = SIG_DFL;
791 sigemptyset( &tmpact.sa_mask );
792 tmpact.sa_flags = SA_SIGINFO;
793 sigaction(SIGCHLD, &tmpact, &oldact);
794 }
795#endif // LL_LINUX
796
797 // Protect against GStreamer resetting the locale, yuck.
798 static std::string saved_locale;
799 saved_locale = setlocale(LC_ALL, NULL);
800
801 // finally, try to initialize GStreamer!
802 GError *err = NULL;
803 gboolean init_gst_success = llgst_init_check(NULL, NULL, &err);
804
805 // restore old locale
806 setlocale(LC_ALL, saved_locale.c_str() );
807
808#if LL_LINUX
809 // restore old SIGCHLD handler
810 if (!llgst_registry_fork_set_enabled)
811 sigaction(SIGCHLD, &oldact, NULL);
812#endif // LL_LINUX
813
814 if (!init_gst_success) // fail
815 {
816 if (err)
817 {
818 WARNMSG("GST init failed: %s", err->message);
819 g_error_free(err);
820 }
821 else
822 {
823 WARNMSG("GST init failed for unspecified reason.");
824 }
825 return false;
826 }
827
828 // Init our custom plugins - only really need do this once.
829 gst_slvideo_init_class();
830
831 mDoneInit = true;
832 }
833
834 return true;
835}
836
837
838bool
839MediaPluginGStreamer010::sizeChanged()
840{
841 // the shared writing space has possibly changed size/location/whatever
842
843 // Check to see whether the movie's natural size has updated
844 if (mNaturalWidth != mPreviousNaturalWidth ||
845 mNaturalHeight != mPreviousNaturalHeight)
846 {
847 mPreviousNaturalWidth = mNaturalWidth;
848 mPreviousNaturalHeight = mNaturalHeight;
849
850 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request");
851 message.setValue("name", mTextureSegmentName);
852 message.setValueS32("width", mNaturalWidth);
853 message.setValueS32("height", mNaturalHeight);
854 DEBUGMSG("<--- Sending size change request to application with name: '%s' - size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight);
855 sendMessage(message);
856 }
857
858 return true;
859}
860
861
862
863//static
864bool
865MediaPluginGStreamer010::closedown()
866{
867 if (!mDoneInit)
868 return false; // error
869
870 ungrab_gst_syms();
871
872 mDoneInit = false;
873
874 return true;
875}
876
877MediaPluginGStreamer010::~MediaPluginGStreamer010()
878{
879 DEBUGMSG("MediaPluginGStreamer010 destructor");
880
881 closedown();
882
883 DEBUGMSG("GStreamer010 closing down");
884}
885
886
887std::string
888MediaPluginGStreamer010::getVersion()
889{
890 std::string plugin_version = "GStreamer010 media plugin, GStreamer version ";
891 if (mDoneInit &&
892 llgst_version)
893 {
894 guint major, minor, micro, nano;
895 llgst_version(&major, &minor, &micro, &nano);
896 plugin_version += llformat("%u.%u.%u.%u (runtime), %u.%u.%u.%u (headers)", (unsigned int)major, (unsigned int)minor, (unsigned int)micro, (unsigned int)nano, (unsigned int)GST_VERSION_MAJOR, (unsigned int)GST_VERSION_MINOR, (unsigned int)GST_VERSION_MICRO, (unsigned int)GST_VERSION_NANO);
897 }
898 else
899 {
900 plugin_version += "(unknown)";
901 }
902 return plugin_version;
903}
904
905void MediaPluginGStreamer010::receiveMessage(const char *message_string)
906{
907 //std::cerr << "MediaPluginGStreamer010::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
908
909 LLPluginMessage message_in;
910
911 if(message_in.parse(message_string) >= 0)
912 {
913 std::string message_class = message_in.getClass();
914 std::string message_name = message_in.getName();
915 if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
916 {
917 if(message_name == "init")
918 {
919 LLPluginMessage message("base", "init_response");
920 LLSD versions = LLSD::emptyMap();
921 versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
922 versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
923 versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION;
924 message.setValueLLSD("versions", versions);
925
926 if ( load() )
927 {
928 DEBUGMSG("GStreamer010 media instance set up");
929 }
930 else
931 {
932 WARNMSG("GStreamer010 media instance failed to set up");
933 }
934
935 message.setValue("plugin_version", getVersion());
936 sendMessage(message);
937
938 // Plugin gets to decide the texture parameters to use.
939 message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
940 // lame to have to decide this now, it depends on the movie. Oh well.
941 mDepth = 4;
942
943 mNaturalWidth = 1;
944 mNaturalHeight = 1;
945 mPreviousNaturalWidth = 1;
946 mPreviousNaturalHeight = 1;
947 mWidth = 1;
948 mHeight = 1;
949 mTextureWidth = 1;
950 mTextureHeight = 1;
951
952 message.setValueU32("format", GL_RGBA);
953 message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV);
954
955 message.setValueS32("depth", mDepth);
956 message.setValueS32("default_width", mWidth);
957 message.setValueS32("default_height", mHeight);
958 message.setValueU32("internalformat", GL_RGBA8);
959 message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left.
960 message.setValueBoolean("allow_downsample", true); // we respond with grace and performance if asked to downscale
961 sendMessage(message);
962 }
963 else if(message_name == "idle")
964 {
965 // no response is necessary here.
966 double time = message_in.getValueReal("time");
967
968 // Convert time to milliseconds for update()
969 update((int)(time * 1000.0f));
970 }
971 else if(message_name == "cleanup")
972 {
973 unload();
974 closedown();
975 }
976 else if(message_name == "shm_added")
977 {
978 SharedSegmentInfo info;
979 info.mAddress = message_in.getValuePointer("address");
980 info.mSize = (size_t)message_in.getValueS32("size");
981 std::string name = message_in.getValue("name");
982
983 std::ostringstream str;
984 INFOMSG("MediaPluginGStreamer010::receiveMessage: shared memory added, name: %s, size: %d, address: %p", name.c_str(), int(info.mSize), info.mAddress);
985
986 mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
987
988 }
989 else if(message_name == "shm_remove")
990 {
991 std::string name = message_in.getValue("name");
992
993 DEBUGMSG("MediaPluginGStreamer010::receiveMessage: shared memory remove, name = %s", name.c_str());
994
995 SharedSegmentMap::iterator iter = mSharedSegments.find(name);
996 if(iter != mSharedSegments.end())
997 {
998 if(mPixels == iter->second.mAddress)
999 {
1000 // This is the currently active pixel buffer. Make sure we stop drawing to it.
1001 mPixels = NULL;
1002 mTextureSegmentName.clear();
1003
1004 // Make sure the movie decoder is no longer pointed at the shared segment.
1005 sizeChanged();
1006 }
1007 mSharedSegments.erase(iter);
1008 }
1009 else
1010 {
1011 WARNMSG("MediaPluginGStreamer010::receiveMessage: unknown shared memory region!");
1012 }
1013
1014 // Send the response so it can be cleaned up.
1015 LLPluginMessage message("base", "shm_remove_response");
1016 message.setValue("name", name);
1017 sendMessage(message);
1018 }
1019 else
1020 {
1021 std::ostringstream str;
1022 INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown base message: %s", message_name.c_str());
1023 }
1024 }
1025 else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
1026 {
1027 if(message_name == "size_change")
1028 {
1029 std::string name = message_in.getValue("name");
1030 S32 width = message_in.getValueS32("width");
1031 S32 height = message_in.getValueS32("height");
1032 S32 texture_width = message_in.getValueS32("texture_width");
1033 S32 texture_height = message_in.getValueS32("texture_height");
1034
1035 std::ostringstream str;
1036 INFOMSG("---->Got size change instruction from application with shm name: %s - size is %d x %d", name.c_str(), width, height);
1037
1038 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
1039 message.setValue("name", name);
1040 message.setValueS32("width", width);
1041 message.setValueS32("height", height);
1042 message.setValueS32("texture_width", texture_width);
1043 message.setValueS32("texture_height", texture_height);
1044 sendMessage(message);
1045
1046 if(!name.empty())
1047 {
1048 // Find the shared memory region with this name
1049 SharedSegmentMap::iterator iter = mSharedSegments.find(name);
1050 if(iter != mSharedSegments.end())
1051 {
1052 INFOMSG("*** Got size change with matching shm, new size is %d x %d", width, height);
1053 INFOMSG("*** Got size change with matching shm, texture size size is %d x %d", texture_width, texture_height);
1054
1055 mPixels = (unsigned char*)iter->second.mAddress;
1056 mTextureSegmentName = name;
1057 mWidth = width;
1058 mHeight = height;
1059
1060 if (texture_width > 1 ||
1061 texture_height > 1) // not a dummy size from the app, a real explicit forced size
1062 {
1063 INFOMSG("**** = REAL RESIZE REQUEST FROM APP");
1064
1065 GST_OBJECT_LOCK(mVideoSink);
1066 mVideoSink->resize_forced = true;
1067 mVideoSink->resize_try_width = texture_width;
1068 mVideoSink->resize_try_height = texture_height;
1069 GST_OBJECT_UNLOCK(mVideoSink);
1070 }
1071
1072 mTextureWidth = texture_width;
1073 mTextureHeight = texture_height;
1074 }
1075 }
1076 }
1077 else if(message_name == "load_uri")
1078 {
1079 std::string uri = message_in.getValue("uri");
1080 navigateTo( uri );
1081 sendStatus();
1082 }
1083 else if(message_name == "mouse_event")
1084 {
1085 std::string event = message_in.getValue("event");
1086 S32 x = message_in.getValueS32("x");
1087 S32 y = message_in.getValueS32("y");
1088
1089 if(event == "down")
1090 {
1091 mouseDown(x, y);
1092 }
1093 else if(event == "up")
1094 {
1095 mouseUp(x, y);
1096 }
1097 else if(event == "move")
1098 {
1099 mouseMove(x, y);
1100 };
1101 };
1102 }
1103 else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
1104 {
1105 if(message_name == "stop")
1106 {
1107 stop();
1108 }
1109 else if(message_name == "start")
1110 {
1111 double rate = 0.0;
1112 if(message_in.hasValue("rate"))
1113 {
1114 rate = message_in.getValueReal("rate");
1115 }
1116 // NOTE: we don't actually support rate.
1117 play(rate);
1118 }
1119 else if(message_name == "pause")
1120 {
1121 pause();
1122 }
1123 else if(message_name == "seek")
1124 {
1125 double time = message_in.getValueReal("time");
1126 // defer the actual seek in case we haven't
1127 // really truly started yet in which case there
1128 // is nothing to seek upon
1129 mSeekWanted = true;
1130 mSeekDestination = time;
1131 }
1132 else if(message_name == "set_loop")
1133 {
1134 bool loop = message_in.getValueBoolean("loop");
1135 mIsLooping = loop;
1136 }
1137 else if(message_name == "set_volume")
1138 {
1139 double volume = message_in.getValueReal("volume");
1140 setVolume(volume);
1141 }
1142 }
1143 else
1144 {
1145 INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown message class: %s", message_class.c_str());
1146 }
1147 }
1148}
1149
1150int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
1151{
1152 if (MediaPluginGStreamer010::startup())
1153 {
1154 MediaPluginGStreamer010 *self = new MediaPluginGStreamer010(host_send_func, host_user_data);
1155 *plugin_send_func = MediaPluginGStreamer010::staticReceiveMessage;
1156 *plugin_user_data = (void*)self;
1157
1158 return 0; // okay
1159 }
1160 else
1161 {
1162 return -1; // failed to init
1163 }
1164}
1165
1166#else // LL_GSTREAMER010_ENABLED
1167
1168// Stubbed-out class with constructor/destructor (necessary or windows linker
1169// will just think its dead code and optimize it all out)
1170class MediaPluginGStreamer010 : public MediaPluginBase
1171{
1172public:
1173 MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
1174 ~MediaPluginGStreamer010();
1175 /* virtual */ void receiveMessage(const char *message_string);
1176};
1177
1178MediaPluginGStreamer010::MediaPluginGStreamer010(
1179 LLPluginInstance::sendMessageFunction host_send_func,
1180 void *host_user_data ) :
1181 MediaPluginBase(host_send_func, host_user_data)
1182{
1183 // no-op
1184}
1185
1186MediaPluginGStreamer010::~MediaPluginGStreamer010()
1187{
1188 // no-op
1189}
1190
1191void MediaPluginGStreamer010::receiveMessage(const char *message_string)
1192{
1193 // no-op
1194}
1195
1196// We're building without GStreamer enabled. Just refuse to initialize.
1197int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
1198{
1199 return -1;
1200}
1201
1202#endif // LL_GSTREAMER010_ENABLED
diff --git a/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp~ b/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp~
new file mode 100755
index 0000000..7d34a1e
--- /dev/null
+++ b/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp~
@@ -0,0 +1,1219 @@
1/**
2 * @file media_plugin_gstreamer010.cpp
3 * @brief GStreamer-0.10 plugin for LLMedia API plugin system
4 *
5 * @cond
6 * $LicenseInfo:firstyear=2007&license=viewergpl$
7 *
8 * Copyright (c) 2007-2010, Linden Research, Inc.
9 *
10 * Second Life Viewer Source Code
11 * The source code in this file ("Source Code") is provided by Linden Lab
12 * to you under the terms of the GNU General Public License, version 2.0
13 * ("GPL"), unless you have obtained a separate licensing agreement
14 * ("Other License"), formally executed by you and Linden Lab. Terms of
15 * the GPL can be found in doc/GPL-license.txt in this distribution, or
16 * online at http://secondlife.com/developers/opensource/gplv2
17 *
18 * There are special exceptions to the terms and conditions of the GPL as
19 * it is applied to this Source Code. View the full text of the exception
20 * in the file doc/FLOSS-exception.txt in this software distribution, or
21 * online at
22 * http://secondlife.com/developers/opensource/flossexception
23 *
24 * By copying, modifying or distributing this software, you acknowledge
25 * that you have read and understood your obligations described above,
26 * and agree to abide by those obligations.
27 *
28 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
29 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
30 * COMPLETENESS OR PERFORMANCE.
31 * $/LicenseInfo$
32 *
33 * @endcond
34 */
35
36#include "linden_common.h"
37
38#include "llgl.h"
39
40#include "llplugininstance.h"
41#include "llpluginmessage.h"
42#include "llpluginmessageclasses.h"
43#include "media_plugin_base.h"
44
45#if LL_GSTREAMER010_ENABLED
46
47extern "C" {
48#include <gst/gst.h>
49}
50
51#include "llmediaimplgstreamer.h"
52#include "llmediaimplgstreamertriviallogging.h"
53
54#include "llmediaimplgstreamervidplug.h"
55
56#include "llmediaimplgstreamer_syms.h"
57
58//////////////////////////////////////////////////////////////////////////////
59//
60class MediaPluginGStreamer010 : public MediaPluginBase
61{
62public:
63 MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
64 ~MediaPluginGStreamer010();
65
66 /* virtual */ void receiveMessage(const char *message_string);
67
68 static bool startup();
69 static bool closedown();
70
71 gboolean processGSTEvents(GstBus *bus,
72 GstMessage *message);
73
74private:
75 std::string getVersion();
76 bool navigateTo( const std::string urlIn );
77 bool seek( double time_sec );
78 bool setVolume( float volume );
79
80 // misc
81 bool pause();
82 bool stop();
83 bool play(double rate);
84 bool getTimePos(double &sec_out);
85
86 static const double MIN_LOOP_SEC = 1.0F;
87
88 bool mIsLooping;
89
90 enum ECommand {
91 COMMAND_NONE,
92 COMMAND_STOP,
93 COMMAND_PLAY,
94 COMMAND_FAST_FORWARD,
95 COMMAND_FAST_REWIND,
96 COMMAND_PAUSE,
97 COMMAND_SEEK,
98 };
99 ECommand mCommand;
100
101private:
102 bool unload();
103 bool load();
104
105 bool update(int milliseconds);
106 void mouseDown( int x, int y );
107 void mouseUp( int x, int y );
108 void mouseMove( int x, int y );
109
110 void sizeChanged();
111
112 static bool mDoneInit;
113
114 guint mBusWatchID;
115
116 float mVolume;
117
118 int mDepth;
119
120 // media NATURAL size
121 int mNaturalWidth;
122 int mNaturalHeight;
123 // media current size
124 int mCurrentWidth;
125 int mCurrentHeight;
126 int mCurrentRowbytes;
127 // previous media size so we can detect changes
128 int mPreviousWidth;
129 int mPreviousHeight;
130 // desired render size from host
131 int mWidth;
132 int mHeight;
133 // padded texture size we need to write into
134 int mTextureWidth;
135 int mTextureHeight;
136
137 int mTextureFormatPrimary;
138 int mTextureFormatType;
139
140 bool mSeekWanted;
141 double mSeekDestination;
142
143 // Very GStreamer-specific
144 GMainLoop *mPump; // event pump for this media
145 GstElement *mPlaybin;
146 GstSLVideo *mVideoSink;
147};
148
149//static
150bool MediaPluginGStreamer010::mDoneInit = false;
151
152MediaPluginGStreamer010::MediaPluginGStreamer010(
153 LLPluginInstance::sendMessageFunction host_send_func,
154 void *host_user_data ) :
155 MediaPluginBase(host_send_func, host_user_data),
156 mBusWatchID ( 0 ),
157 mCurrentRowbytes ( 4 ),
158 mTextureFormatPrimary ( GL_RGBA ),
159 mTextureFormatType ( GL_UNSIGNED_INT_8_8_8_8_REV ),
160 mSeekWanted(false),
161 mSeekDestination(0.0),
162 mPump ( NULL ),
163 mPlaybin ( NULL ),
164 mVideoSink ( NULL ),
165 mCommand ( COMMAND_NONE )
166{
167 std::ostringstream str;
168 INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(getpid()));
169}
170
171///////////////////////////////////////////////////////////////////////////////
172//
173//#define LL_GST_REPORT_STATE_CHANGES
174#ifdef LL_GST_REPORT_STATE_CHANGES
175static char* get_gst_state_name(GstState state)
176{
177 switch (state) {
178 case GST_STATE_VOID_PENDING: return "VOID_PENDING";
179 case GST_STATE_NULL: return "NULL";
180 case GST_STATE_READY: return "READY";
181 case GST_STATE_PAUSED: return "PAUSED";
182 case GST_STATE_PLAYING: return "PLAYING";
183 }
184 return "(unknown)";
185}
186#endif // LL_GST_REPORT_STATE_CHANGES
187
188gboolean
189MediaPluginGStreamer010::processGSTEvents(GstBus *bus,
190 GstMessage *message)
191{
192 if (!message)
193 return TRUE; // shield against GStreamer bug
194
195 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED &&
196 GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
197 {
198 DEBUGMSG("Got GST message type: %s",
199 LLGST_MESSAGE_TYPE_NAME (message));
200 }
201 else
202 {
203 // TODO: grok 'duration' message type
204 DEBUGMSG("Got GST message type: %s",
205 LLGST_MESSAGE_TYPE_NAME (message));
206 }
207
208 switch (GST_MESSAGE_TYPE (message)) {
209 case GST_MESSAGE_BUFFERING: {
210 // NEEDS GST 0.10.11+
211 if (llgst_message_parse_buffering)
212 {
213 gint percent = 0;
214 llgst_message_parse_buffering(message, &percent);
215 DEBUGMSG("GST buffering: %d%%", percent);
216 }
217 break;
218 }
219 case GST_MESSAGE_STATE_CHANGED: {
220 GstState old_state;
221 GstState new_state;
222 GstState pending_state;
223 llgst_message_parse_state_changed(message,
224 &old_state,
225 &new_state,
226 &pending_state);
227#ifdef LL_GST_REPORT_STATE_CHANGES
228 // not generally very useful, and rather spammy.
229 DEBUGMSG("state change (old,<new>,pending): %s,<%s>,%s",
230 get_gst_state_name(old_state),
231 get_gst_state_name(new_state),
232 get_gst_state_name(pending_state));
233#endif // LL_GST_REPORT_STATE_CHANGES
234
235 switch (new_state) {
236 case GST_STATE_VOID_PENDING:
237 break;
238 case GST_STATE_NULL:
239 break;
240 case GST_STATE_READY:
241 setStatus(STATUS_LOADED);
242 break;
243 case GST_STATE_PAUSED:
244 setStatus(STATUS_PAUSED);
245 break;
246 case GST_STATE_PLAYING:
247 setStatus(STATUS_PLAYING);
248 break;
249 }
250 break;
251 }
252 case GST_MESSAGE_ERROR: {
253 GError *err = NULL;
254 gchar *debug = NULL;
255
256 llgst_message_parse_error (message, &err, &debug);
257 WARNMSG("GST error: %s", err?err->message:"(unknown)");
258 if (err)
259 g_error_free (err);
260 g_free (debug);
261
262 mCommand = COMMAND_STOP;
263
264 setStatus(STATUS_ERROR);
265
266 break;
267 }
268 case GST_MESSAGE_INFO: {
269 if (llgst_message_parse_info)
270 {
271 GError *err = NULL;
272 gchar *debug = NULL;
273
274 llgst_message_parse_info (message, &err, &debug);
275 INFOMSG("GST info: %s", err?err->message:"(unknown)");
276 if (err)
277 g_error_free (err);
278 g_free (debug);
279 }
280 break;
281 }
282 case GST_MESSAGE_WARNING: {
283 GError *err = NULL;
284 gchar *debug = NULL;
285
286 llgst_message_parse_warning (message, &err, &debug);
287 WARNMSG("GST warning: %s", err?err->message:"(unknown)");
288 if (err)
289 g_error_free (err);
290 g_free (debug);
291
292 break;
293 }
294 case GST_MESSAGE_EOS:
295 /* end-of-stream */
296 DEBUGMSG("GST end-of-stream.");
297 if (mIsLooping)
298 {
299 DEBUGMSG("looping media...");
300 double eos_pos_sec = 0.0F;
301 bool got_eos_position = getTimePos(eos_pos_sec);
302
303 if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC)
304 {
305 // if we know that the movie is really short, don't
306 // loop it else it can easily become a time-hog
307 // because of GStreamer spin-up overhead
308 DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec);
309 // inject a COMMAND_PAUSE
310 mCommand = COMMAND_PAUSE;
311 }
312 else
313 {
314#undef LLGST_LOOP_BY_SEEKING
315// loop with a stop-start instead of a seek, because it actually seems rather
316// faster than seeking on remote streams.
317#ifdef LLGST_LOOP_BY_SEEKING
318 // first, try looping by an explicit rewind
319 bool seeksuccess = seek(0.0);
320 if (seeksuccess)
321 {
322 play(1.0);
323 }
324 else
325#endif // LLGST_LOOP_BY_SEEKING
326 { // use clumsy stop-start to loop
327 DEBUGMSG("didn't loop by rewinding - stopping and starting instead...");
328 stop();
329 play(1.0);
330 }
331 }
332 }
333 else // not a looping media
334 {
335 // inject a COMMAND_STOP
336 mCommand = COMMAND_STOP;
337 }
338 break;
339 default:
340 /* unhandled message */
341 break;
342 }
343
344 /* we want to be notified again the next time there is a message
345 * on the bus, so return true (false means we want to stop watching
346 * for messages on the bus and our callback should not be called again)
347 */
348 return TRUE;
349}
350
351extern "C" {
352gboolean
353llmediaimplgstreamer_bus_callback (GstBus *bus,
354 GstMessage *message,
355 gpointer data)
356{
357 MediaPluginGStreamer010 *impl = (MediaPluginGStreamer010*)data;
358 return impl->processGSTEvents(bus, message);
359}
360} // extern "C"
361
362
363
364bool
365MediaPluginGStreamer010::navigateTo ( const std::string urlIn )
366{
367 if (!mDoneInit)
368 return false; // error
369
370 setStatus(STATUS_LOADING);
371
372 DEBUGMSG("Setting media URI: %s", urlIn.c_str());
373
374 mSeekWanted = false;
375
376 if (NULL == mPump ||
377 NULL == mPlaybin)
378 {
379 setStatus(STATUS_ERROR);
380 return false; // error
381 }
382
383 // set URI
384 g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
385 //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL);
386
387 // navigateTo implicitly plays, too.
388 play(1.0);
389
390 return true;
391}
392
393
394bool
395MediaPluginGStreamer010::update(int milliseconds)
396{
397 if (!mDoneInit)
398 return false; // error
399
400 DEBUGMSG("updating media...");
401
402 // sanity check
403 if (NULL == mPump ||
404 NULL == mPlaybin)
405 {
406 DEBUGMSG("dead media...");
407 return false;
408 }
409
410 // see if there's an outstanding seek wanted
411 if (mSeekWanted &&
412 // bleh, GST has to be happy that the movie is really truly playing
413 // or it may quietly ignore the seek (with rtsp:// at least).
414 (GST_STATE(mPlaybin) == GST_STATE_PLAYING))
415 {
416 seek(mSeekDestination);
417 mSeekWanted = false;
418 }
419
420 // *TODO: time-limit - but there isn't a lot we can do here, most
421 // time is spent in gstreamer's own opaque worker-threads. maybe
422 // we can do something sneaky like only unlock the video object
423 // for 'milliseconds' and otherwise hold the lock.
424 while (g_main_context_pending(g_main_loop_get_context(mPump)))
425 {
426 g_main_context_iteration(g_main_loop_get_context(mPump), FALSE);
427 }
428
429 // check for availability of a new frame
430
431 if (mVideoSink)
432 {
433 GST_OBJECT_LOCK(mVideoSink);
434 if (mVideoSink->retained_frame_ready)
435 {
436 DEBUGMSG("NEW FRAME READY");
437
438 if (mVideoSink->retained_frame_width != mCurrentWidth ||
439 mVideoSink->retained_frame_height != mCurrentHeight)
440 // *TODO: also check for change in format
441 {
442 // just resize container, don't consume frame
443 int neww = mVideoSink->retained_frame_width;
444 int newh = mVideoSink->retained_frame_height;
445
446 int newd = 4;
447 mTextureFormatPrimary = GL_RGBA;
448 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
449
450 /*
451 int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format];
452 if (SLV_PF_BGRX == mVideoSink->retained_frame_format)
453 {
454 mTextureFormatPrimary = GL_BGRA;
455 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
456 }
457 else
458 {
459 mTextureFormatPrimary = GL_RGBA;
460 mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
461 }
462 */
463
464 GST_OBJECT_UNLOCK(mVideoSink);
465
466 mCurrentRowbytes = neww * newd;
467 DEBUGMSG("video container resized to %dx%d",
468 neww, newh);
469
470 mDepth = newd;
471 mCurrentWidth = neww;
472 mCurrentHeight = newh;
473 sizeChanged();
474 return true;
475 }
476
477 if (mPixels &&
478 mCurrentHeight <= mHeight &&
479 mCurrentWidth <= mWidth &&
480 !mTextureSegmentName.empty())
481 {
482 // we're gonna totally consume this frame - reset 'ready' flag
483 mVideoSink->retained_frame_ready = FALSE;
484 int destination_rowbytes = mWidth * mDepth;
485 for (int row=0; row<mCurrentHeight; ++row)
486 {
487 memcpy(&mPixels
488 [destination_rowbytes * row],
489 &mVideoSink->retained_frame_data
490 [mCurrentRowbytes * row],
491 mCurrentRowbytes);
492 }
493
494 GST_OBJECT_UNLOCK(mVideoSink);
495 DEBUGMSG("NEW FRAME REALLY TRULY CONSUMED, TELLING HOST");
496
497 setDirty(0,0,mCurrentWidth,mCurrentHeight);
498 }
499 else
500 {
501 // new frame ready, but we're not ready to
502 // consume it.
503
504 GST_OBJECT_UNLOCK(mVideoSink);
505
506 DEBUGMSG("NEW FRAME not consumed, still waiting for a shm segment and/or shm resize");
507 }
508
509 return true;
510 }
511 else
512 {
513 // nothing to do yet.
514 GST_OBJECT_UNLOCK(mVideoSink);
515 return true;
516 }
517 }
518
519 return true;
520}
521
522
523void
524MediaPluginGStreamer010::mouseDown( int x, int y )
525{
526 // do nothing
527}
528
529void
530MediaPluginGStreamer010::mouseUp( int x, int y )
531{
532 // do nothing
533}
534
535void
536MediaPluginGStreamer010::mouseMove( int x, int y )
537{
538 // do nothing
539}
540
541
542bool
543MediaPluginGStreamer010::pause()
544{
545 DEBUGMSG("pausing media...");
546 // todo: error-check this?
547 llgst_element_set_state(mPlaybin, GST_STATE_PAUSED);
548 return true;
549}
550
551bool
552MediaPluginGStreamer010::stop()
553{
554 DEBUGMSG("stopping media...");
555 // todo: error-check this?
556 llgst_element_set_state(mPlaybin, GST_STATE_READY);
557 return true;
558}
559
560bool
561MediaPluginGStreamer010::play(double rate)
562{
563 // NOTE: we don't actually support non-natural rate.
564
565 DEBUGMSG("playing media... rate=%f", rate);
566 // todo: error-check this?
567 llgst_element_set_state(mPlaybin, GST_STATE_PLAYING);
568 return true;
569}
570
571bool
572MediaPluginGStreamer010::setVolume( float volume )
573{
574 // we try to only update volume as conservatively as
575 // possible, as many gst-plugins-base versions up to at least
576 // November 2008 have critical race-conditions in setting volume - sigh
577 if (mVolume == volume)
578 return true; // nothing to do, everything's fine
579
580 mVolume = volume;
581 if (mDoneInit && mPlaybin)
582 {
583 g_object_set(mPlaybin, "volume", mVolume, NULL);
584 return true;
585 }
586
587 return false;
588}
589
590bool
591MediaPluginGStreamer010::seek(double time_sec)
592{
593 bool success = false;
594 if (mDoneInit && mPlaybin)
595 {
596 success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME,
597 GstSeekFlags(GST_SEEK_FLAG_FLUSH |
598 GST_SEEK_FLAG_KEY_UNIT),
599 GST_SEEK_TYPE_SET, gint64(time_sec*GST_SECOND),
600 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
601 }
602 DEBUGMSG("MEDIA SEEK REQUEST to %fsec result was %d",
603 float(time_sec), int(success));
604 return success;
605}
606
607bool
608MediaPluginGStreamer010::getTimePos(double &sec_out)
609{
610 bool got_position = false;
611 if (mPlaybin)
612 {
613 gint64 pos;
614 GstFormat timefmt = GST_FORMAT_TIME;
615 got_position =
616 llgst_element_query_position &&
617 llgst_element_query_position(mPlaybin,
618 &timefmt,
619 &pos);
620 got_position = got_position
621 && (timefmt == GST_FORMAT_TIME);
622 // GStreamer may have other ideas, but we consider the current position
623 // undefined if not PLAYING or PAUSED
624 got_position = got_position &&
625 (GST_STATE(mPlaybin) == GST_STATE_PLAYING ||
626 GST_STATE(mPlaybin) == GST_STATE_PAUSED);
627 if (got_position && !GST_CLOCK_TIME_IS_VALID(pos))
628 {
629 if (GST_STATE(mPlaybin) == GST_STATE_PLAYING)
630 {
631 // if we're playing then we treat an invalid clock time
632 // as 0, for complicated reasons (insert reason here)
633 pos = 0;
634 }
635 else
636 {
637 got_position = false;
638 }
639
640 }
641 // If all the preconditions succeeded... we can trust the result.
642 if (got_position)
643 {
644 sec_out = double(pos) / double(GST_SECOND); // gst to sec
645 }
646 }
647 return got_position;
648}
649
650bool
651MediaPluginGStreamer010::load()
652{
653 if (!mDoneInit)
654 return false; // error
655
656 setStatus(STATUS_LOADING);
657
658 DEBUGMSG("setting up media...");
659
660 mIsLooping = false;
661 mVolume = 0.1234567; // minor hack to force an initial volume update
662
663 // Create a pumpable main-loop for this media
664 mPump = g_main_loop_new (NULL, FALSE);
665 if (!mPump)
666 {
667 setStatus(STATUS_ERROR);
668 return false; // error
669 }
670
671 // instantiate a playbin element to do the hard work
672 mPlaybin = llgst_element_factory_make ("playbin", "play");
673 if (!mPlaybin)
674 {
675 setStatus(STATUS_ERROR);
676 return false; // error
677 }
678
679 // get playbin's bus
680 GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
681 if (!bus)
682 {
683 setStatus(STATUS_ERROR);
684 return false; // error
685 }
686 mBusWatchID = llgst_bus_add_watch (bus,
687 llmediaimplgstreamer_bus_callback,
688 this);
689 llgst_object_unref (bus);
690
691 if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
692 // instantiate a custom video sink
693 mVideoSink =
694 GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo"));
695 if (!mVideoSink)
696 {
697 WARNMSG("Could not instantiate private-slvideo element.");
698 // todo: cleanup.
699 setStatus(STATUS_ERROR);
700 return false; // error
701 }
702
703 // connect the pieces
704 g_object_set(mPlaybin, "video-sink", mVideoSink, NULL);
705 }
706
707 return true;
708}
709
710bool
711MediaPluginGStreamer010::unload ()
712{
713 if (!mDoneInit)
714 return false; // error
715
716 DEBUGMSG("unloading media...");
717
718 // stop getting callbacks for this bus
719 g_source_remove(mBusWatchID);
720 mBusWatchID = 0;
721
722 if (mPlaybin)
723 {
724 llgst_element_set_state (mPlaybin, GST_STATE_NULL);
725 llgst_object_unref (GST_OBJECT (mPlaybin));
726 mPlaybin = NULL;
727 }
728
729 if (mPump)
730 {
731 g_main_loop_quit(mPump);
732 mPump = NULL;
733 }
734
735 mVideoSink = NULL;
736
737 setStatus(STATUS_NONE);
738
739 return true;
740}
741
742
743//static
744bool
745MediaPluginGStreamer010::startup()
746{
747 // first - check if GStreamer is explicitly disabled
748 if (NULL != getenv("LL_DISABLE_GSTREAMER"))
749 return false;
750
751 // only do global GStreamer initialization once.
752 if (!mDoneInit)
753 {
754 g_thread_init(NULL);
755
756 // Init the glib type system - we need it.
757 g_type_init();
758
759 // Get symbols!
760#if LL_DARWIN
761 if (! grab_gst_syms("libgstreamer-0.10.dylib",
762 "libgstvideo-0.10.dylib") )
763#elseif LL_WINDOWS
764 if (! grab_gst_syms("libgstreamer-0.10.dll",
765 "libgstvideo-0.10.dll") )
766#else // linux or other ELFy unixoid
767 if (! grab_gst_syms("libgstreamer-0.10.so.0",
768 "libgstvideo-0.10.so.0") )
769#endif
770 {
771 WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled.");
772 return false;
773 }
774
775 if (llgst_segtrap_set_enabled)
776 {
777 llgst_segtrap_set_enabled(FALSE);
778 }
779 else
780 {
781 WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught.");
782 }
783
784#if LL_LINUX
785 // Gstreamer tries a fork during init, waitpid-ing on it,
786 // which conflicts with any installed SIGCHLD handler...
787 struct sigaction tmpact, oldact;
788 if (llgst_registry_fork_set_enabled) {
789 // if we can disable SIGCHLD-using forking behaviour,
790 // do it.
791 llgst_registry_fork_set_enabled(false);
792 }
793 else {
794 // else temporarily install default SIGCHLD handler
795 // while GStreamer initialises
796 tmpact.sa_handler = SIG_DFL;
797 sigemptyset( &tmpact.sa_mask );
798 tmpact.sa_flags = SA_SIGINFO;
799 sigaction(SIGCHLD, &tmpact, &oldact);
800 }
801#endif // LL_LINUX
802
803 // Protect against GStreamer resetting the locale, yuck.
804 static std::string saved_locale;
805 saved_locale = setlocale(LC_ALL, NULL);
806
807 // finally, try to initialize GStreamer!
808 GError *err = NULL;
809 gboolean init_gst_success = llgst_init_check(NULL, NULL, &err);
810
811 // restore old locale
812 setlocale(LC_ALL, saved_locale.c_str() );
813
814#if LL_LINUX
815 // restore old SIGCHLD handler
816 if (!llgst_registry_fork_set_enabled)
817 sigaction(SIGCHLD, &oldact, NULL);
818#endif // LL_LINUX
819
820 if (!init_gst_success) // fail
821 {
822 if (err)
823 {
824 WARNMSG("GST init failed: %s", err->message);
825 g_error_free(err);
826 }
827 else
828 {
829 WARNMSG("GST init failed for unspecified reason.");
830 }
831 return false;
832 }
833
834 // Init our custom plugins - only really need do this once.
835 gst_slvideo_init_class();
836
837 mDoneInit = true;
838 }
839
840 return true;
841}
842
843
844void
845MediaPluginGStreamer010::sizeChanged()
846{
847 // the shared writing space has possibly changed size/location/whatever
848
849 // Check to see whether the movie's NATURAL size has been set yet
850 if (1 == mNaturalWidth &&
851 1 == mNaturalHeight)
852 {
853 mNaturalWidth = mCurrentWidth;
854 mNaturalHeight = mCurrentHeight;
855 DEBUGMSG("Media NATURAL size better detected as %dx%d",
856 mNaturalWidth, mNaturalHeight);
857 }
858
859 // if the size has changed then the shm has changed and the app needs telling
860 if (mCurrentWidth != mPreviousWidth ||
861 mCurrentHeight != mPreviousHeight)
862 {
863 mPreviousWidth = mCurrentWidth;
864 mPreviousHeight = mCurrentHeight;
865
866 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request");
867 message.setValue("name", mTextureSegmentName);
868 message.setValueS32("width", mNaturalWidth);
869 message.setValueS32("height", mNaturalHeight);
870 DEBUGMSG("<--- Sending size change request to application with name: '%s' - natural size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight);
871 sendMessage(message);
872 }
873}
874
875
876
877//static
878bool
879MediaPluginGStreamer010::closedown()
880{
881 if (!mDoneInit)
882 return false; // error
883
884 ungrab_gst_syms();
885
886 mDoneInit = false;
887
888 return true;
889}
890
891MediaPluginGStreamer010::~MediaPluginGStreamer010()
892{
893 DEBUGMSG("MediaPluginGStreamer010 destructor");
894
895 closedown();
896
897 DEBUGMSG("GStreamer010 closing down");
898}
899
900
901std::string
902MediaPluginGStreamer010::getVersion()
903{
904 std::string plugin_version = "GStreamer010 media plugin, GStreamer version ";
905 if (mDoneInit &&
906 llgst_version)
907 {
908 guint major, minor, micro, nano;
909 llgst_version(&major, &minor, &micro, &nano);
910 plugin_version += llformat("%u.%u.%u.%u (runtime), %u.%u.%u.%u (headers)", (unsigned int)major, (unsigned int)minor, (unsigned int)micro, (unsigned int)nano, (unsigned int)GST_VERSION_MAJOR, (unsigned int)GST_VERSION_MINOR, (unsigned int)GST_VERSION_MICRO, (unsigned int)GST_VERSION_NANO);
911 }
912 else
913 {
914 plugin_version += "(unknown)";
915 }
916 return plugin_version;
917}
918
919void MediaPluginGStreamer010::receiveMessage(const char *message_string)
920{
921 //std::cerr << "MediaPluginGStreamer010::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
922
923 LLPluginMessage message_in;
924
925 if(message_in.parse(message_string) >= 0)
926 {
927 std::string message_class = message_in.getClass();
928 std::string message_name = message_in.getName();
929 if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
930 {
931 if(message_name == "init")
932 {
933 LLPluginMessage message("base", "init_response");
934 LLSD versions = LLSD::emptyMap();
935 versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
936 versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
937 versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION;
938 message.setValueLLSD("versions", versions);
939
940 if ( load() )
941 {
942 DEBUGMSG("GStreamer010 media instance set up");
943 }
944 else
945 {
946 WARNMSG("GStreamer010 media instance failed to set up");
947 }
948
949 message.setValue("plugin_version", getVersion());
950 sendMessage(message);
951 }
952 else if(message_name == "idle")
953 {
954 // no response is necessary here.
955 double time = message_in.getValueReal("time");
956
957 // Convert time to milliseconds for update()
958 update((int)(time * 1000.0f));
959 }
960 else if(message_name == "cleanup")
961 {
962 unload();
963 closedown();
964 }
965 else if(message_name == "shm_added")
966 {
967 SharedSegmentInfo info;
968 info.mAddress = message_in.getValuePointer("address");
969 info.mSize = (size_t)message_in.getValueS32("size");
970 std::string name = message_in.getValue("name");
971
972 std::ostringstream str;
973 INFOMSG("MediaPluginGStreamer010::receiveMessage: shared memory added, name: %s, size: %d, address: %p", name.c_str(), int(info.mSize), info.mAddress);
974
975 mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
976 }
977 else if(message_name == "shm_remove")
978 {
979 std::string name = message_in.getValue("name");
980
981 DEBUGMSG("MediaPluginGStreamer010::receiveMessage: shared memory remove, name = %s", name.c_str());
982
983 SharedSegmentMap::iterator iter = mSharedSegments.find(name);
984 if(iter != mSharedSegments.end())
985 {
986 if(mPixels == iter->second.mAddress)
987 {
988 // This is the currently active pixel buffer. Make sure we stop drawing to it.
989 mPixels = NULL;
990 mTextureSegmentName.clear();
991
992 // Make sure the movie decoder is no longer pointed at the shared segment.
993 sizeChanged();
994 }
995 mSharedSegments.erase(iter);
996 }
997 else
998 {
999 WARNMSG("MediaPluginGStreamer010::receiveMessage: unknown shared memory region!");
1000 }
1001
1002 // Send the response so it can be cleaned up.
1003 LLPluginMessage message("base", "shm_remove_response");
1004 message.setValue("name", name);
1005 sendMessage(message);
1006 }
1007 else
1008 {
1009 std::ostringstream str;
1010 INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown base message: %s", message_name.c_str());
1011 }
1012 }
1013 else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
1014 {
1015 if(message_name == "init")
1016 {
1017 // Plugin gets to decide the texture parameters to use.
1018 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
1019 // lame to have to decide this now, it depends on the movie. Oh well.
1020 mDepth = 4;
1021
1022 mCurrentWidth = 1;
1023 mCurrentHeight = 1;
1024 mPreviousWidth = 1;
1025 mPreviousHeight = 1;
1026 mNaturalWidth = 1;
1027 mNaturalHeight = 1;
1028 mWidth = 1;
1029 mHeight = 1;
1030 mTextureWidth = 1;
1031 mTextureHeight = 1;
1032
1033 message.setValueU32("format", GL_RGBA);
1034 message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV);
1035
1036 message.setValueS32("depth", mDepth);
1037 message.setValueS32("default_width", mWidth);
1038 message.setValueS32("default_height", mHeight);
1039 message.setValueU32("internalformat", GL_RGBA8);
1040 message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left.
1041 message.setValueBoolean("allow_downsample", true); // we respond with grace and performance if asked to downscale
1042 sendMessage(message);
1043 }
1044 else if(message_name == "size_change")
1045 {
1046 std::string name = message_in.getValue("name");
1047 S32 width = message_in.getValueS32("width");
1048 S32 height = message_in.getValueS32("height");
1049 S32 texture_width = message_in.getValueS32("texture_width");
1050 S32 texture_height = message_in.getValueS32("texture_height");
1051
1052 std::ostringstream str;
1053 INFOMSG("---->Got size change instruction from application with shm name: %s - size is %d x %d", name.c_str(), width, height);
1054
1055 LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
1056 message.setValue("name", name);
1057 message.setValueS32("width", width);
1058 message.setValueS32("height", height);
1059 message.setValueS32("texture_width", texture_width);
1060 message.setValueS32("texture_height", texture_height);
1061 sendMessage(message);
1062
1063 if(!name.empty())
1064 {
1065 // Find the shared memory region with this name
1066 SharedSegmentMap::iterator iter = mSharedSegments.find(name);
1067 if(iter != mSharedSegments.end())
1068 {
1069 INFOMSG("*** Got size change with matching shm, new size is %d x %d", width, height);
1070 INFOMSG("*** Got size change with matching shm, texture size size is %d x %d", texture_width, texture_height);
1071
1072 mPixels = (unsigned char*)iter->second.mAddress;
1073 mTextureSegmentName = name;
1074 mWidth = width;
1075 mHeight = height;
1076
1077 if (texture_width > 1 ||
1078 texture_height > 1) // not a dummy size from the app, a real explicit forced size
1079 {
1080 INFOMSG("**** = REAL RESIZE REQUEST FROM APP");
1081
1082 GST_OBJECT_LOCK(mVideoSink);
1083 mVideoSink->resize_forced_always = true;
1084 mVideoSink->resize_try_width = texture_width;
1085 mVideoSink->resize_try_height = texture_height;
1086 GST_OBJECT_UNLOCK(mVideoSink);
1087 }
1088
1089 mTextureWidth = texture_width;
1090 mTextureHeight = texture_height;
1091 }
1092 }
1093 }
1094 else if(message_name == "load_uri")
1095 {
1096 std::string uri = message_in.getValue("uri");
1097 navigateTo( uri );
1098 sendStatus();
1099 }
1100 else if(message_name == "mouse_event")
1101 {
1102 std::string event = message_in.getValue("event");
1103 S32 x = message_in.getValueS32("x");
1104 S32 y = message_in.getValueS32("y");
1105
1106 if(event == "down")
1107 {
1108 mouseDown(x, y);
1109 }
1110 else if(event == "up")
1111 {
1112 mouseUp(x, y);
1113 }
1114 else if(event == "move")
1115 {
1116 mouseMove(x, y);
1117 };
1118 };
1119 }
1120 else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
1121 {
1122 if(message_name == "stop")
1123 {
1124 stop();
1125 }
1126 else if(message_name == "start")
1127 {
1128 double rate = 0.0;
1129 if(message_in.hasValue("rate"))
1130 {
1131 rate = message_in.getValueReal("rate");
1132 }
1133 // NOTE: we don't actually support rate.
1134 play(rate);
1135 }
1136 else if(message_name == "pause")
1137 {
1138 pause();
1139 }
1140 else if(message_name == "seek")
1141 {
1142 double time = message_in.getValueReal("time");
1143 // defer the actual seek in case we haven't
1144 // really truly started yet in which case there
1145 // is nothing to seek upon
1146 mSeekWanted = true;
1147 mSeekDestination = time;
1148 }
1149 else if(message_name == "set_loop")
1150 {
1151 bool loop = message_in.getValueBoolean("loop");
1152 mIsLooping = loop;
1153 }
1154 else if(message_name == "set_volume")
1155 {
1156 double volume = message_in.getValueReal("volume");
1157 setVolume(volume);
1158 }
1159 }
1160 else
1161 {
1162 INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown message class: %s", message_class.c_str());
1163 }
1164 }
1165}
1166
1167int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
1168{
1169 if (MediaPluginGStreamer010::startup())
1170 {
1171 MediaPluginGStreamer010 *self = new MediaPluginGStreamer010(host_send_func, host_user_data);
1172 *plugin_send_func = MediaPluginGStreamer010::staticReceiveMessage;
1173 *plugin_user_data = (void*)self;
1174
1175 return 0; // okay
1176 }
1177 else
1178 {
1179 return -1; // failed to init
1180 }
1181}
1182
1183#else // LL_GSTREAMER010_ENABLED
1184
1185// Stubbed-out class with constructor/destructor (necessary or windows linker
1186// will just think its dead code and optimize it all out)
1187class MediaPluginGStreamer010 : public MediaPluginBase
1188{
1189public:
1190 MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
1191 ~MediaPluginGStreamer010();
1192 /* virtual */ void receiveMessage(const char *message_string);
1193};
1194
1195MediaPluginGStreamer010::MediaPluginGStreamer010(
1196 LLPluginInstance::sendMessageFunction host_send_func,
1197 void *host_user_data ) :
1198 MediaPluginBase(host_send_func, host_user_data)
1199{
1200 // no-op
1201}
1202
1203MediaPluginGStreamer010::~MediaPluginGStreamer010()
1204{
1205 // no-op
1206}
1207
1208void MediaPluginGStreamer010::receiveMessage(const char *message_string)
1209{
1210 // no-op
1211}
1212
1213// We're building without GStreamer enabled. Just refuse to initialize.
1214int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
1215{
1216 return -1;
1217}
1218
1219#endif // LL_GSTREAMER010_ENABLED