X-Git-Url: http://research.m1stereo.tv/gitweb?a=blobdiff_plain;f=src%2Fmodules%2Fffmpeg%2Fproducer_ffmpeg.c;h=662b1e46671b970f7680a63405956b917162e6fc;hb=8bf137cd71aafb9c8f6a42c78ddb6bd0a8fe99db;hp=4506ff879ade6a0b4f6bf2408643258386a83620;hpb=5c7accb28f7fe743270523b85e434fedafd8b053;p=melted diff --git a/src/modules/ffmpeg/producer_ffmpeg.c b/src/modules/ffmpeg/producer_ffmpeg.c index 4506ff8..662b1e4 100644 --- a/src/modules/ffmpeg/producer_ffmpeg.c +++ b/src/modules/ffmpeg/producer_ffmpeg.c @@ -19,15 +19,125 @@ */ #include "producer_ffmpeg.h" + #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; + rwpipe *video_pipe; + rwpipe *audio_pipe; FILE *video; FILE *audio; uint64_t expected; @@ -48,7 +158,7 @@ static void producer_close( mlt_producer parent ); 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 ) { // Get the producer mlt_producer producer = &this->parent; @@ -62,19 +172,31 @@ mlt_producer producer_ffmpeg_init( char *file ) // Set the properties mlt_properties_set( properties, "mlt_type", "producer_ffmpeg" ); - mlt_properties_set_int( properties, "known_length", 0 ); - mlt_properties_set( properties, "video_type", file ); - if ( file != NULL && !strcmp( file, "v4l" ) ) + + 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 { + 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 ); } + 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 ); + this->buffer = malloc( 1024 * 1024 * 2 ); return producer; @@ -106,41 +228,44 @@ 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_timecode position ) { - // Get the producer - mlt_producer producer = &this->parent; + if ( this->video == NULL ) + { + // Get the producer + mlt_producer producer = &this->parent; - // Get the properties of the producer - mlt_properties properties = mlt_producer_properties( producer ); + // Get the properties of the producer + mlt_properties properties = mlt_producer_properties( producer ); - char *video_type = mlt_properties_get( properties, "video_type" ); - char *video_file = mlt_properties_get( properties, "video_file" ); - int video_loop = mlt_properties_get_int( properties, "video_loop" ); + // Get the video loop property + int video_loop = mlt_properties_get_int( properties, "video_loop" ); - if ( this->video == NULL ) - { if ( !this->open || video_loop ) { + 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 ] = ""; - float fps = mlt_producer_get_fps( &this->parent ); - float position = mlt_producer_position( &this->parent ); - if ( video_loop ) position = 0; + 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 ); - if ( video_type != NULL && !strcmp( video_type, "v4l" ) ) - sprintf( command, "ffmpeg -r %f -s 640x480 -vd \"%s\" -f imagepipe -f yuv4mpegpipe - 2>/dev/null", fps, video_file ); - else if ( video_file != NULL && strcmp( video_file, "" ) ) - sprintf( command, "ffmpeg -i \"%s\" -ss %f -f imagepipe -r %f -f yuv4mpegpipe - 2>/dev/null", video_file, position, fps ); - - if ( strcmp( command, "" ) ) - this->video = popen( command, "r" ); + 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_timecode position ) { // Get the producer mlt_producer producer = &this->parent; @@ -148,26 +273,31 @@ FILE *producer_ffmpeg_run_audio( producer_ffmpeg this ) // Get the properties of the producer mlt_properties properties = mlt_producer_properties( producer ); - char *video_type = mlt_properties_get( properties, "video_type" ); - char *audio_file = mlt_properties_get( properties, "audio_file" ); - int audio_loop = mlt_properties_get_int( properties, "audio_loop" ); - if ( this->audio == NULL ) { + int audio_loop = mlt_properties_get_int( properties, "audio_loop" ); + if ( !this->open || audio_loop ) { + 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 ] = ""; - float position = mlt_producer_position( &this->parent ); - if ( audio_loop ) position = 0; - - if ( video_type != NULL && !strcmp( video_type, "v4l" ) ) - sprintf( command, "ffmpeg -ad \"%s\" -f s16le -ar 48000 -ac 2 - 2>/dev/null", audio_file ); - else if ( audio_file != NULL ) - sprintf( command, "ffmpeg -i \"%s\" -ss %f -f s16le -ar 48000 -ac 2 - 2>/dev/null", audio_file, position ); - - if ( strcmp( command, "" ) ) - this->audio = popen( command, "r" ); + 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; @@ -186,12 +316,12 @@ static void producer_ffmpeg_position( producer_ffmpeg this, uint64_t requested, { // 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 @@ -201,13 +331,13 @@ static void producer_ffmpeg_position( producer_ffmpeg this, uint64_t requested, } // 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; @@ -219,15 +349,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 ); @@ -237,23 +369,28 @@ static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_forma { do { - if ( fread( *buffer, size, 1, producer->audio ) != 1 ) + *samples = mlt_sample_calculator( fps, *frequency, target - skip ); + if ( fread( *buffer, *samples * *channels * 2, 1, producer->audio ) != 1 ) { - pclose( producer->audio ); + rwpipe_close( producer->audio_pipe ); producer->audio = NULL; producer->end_of_audio = 1; } } - while( skip -- ); + 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; } @@ -299,11 +436,15 @@ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int i // Are we at the position expected? 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 @@ -341,9 +482,11 @@ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int i // Clean up if ( this->video != NULL ) { + int video_loop = mlt_properties_get_int( producer_properties, "video_loop" ); + // Inform caller that end of clip is reached - this->end_of_video = 1; - pclose( this->video ); + this->end_of_video = !video_loop; + rwpipe_close( this->video_pipe ); this->video = NULL; } @@ -353,27 +496,31 @@ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int i // Set the audio pipe mlt_properties_set_data( properties, "producer_ffmpeg", this, 0, NULL, NULL ); - mlt_properties_set_int( properties, "end_of_clip", this->end_of_video && this->end_of_audio ); // Hmm - register audio callback ( *frame )->get_audio = producer_get_audio; - // Get properties objects - mlt_properties producer_properties = mlt_producer_properties( &this->parent ); - // Get the additional properties double aspect_ratio = mlt_properties_get_double( producer_properties, "aspect_ratio" ); double speed = mlt_properties_get_double( producer_properties, "speed" ); + char *video_file = mlt_properties_get( producer_properties, "video_file" ); // Set them on the frame mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio ); mlt_properties_set_double( properties, "speed", speed ); + if ( strchr( video_file, '/' ) != NULL ) + mlt_properties_set( properties, "file", strrchr( video_file, '/' ) + 1 ); + else + mlt_properties_set( properties, "file", video_file ); + // Set the out point on the producer - if ( !this->end_of_video || !this->end_of_audio ) - mlt_producer_set_in_and_out( &this->parent, mlt_producer_get_in( &this->parent ), mlt_producer_position( &this->parent ) + 1 ); - else + if ( this->end_of_video && this->end_of_audio ) + { + mlt_properties_set_int( properties, "end_of_clip", 1 ); + mlt_properties_set_timecode( 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 ) ); @@ -388,9 +535,9 @@ 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 ); + rwpipe_close( this->audio_pipe ); parent->close = NULL; mlt_producer_close( parent ); free( this->buffer );