]> git.sesse.net Git - mlt/blob - src/framework/mlt_tractor.c
mlt_tractor.[ch], mlt_multitrack.[ch]: improve doxygen documentation for the tractor...
[mlt] / src / framework / mlt_tractor.c
1 /**
2  * \file mlt_tractor.c
3  * \brief tractor service class
4  *
5  * Copyright (C) 2003-2008 Ushodaya Enterprises Limited
6  * \author Charles Yates <charles.yates@pandora.be>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "mlt_tractor.h"
24 #include "mlt_frame.h"
25 #include "mlt_multitrack.h"
26 #include "mlt_field.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 /* Forward references to static methods.
34 */
35
36 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track );
37 static void mlt_tractor_listener( mlt_multitrack tracks, mlt_tractor this );
38
39 /** Construct a tractor without a field or multitrack.
40  *
41  * Sets the resource property to "<tractor>", the mlt_type to "mlt_producer",
42  * and mlt_service to "tractor".
43  *
44  * \public \memberof mlt_tractor_s
45  * \return the new tractor
46  */
47
48 mlt_tractor mlt_tractor_init( )
49 {
50         mlt_tractor this = calloc( sizeof( struct mlt_tractor_s ), 1 );
51         if ( this != NULL )
52         {
53                 mlt_producer producer = &this->parent;
54                 if ( mlt_producer_init( producer, this ) == 0 )
55                 {
56                         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
57
58                         mlt_properties_set( properties, "resource", "<tractor>" );
59                         mlt_properties_set( properties, "mlt_type", "mlt_producer" );
60                         mlt_properties_set( properties, "mlt_service", "tractor" );
61                         mlt_properties_set_int( properties, "in", 0 );
62                         mlt_properties_set_int( properties, "out", -1 );
63                         mlt_properties_set_int( properties, "length", 0 );
64
65                         producer->get_frame = producer_get_frame;
66                         producer->close = ( mlt_destructor )mlt_tractor_close;
67                         producer->close_object = this;
68                 }
69                 else
70                 {
71                         free( this );
72                         this = NULL;
73                 }
74         }
75         return this;
76 }
77
78 /** Construct a tractor as well as a field and multitrack.
79  *
80  * Sets the resource property to "<tractor>", the mlt_type to "mlt_producer",
81  * and mlt_service to "tractor".
82  *
83  * \public \memberof mlt_tractor_s
84  * \return the new tractor
85  */
86
87 mlt_tractor mlt_tractor_new( )
88 {
89         mlt_tractor this = calloc( sizeof( struct mlt_tractor_s ), 1 );
90         if ( this != NULL )
91         {
92                 mlt_producer producer = &this->parent;
93                 if ( mlt_producer_init( producer, this ) == 0 )
94                 {
95                         mlt_multitrack multitrack = mlt_multitrack_init( );
96                         mlt_field field = mlt_field_new( multitrack, this );
97                         mlt_properties props = MLT_PRODUCER_PROPERTIES( producer );
98
99                         mlt_properties_set( props, "resource", "<tractor>" );
100                         mlt_properties_set( props, "mlt_type", "mlt_producer" );
101                         mlt_properties_set( props, "mlt_service", "tractor" );
102                         mlt_properties_set_position( props, "in", 0 );
103                         mlt_properties_set_position( props, "out", 0 );
104                         mlt_properties_set_position( props, "length", 0 );
105                         mlt_properties_set_data( props, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL );
106                         mlt_properties_set_data( props, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL );
107
108                         mlt_events_listen( MLT_MULTITRACK_PROPERTIES( multitrack ), this, "producer-changed", ( mlt_listener )mlt_tractor_listener );
109
110                         producer->get_frame = producer_get_frame;
111                         producer->close = ( mlt_destructor )mlt_tractor_close;
112                         producer->close_object = this;
113                 }
114                 else
115                 {
116                         free( this );
117                         this = NULL;
118                 }
119         }
120         return this;
121 }
122
123 /** Get the service object associated to the tractor.
124  *
125  * \public \memberof mlt_tractor_s
126  * \param this a tractor
127  * \return the parent service object
128  * \see MLT_TRACTOR_SERVICE
129  */
130
131 mlt_service mlt_tractor_service( mlt_tractor this )
132 {
133         return MLT_PRODUCER_SERVICE( &this->parent );
134 }
135
136 /** Get the producer object associated to the tractor.
137  *
138  * \public \memberof mlt_tractor_s
139  * \param this a tractor
140  * \return the parent producer object
141  * \see MLT_TRACTOR_PRODUCER
142  */
143
144 mlt_producer mlt_tractor_producer( mlt_tractor this )
145 {
146         return this != NULL ? &this->parent : NULL;
147 }
148
149 /** Get the properties object associated to the tractor.
150  *
151  * \public \memberof mlt_tractor_s
152  * \param this a tractor
153  * \return the tractor's property list
154  * \see MLT_TRACTOR_PROPERTIES
155  */
156
157 mlt_properties mlt_tractor_properties( mlt_tractor this )
158 {
159         return MLT_PRODUCER_PROPERTIES( &this->parent );
160 }
161
162 /** Get the field this tractor is harvesting.
163  *
164  * \public \memberof mlt_tractor_s
165  * \param this a tractor
166  * \return a field or NULL if there is no field for this tractor
167  */
168
169 mlt_field mlt_tractor_field( mlt_tractor this )
170 {
171         return mlt_properties_get_data( MLT_TRACTOR_PROPERTIES( this ), "field", NULL );
172 }
173
174 /** Get the multitrack this tractor is pulling.
175  *
176  * \public \memberof mlt_tractor_s
177  * \param this a tractor
178  * \return a multitrack or NULL if there is none
179  */
180
181 mlt_multitrack mlt_tractor_multitrack( mlt_tractor this )
182 {
183         return mlt_properties_get_data( MLT_TRACTOR_PROPERTIES( this ), "multitrack", NULL );
184 }
185
186 /** Ensure the tractors in/out points match the multitrack.
187  *
188  * \public \memberof mlt_tractor_s
189  * \param this a tractor
190  */
191
192 void mlt_tractor_refresh( mlt_tractor this )
193 {
194         mlt_multitrack multitrack = mlt_tractor_multitrack( this );
195         mlt_properties properties = MLT_MULTITRACK_PROPERTIES( multitrack );
196         mlt_properties self = MLT_TRACTOR_PROPERTIES( this );
197         mlt_events_block( properties, self );
198         mlt_events_block( self, self );
199         mlt_multitrack_refresh( multitrack );
200         mlt_properties_set_position( self, "in", 0 );
201         mlt_properties_set_position( self, "out", mlt_properties_get_position( properties, "out" ) );
202         mlt_events_unblock( self, self );
203         mlt_events_unblock( properties, self );
204         mlt_properties_set_position( self, "length", mlt_properties_get_position( properties, "length" ) );
205 }
206
207 static void mlt_tractor_listener( mlt_multitrack tracks, mlt_tractor this )
208 {
209         mlt_tractor_refresh( this );
210 }
211
212 /** Connect the tractor.
213  *
214  * \public \memberof mlt_tractor_s
215  * \param this a tractor
216  * \param producer a producer
217  * \return true on error
218  */
219
220 int mlt_tractor_connect( mlt_tractor this, mlt_service producer )
221 {
222         int ret = mlt_service_connect_producer( MLT_TRACTOR_SERVICE( this ), producer, 0 );
223
224         // This is the producer we're going to connect to
225         if ( ret == 0 )
226                 this->producer = producer;
227
228         return ret;
229 }
230
231 /** Set the producer for a specific track.
232  *
233  * \public \memberof mlt_tractor_s
234  * \param this a tractor
235  * \param producer a producer
236  * \param index the 0-based track index
237  * \return true on error
238  */
239
240 int mlt_tractor_set_track( mlt_tractor this, mlt_producer producer, int index )
241 {
242         return mlt_multitrack_connect( mlt_tractor_multitrack( this ), producer, index );
243 }
244
245 /** Get the producer for a specific track.
246  *
247  * \public \memberof mlt_tractor_s
248  * \param this a tractor
249  * \param index the 0-based track index
250  * \return the producer for track \p index
251  */
252
253 mlt_producer mlt_tractor_get_track( mlt_tractor this, int index )
254 {
255         return mlt_multitrack_track( mlt_tractor_multitrack( this ), index );
256 }
257
258 static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
259 {
260         uint8_t *data = NULL;
261         mlt_properties properties = MLT_FRAME_PROPERTIES( this );
262         mlt_frame frame = mlt_frame_pop_service( this );
263         mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
264         mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale.interp" ) );
265         mlt_properties_set_int( frame_properties, "resize_alpha", mlt_properties_get_int( properties, "resize_alpha" ) );
266         mlt_properties_set_int( frame_properties, "distort", mlt_properties_get_int( properties, "distort" ) );
267         mlt_properties_set_double( frame_properties, "consumer_aspect_ratio", mlt_properties_get_double( properties, "consumer_aspect_ratio" ) );
268         mlt_properties_set_int( frame_properties, "consumer_deinterlace", mlt_properties_get_int( properties, "consumer_deinterlace" ) );
269         mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) );
270         mlt_properties_set_int( frame_properties, "normalised_width", mlt_properties_get_int( properties, "normalised_width" ) );
271         mlt_properties_set_int( frame_properties, "normalised_height", mlt_properties_get_int( properties, "normalised_height" ) );
272         mlt_frame_get_image( frame, buffer, format, width, height, writable );
273         mlt_properties_set_data( properties, "image", *buffer, *width * *height * 2, NULL, NULL );
274         mlt_properties_set_int( properties, "width", *width );
275         mlt_properties_set_int( properties, "height", *height );
276         mlt_properties_set_int( properties, "format", *format );
277         mlt_properties_set_double( properties, "aspect_ratio", mlt_frame_get_aspect_ratio( frame ) );
278         mlt_properties_set_int( properties, "progressive", mlt_properties_get_int( frame_properties, "progressive" ) );
279         mlt_properties_set_int( properties, "distort", mlt_properties_get_int( frame_properties, "distort" ) );
280         data = mlt_frame_get_alpha_mask( frame );
281         mlt_properties_set_data( properties, "alpha", data, 0, NULL, NULL );
282         return 0;
283 }
284
285 static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
286 {
287         mlt_properties properties = MLT_FRAME_PROPERTIES( this );
288         mlt_frame frame = mlt_frame_pop_audio( this );
289         mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
290         mlt_properties_set_data( properties, "audio", *buffer, 0, NULL, NULL );
291         mlt_properties_set_int( properties, "frequency", *frequency );
292         mlt_properties_set_int( properties, "channels", *channels );
293         return 0;
294 }
295
296 static void destroy_data_queue( void *arg )
297 {
298         if ( arg != NULL )
299         {
300                 // Assign the correct type
301                 mlt_deque queue = arg;
302
303                 // Iterate through each item and destroy them
304                 while ( mlt_deque_peek_front( queue ) != NULL )
305                         mlt_properties_close( mlt_deque_pop_back( queue ) );
306
307                 // Close the deque
308                 mlt_deque_close( queue );
309         }
310 }
311
312 /** Get the next frame.
313  *
314  * \private \memberof mlt_tractor_s
315  * \param parent the producer interface to the tractor
316  * \param[out] frame a frame by reference
317  * \param track the 0-based track index
318  * \return true on error
319  */
320
321 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int track )
322 {
323         mlt_tractor this = parent->child;
324
325         // We only respond to the first track requests
326         if ( track == 0 && this->producer != NULL )
327         {
328                 int i = 0;
329                 int done = 0;
330                 mlt_frame temp = NULL;
331                 int count = 0;
332                 int image_count = 0;
333
334                 // Get the properties of the parent producer
335                 mlt_properties properties = MLT_PRODUCER_PROPERTIES( parent );
336
337                 // Try to obtain the multitrack associated to the tractor
338                 mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
339
340                 // Or a specific producer
341                 mlt_producer producer = mlt_properties_get_data( properties, "producer", NULL );
342
343                 // The output frame will hold the 'global' data feeds (ie: those which are targetted for the final frame)
344                 mlt_deque data_queue = mlt_deque_init( );
345
346                 // Determine whether this tractor feeds to the consumer or stops here
347                 int global_feed = mlt_properties_get_int( properties, "global_feed" );
348
349                 // If we don't have one, we're in trouble...
350                 if ( multitrack != NULL )
351                 {
352                         // Used to garbage collect all frames
353                         char label[ 30 ];
354
355                         // Get the id of the tractor
356                         char *id = mlt_properties_get( properties, "_unique_id" );
357
358                         // Will be used to store the frame properties object
359                         mlt_properties frame_properties = NULL;
360
361                         // We'll store audio and video frames to use here
362                         mlt_frame audio = NULL;
363                         mlt_frame video = NULL;
364                         mlt_frame first_video = NULL;
365
366                         // Temporary properties
367                         mlt_properties temp_properties = NULL;
368
369                         // Get the multitrack's producer
370                         mlt_producer target = MLT_MULTITRACK_PRODUCER( multitrack );
371                         mlt_producer_seek( target, mlt_producer_frame( parent ) );
372                         mlt_producer_set_speed( target, mlt_producer_get_speed( parent ) );
373
374                         // We will create one frame and attach everything to it
375                         *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
376
377                         // Get the properties of the frame
378                         frame_properties = MLT_FRAME_PROPERTIES( *frame );
379
380                         // Loop through each of the tracks we're harvesting
381                         for ( i = 0; !done; i ++ )
382                         {
383                                 // Get a frame from the producer
384                                 mlt_service_get_frame( this->producer, &temp, i );
385
386                                 // Get the temporary properties
387                                 temp_properties = MLT_FRAME_PROPERTIES( temp );
388
389                                 // Check for last track
390                                 done = mlt_properties_get_int( temp_properties, "last_track" );
391
392                                 // Handle fx only tracks
393                                 if ( mlt_properties_get_int( temp_properties, "fx_cut" ) )
394                                 {
395                                         int hide = ( video == NULL ? 1 : 0 ) | ( audio == NULL ? 2 : 0 );
396                                         mlt_properties_set_int( temp_properties, "hide", hide );
397                                 }
398
399                                 // We store all frames with a destructor on the output frame
400                                 sprintf( label, "_%s_%d", id, count ++ );
401                                 mlt_properties_set_data( frame_properties, label, temp, 0, ( mlt_destructor )mlt_frame_close, NULL );
402
403                                 // We want to append all 'final' feeds to the global queue
404                                 if ( !done && mlt_properties_get_data( temp_properties, "data_queue", NULL ) != NULL )
405                                 {
406                                         // Move the contents of this queue on to the output frames data queue
407                                         mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "data_queue", NULL );
408                                         mlt_deque temp = mlt_deque_init( );
409                                         while ( global_feed && mlt_deque_count( sub_queue ) )
410                                         {
411                                                 mlt_properties p = mlt_deque_pop_back( sub_queue );
412                                                 if ( mlt_properties_get_int( p, "final" ) )
413                                                         mlt_deque_push_back( data_queue, p );
414                                                 else
415                                                         mlt_deque_push_back( temp, p );
416                                         }
417                                         while( mlt_deque_count( temp ) )
418                                                 mlt_deque_push_front( sub_queue, mlt_deque_pop_back( temp ) );
419                                         mlt_deque_close( temp );
420                                 }
421
422                                 // Now do the same with the global queue but without the conditional behaviour
423                                 if ( mlt_properties_get_data( temp_properties, "global_queue", NULL ) != NULL )
424                                 {
425                                         mlt_deque sub_queue = mlt_properties_get_data( MLT_FRAME_PROPERTIES( temp ), "global_queue", NULL );
426                                         while ( mlt_deque_count( sub_queue ) )
427                                         {
428                                                 mlt_properties p = mlt_deque_pop_back( sub_queue );
429                                                 mlt_deque_push_back( data_queue, p );
430                                         }
431                                 }
432
433                                 // Pick up first video and audio frames
434                                 if ( !done && !mlt_frame_is_test_audio( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 2 ) )
435                                 {
436                                         // Order of frame creation is starting to get problematic
437                                         if ( audio != NULL )
438                                         {
439                                                 mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), producer_get_audio );
440                                                 mlt_deque_push_front( MLT_FRAME_AUDIO_STACK( temp ), audio );
441                                         }
442                                         audio = temp;
443                                 }
444                                 if ( !done && !mlt_frame_is_test_card( temp ) && !( mlt_properties_get_int( temp_properties, "hide" ) & 1 ) )
445                                 {
446                                         if ( video != NULL )
447                                         {
448                                                 mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), producer_get_image );
449                                                 mlt_deque_push_front( MLT_FRAME_IMAGE_STACK( temp ), video );
450                                         }
451                                         video = temp;
452                                         if ( first_video == NULL )
453                                                 first_video = temp;
454
455                                         // Ensure that all frames know the aspect ratio of the background
456                                         mlt_properties_set_double( temp_properties, "output_ratio",
457                                                                                            mlt_properties_get_double( MLT_FRAME_PROPERTIES( first_video ), "aspect_ratio" ) );
458
459                                         mlt_properties_set_int( MLT_FRAME_PROPERTIES( temp ), "image_count", ++ image_count );
460                                         image_count = 1;
461                                 }
462                         }
463
464                         // Now stack callbacks
465                         if ( audio != NULL )
466                         {
467                                 mlt_frame_push_audio( *frame, audio );
468                                 mlt_frame_push_audio( *frame, producer_get_audio );
469                         }
470
471                         if ( video != NULL )
472                         {
473                                 mlt_properties video_properties = MLT_FRAME_PROPERTIES( first_video );
474                                 mlt_frame_push_service( *frame, video );
475                                 mlt_frame_push_service( *frame, producer_get_image );
476                                 if ( global_feed )
477                                         mlt_properties_set_data( frame_properties, "data_queue", data_queue, 0, NULL, NULL );
478                                 mlt_properties_set_data( video_properties, "global_queue", data_queue, 0, destroy_data_queue, NULL );
479                                 mlt_properties_set_int( frame_properties, "width", mlt_properties_get_int( video_properties, "width" ) );
480                                 mlt_properties_set_int( frame_properties, "height", mlt_properties_get_int( video_properties, "height" ) );
481                                 mlt_properties_set_int( frame_properties, "real_width", mlt_properties_get_int( video_properties, "real_width" ) );
482                                 mlt_properties_set_int( frame_properties, "real_height", mlt_properties_get_int( video_properties, "real_height" ) );
483                                 mlt_properties_set_int( frame_properties, "progressive", mlt_properties_get_int( video_properties, "progressive" ) );
484                                 mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_properties_get_double( video_properties, "aspect_ratio" ) );
485                                 mlt_properties_set_int( frame_properties, "image_count", image_count );
486                         }
487                         else
488                         {
489                                 destroy_data_queue( data_queue );
490                         }
491
492                         mlt_frame_set_position( *frame, mlt_producer_frame( parent ) );
493                         mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_audio", audio == NULL );
494                         mlt_properties_set_int( MLT_FRAME_PROPERTIES( *frame ), "test_image", video == NULL );
495                         mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "consumer_lock_service", this, 0, NULL, NULL );
496                 }
497                 else if ( producer != NULL )
498                 {
499                         mlt_producer_seek( producer, mlt_producer_frame( parent ) );
500                         mlt_producer_set_speed( producer, mlt_producer_get_speed( parent ) );
501                         mlt_service_get_frame( this->producer, frame, track );
502                 }
503                 else
504                 {
505                         fprintf( stderr, "tractor without a multitrack!!\n" );
506                         mlt_service_get_frame( this->producer, frame, track );
507                 }
508
509                 // Prepare the next frame
510                 mlt_producer_prepare_next( parent );
511
512                 // Indicate our found status
513                 return 0;
514         }
515         else
516         {
517                 // Generate a test card
518                 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( parent ) );
519                 return 0;
520         }
521 }
522
523 /** Close the tractor and free its resources.
524  *
525  * \public \memberof mlt_tractor_s
526  * \param this a tractor
527  */
528
529 void mlt_tractor_close( mlt_tractor this )
530 {
531         if ( this != NULL && mlt_properties_dec_ref( MLT_TRACTOR_PROPERTIES( this ) ) <= 0 )
532         {
533                 this->parent.close = NULL;
534                 mlt_producer_close( &this->parent );
535                 free( this );
536         }
537 }
538