2 * producer_count.c -- counting producer
3 * Copyright (C) 2013 Brian Matherly
4 * Author: Brian Matherly <pez4brian@yahoo.com>
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.
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.
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
21 #include <framework/mlt.h>
27 /* Private Constants */
28 #define MAX_TEXT_LEN 512
29 #define LINE_PIXEL_VALUE 0x00
30 #define RING_PIXEL_VALUE 0xff
31 #define CLOCK_PIXEL_VALUE 0x50
32 #define FRAME_BACKGROUND_COLOR "0xd0d0d0ff"
33 #define TEXT_BACKGROUND_COLOR "0x00000000"
34 #define TEXT_FOREGROUND_COLOR "0x000000ff"
35 // Ratio of graphic elements relative to image size
36 #define LINE_WIDTH_RATIO 1
37 #define OUTER_RING_RATIO 90
38 #define INNER_RING_RATIO 80
39 #define TEXT_SIZE_RATIO 70
41 static inline void mix_pixel( uint8_t* image, int width, int x, int y, int value, float mix )
43 uint8_t* p = image + (( y * width ) + x ) * 4;
47 value = ((float)value * mix) + ((float)*p * (1.0 - mix));
57 /** Fill an audio buffer with 1kHz samples.
60 static void fill_beep( mlt_properties producer_properties, float* buffer, int frequency, int channels, int samples )
65 for( s = 0; s < samples; s++ )
68 float t = (float)s/(float)frequency;
69 float value = sin( 2*M_PI*f*t );
71 for( c = 0; c < channels; c++ )
73 float* sample_ptr = buffer + (c * samples) + s;
79 static int producer_get_audio( mlt_frame frame, int16_t** buffer, mlt_audio_format* format, int* frequency, int* channels, int* samples )
81 mlt_producer producer = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "_producer_count", NULL );
82 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
83 char* sound = mlt_properties_get( producer_properties, "sound" );
84 double fps = mlt_producer_get_fps( producer );
85 int position = mlt_frame_get_position( frame );
89 if( fps == 0 ) fps = 25;
91 // Correct the returns if necessary
92 *format = mlt_audio_float;
93 *frequency = *frequency <= 0 ? 48000 : *frequency;
94 *channels = *channels <= 0 ? 2 : *channels;
95 *samples = *samples <= 0 ? mlt_sample_calculator( fps, *frequency, position ) : *samples;
97 // Allocate the buffer
98 size = *samples * *channels * sizeof( float );
99 *buffer = mlt_pool_alloc( size );
101 // Determine if this should be a tone or silence.
102 if( strcmp( sound, "none") )
104 if( !strcmp( sound, "2pop" ) )
106 int out = mlt_properties_get_int( producer_properties, "out" );
107 int frames = out - position;
109 if( frames == lrint( fps * 2 ) )
114 else if( !strcmp( sound, "frame0" ) )
116 int frames = position;
118 // Apply the direction
119 char* direction = mlt_properties_get( producer_properties, "direction" );
120 if( !strcmp( direction, "down" ) )
122 int out = mlt_properties_get_int( producer_properties, "out" );
123 frames = out - position;
126 frames = position % lrint( fps );
136 fill_beep( producer_properties, (float*)*buffer, *frequency, *channels, *samples );
141 memset( *buffer, 0, size );
144 // Set the buffer for destruction
145 mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release );
149 static mlt_frame get_background_frame( mlt_producer producer )
151 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
152 mlt_frame bg_frame = NULL;
153 mlt_producer color_producer = mlt_properties_get_data( producer_properties, "_color_producer", NULL );
155 if( !color_producer )
157 mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
158 color_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "colour:" );
159 mlt_properties_set_data( producer_properties, "_color_producer", color_producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
161 mlt_properties color_properties = MLT_PRODUCER_PROPERTIES( color_producer );
162 mlt_properties_set( color_properties, "colour", FRAME_BACKGROUND_COLOR );
167 mlt_producer_seek( color_producer, 0 );
168 mlt_service_get_frame( MLT_PRODUCER_SERVICE( color_producer ), &bg_frame, 0 );
174 static mlt_frame get_text_frame( mlt_producer producer, mlt_position position )
176 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
177 mlt_producer text_producer = mlt_properties_get_data( producer_properties, "_text_producer", NULL );
178 mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
179 mlt_frame text_frame = NULL;
183 text_producer = mlt_factory_producer( profile, mlt_environment( "MLT_PRODUCER" ), "qtext:" );
185 // Save the producer for future use.
186 mlt_properties_set_data( producer_properties, "_text_producer", text_producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
188 // Calculate the font size.
189 char font_size[MAX_TEXT_LEN];
190 snprintf( font_size, MAX_TEXT_LEN - 1, "%dpx", profile->height * TEXT_SIZE_RATIO / 100 );
192 // Configure the producer.
193 mlt_properties text_properties = MLT_PRODUCER_PROPERTIES( text_producer );
194 mlt_properties_set( text_properties, "size", font_size );
195 mlt_properties_set( text_properties, "weight", "400" );
196 mlt_properties_set( text_properties, "fgcolour", TEXT_FOREGROUND_COLOR );
197 mlt_properties_set( text_properties, "bgcolour", TEXT_BACKGROUND_COLOR );
198 mlt_properties_set( text_properties, "pad", "0" );
199 mlt_properties_set( text_properties, "outline", "0" );
200 mlt_properties_set( text_properties, "align", "center" );
205 mlt_properties text_properties = MLT_PRODUCER_PROPERTIES( text_producer );
206 char* direction = mlt_properties_get( producer_properties, "direction" );
207 char* style = mlt_properties_get( producer_properties, "style" );
208 char text[MAX_TEXT_LEN] = "";
209 int fps = lrint(mlt_profile_fps( profile )); if( fps == 0 ) fps = 25;
211 // Apply the direction
212 if( !strcmp( direction, "down" ) )
214 int out = mlt_properties_get_int( producer_properties, "out" );
215 position = out - position;
218 // Calculate clock values
219 int seconds = position / fps;
220 int frames = MLT_POSITION_MOD(position, fps);
221 int minutes = seconds / 60;
222 seconds = seconds % 60;
223 int hours = minutes / 60;
224 minutes = minutes % 60;
226 // Apply the time style
227 if( !strcmp( style, "frames" ) )
229 snprintf( text, MAX_TEXT_LEN - 1, MLT_POSITION_FMT, position );
231 else if( !strcmp( style, "timecode" ) )
233 snprintf( text, MAX_TEXT_LEN - 1, "%.2d:%.2d:%.2d:%.2d", hours, minutes, seconds, frames );
235 else if( !strcmp( style, "clock" ) )
237 snprintf( text, MAX_TEXT_LEN - 1, "%.2d:%.2d:%.2d", hours, minutes, seconds );
239 else if( !strcmp( style, "seconds+1" ) )
241 snprintf( text, MAX_TEXT_LEN - 1, "%d", seconds + 1 );
245 snprintf( text, MAX_TEXT_LEN - 1, "%d", seconds );
248 mlt_properties_set( text_properties, "text", text );
251 mlt_service_get_frame( MLT_PRODUCER_SERVICE( text_producer ), &text_frame, 0 );
257 static void draw_ring( uint8_t* image, mlt_profile profile, int radius, int line_width )
259 float sar = mlt_profile_sar( profile );
260 int x_center = profile->width / 2;
261 int y_center = profile->height / 2;
262 int max_radius = radius + line_width;
263 int a = max_radius + 1;
266 line_width += 1; // Compensate for aliasing.
268 // Scan through each pixel in one quadrant of the circle.
271 b = ( max_radius / sar ) + 1.0;
274 // Use Pythagorean theorem to determine the distance from this pixel to the center.
276 float b2 = b*sar*b*sar;
277 float c = sqrtf( a2 + b2 );
278 float distance = c - radius;
280 if( distance > 0 && distance < line_width )
282 // This pixel is within the ring.
287 // Antialias the outside of the ring
290 else if( (float)line_width - distance < 1.0 )
292 // Antialias the inside of the ring
293 mix = (float)line_width - distance;
296 // Apply this value to all 4 quadrants of the circle.
297 mix_pixel( image, profile->width, x_center + b, y_center - a, RING_PIXEL_VALUE, mix );
298 mix_pixel( image, profile->width, x_center - b, y_center - a, RING_PIXEL_VALUE, mix );
299 mix_pixel( image, profile->width, x_center + b, y_center + a, RING_PIXEL_VALUE, mix );
300 mix_pixel( image, profile->width, x_center - b, y_center + a, RING_PIXEL_VALUE, mix );
306 static void draw_cross( uint8_t* image, mlt_profile profile, int line_width )
312 // Draw a horizontal line
316 y = ( profile->height - line_width ) / 2 + i;
317 x = profile->width - 1;
320 mix_pixel( image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0 );
324 // Draw a vertical line
325 line_width = lrint((float)line_width * mlt_profile_sar( profile ));
329 x = ( profile->width - line_width ) / 2 + i;
330 y = profile->height - 1;
333 mix_pixel( image, profile->width, x, y, LINE_PIXEL_VALUE, 1.0 );
338 static void draw_clock( uint8_t* image, mlt_profile profile, int angle, int line_width )
340 float sar = mlt_profile_sar( profile );
342 int x_center = profile->width / 2;
343 int y_center = profile->height / 2;
345 line_width += 1; // Compensate for aliasing.
347 // Look at each quadrant of the frame to see what should be done.
348 for( q = 1; q <= 4; q++ )
350 int max_angle = q * 90;
351 int x_sign = ( q == 1 || q == 2 ) ? 1 : -1;
352 int y_sign = ( q == 1 || q == 4 ) ? 1 : -1;
353 int x_start = x_center * x_sign;
354 int y_start = y_center * y_sign;
356 // Compensate for rounding error of even lengths
357 // (there is no "middle" pixel so everything is offset).
358 if( x_sign == 1 && profile->width % 2 == 0 ) x_start--;
359 if( y_sign == -1 && profile->height % 2 == 0 ) y_start++;
361 if( angle >= max_angle )
363 // This quadrant is completely behind the clock hand. Fill it in.
364 int dx = x_start + x_sign;
368 int dy = y_start + y_sign;
372 mix_pixel( image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0 );
376 else if ( max_angle - angle < 90 )
378 // This quadrant is partially filled
379 // Calculate a point (vx,vy) that lies on the line created by the angle from 0,0.
384 // Assume maximum y and calculate the corresponding x value
385 // for a point at the other end of this line.
386 if( x_sign * y_sign == 1 )
388 vx = x_sign * sar * y_center / tan( ( max_angle - angle ) * M_PI / 180.0 );
392 vx = x_sign * sar * y_center * tan( ( max_angle - angle ) * M_PI / 180.0 );
395 // Calculate the length of the line defined by vx,vy
396 lv = sqrtf((float)(vx*vx)*sar*sar + (float)vy*vy);
398 // Scan through each pixel in the quadrant counting up/down to 0,0.
399 int dx = x_start + x_sign;
403 int dy = y_start + y_sign;
407 // Calculate the cross product to determine which side of
408 // the line this pixel lies on.
409 int xp = vx * (vy - dy) - vy * (vx - dx);
410 xp = xp * -1; // Easier to work with positive. Positive number means "behind" the line.
413 // This pixel is behind the clock hand and should be filled in.
414 // Calculate the distance from the pixel to the line to determine
415 // if it is part of the clock hand.
416 float distance = (float)xp / lv;
417 int val = CLOCK_PIXEL_VALUE;
420 if( distance < line_width )
422 // This pixel makes up the clock hand.
423 val = LINE_PIXEL_VALUE;
427 // Antialias the outside of the clock hand
430 else if( (float)line_width - distance < 1.0 )
432 // Antialias the inside of the clock hand
433 mix_pixel( image, profile->width, x_center + dx, y_center - dy, CLOCK_PIXEL_VALUE, 1.0 );
434 mix = (float)line_width - distance;
438 mix_pixel( image, profile->width, x_center + dx, y_center - dy, val, mix );
446 static void add_clock_to_frame( mlt_producer producer, mlt_frame frame, mlt_position position )
448 mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
449 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
450 uint8_t* image = NULL;
451 mlt_image_format format = mlt_image_rgb24a;
453 int width = profile->width;
454 int height = profile->height;
455 int line_width = LINE_WIDTH_RATIO * (width > height ? height : width) / 100;
456 int fps = lrint(mlt_profile_fps( profile )); if( fps == 0 ) fps = 25;
457 int radius = (width > height ? height : width) / 2;
458 char* direction = mlt_properties_get( producer_properties, "direction" );
461 mlt_frame_get_image( frame, &image, &format, &width, &height, 1 );
463 // Calculate the angle for the clock.
464 if( !strcmp( direction, "down" ) )
466 int out = mlt_producer_get_out( producer );
467 int frames = fps - MLT_POSITION_MOD(out - position, fps);
468 clock_angle = lrint( (frames + 1) * 360 / fps );
472 int frames = MLT_POSITION_MOD(position, fps);
473 clock_angle = lrint( (frames + 1) * 360 / fps );
476 draw_clock( image, profile, clock_angle, line_width );
477 draw_cross( image, profile, line_width );
478 draw_ring( image, profile, ( radius * OUTER_RING_RATIO ) / 100, line_width );
479 draw_ring( image, profile, ( radius * INNER_RING_RATIO ) / 100, line_width );
481 size = mlt_image_format_size( format, width, height, NULL );
482 mlt_frame_set_image( frame, image, size, mlt_pool_release );
486 static void add_text_to_bg( mlt_producer producer, mlt_frame bg_frame, mlt_frame text_frame )
488 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES( producer );
489 mlt_transition transition = mlt_properties_get_data( producer_properties, "_transition", NULL );
493 mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
494 transition = mlt_factory_transition( profile, "composite", NULL );
496 // Save the transition for future use.
497 mlt_properties_set_data( producer_properties, "_transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL );
499 // Configure the transition.
500 mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition );
501 mlt_properties_set( transition_properties, "geometry", "0%/0%:100%x100%:100" );
502 mlt_properties_set( transition_properties, "halign", "center" );
503 mlt_properties_set( transition_properties, "valign", "middle" );
506 if( transition && bg_frame && text_frame )
508 // Apply the transition.
509 mlt_transition_process( transition, bg_frame, text_frame );
513 static int producer_get_image( mlt_frame frame, uint8_t** image, mlt_image_format* format, int* width, int* height, int writable )
515 mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
516 mlt_position position = mlt_frame_original_position( frame );
517 mlt_producer producer = mlt_properties_get_data( properties, "_producer_count", NULL );
518 mlt_frame bg_frame = NULL;
519 mlt_frame text_frame = NULL;
522 char* background = mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "background" );
524 mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) );
526 bg_frame = get_background_frame( producer );
527 if( !strcmp( background, "clock" ) )
529 add_clock_to_frame( producer, bg_frame, position );
531 text_frame = get_text_frame( producer, position );
532 add_text_to_bg( producer, bg_frame, text_frame );
536 // Get the image from the background frame.
537 error = mlt_frame_get_image( bg_frame, image, format, width, height, writable );
538 size = mlt_image_format_size( *format, *width, *height, NULL );
539 // Detach the image from the bg_frame so it is not released.
540 mlt_frame_set_image( bg_frame, *image, size, NULL );
541 // Attach the image to the input frame.
542 mlt_frame_set_image( frame, *image, size, mlt_pool_release );
543 mlt_frame_close( bg_frame );
548 mlt_frame_close( text_frame );
551 mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) );
556 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
559 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
560 mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
562 if ( *frame != NULL )
564 // Obtain properties of frame
565 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( *frame );
567 // Save the producer to be used later
568 mlt_properties_set_data( frame_properties, "_producer_count", producer, 0, NULL, NULL );
570 // Update time code on the frame
571 mlt_frame_set_position( *frame, mlt_producer_frame( producer ) );
573 mlt_properties_set_int( frame_properties, "progressive", 1 );
574 mlt_properties_set_double( frame_properties, "aspect_ratio", mlt_profile_sar( profile ) );
575 mlt_properties_set_int( frame_properties, "meta.media.width", profile->width );
576 mlt_properties_set_int( frame_properties, "meta.media.height", profile->height );
578 // Configure callbacks
579 mlt_frame_push_get_image( *frame, producer_get_image );
580 mlt_frame_push_audio( *frame, producer_get_audio );
583 // Calculate the next time code
584 mlt_producer_prepare_next( producer );
589 static void producer_close( mlt_producer this )
592 mlt_producer_close( this );
599 mlt_producer producer_count_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
601 // Create a new producer object
602 mlt_producer producer = mlt_producer_new( profile );
604 // Initialize the producer
607 mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
608 mlt_properties_set( properties, "direction", "down" );
609 mlt_properties_set( properties, "style", "seconds+1" );
610 mlt_properties_set( properties, "sound", "none" );
611 mlt_properties_set( properties, "background", "clock" );
613 // Callback registration
614 producer->get_frame = producer_get_frame;
615 producer->close = ( mlt_destructor )producer_close;