From: lilo_booter Date: Wed, 20 Oct 2004 20:48:31 +0000 (+0000) Subject: SDL Preview provisional checkin X-Git-Url: http://research.m1stereo.tv/gitweb?a=commitdiff_plain;h=945a27e179cc4d1290b488a5b7cba466f06d009b;p=melted SDL Preview provisional checkin git-svn-id: https://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt@485 d19143bc-622f-0410-bfdd-b5b2a6649095 --- diff --git a/src/framework/mlt_consumer.c b/src/framework/mlt_consumer.c index 2ba2901..87d9e9d 100644 --- a/src/framework/mlt_consumer.c +++ b/src/framework/mlt_consumer.c @@ -88,6 +88,11 @@ int mlt_consumer_init( mlt_consumer this, void *child ) mlt_events_register( properties, "consumer-frame-show", ( mlt_transmitter )mlt_consumer_frame_show ); mlt_events_register( properties, "consumer-stopped", NULL ); + + // Create the push mutex and condition + pthread_mutex_init( &this->put_mutex, NULL ); + pthread_cond_init( &this->put_cond, NULL ); + } return error; } @@ -188,6 +193,37 @@ int mlt_consumer_start( mlt_consumer this ) return 0; } +/** An alternative method to feed frames into the consumer - only valid if + the consumer itself is not connected. +*/ + +int mlt_consumer_put_frame( mlt_consumer this, mlt_frame frame ) +{ + int error = 1; + + // Get the service assoicated to the consumer + mlt_service service = mlt_consumer_service( this ); + + if ( mlt_service_producer( service ) == NULL ) + { + pthread_mutex_lock( &this->put_mutex ); + if ( this->put != NULL ) + pthread_cond_wait( &this->put_cond, &this->put_mutex ); + if ( this->put == NULL ) + this->put = frame; + else + mlt_frame_close( frame ); + pthread_cond_broadcast( &this->put_cond ); + pthread_mutex_unlock( &this->put_mutex ); + } + else + { + mlt_frame_close( frame ); + } + + return error; +} + /** Protected method for consumer to get frames from connected service */ @@ -200,7 +236,20 @@ mlt_frame mlt_consumer_get_frame( mlt_consumer this ) mlt_service service = mlt_consumer_service( this ); // Get the frame - if ( mlt_service_get_frame( service, &frame, 0 ) == 0 ) + if ( mlt_service_producer( service ) == NULL ) + { + pthread_mutex_lock( &this->put_mutex ); + if ( this->put == NULL ) + pthread_cond_wait( &this->put_cond, &this->put_mutex ); + frame = this->put; + this->put = NULL; + pthread_cond_broadcast( &this->put_cond ); + pthread_mutex_unlock( &this->put_mutex ); + if ( frame != NULL ) + mlt_service_apply_filters( service, frame, 0 ); + } + + if ( frame != NULL || mlt_service_get_frame( service, &frame, 0 ) == 0 ) { // Get the consumer properties mlt_properties properties = mlt_consumer_properties( this ); @@ -415,6 +464,11 @@ static void consumer_read_ahead_stop( mlt_consumer this ) pthread_cond_broadcast( &this->cond ); pthread_mutex_unlock( &this->mutex ); + // Broadcast to the put condition in case it's waiting + pthread_mutex_lock( &this->put_mutex ); + pthread_cond_broadcast( &this->put_cond ); + pthread_mutex_unlock( &this->put_mutex ); + // Join the thread pthread_join( this->ahead_thread, NULL ); @@ -513,6 +567,11 @@ int mlt_consumer_stop( mlt_consumer this ) if ( mlt_properties_get_int( properties, "real_time" ) ) consumer_read_ahead_stop( this ); + // Just in case... + pthread_mutex_lock( &this->put_mutex ); + pthread_cond_broadcast( &this->put_cond ); + pthread_mutex_unlock( &this->put_mutex ); + // Kill the test card mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); @@ -549,6 +608,11 @@ void mlt_consumer_close( mlt_consumer this ) this->close = NULL; this->parent.close = NULL; + // Destroy the push mutex and condition + pthread_cond_broadcast( &this->put_cond ); + pthread_mutex_destroy( &this->put_mutex ); + pthread_cond_destroy( &this->put_cond ); + // Call the childs close if available if ( consumer_close != NULL ) consumer_close( this ); diff --git a/src/framework/mlt_consumer.h b/src/framework/mlt_consumer.h index 2439b97..d05d307 100644 --- a/src/framework/mlt_consumer.h +++ b/src/framework/mlt_consumer.h @@ -49,6 +49,9 @@ struct mlt_consumer_s pthread_t ahead_thread; pthread_mutex_t mutex; pthread_cond_t cond; + pthread_mutex_t put_mutex; + pthread_cond_t put_cond; + mlt_frame put; }; /** Public final methods @@ -61,6 +64,7 @@ extern mlt_properties mlt_consumer_properties( mlt_consumer self ); extern int mlt_consumer_connect( mlt_consumer self, mlt_service producer ); extern int mlt_consumer_start( mlt_consumer self ); extern void mlt_consumer_purge( mlt_consumer self ); +extern int mlt_consumer_put_frame( mlt_consumer self, mlt_frame frame ); extern mlt_frame mlt_consumer_get_frame( mlt_consumer self ); extern mlt_frame mlt_consumer_rt_frame( mlt_consumer self ); extern int mlt_consumer_stop( mlt_consumer self ); diff --git a/src/modules/sdl/Makefile b/src/modules/sdl/Makefile index f79c93e..7a7004c 100644 --- a/src/modules/sdl/Makefile +++ b/src/modules/sdl/Makefile @@ -3,7 +3,9 @@ include ../../../config.mak TARGET = ../libmltsdl.so OBJS = factory.o \ - consumer_sdl.o + consumer_sdl.o \ + consumer_sdl_preview.o \ + consumer_sdl_still.o CFLAGS += -I../.. `sdl-config --cflags` diff --git a/src/modules/sdl/configure b/src/modules/sdl/configure index e7572ca..f4556a1 100755 --- a/src/modules/sdl/configure +++ b/src/modules/sdl/configure @@ -5,6 +5,8 @@ then cat << EOF >> ../consumers.dat sdl libmltsdl.so +sdl_preview libmltsdl.so +sdl_still libmltsdl.so EOF fi diff --git a/src/modules/sdl/consumer_sdl.c b/src/modules/sdl/consumer_sdl.c index e78e329..3ee54ed 100644 --- a/src/modules/sdl/consumer_sdl.c +++ b/src/modules/sdl/consumer_sdl.c @@ -494,6 +494,7 @@ static int consumer_play_video( consumer_sdl this, mlt_frame frame ) // open SDL window with video overlay, if possible sdl_lock_display(); this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags ); + SDL_SetClipRect( this->sdl_screen, &this->rect ); sdl_unlock_display(); if ( this->sdl_screen != NULL ) @@ -637,14 +638,24 @@ static void *consumer_thread( void *arg ) int64_t playtime = 0; struct timespec tm = { 0, 100000 }; - if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ) < 0 ) + if ( mlt_properties_get_int( mlt_consumer_properties( consumer ), "sdl_started" ) == 0 ) { - fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() ); - return NULL; + if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 ) + { + fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() ); + return NULL; + } + + SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL ); + SDL_EnableUNICODE( 1 ); + } + else + { + if ( SDL_GetVideoSurface( ) != NULL ) + consumer_get_dimensions( &this->window_width, &this->window_height ); } - SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL ); - SDL_EnableUNICODE( 1 ); + SDL_InitSubSystem( SDL_INIT_AUDIO ); // Loop until told not to while( this->running ) @@ -700,7 +711,11 @@ static void *consumer_thread( void *arg ) // internal cleanup if ( this->sdl_overlay != NULL ) SDL_FreeYUVOverlay( this->sdl_overlay ); - SDL_Quit( ); + + SDL_QuitSubSystem( SDL_INIT_AUDIO ); + + if ( mlt_properties_get_int( mlt_consumer_properties( consumer ), "sdl_started" ) == 0 ) + SDL_Quit( ); while( mlt_deque_count( this->queue ) ) mlt_frame_close( mlt_deque_pop_back( this->queue ) ); diff --git a/src/modules/sdl/consumer_sdl.h b/src/modules/sdl/consumer_sdl.h index bf2fefd..a518420 100644 --- a/src/modules/sdl/consumer_sdl.h +++ b/src/modules/sdl/consumer_sdl.h @@ -24,5 +24,7 @@ #include extern mlt_consumer consumer_sdl_init( char * ); +extern mlt_consumer consumer_sdl_still_init( char * ); +extern mlt_consumer consumer_sdl_preview_init( char * ); #endif diff --git a/src/modules/sdl/consumer_sdl_preview.c b/src/modules/sdl/consumer_sdl_preview.c new file mode 100644 index 0000000..a17b59f --- /dev/null +++ b/src/modules/sdl/consumer_sdl_preview.c @@ -0,0 +1,250 @@ +/* + * consumer_sdl_preview.c -- A Simple DirectMedia Layer consumer + * Copyright (C) 2004-2005 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "consumer_sdl.h" +#include +#include +#include +#include +#include +#include +#include + +typedef struct consumer_sdl_s *consumer_sdl; + +struct consumer_sdl_s +{ + struct mlt_consumer_s parent; + mlt_consumer play; + mlt_consumer still; + pthread_t thread; + int joined; + int running; + int sdl_flags; + double last_speed; +}; + +/** Forward references to static functions. +*/ + +static int consumer_start( mlt_consumer parent ); +static int consumer_stop( mlt_consumer parent ); +static int consumer_is_stopped( mlt_consumer parent ); +static void consumer_close( mlt_consumer parent ); +static void *consumer_thread( void * ); +static void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer this, mlt_frame frame ); + +mlt_consumer consumer_sdl_preview_init( char *arg ) +{ + consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 ); + if ( this != NULL && mlt_consumer_init( &this->parent, this ) == 0 ) + { + // Get the parent consumer object + mlt_consumer parent = &this->parent; + this->play = mlt_factory_consumer( "sdl", arg ); + this->still = mlt_factory_consumer( "sdl_still", arg ); + mlt_properties_set( mlt_consumer_properties( parent ), "real_time", "0" ); + parent->close = consumer_close; + parent->start = consumer_start; + parent->stop = consumer_stop; + parent->is_stopped = consumer_is_stopped; + this->joined = 1; + mlt_events_listen( mlt_consumer_properties( this->play ), this, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb ); + mlt_events_listen( mlt_consumer_properties( this->still ), this, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb ); + return parent; + } + free( this ); + return NULL; +} + +void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer parent, mlt_frame frame ) +{ + consumer_sdl this = parent->child; + this->last_speed = mlt_properties_get_double( mlt_frame_properties( frame ), "_speed" ); + mlt_events_fire( mlt_consumer_properties( parent ), "consumer-frame-show", frame, NULL ); +} + +static int consumer_start( mlt_consumer parent ) +{ + consumer_sdl this = parent->child; + + if ( !this->running ) + { + pthread_attr_t thread_attributes; + + consumer_stop( parent ); + + this->running = 1; + this->joined = 0; + this->last_speed = 1; + + // Inherit the scheduling priority + pthread_attr_init( &thread_attributes ); + pthread_attr_setinheritsched( &thread_attributes, PTHREAD_INHERIT_SCHED ); + + pthread_create( &this->thread, &thread_attributes, consumer_thread, this ); + } + + return 0; +} + +static int consumer_stop( mlt_consumer parent ) +{ + // Get the actual object + consumer_sdl this = parent->child; + + if ( this->joined == 0 ) + { + // Kill the thread and clean up + this->running = 0; + + pthread_join( this->thread, NULL ); + this->joined = 1; + } + + return 0; +} + +static int consumer_is_stopped( mlt_consumer parent ) +{ + consumer_sdl this = parent->child; + return !this->running; +} + +static void *consumer_thread( void *arg ) +{ + // Identify the arg + consumer_sdl this = arg; + + // Get the consumer + mlt_consumer consumer = &this->parent; + + // internal intialization + int first = 1; + mlt_frame frame = NULL; + + // properties + mlt_properties properties = mlt_consumer_properties( consumer ); + mlt_properties play = mlt_consumer_properties( this->play ); + mlt_properties still = mlt_consumer_properties( this->still ); + + if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 ) + { + fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() ); + return NULL; + } + + SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL ); + SDL_EnableUNICODE( 1 ); + + // Inform child consumers that we control the sdl + mlt_properties_set_int( play, "sdl_started", 1 ); + mlt_properties_set_int( still, "sdl_started", 1 ); + + // Pass properties down + mlt_properties_set_data( play, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL ); + mlt_properties_set_data( still, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL ); + mlt_properties_set_data( play, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL ); + mlt_properties_set_data( still, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL ); + mlt_properties_set( play, "rescale", mlt_properties_get( properties, "rescale" ) ); + mlt_properties_set( still, "rescale", mlt_properties_get( properties, "rescale" ) ); + mlt_properties_set( play, "width", mlt_properties_get( properties, "width" ) ); + mlt_properties_set( still, "width", mlt_properties_get( properties, "width" ) ); + mlt_properties_set( play, "height", mlt_properties_get( properties, "height" ) ); + mlt_properties_set( still, "height", mlt_properties_get( properties, "height" ) ); + + mlt_properties_pass( play, mlt_consumer_properties( consumer ), "play." ); + mlt_properties_pass( still, mlt_consumer_properties( consumer ), "still." ); + + // Loop until told not to + while( this->running ) + { + // Get a frame from the attached producer + frame = mlt_consumer_get_frame( consumer ); + + // Ensure that we have a frame + if ( frame != NULL ) + { + // Get the speed of the frame + double speed = mlt_properties_get_double( mlt_frame_properties( frame ), "_speed" ); + + // Make sure the recipient knows that this frame isn't really rendered + mlt_properties_set_int( mlt_frame_properties( frame ), "rendered", 0 ); + + if ( !first && mlt_consumer_is_stopped( this->play ) && mlt_consumer_is_stopped( this->still ) ) + { + this->running = 0; + mlt_frame_close( frame ); + } + else if ( this->last_speed != 1 ) + { + if ( !mlt_consumer_is_stopped( this->play ) ) + mlt_consumer_stop( this->play ); + if ( mlt_consumer_is_stopped( this->still ) ) + { + this->last_speed = speed; + mlt_consumer_start( this->still ); + } + mlt_consumer_put_frame( this->still, frame ); + } + else + { + if ( !mlt_consumer_is_stopped( this->still ) ) + mlt_consumer_stop( this->still ); + if ( mlt_consumer_is_stopped( this->play ) ) + { + this->last_speed = speed; + mlt_consumer_start( this->play ); + } + mlt_consumer_put_frame( this->play, frame ); + } + first = 0; + } + } + + mlt_consumer_stop( this->play ); + mlt_consumer_stop( this->still ); + + SDL_Quit( ); + + return NULL; +} + +/** Callback to allow override of the close method. +*/ + +static void consumer_close( mlt_consumer parent ) +{ + // Get the actual object + consumer_sdl this = parent->child; + + // Stop the consumer + mlt_consumer_stop( parent ); + + // Now clean up the rest + mlt_consumer_close( parent ); + + // Close the child consumers + mlt_consumer_close( this->play ); + mlt_consumer_close( this->still ); + + // Finally clean up this + free( this ); +} diff --git a/src/modules/sdl/consumer_sdl_still.c b/src/modules/sdl/consumer_sdl_still.c new file mode 100644 index 0000000..105d887 --- /dev/null +++ b/src/modules/sdl/consumer_sdl_still.c @@ -0,0 +1,551 @@ +/* + * consumer_sdl_still.c -- A Simple DirectMedia Layer consumer + * Copyright (C) 2003-2004 Ushodaya Enterprises Limited + * Author: Charles Yates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "consumer_sdl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** This classes definition. +*/ + +typedef struct consumer_sdl_s *consumer_sdl; + +struct consumer_sdl_s +{ + struct mlt_consumer_s parent; + mlt_properties properties; + pthread_t thread; + int joined; + int running; + int window_width; + int window_height; + float aspect_ratio; + float display_aspect; + double last_frame_aspect; + int width; + int height; + int playing; + int sdl_flags; + SDL_Surface *sdl_screen; + SDL_Rect rect; + uint8_t *buffer; + int last_position; + mlt_producer last_producer; +}; + +/** Forward references to static functions. +*/ + +static int consumer_start( mlt_consumer parent ); +static int consumer_stop( mlt_consumer parent ); +static int consumer_is_stopped( mlt_consumer parent ); +static void consumer_close( mlt_consumer parent ); +static void *consumer_thread( void * ); +static int consumer_get_dimensions( int *width, int *height ); +static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args ); + +/** This is what will be called by the factory - anything can be passed in + via the argument, but keep it simple. +*/ + +mlt_consumer consumer_sdl_still_init( char *arg ) +{ + // Create the consumer object + consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 ); + + // If no malloc'd and consumer init ok + if ( this != NULL && mlt_consumer_init( &this->parent, this ) == 0 ) + { + // Get the parent consumer object + mlt_consumer parent = &this->parent; + + // Attach a colour space converter + mlt_filter filter = mlt_factory_filter( "avcolour_space", NULL ); + mlt_properties_set_int( mlt_filter_properties( filter ), "forced", mlt_image_yuv422 ); + mlt_service_attach( mlt_consumer_service( &this->parent ), filter ); + mlt_filter_close( filter ); + + // We have stuff to clean up, so override the close method + parent->close = consumer_close; + + // get a handle on properties + mlt_service service = mlt_consumer_service( parent ); + this->properties = mlt_service_properties( service ); + + // Default scaler (for now we'll use nearest) + mlt_properties_set( this->properties, "rescale", "nearest" ); + + // We're always going to run this in non-realtime mode + mlt_properties_set( this->properties, "real_time", "0" ); + + // Default progressive true + mlt_properties_set_int( this->properties, "progressive", 1 ); + + // Get sample aspect ratio + this->aspect_ratio = mlt_properties_get_double( this->properties, "aspect_ratio" ); + + // Ensure we don't join on a non-running object + this->joined = 1; + + // Default display aspect ratio + this->display_aspect = 4.0 / 3.0; + + // process actual param + if ( arg == NULL || sscanf( arg, "%dx%d", &this->width, &this->height ) != 2 ) + { + this->width = mlt_properties_get_int( this->properties, "width" ); + this->height = mlt_properties_get_int( this->properties, "height" ); + } + + // Default window size + this->window_width = ( float )this->height * this->display_aspect; + this->window_height = this->height; + + // Set the sdl flags + this->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE | SDL_DOUBLEBUF; + + // Allow thread to be started/stopped + parent->start = consumer_start; + parent->stop = consumer_stop; + parent->is_stopped = consumer_is_stopped; + + // Register specific events + mlt_events_register( this->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event ); + + // Return the consumer produced + return parent; + } + + // malloc or consumer init failed + free( this ); + + // Indicate failure + return NULL; +} + +static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args ) +{ + if ( listener != NULL ) + listener( owner, this, ( SDL_Event * )args[ 0 ] ); +} + +static int consumer_start( mlt_consumer parent ) +{ + consumer_sdl this = parent->child; + + if ( !this->running ) + { + pthread_attr_t thread_attributes; + + consumer_stop( parent ); + + this->last_position = -1; + this->running = 1; + this->joined = 0; + + // Allow the user to force resizing to window size + if ( mlt_properties_get_int( this->properties, "resize" ) ) + { + mlt_properties_set_int( this->properties, "width", this->width ); + mlt_properties_set_int( this->properties, "height", this->height ); + } + + // Inherit the scheduling priority + pthread_attr_init( &thread_attributes ); + pthread_attr_setinheritsched( &thread_attributes, PTHREAD_INHERIT_SCHED ); + + pthread_create( &this->thread, &thread_attributes, consumer_thread, this ); + } + + return 0; +} + +static int consumer_stop( mlt_consumer parent ) +{ + // Get the actual object + consumer_sdl this = parent->child; + + if ( this->joined == 0 ) + { + // Kill the thread and clean up + this->running = 0; + + pthread_join( this->thread, NULL ); + this->joined = 1; + + mlt_frame_close( parent->put ); + parent->put = NULL; + } + + return 0; +} + +static int consumer_is_stopped( mlt_consumer parent ) +{ + consumer_sdl this = parent->child; + return !this->running; +} + +static int sdl_lock_display( ) +{ + SDL_Surface *screen = SDL_GetVideoSurface( ); + return screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 ); +} + +static void sdl_unlock_display( ) +{ + SDL_Surface *screen = SDL_GetVideoSurface( ); + if ( screen != NULL && SDL_MUSTLOCK( screen ) ) + SDL_UnlockSurface( screen ); +} + +static int consumer_play_video( consumer_sdl this, mlt_frame frame ) +{ + // Get the properties of this consumer + mlt_properties properties = this->properties; + + mlt_image_format vfmt = mlt_image_rgb24; + int width = this->width, height = this->height; + uint8_t *image; + int changed = 0; + + + // Handle events + if ( this->sdl_screen != NULL ) + { + SDL_Event event; + + changed = consumer_get_dimensions( &this->window_width, &this->window_height ); + + while ( SDL_PollEvent( &event ) ) + { + mlt_events_fire( this->properties, "consumer-sdl-event", &event, NULL ); + + switch( event.type ) + { + case SDL_VIDEORESIZE: + this->window_width = event.resize.w; + this->window_height = event.resize.h; + changed = 1; + break; + case SDL_QUIT: + this->running = 0; + break; + case SDL_KEYDOWN: + { + mlt_producer producer = mlt_properties_get_data( properties, "transport_producer", NULL ); + char keyboard[ 2 ] = " "; + void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( properties, "transport_callback", NULL ); + if ( callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 ) + { + keyboard[ 0 ] = ( char )event.key.keysym.unicode; + callback( producer, keyboard ); + } + } + break; + } + } + } + + if ( width != this->width || height != this->height || + ( ( int )( this->last_frame_aspect * 1000 ) != ( int )( mlt_frame_get_aspect_ratio( frame ) * 1000 ) && + ( mlt_frame_get_aspect_ratio( frame ) != 1.0 || this->last_frame_aspect == 0.0 ) ) ) + + { + this->width = width; + this->height = height; + this->last_frame_aspect = mlt_frame_get_aspect_ratio( frame ); + changed = 1; + } + + if ( this->sdl_screen == NULL || changed ) + { + // Determine frame's display aspect ratio + float frame_aspect = mlt_frame_get_aspect_ratio( frame ) * this->width / this->height; + + // Determine window's new display aspect ratio + float this_aspect = ( float )this->window_width / this->window_height; + + // If using hardware scaler + if ( mlt_properties_get( properties, "rescale" ) != NULL && + !strcmp( mlt_properties_get( properties, "rescale" ), "none" ) ) + { + // Special case optimisation to negate odd effect of sample aspect ratio + // not corresponding exactly with image resolution. + if ( ( (int)( this_aspect * 1000 ) == (int)( this->display_aspect * 1000 ) ) && + ( (int)( mlt_frame_get_aspect_ratio( frame ) * 1000 ) == (int)( this->aspect_ratio * 1000 ) ) ) + { + this->rect.w = this->window_width; + this->rect.h = this->window_height; + } + else + { + // Use hardware scaler to normalise display aspect ratio + this->rect.w = frame_aspect / this_aspect * this->window_width; + this->rect.h = this->window_height; + if ( this->rect.w > this->window_width ) + { + this->rect.w = this->window_width; + this->rect.h = this_aspect / frame_aspect * this->window_height; + } + } + } + // Special case optimisation to negate odd effect of sample aspect ratio + // not corresponding exactly with image resolution. + else if ( (int)( this_aspect * 1000 ) == (int)( this->display_aspect * 1000 ) ) + { + this->rect.w = this->window_width; + this->rect.h = this->window_height; + } + // Use hardware scaler to normalise sample aspect ratio + else if ( this->window_height * this->display_aspect > this->window_width ) + { + this->rect.w = this->window_width; + this->rect.h = this->window_width / this->display_aspect; + } + else + { + this->rect.w = this->window_height * this->display_aspect; + this->rect.h = this->window_height; + } + + this->rect.x = ( this->window_width - this->rect.w ) / 2; + this->rect.y = ( this->window_height - this->rect.h ) / 2; + + mlt_properties_set_int( this->properties, "rect_x", this->rect.x ); + mlt_properties_set_int( this->properties, "rect_y", this->rect.y ); + mlt_properties_set_int( this->properties, "rect_w", this->rect.w ); + mlt_properties_set_int( this->properties, "rect_h", this->rect.h ); + + // open SDL window + sdl_lock_display(); + this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 16, this->sdl_flags ); + consumer_get_dimensions( &this->window_width, &this->window_height ); + sdl_unlock_display(); + changed = 1; + } + + if ( mlt_properties_get_int( properties, "changed" ) ) + { + sdl_lock_display(); + this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 16, this->sdl_flags ); + SDL_SetClipRect( this->sdl_screen, &this->rect ); + SDL_Flip( this->sdl_screen ); + consumer_get_dimensions( &this->window_width, &this->window_height ); + sdl_unlock_display(); + mlt_properties_set_int( properties, "changed", 0 ); + changed = 1; + } + + if ( changed == 0 && + this->last_position == mlt_frame_get_position( frame ) && + this->last_producer == mlt_properties_get_data( mlt_frame_properties( frame ), "_producer", NULL ) ) + return 0; + + // Update last frame shown info + this->last_position = mlt_frame_get_position( frame ); + this->last_producer = mlt_properties_get_data( mlt_frame_properties( frame ), "_producer", NULL ); + + // Get the image, width and height + mlt_events_fire( properties, "consumer-frame-show", frame, NULL ); + mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 ); + + if ( this->sdl_screen != NULL ) + { + sdl_lock_display(); + + // Calculate the scan length + int scanlength = this->sdl_screen->pitch / 2; + + // Obtain the clip rect from the screen + SDL_Rect rect = this->rect; + + // Generate the affine transform scaling values + float scale_width = ( float )width / ( float )rect.w; + float scale_height = ( float )height / ( float )rect.h; + + // Constants defined for clarity and optimisation + int stride = width * 3; + uint16_t *start = ( uint16_t * )this->sdl_screen->pixels + rect.y * scanlength + rect.x; + + int y; + + // Iterate through the screen using a very basic scaling algorithm + for ( y = 0; y < rect.h; y ++ ) + { + // Obtain the pointer to the current screen row + uint16_t *p = start; + + // Calculate the row_index + int row_index = ( int )( scale_height * y ); + + // Calculate the pointer for the y offset (positioned on B instead of R) + uint8_t *row = image + stride * row_index; + + int x; + + // Iterate through the screen width + for ( x = 0; x < rect.w; x ++ ) + { + // Obtain the pixel pointer + uint8_t *q = row + ( ( int )( scale_width * x ) * 3 ); + + // Map the BGR colour from the frame to the SDL format + *p ++ = SDL_MapRGB( this->sdl_screen->format, *q, *( q + 1 ), *( q + 2 ) ); + } + + // Move to the next row + start += scanlength; + } + + // Flip it into sight + SDL_Flip( this->sdl_screen ); + + sdl_unlock_display(); + } + + return 0; +} + +/** Threaded wrapper for pipe. +*/ + +static void *consumer_thread( void *arg ) +{ + // Identify the arg + consumer_sdl this = arg; + + // Get the consumer + mlt_consumer consumer = &this->parent; + + // internal intialization + mlt_frame frame = NULL; + struct timespec tm = { 0, 1000 }; + + if ( mlt_properties_get_int( mlt_consumer_properties( consumer ), "sdl_started" ) == 0 ) + { + if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) < 0 ) + { + fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() ); + return NULL; + } + + SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL ); + SDL_EnableUNICODE( 1 ); + } + else + { + this->sdl_screen = SDL_GetVideoSurface( ); + mlt_properties_set_int( mlt_consumer_properties( consumer ), "changed", 1 ); + } + + // Loop until told not to + while( this->running ) + { + // Get a frame from the attached producer + frame = mlt_consumer_rt_frame( consumer ); + + // Ensure that we have a frame + if ( frame != NULL ) + { + consumer_play_video( this, frame ); + mlt_frame_close( frame ); + nanosleep( &tm, NULL ); + } + } + + if ( mlt_properties_get_int( mlt_consumer_properties( consumer ), "sdl_started" ) == 0 ) + SDL_Quit( ); + + this->sdl_screen = NULL; + + return NULL; +} + +static int consumer_get_dimensions( int *width, int *height ) +{ + int changed = 0; + + // SDL windows manager structure + SDL_SysWMinfo wm; + + // Specify the SDL Version + SDL_VERSION( &wm.version ); + + // Lock the display + sdl_lock_display(); + + // Get the wm structure + if ( SDL_GetWMInfo( &wm ) == 1 ) + { + // Check that we have the X11 wm + if ( wm.subsystem == SDL_SYSWM_X11 ) + { + // Get the SDL window + Window window = wm.info.x11.window; + + // Get the display session + Display *display = wm.info.x11.display; + + // Get the window attributes + XWindowAttributes attr; + XGetWindowAttributes( display, window, &attr ); + + // Determine whether window has changed + changed = *width != attr.width || *height != attr.height; + + // Return width and height + *width = attr.width; + *height = attr.height; + } + } + + // Unlock the display + sdl_lock_display(); + + return changed; +} + +/** Callback to allow override of the close method. +*/ + +static void consumer_close( mlt_consumer parent ) +{ + // Get the actual object + consumer_sdl this = parent->child; + + // Stop the consumer + mlt_consumer_stop( parent ); + + // Now clean up the rest + mlt_consumer_close( parent ); + + // Finally clean up this + free( this ); +} diff --git a/src/modules/sdl/factory.c b/src/modules/sdl/factory.c index 9e5f56e..dd43f99 100644 --- a/src/modules/sdl/factory.c +++ b/src/modules/sdl/factory.c @@ -41,6 +41,10 @@ void *mlt_create_consumer( char *id, void *arg ) { if ( !strcmp( id, "sdl" ) ) return consumer_sdl_init( arg ); + if ( !strcmp( id, "sdl_still" ) ) + return consumer_sdl_still_init( arg ); + if ( !strcmp( id, "sdl_preview" ) ) + return consumer_sdl_preview_init( arg ); return NULL; }