]> git.sesse.net Git - mlt/blob - src/modules/avsync/producer_blipflash.c
Initial implementation of producer_qtext
[mlt] / src / modules / avsync / producer_blipflash.c
1 /*
2  * producer_blipflash.c -- blip/flash generating producer
3  * Copyright (C) 2013 Brian Matherly
4  * Author: Brian Matherly <pez4brian@yahoo.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <framework/mlt.h>
22 #include <math.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 /** Fill an audio buffer with 1kHz "blip" samples.
28 */
29
30 static void fill_blip( mlt_properties producer_properties, float* buffer, int frequency, int channels, int samples )
31 {
32         int new_size = samples * channels * sizeof( float );
33         int old_size = 0;
34         float* blip = mlt_properties_get_data( producer_properties, "_blip", &old_size );
35
36         if( !blip || new_size > old_size )
37         {
38                 blip = mlt_pool_alloc( new_size );
39
40                 // Fill the blip buffer
41                 if ( blip != NULL )
42                 {
43                         int s = 0;
44                         int c = 0;
45
46                         for( s = 0; s < samples; s++ )
47                         {
48                                 float f = 1000.0;
49                                 float t = (float)s/(float)frequency;
50                                 // Add 90 deg so the blip always starts at 1 for easy detection.
51                                 float phase = M_PI / 2;
52                                 float value = sin( 2*M_PI*f*t + phase );
53
54                                 for( c = 0; c < channels; c++ )
55                                 {
56                                         float* sample_ptr = ((float*) blip) + (c * samples) + s;
57                                         *sample_ptr = value;
58                                 }
59                         }
60                 }
61                 // Cache the audio blip to save from regenerating it with every blip.
62                 mlt_properties_set_data( producer_properties, "_blip", blip, new_size, mlt_pool_release, NULL );
63         };
64
65         if( blip ) memcpy( buffer, blip, new_size );
66 }
67
68 static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples )
69 {
70         mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_blipflash", NULL );
71         mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
72         int size = *samples * *channels * sizeof( float );
73         double fps = mlt_producer_get_fps( producer );
74         int frames = mlt_frame_get_position( frame )  + mlt_properties_get_int( producer_properties, "offset" );
75         int seconds = frames / fps;
76
77         // Correct the returns if necessary
78         *format = mlt_audio_float;
79         *frequency = *frequency <= 0 ? 48000 : *frequency;
80         *channels = *channels <= 0 ? 2 : *channels;
81         *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, frames ) : *samples;
82
83         // Allocate the buffer
84         *buffer = mlt_pool_alloc( size );
85
86         // Determine if this should be a blip or silence.
87         frames = frames % lrint( fps );
88         seconds = seconds % mlt_properties_get_int( producer_properties, "period" );
89         if( seconds == 0 && frames == 0 )
90         {
91                 fill_blip( producer_properties, (float*)*buffer, *frequency, *channels, *samples );
92         }
93         else
94         {
95                 // Fill silence.
96                 memset( *buffer, 0, size );
97         }
98
99         // Set the buffer for destruction
100         mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release );
101
102         return 0;
103 }
104
105 /** Fill an image buffer with either white (flash) or black as requested.
106 */
107
108 static void fill_image( mlt_properties producer_properties, char* color, uint8_t* buffer, mlt_image_format format, int width, int height )
109 {
110
111         int new_size = mlt_image_format_size( format, width, height, NULL );
112         int old_size = 0;
113         uint8_t* image = mlt_properties_get_data( producer_properties, color, &old_size );
114
115         if( !image || new_size > old_size )
116         {
117                 // Need to create a new cached image.
118                 image = mlt_pool_alloc( new_size );
119
120                 if ( image != NULL )
121                 {
122                         uint8_t r, g, b;
123                         uint8_t* p = image;
124
125                         if( !strcmp( color, "_flash" ) )
126                         {
127                                 r = g = b = 255; // White
128                         }
129                         else
130                         {
131                                 r = g = b = 0; // Black
132                         }
133
134                         switch( format )
135                         {
136                                 default:
137                                 case mlt_image_yuv422:
138                                 {
139                                         int uneven = width % 2;
140                                         int count = ( width - uneven ) / 2 + 1;
141                                         uint8_t y, u, v;
142
143                                         RGB2YUV_601_SCALED( r, g, b, y, u, v );
144                                         int i = height + 1;
145                                         while ( --i )
146                                         {
147                                                 int j = count;
148                                                 while ( --j )
149                                                 {
150                                                         *p ++ = y;
151                                                         *p ++ = u;
152                                                         *p ++ = y;
153                                                         *p ++ = v;
154                                                 }
155                                                 if ( uneven )
156                                                 {
157                                                         *p ++ = y;
158                                                         *p ++ = u;
159                                                 }
160                                         }
161                                         break;
162                                 }
163                                 case mlt_image_rgb24:
164                                 {
165                                         int i = width * height + 1;
166                                         while ( --i )
167                                         {
168                                                 *p ++ = r;
169                                                 *p ++ = g;
170                                                 *p ++ = b;
171                                         }
172                                         break;
173                                 }
174                                 case mlt_image_rgb24a:
175                                 {
176                                         int i = width * height + 1;
177                                         while ( --i )
178                                         {
179                                                 *p ++ = r;
180                                                 *p ++ = g;
181                                                 *p ++ = b;
182                                                 *p ++ = 255; // alpha
183                                         }
184                                         break;
185                                 }
186                         }
187                         // Cache the image to save from regenerating it with every frame.
188                         mlt_properties_set_data( producer_properties, color, image, new_size, mlt_pool_release, NULL );
189                 }
190         }
191
192         if( image ) memcpy( buffer, image, new_size );
193 }
194
195 static int producer_get_image( mlt_frame frame, uint8_t** buffer, mlt_image_format* format, int* width, int* height, int writable )
196 {
197         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
198         mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_blipflash", NULL );
199         mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
200         int size = 0;
201         double fps = mlt_producer_get_fps( producer );
202         int frames = mlt_frame_get_position( frame );
203         int seconds = frames / fps;
204
205         mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) );
206
207         // Correct the returns if necessary
208         if( *format != mlt_image_yuv422 && *format != mlt_image_rgb24 && *format != mlt_image_rgb24a )
209                 *format = mlt_image_yuv422;
210         if( *width <= 0 )
211                 *width = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->width;
212         if ( *height <= 0 )
213                 *height = mlt_service_profile( MLT_PRODUCER_SERVICE(producer) )->height;
214
215         // Allocate the buffer
216         size = mlt_image_format_size( *format, *width, *height, NULL );
217         *buffer = mlt_pool_alloc( size );
218
219         // Determine if this should be a flash or black.
220         frames = frames % lrint( fps );
221         seconds = seconds % mlt_properties_get_int( producer_properties, "period" );
222         if( seconds == 0 && frames == 0 )
223         {
224                 fill_image( producer_properties, "_flash", *buffer, *format, *width, *height );
225         }
226         else
227         {
228                 fill_image( producer_properties, "_black", *buffer, *format, *width, *height );
229         }
230
231         mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) );
232
233         // Create the alpha channel
234         int alpha_size = *width * *height;
235         uint8_t *alpha = mlt_pool_alloc( alpha_size );
236         if ( alpha )
237                 memset( alpha, 255, alpha_size );
238
239         // Update the frame
240         mlt_frame_set_image( frame, *buffer, size, mlt_pool_release );
241         mlt_frame_set_alpha( frame, alpha, alpha_size, mlt_pool_release );
242         mlt_properties_set_double( properties, "aspect_ratio", mlt_properties_get_double( producer_properties, "aspect_ratio" ) );
243         mlt_properties_set_int( properties, "progressive", 1 );
244         mlt_properties_set_int( properties, "meta.media.width", *width );
245         mlt_properties_set_int( properties, "meta.media.height", *height );
246
247         return 0;
248 }
249
250 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
251 {
252         // Generate a frame
253         *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
254
255         if ( *frame != NULL )
256         {
257                 // Obtain properties of frame
258                 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame );
259
260                 // Save the producer to be used later
261                 mlt_properties_set_data( frame_properties, "_producer_blipflash", producer, 0, NULL, NULL );
262
263                 // Update time code on the frame
264                 mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
265
266                 // Configure callbacks
267                 mlt_frame_push_get_image( *frame, producer_get_image );
268                 mlt_frame_push_audio( *frame, producer_get_audio );
269         }
270
271         // Calculate the next time code
272         mlt_producer_prepare_next( producer );
273
274         return 0;
275 }
276
277 static void producer_close( mlt_producer this )
278 {
279         this->close = NULL;
280         mlt_producer_close( this );
281         free( this );
282 }
283
284 /** Initialize.
285 */
286
287 mlt_producer producer_blipflash_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
288 {
289         // Create a new producer object
290         mlt_producer producer = mlt_producer_new( profile );
291         mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
292
293         // Initialize the producer
294         if ( producer )
295         {
296                 mlt_properties_set_int( producer_properties, "period", 1 );
297                 mlt_properties_set_int( producer_properties, "offset", 0 );
298
299                 // Callback registration
300                 producer->get_frame = producer_get_frame;
301                 producer->close = ( mlt_destructor )producer_close;
302         }
303
304         return producer;
305 }