]> git.sesse.net Git - mlt/blob - src/modules/opengl/filter_glsl_manager.cpp
Make opengl filters support attach, detach, and disable.
[mlt] / src / modules / opengl / filter_glsl_manager.cpp
1 /*
2  * filter_glsl_manager.cpp
3  * Copyright (C) 2011-2012 Christophe Thommeret <hftom@free.fr>
4  * Copyright (C) 2013 Dan Dennedy <dan@dennedy.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include <stdlib.h>
22 #include <string>
23 #include "glsl_manager.h"
24 #include <movit/init.h>
25 #include <movit/effect_chain.h>
26 #include "mlt_movit_input.h"
27 #include "mlt_flip_effect.h"
28 #include <mlt++/MltEvent.h>
29 #include <mlt++/MltProducer.h>
30
31 extern "C" {
32 #include <framework/mlt_factory.h>
33 }
34
35 void deleteManager(GlslManager *p)
36 {
37         delete p;
38 }
39
40 GlslManager::GlslManager()
41         : Mlt::Filter( mlt_filter_new() )
42         , pbo(0)
43 {
44         mlt_filter filter = get_filter();
45         if ( filter ) {
46                 // Set the mlt_filter child in case we choose to override virtual functions.
47                 filter->child = this;
48                 mlt_properties_set_data(mlt_global_properties(), "glslManager", this, 0,
49                         (mlt_destructor) deleteManager, NULL);
50
51                 mlt_events_register( get_properties(), "init glsl", NULL );
52                 listen("init glsl", this, (mlt_listener) GlslManager::onInit);
53         }
54 }
55
56 GlslManager::~GlslManager()
57 {
58         mlt_log_debug(get_service(), "%s\n", __FUNCTION__);
59         while (fbo_list.peek_back())
60                 delete (glsl_fbo) fbo_list.pop_back();
61         while (texture_list.peek_back())
62                 delete (glsl_texture) texture_list.pop_back();
63         delete pbo;
64 }
65
66 GlslManager* GlslManager::get_instance()
67 {
68         return (GlslManager*) mlt_properties_get_data(mlt_global_properties(), "glslManager", 0);
69 }
70
71 glsl_fbo GlslManager::get_fbo(int width, int height)
72 {
73         for (int i = 0; i < fbo_list.count(); ++i) {
74                 glsl_fbo fbo = (glsl_fbo) fbo_list.peek(i);
75                 if (!fbo->used && (fbo->width == width) && (fbo->height == height)) {
76                         fbo->used = 1;
77                         return fbo;
78                 }
79         }
80         GLuint fb = 0;
81         glGenFramebuffers(1, &fb);
82         if (!fb)
83                 return NULL;
84
85         glsl_fbo fbo = new glsl_fbo_s;
86         if (!fbo) {
87                 glDeleteFramebuffers(1, &fb);
88                 return NULL;
89         }
90         fbo->fbo = fb;
91         fbo->width = width;
92         fbo->height = height;
93         fbo->used = 1;
94         fbo_list.push_back(fbo);
95         return fbo;
96 }
97
98 void GlslManager::release_fbo(glsl_fbo fbo)
99 {
100         fbo->used = 0;
101 }
102
103 glsl_texture GlslManager::get_texture(int width, int height, GLint internal_format)
104 {
105         for (int i = 0; i < texture_list.count(); ++i) {
106                 glsl_texture tex = (glsl_texture) texture_list.peek(i);
107                 if (!tex->used && (tex->width == width) && (tex->height == height) && (tex->internal_format == internal_format)) {
108                         glBindTexture(GL_TEXTURE_2D, tex->texture);
109                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
110                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
111                         glBindTexture( GL_TEXTURE_2D, 0);
112                         tex->used = 1;
113                         return tex;
114                 }
115         }
116         GLuint tex = 0;
117         glGenTextures(1, &tex);
118         if (!tex)
119                 return NULL;
120
121         glsl_texture gtex = new glsl_texture_s;
122         if (!gtex) {
123                 glDeleteTextures(1, &tex);
124                 return NULL;
125         }
126         glBindTexture( GL_TEXTURE_2D, tex );
127         glTexImage2D( GL_TEXTURE_2D, 0, internal_format, width, height, 0, internal_format, GL_UNSIGNED_BYTE, NULL );
128     glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
129     glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
130     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
131     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
132     glBindTexture( GL_TEXTURE_2D, 0 );
133
134         gtex->texture = tex;
135         gtex->width = width;
136         gtex->height = height;
137         gtex->internal_format = internal_format;
138         gtex->used = 1;
139         texture_list.push_back(gtex);
140         return gtex;
141 }
142
143 void GlslManager::release_texture(glsl_texture texture)
144 {
145         texture->used = 0;
146 }
147
148 glsl_pbo GlslManager::get_pbo(int size)
149 {
150         if (!pbo) {
151                 GLuint pb = 0;
152                 glGenBuffers(1, &pb);
153                 if (!pb)
154                         return NULL;
155
156                 pbo = new glsl_pbo_s;
157                 if (!pbo) {
158                         glDeleteBuffers(1, &pb);
159                         return NULL;
160                 }
161                 pbo->pbo = pb;
162         }
163         if (size > pbo->size) {
164                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo->pbo);
165                 glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, size, NULL, GL_STREAM_DRAW);
166                 glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
167                 pbo->size = size;
168         }
169         return pbo;
170 }
171
172 void GlslManager::onInit( mlt_properties owner, GlslManager* filter )
173 {
174         mlt_log_debug( filter->get_service(), "%s\n", __FUNCTION__ );
175 #ifdef WIN32
176         std::string path = std::string(mlt_environment("MLT_APPDIR")).append("\\share\\movit");
177 #elif defined(__DARWIN__) && defined(RELOCATABLE)
178         std::string path = std::string(mlt_environment("MLT_APPDIR")).append("/share/movit");
179 #else
180         std::string path = std::string(getenv("MLT_MOVIT_PATH") ? getenv("MLT_MOVIT_PATH") : SHADERDIR);
181 #endif
182         ::init_movit( path, mlt_log_get_level() == MLT_LOG_DEBUG? MOVIT_DEBUG_ON : MOVIT_DEBUG_OFF );
183         filter->set( "glsl_supported", movit_initialized );
184 }
185
186 void GlslManager::onServiceChanged( mlt_properties owner, mlt_service aservice )
187 {
188         Mlt::Service service( aservice );
189         service.lock();
190         service.set( "movit chain", NULL, 0 );
191         service.set( "movit input", NULL, 0 );
192         // Destroy the effect list.
193         GlslManager::get_instance()->set( service.get( "_unique_id" ), NULL, 0 );
194         service.unlock();
195 }
196
197 void GlslManager::onPropertyChanged( mlt_properties owner, mlt_service service, const char* property )
198 {
199         if ( property && std::string( property ) == "disable" )
200                 onServiceChanged( owner, service );
201 }
202
203 extern "C" {
204
205 mlt_filter filter_glsl_manager_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
206 {
207         GlslManager* g = GlslManager::get_instance();
208         if (g)
209                 g->inc_ref();
210         else
211                 g = new GlslManager();
212         return g->get_filter();
213 }
214
215 } // extern "C"
216
217 Mlt::Properties GlslManager::effect_list( Mlt::Service& service )
218 {
219         char *unique_id =  service.get( "_unique_id" );
220         mlt_properties properties = (mlt_properties) get_data( unique_id );
221         if ( !properties ) {
222                 properties = mlt_properties_new();
223                 set( unique_id, properties, 0, (mlt_destructor) mlt_properties_close );
224         }
225         Mlt::Properties p( properties );
226         return p;
227 }
228
229 static void deleteChain( EffectChain* chain )
230 {
231         delete chain;
232 }
233
234 bool GlslManager::init_chain( mlt_service aservice )
235 {
236         bool error = true;
237         Mlt::Service service( aservice );
238         EffectChain* chain = (EffectChain*) service.get_data( "movit chain" );
239         if ( !chain ) {
240                 mlt_profile profile = mlt_service_profile( aservice );
241                 Input* input = new MltInput( profile->width, profile->height );
242                 chain = new EffectChain( profile->display_aspect_num, profile->display_aspect_den );
243                 chain->add_input( input );
244                 service.set( "movit chain", chain, 0, (mlt_destructor) deleteChain );
245                 service.set( "movit input", input, 0 );
246                 service.set( "_movit finalized", 0 );
247                 service.listen( "service-changed", aservice, (mlt_listener) GlslManager::onServiceChanged );
248                 service.listen( "property-changed", aservice, (mlt_listener) GlslManager::onPropertyChanged );
249                 error = false;
250         }
251         return error;
252 }
253
254 EffectChain* GlslManager::get_chain( mlt_service service )
255 {
256         return (EffectChain*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "movit chain", NULL );
257 }
258
259 MltInput *GlslManager::get_input( mlt_service service )
260 {
261         return (MltInput*) mlt_properties_get_data( MLT_SERVICE_PROPERTIES(service), "movit input", NULL );
262 }
263
264 void GlslManager::reset_finalized( mlt_service service )
265 {
266         mlt_properties_set_int( MLT_SERVICE_PROPERTIES(service), "_movit finalized", 0 );
267 }
268
269 Effect* GlslManager::get_effect( mlt_filter filter, mlt_frame frame )
270 {
271         Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
272         char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" );
273         return (Effect*) GlslManager::get_instance()->effect_list( producer ).get_data( unique_id );
274 }
275
276 Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* effect )
277 {
278         Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
279         EffectChain* chain = (EffectChain*) producer.get_data( "movit chain" );
280         chain->add_effect( effect );
281         char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" );
282         GlslManager::get_instance()->effect_list( producer ).set( unique_id, effect, 0 );
283         return effect;
284 }
285
286 Effect* GlslManager::add_effect( mlt_filter filter, mlt_frame frame, Effect* effect, Effect* input_b )
287 {
288         Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
289         EffectChain* chain = (EffectChain*) producer.get_data( "movit chain" );
290         chain->add_effect( effect, chain->last_added_effect(),
291                 input_b? input_b : chain->last_added_effect() );
292         char *unique_id = mlt_properties_get( MLT_FILTER_PROPERTIES(filter), "_unique_id" );
293         GlslManager::get_instance()->effect_list( producer ).set( unique_id, effect, 0 );
294         return effect;
295 }
296
297 void GlslManager::render( mlt_service service, void* chain, GLuint fbo, int width, int height )
298 {
299         EffectChain* effect_chain = (EffectChain*) chain;
300         mlt_properties properties = MLT_SERVICE_PROPERTIES( service );
301         if ( !mlt_properties_get_int( properties, "_movit finalized" ) ) {
302                 mlt_properties_set_int( properties, "_movit finalized", 1 );
303                 effect_chain->add_effect( new Mlt::VerticalFlip() );
304                 effect_chain->finalize();
305         }
306         effect_chain->render_to_fbo( fbo, width, height );
307 }
308
309 void GlslManager::lock_service( mlt_frame frame )
310 {
311         Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
312         producer.lock();
313 }
314
315 void GlslManager::unlock_service( mlt_frame frame )
316 {
317         Mlt::Producer producer( mlt_producer_cut_parent( mlt_frame_get_original_producer( frame ) ) );
318         producer.unlock();
319 }