X-Git-Url: http://research.m1stereo.tv/gitweb?a=blobdiff_plain;f=mlt%2Fsrc%2Fmodules%2Fffmpeg%2Fproducer_ffmpeg.c;h=15b1c89ec5c0698f67e2304e507f8c6d75c30a69;hb=6336039a203c6496691784682f9ad56eb13abcc3;hp=9e2eed0f705f5f5cc36c39b57ca0fdaa7071dcc1;hpb=7da5daf600f8b97781d85a137c3aa81effb7e610;p=melted diff --git a/mlt/src/modules/ffmpeg/producer_ffmpeg.c b/mlt/src/modules/ffmpeg/producer_ffmpeg.c index 9e2eed0..15b1c89 100644 --- a/mlt/src/modules/ffmpeg/producer_ffmpeg.c +++ b/mlt/src/modules/ffmpeg/producer_ffmpeg.c @@ -19,16 +19,126 @@ */ #include "producer_ffmpeg.h" + #include +#include + +#include #include #include +#include +#include +#include +#include +#include typedef struct producer_ffmpeg_s *producer_ffmpeg; +/** Bi-directional pipe structure. +*/ + +typedef struct rwpipe +{ + int pid; + FILE *reader; + FILE *writer; +} +rwpipe; + +/** Create a bidirectional pipe for the given command. +*/ + +rwpipe *rwpipe_open( char *command ) +{ + rwpipe *this = malloc( sizeof( rwpipe ) ); + + if ( this != NULL ) + { + int input[ 2 ]; + int output[ 2 ]; + + pipe( input ); + pipe( output ); + + this->pid = fork(); + + if ( this->pid == 0 ) + { + signal( SIGPIPE, SIG_DFL ); + signal( SIGHUP, SIG_DFL ); + signal( SIGINT, SIG_DFL ); + signal( SIGTERM, SIG_DFL ); + signal( SIGSTOP, SIG_DFL ); + signal( SIGCHLD, SIG_DFL ); + + dup2( output[ 0 ], STDIN_FILENO ); + dup2( input[ 1 ], STDOUT_FILENO ); + + close( input[ 0 ] ); + close( input[ 1 ] ); + close( output[ 0 ] ); + close( output[ 1 ] ); + + execl( "/bin/sh", "sh", "-c", command, NULL ); + exit( 255 ); + } + else + { + setpgid( this->pid, this->pid ); + + close( input[ 1 ] ); + close( output[ 0 ] ); + + this->reader = fdopen( input[ 0 ], "r" ); + this->writer = fdopen( output[ 1 ], "w" ); + } + } + + return this; +} + +/** Read data from the pipe. +*/ + +FILE *rwpipe_reader( rwpipe *this ) +{ + if ( this != NULL ) + return this->reader; + else + return NULL; +} + +/** Write data to the pipe. +*/ + +FILE *rwpipe_writer( rwpipe *this ) +{ + if ( this != NULL ) + return this->writer; + else + return NULL; +} + +/** Close the pipe and process. +*/ + +void rwpipe_close( rwpipe *this ) +{ + if ( this != NULL ) + { + fclose( this->reader ); + fclose( this->writer ); + kill( - this->pid, SIGKILL ); + waitpid( - this->pid, NULL, 0 ); + free( this ); + } +} + struct producer_ffmpeg_s { struct mlt_producer_s parent; - char *command; + rwpipe *video_pipe; + rwpipe *audio_pipe; FILE *video; FILE *audio; uint64_t expected; @@ -36,25 +146,72 @@ struct producer_ffmpeg_s int open; int width; int height; + int end_of_video; + int end_of_audio; }; static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); static void producer_close( mlt_producer parent ); -mlt_producer producer_ffmpeg_init( char *command ) +/** Consutruct an ffmpeg producer. +*/ + +mlt_producer producer_ffmpeg_init( char *file ) { producer_ffmpeg this = calloc( sizeof( struct producer_ffmpeg_s ), 1 ); - if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) + if ( file != NULL && this != NULL && mlt_producer_init( &this->parent, this ) == 0 ) { + int usable = 1; + + // Get the producer mlt_producer producer = &this->parent; + // Get the properties of the producer + mlt_properties properties = mlt_producer_properties( producer ); + + // Override get_frame and close methods producer->get_frame = producer_get_frame; producer->close = producer_close; - if ( command != NULL ) - this->command = strdup( command ); + // Set the properties + mlt_properties_set( properties, "mlt_type", "producer_ffmpeg" ); + + if ( !strcmp( file, "v4l" ) ) + { + mlt_properties_set( properties, "video_type", "v4l" ); + mlt_properties_set( properties, "video_file", "/dev/video0" ); + mlt_properties_set( properties, "video_size", "640x480" ); + mlt_properties_set( properties, "audio_type", "dsp" ); + mlt_properties_set( properties, "audio_file", "/dev/dsp" ); + } + else + { + struct stat buf; + if ( stat( file, &buf ) != 0 || !S_ISREG( buf.st_mode ) ) + usable = 0; + mlt_properties_set( properties, "video_type", "file" ); + mlt_properties_set( properties, "video_file", file ); + mlt_properties_set( properties, "video_size", "" ); + mlt_properties_set( properties, "audio_type", "file" ); + mlt_properties_set( properties, "audio_file", file ); + } - this->buffer = malloc( 1024 * 1024 ); + mlt_properties_set_int( properties, "audio_rate", 48000 ); + mlt_properties_set_int( properties, "audio_channels", 2 ); + mlt_properties_set_int( properties, "audio_track", 0 ); + + mlt_properties_set( properties, "log_id", file ); + mlt_properties_set( properties, "resource", file ); + mlt_properties_set_position( properties, "length", 36000 ); + mlt_properties_set_position( properties, "out", 36000 ); + + this->buffer = malloc( 1024 * 1024 * 2 ); + + if ( !usable ) + { + mlt_producer_close( &this->parent ); + producer = NULL; + } return producer; } @@ -85,60 +242,116 @@ static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_forma return 0; } -FILE *producer_ffmpeg_run_video( producer_ffmpeg this ) +FILE *producer_ffmpeg_run_video( producer_ffmpeg this, mlt_position position ) { - if ( this->video == NULL && !this->open ) + if ( this->video == NULL ) { - if ( this->command != NULL ) + // Get the producer + mlt_producer producer = &this->parent; + + // Get the properties of the producer + mlt_properties properties = mlt_producer_properties( producer ); + + // Get the video loop property + int video_loop = mlt_properties_get_int( properties, "video_loop" ); + + if ( !this->open || video_loop ) { - char command[ 1024 ]; - float fps = mlt_producer_get_fps( &this->parent ); - float position = mlt_producer_position( &this->parent ); - sprintf( command, "ffmpeg -i \"%s\" -ss %f -f imagepipe -r %f -f yuv4mpegpipe - 2>/dev/null", this->command, position, fps ); - this->video = popen( command, "r" ); + const char *mlt_prefix = mlt_factory_prefix( ); + char *video_type = mlt_properties_get( properties, "video_type" ); + char *video_file = mlt_properties_get( properties, "video_file" ); + float video_rate = mlt_properties_get_double( properties, "fps" ); + char *video_size = mlt_properties_get( properties, "video_size" ); + char command[ 1024 ] = ""; + + sprintf( command, "%s/ffmpeg/video.sh \"%s\" \"%s\" \"%s\" %f %f 2>/dev/null", + mlt_prefix, + video_type, + video_file, + video_size, + video_rate, + ( float )position ); + + this->video_pipe = rwpipe_open( command ); + this->video = rwpipe_reader( this->video_pipe ); } } return this->video; } -FILE *producer_ffmpeg_run_audio( producer_ffmpeg this ) +FILE *producer_ffmpeg_run_audio( producer_ffmpeg this, mlt_position position ) { - if ( this->audio == NULL && !this->open ) + // Get the producer + mlt_producer producer = &this->parent; + + // Get the properties of the producer + mlt_properties properties = mlt_producer_properties( producer ); + + if ( this->audio == NULL ) { - if ( this->command != NULL ) + int audio_loop = mlt_properties_get_int( properties, "audio_loop" ); + + if ( !this->open || audio_loop ) { - char command[ 1024 ]; - float position = mlt_producer_position( &this->parent ); - sprintf( command, "ffmpeg -i \"%s\" -ss %f -f s16le -ar 48000 -ac 2 - 2>/dev/null", this->command, position ); - this->audio = popen( command, "r" ); + const char *mlt_prefix = mlt_factory_prefix( ); + char *audio_type = mlt_properties_get( properties, "audio_type" ); + char *audio_file = mlt_properties_get( properties, "audio_file" ); + int frequency = mlt_properties_get_int( properties, "audio_rate" ); + int channels = mlt_properties_get_int( properties, "audio_channels" ); + int track = mlt_properties_get_int( properties, "audio_track" ); + char command[ 1024 ] = ""; + + sprintf( command, "%s/ffmpeg/audio.sh \"%s\" \"%s\" %f %d %d %d 2>/dev/null", + mlt_prefix, + audio_type, + audio_file, + ( float )position, + frequency, + channels, + track ); + + this->audio_pipe = rwpipe_open( command ); + this->audio = rwpipe_reader( this->audio_pipe ); } } return this->audio; } -static void producer_ffmpeg_position( producer_ffmpeg this, uint64_t requested ) +static void producer_ffmpeg_position( producer_ffmpeg this, uint64_t requested, int *skip ) { - if ( requested != this->expected ) + *skip = 0; + + if ( this->open && requested > this->expected ) + { + // Skip the following n frames + *skip = requested - this->expected; + } + else if ( requested != this->expected ) { + // Close the video pipe if ( this->video != NULL ) - pclose( this->video ); + rwpipe_close( this->video_pipe ); this->video = NULL; + + // Close the audio pipe if ( this->audio != NULL ) - pclose( this->audio ); + rwpipe_close( this->audio_pipe ); this->audio = NULL; // We should not be open now this->open = 0; + this->end_of_video = 0; + this->end_of_audio = 0; } // This is the next frame we expect - this->expected = mlt_producer_frame( &this->parent ) + 1; + this->expected = requested + 1; // Open the pipe - this->video = producer_ffmpeg_run_video( this ); + this->video = producer_ffmpeg_run_video( this, 0 ); // Open the audio pipe - this->audio = producer_ffmpeg_run_audio( this ); + this->audio = producer_ffmpeg_run_audio( this, 0 ); // We should be open now this->open = 1; @@ -150,13 +363,17 @@ static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_forma mlt_properties properties = mlt_frame_properties( this ); producer_ffmpeg producer = mlt_properties_get_data( properties, "producer_ffmpeg", NULL ); + mlt_properties producer_properties = mlt_producer_properties( &producer->parent ); + + int64_t target = mlt_properties_get_double( properties, "target" ); + int skip = mlt_properties_get_int( properties, "skip" ); - *frequency = 48000; - *channels = 2; - *samples = 1920; + float fps = mlt_properties_get_double( producer_properties, "fps" ); + *frequency = mlt_properties_get_int( producer_properties, "audio_rate" ); + *channels = mlt_properties_get_int( producer_properties, "audio_channels" ); - // Size - int size = *samples * *channels * 2; + // Maximum Size (?) + int size = ( *frequency / 25 ) * *channels * 2; // Allocate an image *buffer = malloc( size ); @@ -164,20 +381,30 @@ static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_forma // Read it if ( producer->audio != NULL ) { - if ( fread( *buffer, size, 1, producer->audio ) != 1 ) + do { - pclose( producer->audio ); - producer->audio = NULL; + *samples = mlt_sample_calculator( fps, *frequency, target - skip ); + if ( fread( *buffer, *samples * *channels * 2, 1, producer->audio ) != 1 ) + { + rwpipe_close( producer->audio_pipe ); + producer->audio = NULL; + producer->end_of_audio = 1; + } } + while( producer->audio != NULL && skip -- ); } else { + *samples = mlt_sample_calculator( fps, *frequency, target ); memset( *buffer, 0, size ); } // Pass the data on the frame properties mlt_properties_set_data( properties, "audio", *buffer, size, free, NULL ); + // Set the producer properties + mlt_properties_set_int( producer_properties, "end_of_clip", producer->end_of_video && producer->end_of_audio ); + return 0; } @@ -215,18 +442,25 @@ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int i producer_ffmpeg this = producer->child; int width; int height; + int skip; // Construct a test frame *frame = mlt_frame_init( ); // Are we at the position expected? - producer_ffmpeg_position( this, mlt_producer_frame( producer ) ); + producer_ffmpeg_position( this, mlt_producer_frame( producer ), &skip ); + + // Get properties objects + mlt_properties producer_properties = mlt_producer_properties( &this->parent ); // Get the frames properties mlt_properties properties = mlt_frame_properties( *frame ); FILE *video = this->video; + mlt_properties_set_double( properties, "target", mlt_producer_frame( producer ) ); + mlt_properties_set_int( properties, "skip", skip ); + // Read the video if ( video != NULL && read_ffmpeg_header( this, &width, &height ) == 2 ) { @@ -234,6 +468,14 @@ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int i uint8_t *image = malloc( width * height * 2 ); // Read it + while( skip -- ) + { + if ( fread( this->buffer, width * height * 3 / 2, 1, video ) == 1 ) + read_ffmpeg_header( this, &width, &height ); + else + skip = 0; + } + fread( this->buffer, width * height * 3 / 2, 1, video ); // Convert it @@ -253,22 +495,44 @@ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int i // Clean up if ( this->video != NULL ) { - pclose( this->video ); + int video_loop = mlt_properties_get_int( producer_properties, "video_loop" ); + + // Inform caller that end of clip is reached + this->end_of_video = !video_loop; + rwpipe_close( this->video_pipe ); this->video = NULL; } // Push the image callback - mlt_frame_push_get_image( *frame, producer_get_image ); + if ( !this->end_of_video ) + mlt_frame_push_get_image( *frame, producer_get_image ); } // Set the audio pipe mlt_properties_set_data( properties, "producer_ffmpeg", this, 0, NULL, NULL ); // Hmm - register audio callback - ( *frame )->get_audio = producer_get_audio; + if ( !this->end_of_audio ) + ( *frame )->get_audio = producer_get_audio; + + // Get the additional properties + double aspect_ratio = mlt_properties_get_double( producer_properties, "aspect_ratio" ); + double speed = mlt_properties_get_double( producer_properties, "speed" ); + + // Set them on the frame + mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio ); + mlt_properties_set_double( properties, "speed", speed ); + + // Set the out point on the producer + if ( this->end_of_video && this->end_of_audio ) + { + mlt_properties_set_int( properties, "end_of_clip", 1 ); + mlt_properties_set_position( producer_properties, "length", mlt_producer_position( &this->parent ) ); + mlt_producer_set_in_and_out( &this->parent, mlt_producer_get_in( &this->parent ), mlt_producer_position( &this->parent ) ); + } // Update timecode on the frame we're creating - mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) ); + mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); // Calculate the next timecode mlt_producer_prepare_next( producer ); @@ -280,12 +544,12 @@ static void producer_close( mlt_producer parent ) { producer_ffmpeg this = parent->child; if ( this->video ) - pclose( this->video ); + rwpipe_close( this->video_pipe ); if ( this->audio ) - pclose( this->audio ); - free( this->command ); + rwpipe_close( this->audio_pipe ); parent->close = NULL; mlt_producer_close( parent ); + free( this->buffer ); free( this ); }