diff options
author | Armin Weatherwax | 2010-06-14 12:04:49 +0200 |
---|---|---|
committer | Armin Weatherwax | 2010-09-23 15:38:25 +0200 |
commit | 35df5441d3e2789663532c948731aff3a1e04728 (patch) | |
tree | ac7674289784a5f96106ea507637055a8dada78a /linden/indra/media_plugins/gstreamer010 | |
parent | Changed version to Experimental 2010.09.18 (diff) | |
download | meta-impy-35df5441d3e2789663532c948731aff3a1e04728.zip meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.gz meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.bz2 meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.xz |
llmediaplugins first step
Diffstat (limited to '')
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 | |||
3 | project(media_plugin_gstreamer010) | ||
4 | |||
5 | include(00-Common) | ||
6 | include(LLCommon) | ||
7 | include(LLImage) | ||
8 | include(LLPlugin) | ||
9 | include(LLMath) | ||
10 | include(LLRender) | ||
11 | include(LLWindow) | ||
12 | include(Linking) | ||
13 | include(PluginAPI) | ||
14 | include(MediaPluginBase) | ||
15 | include(FindOpenGL) | ||
16 | |||
17 | include(GStreamer010Plugin) | ||
18 | |||
19 | include_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 | |||
33 | set(media_plugin_gstreamer010_SOURCE_FILES | ||
34 | media_plugin_gstreamer010.cpp | ||
35 | llmediaimplgstreamer_syms.cpp | ||
36 | llmediaimplgstreamervidplug.cpp | ||
37 | ) | ||
38 | |||
39 | set(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 | |||
52 | add_library(media_plugin_gstreamer010 | ||
53 | SHARED | ||
54 | ${media_plugin_gstreamer010_SOURCE_FILES} | ||
55 | ) | ||
56 | |||
57 | target_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 | |||
65 | add_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" | 40 | extern "C" { |
41 | #include <stdio.h> | ||
42 | #include <gst/gst.h> | ||
39 | 43 | ||
40 | /////////////////////////////////////////////////////////////////////////////// | 44 | #include "apr_pools.h" |
41 | // | 45 | #include "apr_dso.h" |
42 | class 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: | 49 | extern "C" { |
57 | std::string mImplName; | 50 | gboolean 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 | |||
37 | extern "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 | ||
52 | GstDebugCategory* | ||
53 | ll_gst_debug_category_new(gchar *name, guint color, gchar *description) | ||
54 | { | ||
55 | static GstDebugCategory dummy; | ||
56 | return &dummy; | ||
57 | } | ||
58 | void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname) | ||
59 | { | ||
60 | } | ||
61 | |||
62 | static bool sSymsGrabbed = false; | ||
63 | static apr_pool_t *sSymGSTDSOMemoryPool = NULL; | ||
64 | static apr_dso_handle_t *sSymGSTDSOHandleG = NULL; | ||
65 | static apr_dso_handle_t *sSymGSTDSOHandleV = NULL; | ||
66 | |||
67 | |||
68 | bool 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 | |||
138 | void 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 | |||
37 | extern "C" { | ||
38 | #include <gst/gst.h> | ||
39 | } | ||
40 | |||
41 | bool grab_gst_syms(std::string gst_dso_name, | ||
42 | std::string gst_dso_name_vid); | ||
43 | void 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 | ||
66 | void 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 | ||
69 | GstDebugCategory* 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 | ||
3 | LL_GST_SYM(true, gst_pad_peer_accept_caps, gboolean, GstPad *pad, GstCaps *caps); | ||
4 | LL_GST_SYM(true, gst_buffer_new, GstBuffer*, void); | ||
5 | LL_GST_SYM(true, gst_buffer_set_caps, void, GstBuffer*, GstCaps *); | ||
6 | LL_GST_SYM(true, gst_structure_set_value, void, GstStructure *, const gchar *, const GValue*); | ||
7 | LL_GST_SYM(true, gst_init_check, gboolean, int *argc, char **argv[], GError ** err); | ||
8 | LL_GST_SYM(true, gst_message_get_type, GType, void); | ||
9 | LL_GST_SYM(true, gst_message_type_get_name, const gchar*, GstMessageType type); | ||
10 | LL_GST_SYM(true, gst_message_parse_error, void, GstMessage *message, GError **gerror, gchar **debug); | ||
11 | LL_GST_SYM(true, gst_message_parse_warning, void, GstMessage *message, GError **gerror, gchar **debug); | ||
12 | LL_GST_SYM(true, gst_message_parse_state_changed, void, GstMessage *message, GstState *oldstate, GstState *newstate, GstState *pending); | ||
13 | LL_GST_SYM(true, gst_element_set_state, GstStateChangeReturn, GstElement *element, GstState state); | ||
14 | LL_GST_SYM(true, gst_object_unref, void, gpointer object); | ||
15 | LL_GST_SYM(true, gst_object_get_type, GType, void); | ||
16 | LL_GST_SYM(true, gst_pipeline_get_type, GType, void); | ||
17 | LL_GST_SYM(true, gst_pipeline_get_bus, GstBus*, GstPipeline *pipeline); | ||
18 | LL_GST_SYM(true, gst_bus_add_watch, guint, GstBus * bus, GstBusFunc func, gpointer user_data); | ||
19 | LL_GST_SYM(true, gst_element_factory_make, GstElement*, const gchar *factoryname, const gchar *name); | ||
20 | LL_GST_SYM(true, gst_element_get_type, GType, void); | ||
21 | LL_GST_SYM(true, gst_static_pad_template_get, GstPadTemplate*, GstStaticPadTemplate *pad_template); | ||
22 | LL_GST_SYM(true, gst_element_class_add_pad_template, void, GstElementClass *klass, GstPadTemplate *temp); | ||
23 | LL_GST_SYM(true, gst_element_class_set_details, void, GstElementClass *klass, const GstElementDetails *details); | ||
24 | LL_GST_SYM(true, gst_caps_unref, void, GstCaps* caps); | ||
25 | LL_GST_SYM(true, gst_caps_ref, GstCaps *, GstCaps* caps); | ||
26 | //LL_GST_SYM(true, gst_caps_is_empty, gboolean, const GstCaps *caps); | ||
27 | LL_GST_SYM(true, gst_caps_from_string, GstCaps *, const gchar *string); | ||
28 | LL_GST_SYM(true, gst_caps_replace, void, GstCaps **caps, GstCaps *newcaps); | ||
29 | LL_GST_SYM(true, gst_caps_get_structure, GstStructure *, const GstCaps *caps, guint index); | ||
30 | LL_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); | ||
32 | LL_GST_SYM(true, gst_element_register, gboolean, GstPlugin *plugin, const gchar *name, guint rank, GType type); | ||
33 | LL_GST_SYM(true, _gst_plugin_register_static, void, GstPluginDesc *desc); | ||
34 | LL_GST_SYM(true, gst_structure_get_int, gboolean, const GstStructure *structure, const gchar *fieldname, gint *value); | ||
35 | LL_GST_SYM(true, gst_structure_get_value, G_CONST_RETURN GValue *, const GstStructure *structure, const gchar *fieldname); | ||
36 | LL_GST_SYM(true, gst_value_get_fraction_numerator, gint, const GValue *value); | ||
37 | LL_GST_SYM(true, gst_value_get_fraction_denominator, gint, const GValue *value); | ||
38 | LL_GST_SYM(true, gst_structure_get_name, G_CONST_RETURN gchar *, const GstStructure *structure); | ||
39 | LL_GST_SYM(true, gst_element_seek, bool, GstElement *, gdouble, GstFormat, GstSeekFlags, GstSeekType, gint64, GstSeekType, gint64); | ||
40 | |||
41 | // optional symbols to grab | ||
42 | LL_GST_SYM(false, gst_registry_fork_set_enabled, void, gboolean enabled); | ||
43 | LL_GST_SYM(false, gst_segtrap_set_enabled, void, gboolean enabled); | ||
44 | LL_GST_SYM(false, gst_message_parse_buffering, void, GstMessage *message, gint *percent); | ||
45 | LL_GST_SYM(false, gst_message_parse_info, void, GstMessage *message, GError **gerror, gchar **debug); | ||
46 | LL_GST_SYM(false, gst_element_query_position, gboolean, GstElement *element, GstFormat *format, gint64 *cur); | ||
47 | LL_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 | ||
3 | LL_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 | |||
45 | GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); | 47 | GST_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 */ | ||
49 | enum | ||
50 | { | ||
51 | /* FILL ME */ | ||
52 | LAST_SIGNAL | ||
53 | }; | ||
54 | |||
55 | enum | ||
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 | ||
63 | static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ( | 54 | static 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 | |||
111 | gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) | 102 | gst_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 | ||
312 | static GstFlowReturn | ||
313 | gst_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 */ |
321 | static void | 404 | static void |
322 | gst_slvideo_class_init (GstSLVideoClass * klass) | 405 | gst_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 | ||
354 | static void | ||
355 | gst_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 | |||
374 | gst_slvideo_init (GstSLVideo * filter, | 443 | gst_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 | ||
393 | static void | 466 | static void |
394 | gst_slvideo_set_property (GObject * object, guint prop_id, | 467 | gst_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 | |||
405 | gst_slvideo_get_property (GObject * object, guint prop_id, | 480 | gst_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, | |||
421 | static gboolean | 498 | static gboolean |
422 | plugin_init (GstPlugin * plugin) | 499 | plugin_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 | */ | ||
434 | void gst_slvideo_init_class (void) | 516 | void 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 | ||
38 | extern "C" { | 38 | extern "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 | ||
45 | G_BEGIN_DECLS | 44 | G_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 | ||
93 | struct _GstSLVideoClass | 96 | struct _GstSLVideoClass |
@@ -101,6 +104,6 @@ void gst_slvideo_init_class (void); | |||
101 | 104 | ||
102 | G_END_DECLS | 105 | G_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 | |||
44 | extern "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 | // | ||
57 | class MediaPluginGStreamer010 : public MediaPluginBase | ||
58 | { | ||
59 | public: | ||
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 | |||
71 | private: | ||
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 | |||
98 | private: | ||
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 | ||
144 | bool MediaPluginGStreamer010::mDoneInit = false; | ||
145 | |||
146 | MediaPluginGStreamer010::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 | ||
169 | static 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 | |||
182 | gboolean | ||
183 | MediaPluginGStreamer010::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 | |||
344 | extern "C" { | ||
345 | gboolean | ||
346 | llmediaimplgstreamer_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 | |||
357 | bool | ||
358 | MediaPluginGStreamer010::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 | |||
387 | bool | ||
388 | MediaPluginGStreamer010::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 | |||
517 | void | ||
518 | MediaPluginGStreamer010::mouseDown( int x, int y ) | ||
519 | { | ||
520 | // do nothing | ||
521 | } | ||
522 | |||
523 | void | ||
524 | MediaPluginGStreamer010::mouseUp( int x, int y ) | ||
525 | { | ||
526 | // do nothing | ||
527 | } | ||
528 | |||
529 | void | ||
530 | MediaPluginGStreamer010::mouseMove( int x, int y ) | ||
531 | { | ||
532 | // do nothing | ||
533 | } | ||
534 | |||
535 | |||
536 | bool | ||
537 | MediaPluginGStreamer010::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 | |||
545 | bool | ||
546 | MediaPluginGStreamer010::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 | |||
554 | bool | ||
555 | MediaPluginGStreamer010::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 | |||
565 | bool | ||
566 | MediaPluginGStreamer010::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 | |||
584 | bool | ||
585 | MediaPluginGStreamer010::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 | |||
601 | bool | ||
602 | MediaPluginGStreamer010::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 | |||
644 | bool | ||
645 | MediaPluginGStreamer010::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 | |||
704 | bool | ||
705 | MediaPluginGStreamer010::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 | ||
738 | bool | ||
739 | MediaPluginGStreamer010::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 | |||
838 | bool | ||
839 | MediaPluginGStreamer010::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 | ||
864 | bool | ||
865 | MediaPluginGStreamer010::closedown() | ||
866 | { | ||
867 | if (!mDoneInit) | ||
868 | return false; // error | ||
869 | |||
870 | ungrab_gst_syms(); | ||
871 | |||
872 | mDoneInit = false; | ||
873 | |||
874 | return true; | ||
875 | } | ||
876 | |||
877 | MediaPluginGStreamer010::~MediaPluginGStreamer010() | ||
878 | { | ||
879 | DEBUGMSG("MediaPluginGStreamer010 destructor"); | ||
880 | |||
881 | closedown(); | ||
882 | |||
883 | DEBUGMSG("GStreamer010 closing down"); | ||
884 | } | ||
885 | |||
886 | |||
887 | std::string | ||
888 | MediaPluginGStreamer010::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, µ, &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 | |||
905 | void 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 | |||
1150 | int 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) | ||
1170 | class MediaPluginGStreamer010 : public MediaPluginBase | ||
1171 | { | ||
1172 | public: | ||
1173 | MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); | ||
1174 | ~MediaPluginGStreamer010(); | ||
1175 | /* virtual */ void receiveMessage(const char *message_string); | ||
1176 | }; | ||
1177 | |||
1178 | MediaPluginGStreamer010::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 | |||
1186 | MediaPluginGStreamer010::~MediaPluginGStreamer010() | ||
1187 | { | ||
1188 | // no-op | ||
1189 | } | ||
1190 | |||
1191 | void MediaPluginGStreamer010::receiveMessage(const char *message_string) | ||
1192 | { | ||
1193 | // no-op | ||
1194 | } | ||
1195 | |||
1196 | // We're building without GStreamer enabled. Just refuse to initialize. | ||
1197 | int 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 | |||
47 | extern "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 | // | ||
60 | class MediaPluginGStreamer010 : public MediaPluginBase | ||
61 | { | ||
62 | public: | ||
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 | |||
74 | private: | ||
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 | |||
101 | private: | ||
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 | ||
150 | bool MediaPluginGStreamer010::mDoneInit = false; | ||
151 | |||
152 | MediaPluginGStreamer010::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 | ||
175 | static 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 | |||
188 | gboolean | ||
189 | MediaPluginGStreamer010::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 | |||
351 | extern "C" { | ||
352 | gboolean | ||
353 | llmediaimplgstreamer_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 | |||
364 | bool | ||
365 | MediaPluginGStreamer010::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 | |||
394 | bool | ||
395 | MediaPluginGStreamer010::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 | |||
523 | void | ||
524 | MediaPluginGStreamer010::mouseDown( int x, int y ) | ||
525 | { | ||
526 | // do nothing | ||
527 | } | ||
528 | |||
529 | void | ||
530 | MediaPluginGStreamer010::mouseUp( int x, int y ) | ||
531 | { | ||
532 | // do nothing | ||
533 | } | ||
534 | |||
535 | void | ||
536 | MediaPluginGStreamer010::mouseMove( int x, int y ) | ||
537 | { | ||
538 | // do nothing | ||
539 | } | ||
540 | |||
541 | |||
542 | bool | ||
543 | MediaPluginGStreamer010::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 | |||
551 | bool | ||
552 | MediaPluginGStreamer010::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 | |||
560 | bool | ||
561 | MediaPluginGStreamer010::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 | |||
571 | bool | ||
572 | MediaPluginGStreamer010::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 | |||
590 | bool | ||
591 | MediaPluginGStreamer010::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 | |||
607 | bool | ||
608 | MediaPluginGStreamer010::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 | |||
650 | bool | ||
651 | MediaPluginGStreamer010::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 | |||
710 | bool | ||
711 | MediaPluginGStreamer010::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 | ||
744 | bool | ||
745 | MediaPluginGStreamer010::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 | |||
844 | void | ||
845 | MediaPluginGStreamer010::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 | ||
878 | bool | ||
879 | MediaPluginGStreamer010::closedown() | ||
880 | { | ||
881 | if (!mDoneInit) | ||
882 | return false; // error | ||
883 | |||
884 | ungrab_gst_syms(); | ||
885 | |||
886 | mDoneInit = false; | ||
887 | |||
888 | return true; | ||
889 | } | ||
890 | |||
891 | MediaPluginGStreamer010::~MediaPluginGStreamer010() | ||
892 | { | ||
893 | DEBUGMSG("MediaPluginGStreamer010 destructor"); | ||
894 | |||
895 | closedown(); | ||
896 | |||
897 | DEBUGMSG("GStreamer010 closing down"); | ||
898 | } | ||
899 | |||
900 | |||
901 | std::string | ||
902 | MediaPluginGStreamer010::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, µ, &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 | |||
919 | void 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 | |||
1167 | int 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) | ||
1187 | class MediaPluginGStreamer010 : public MediaPluginBase | ||
1188 | { | ||
1189 | public: | ||
1190 | MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); | ||
1191 | ~MediaPluginGStreamer010(); | ||
1192 | /* virtual */ void receiveMessage(const char *message_string); | ||
1193 | }; | ||
1194 | |||
1195 | MediaPluginGStreamer010::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 | |||
1203 | MediaPluginGStreamer010::~MediaPluginGStreamer010() | ||
1204 | { | ||
1205 | // no-op | ||
1206 | } | ||
1207 | |||
1208 | void MediaPluginGStreamer010::receiveMessage(const char *message_string) | ||
1209 | { | ||
1210 | // no-op | ||
1211 | } | ||
1212 | |||
1213 | // We're building without GStreamer enabled. Just refuse to initialize. | ||
1214 | int 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 | ||