From 16c5c516008b9debec7765e34f4756f8063dee7e Mon Sep 17 00:00:00 2001 From: lilo_booter Date: Mon, 26 Jan 2004 12:15:28 +0000 Subject: [PATCH] Added avformat git-svn-id: https://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt@93 d19143bc-622f-0410-bfdd-b5b2a6649095 --- README | 21 +- docs/inigo.txt | 8 +- src/framework/mlt_producer.c | 65 +- src/inigo/inigo.c | 33 +- src/modules/Makefile | 2 +- src/modules/avformat/Makefile | 29 + src/modules/avformat/configure | 11 + src/modules/avformat/factory.c | 46 ++ src/modules/avformat/producer_avformat.c | 759 +++++++++++++++++++++++ src/modules/avformat/producer_avformat.h | 28 + src/modules/inigo/producer_inigo.c | 2 +- src/modules/sdl/consumer_sdl.c | 3 + 12 files changed, 949 insertions(+), 58 deletions(-) create mode 100644 src/modules/avformat/Makefile create mode 100755 src/modules/avformat/configure create mode 100644 src/modules/avformat/factory.c create mode 100644 src/modules/avformat/producer_avformat.c create mode 100644 src/modules/avformat/producer_avformat.h diff --git a/README b/README index 902bb32a..ff7887cd 100644 --- a/README +++ b/README @@ -11,17 +11,18 @@ Directories The directory heirarchy is defined as follows: - + docs - Location of all text and source format - documentation + + docs - Location of all documentation + src - All project source is provided here - + framework - The media framework - + modules - All components are defined here - + core - Non-dependent MLT service - + dv - libdv services - + ffmpeg - ffmpeg dependent modules and test code - + sdl - SDL dependent modules and test code - + bluefish - Bluefish dependent modules and test code (*) - + mainconcept - mainconcept dependent modules and test code (*) + + framework - The mlt media framework + + modules - All services are defined here + + core - Independent MLT services + + dv - libdv dependent services + + ffmpeg - ffmpeg dependent services + + sdl - SDL dependent services + + resample - libresample dependent services + + gtk2 - pango and pixbuf dependent services + + bluefish - Bluefish dependent services (*) + + mainconcept - mainconcept dependent services (*) + inigo - A media playing test application + valerie - Client API to access the server + miracle - The server implementation diff --git a/docs/inigo.txt b/docs/inigo.txt index 9fa53e10..0ea0b93e 100644 --- a/docs/inigo.txt +++ b/docs/inigo.txt @@ -33,10 +33,12 @@ General rules: available; 4. The MLT framework, from which inigo has inherited its - naming convention - is very ego-centric. Producers produce + naming convention, is very mlt-centric. Producers produce MLT frame objects and consumers consume MLT frame objects. The distinction is important - a DV producer does not produce - DV and a DV consumer does not consume DV. + DV, it produces MLT frames from a DV source, and similarly a + DV consumer does not consume DV, it consumes MLT frames and + produces DV frames. Terminoligy: @@ -173,7 +175,7 @@ Introducing Tracks and Blanks: It is best to visualise a track arrangement, so we'll start with an example: - $ inigo a.dv out=49 -track b.dv + $ inigo a.dv in=0 out=49 -track b.dv This can be visualised as follows: diff --git a/src/framework/mlt_producer.c b/src/framework/mlt_producer.c index 21f52774..2a5b60e7 100644 --- a/src/framework/mlt_producer.c +++ b/src/framework/mlt_producer.c @@ -36,39 +36,46 @@ static int producer_get_frame( mlt_service this, mlt_frame_ptr frame, int index int mlt_producer_init( mlt_producer this, void *child ) { - // Initialise the producer - memset( this, 0, sizeof( struct mlt_producer_s ) ); - - // Associate with the child - this->child = child; + // Check that we haven't received NULL + int error = this == NULL; - // Initialise the service - if ( mlt_service_init( &this->parent, this ) == 0 ) + // Continue if no error + if ( error == 0 ) { - // The parent is the service - mlt_service parent = &this->parent; - - // Get the properties of the parent - mlt_properties properties = mlt_service_properties( parent ); - - // Set the default properties - mlt_properties_set( properties, "mlt_type", "mlt_producer" ); - mlt_properties_set_position( properties, "position", 0.0 ); - mlt_properties_set_double( properties, "frame", 0 ); - mlt_properties_set_double( properties, "fps", 25.0 ); - mlt_properties_set_double( properties, "speed", 1.0 ); - mlt_properties_set_position( properties, "in", 0 ); - mlt_properties_set_position( properties, "out", 1799999 ); - mlt_properties_set_position( properties, "length", 1800000 ); - mlt_properties_set_double( properties, "aspect_ratio", 4.0 / 3.0 ); - mlt_properties_set( properties, "eof", "pause" ); - mlt_properties_set( properties, "resource", "" ); - - // Override service get_frame - parent->get_frame = producer_get_frame; + // Initialise the producer + memset( this, 0, sizeof( struct mlt_producer_s ) ); + + // Associate with the child + this->child = child; + + // Initialise the service + if ( mlt_service_init( &this->parent, this ) == 0 ) + { + // The parent is the service + mlt_service parent = &this->parent; + + // Get the properties of the parent + mlt_properties properties = mlt_service_properties( parent ); + + // Set the default properties + mlt_properties_set( properties, "mlt_type", "mlt_producer" ); + mlt_properties_set_position( properties, "position", 0.0 ); + mlt_properties_set_double( properties, "frame", 0 ); + mlt_properties_set_double( properties, "fps", 25.0 ); + mlt_properties_set_double( properties, "speed", 1.0 ); + mlt_properties_set_position( properties, "in", 0 ); + mlt_properties_set_position( properties, "out", 1799999 ); + mlt_properties_set_position( properties, "length", 1800000 ); + mlt_properties_set_double( properties, "aspect_ratio", 4.0 / 3.0 ); + mlt_properties_set( properties, "eof", "pause" ); + mlt_properties_set( properties, "resource", "" ); + + // Override service get_frame + parent->get_frame = producer_get_frame; + } } - return 0; + return error; } /** Get the parent service object. diff --git a/src/inigo/inigo.c b/src/inigo/inigo.c index cfef4fdd..40c99b40 100644 --- a/src/inigo/inigo.c +++ b/src/inigo/inigo.c @@ -137,24 +137,27 @@ static mlt_consumer create_consumer( char *id, mlt_producer producer ) return consumer; } -static void transport( mlt_producer producer ) +static void transport( mlt_producer producer, mlt_consumer consumer ) { mlt_properties properties = mlt_producer_properties( producer ); term_init( ); - fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" ); - fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5= 0| |6= 1| |7= 2| |8= 5| |9= 10|\n" ); - fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" ); - - fprintf( stderr, "+---------------------------------------------------------------------+\n" ); - fprintf( stderr, "| H = back 1 minute, L = forward 1 minute |\n" ); - fprintf( stderr, "| h = previous frame, l = next frame |\n" ); - fprintf( stderr, "| g = start of clip, j = next clip, k = previous clip |\n" ); - fprintf( stderr, "| 0 = restart, q = quit, space = play |\n" ); - fprintf( stderr, "+---------------------------------------------------------------------+\n" ); + if ( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) ) + { + fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" ); + fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5= 0| |6= 1| |7= 2| |8= 5| |9= 10|\n" ); + fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" ); + + fprintf( stderr, "+---------------------------------------------------------------------+\n" ); + fprintf( stderr, "| H = back 1 minute, L = forward 1 minute |\n" ); + fprintf( stderr, "| h = previous frame, l = next frame |\n" ); + fprintf( stderr, "| g = start of clip, j = next clip, k = previous clip |\n" ); + fprintf( stderr, "| 0 = restart, q = quit, space = play |\n" ); + fprintf( stderr, "+---------------------------------------------------------------------+\n" ); + } - while( mlt_properties_get_int( properties, "done" ) == 0 ) + while( mlt_properties_get_int( properties, "done" ) == 0 && !mlt_consumer_is_stopped( consumer ) ) { int value = term_read( ); if ( value != -1 ) @@ -173,6 +176,7 @@ int main( int argc, char **argv ) // Construct the factory mlt_factory_init( getenv( "MLT_REPOSITORY" ) ); + // Check for serialisation switch first for ( i = 1; i < argc; i ++ ) { if ( !strcmp( argv[ i ], "-serialise" ) ) @@ -184,7 +188,8 @@ int main( int argc, char **argv ) } // Get inigo producer - inigo = mlt_factory_producer( "inigo", &argv[ 1 ] ); + if ( argc > 1 ) + inigo = mlt_factory_producer( "inigo", &argv[ 1 ] ); if ( argc > 1 && inigo != NULL && mlt_producer_get_length( inigo ) > 0 ) { @@ -245,7 +250,7 @@ int main( int argc, char **argv ) mlt_consumer_start( consumer ); // Transport functionality - transport( inigo ); + transport( inigo, consumer ); // Stop the consumer mlt_consumer_stop( consumer ); diff --git a/src/modules/Makefile b/src/modules/Makefile index 45f7a31c..8a14a7fb 100644 --- a/src/modules/Makefile +++ b/src/modules/Makefile @@ -1,4 +1,4 @@ -SUBDIRS = core gtk2 dv sdl mainconcept bluefish ffmpeg resample inigo westley +SUBDIRS = core gtk2 dv sdl mainconcept bluefish ffmpeg resample inigo avformat westley all clean depend install: list='$(SUBDIRS)'; \ diff --git a/src/modules/avformat/Makefile b/src/modules/avformat/Makefile new file mode 100644 index 00000000..8a670467 --- /dev/null +++ b/src/modules/avformat/Makefile @@ -0,0 +1,29 @@ + +TARGET = ../libmltavformat.so + +OBJS = factory.o \ + producer_avformat.o + +CFLAGS = -I../../ -Wall -g -D_FILE_OFFSET_BITS=64 + +LDFLAGS = -lavformat -lavcodec + +SRCS := $(OBJS:.o=.c) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -shared -o $@ $(OBJS) $(LDFLAGS) + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $^ 1>.depend + +dist-clean: clean + rm -f .depend + +clean: + rm -f $(OBJS) $(TARGET) + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/src/modules/avformat/configure b/src/modules/avformat/configure new file mode 100755 index 00000000..a3be0c65 --- /dev/null +++ b/src/modules/avformat/configure @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ "$help" != "1" ] +then + +cat << EOF >> ../producers.dat +avformat libmltavformat.so +EOF + +fi + diff --git a/src/modules/avformat/factory.c b/src/modules/avformat/factory.c new file mode 100644 index 00000000..4d984ba1 --- /dev/null +++ b/src/modules/avformat/factory.c @@ -0,0 +1,46 @@ +/* + * factory.c -- the factory method interfaces + * 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 + +#include "producer_avformat.h" + +void *mlt_create_producer( char *id, void *arg ) +{ + if ( !strcmp( id, "avformat" ) ) + return producer_avformat_init( arg ); + return NULL; +} + +void *mlt_create_filter( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_transition( char *id, void *arg ) +{ + return NULL; +} + +void *mlt_create_consumer( char *id, void *arg ) +{ + return NULL; +} + diff --git a/src/modules/avformat/producer_avformat.c b/src/modules/avformat/producer_avformat.c new file mode 100644 index 00000000..1b609f8b --- /dev/null +++ b/src/modules/avformat/producer_avformat.c @@ -0,0 +1,759 @@ +/* + * producer_avformat.c -- avformat producer + * 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. + */ + +// Local header files +#include "producer_avformat.h" + +// MLT Header files +#include + +// ffmpeg Header files +#include + +// System header files +#include +#include + +// Forward references. +static int producer_open( mlt_producer this, char *file ); +static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index ); + +// A static flag used to determine if avformat has been initialised +static int avformat_initialised = 0; + +/** Constructor for libavformat. +*/ + +mlt_producer producer_avformat_init( char *file ) +{ + mlt_producer this = NULL; + + // Check that we have a non-NULL argument + if ( file != NULL ) + { + // Construct the producer + this = calloc( 1, sizeof( struct mlt_producer_s ) ); + + // Initialise it + if ( mlt_producer_init( this, NULL ) == 0 ) + { + // Get the properties + mlt_properties properties = mlt_producer_properties( this ); + + // Set the resource property (required for all producers) + mlt_properties_set( properties, "resource", file ); + + // TEST: audio sync tweaking + mlt_properties_set_double( properties, "discrepancy", 1 ); + + // Register our get_frame implementation + this->get_frame = producer_get_frame; + + // Initialise avformat if necessary + if ( avformat_initialised == 0 ) + { + avformat_initialised = 1; + av_register_all( ); + } + + // Open the file + if ( producer_open( this, file ) != 0 ) + { + // Clean up + mlt_producer_close( this ); + this = NULL; + } + } + } + + return this; +} + +/** Find the default streams. +*/ + +void find_default_streams( AVFormatContext *context, int *audio_index, int *video_index ) +{ + int i; + + // Allow for multiple audio and video streams in the file and select first of each (if available) + for( i = 0; i < context->nb_streams; i++ ) + { + // Get the codec context + AVCodecContext *codec_context = &context->streams[ i ]->codec; + + // Determine the type and obtain the first index of each type + switch( codec_context->codec_type ) + { + case CODEC_TYPE_VIDEO: + if ( *video_index < 0 ) + *video_index = i; + break; + case CODEC_TYPE_AUDIO: + if ( *audio_index < 0 ) + *audio_index = i; + break; + default: + break; + } + } +} + +/** Open the file. + + NOTE: We need to have a valid [PAL or NTSC] frame rate before we can determine the + number of frames in the file. However, this is at odds with the way things work - the + constructor needs to provide in/out points before the user of the producer is able + to specify properties :-/. However, the PAL/NTSC distinction applies to all producers + and while we currently accept whatever the producer provides, this will not work in + the more general case. Plans are afoot... and this one will work without modification + (in theory anyway ;-)). +*/ + +static int producer_open( mlt_producer this, char *file ) +{ + // Return an error code (0 == no error) + int error = 0; + + // Context for avformat + AVFormatContext *context = NULL; + + // Get the properties + mlt_properties properties = mlt_producer_properties( this ); + + // We will treat everything with the producer fps + double fps = mlt_properties_get_double( properties, "fps" ); + + // Now attempt to open the file + error = av_open_input_file( &context, file, NULL, 0, NULL ) < 0; + + // If successful, then try to get additional info + if ( error == 0 ) + { + // Get the stream info + error = av_find_stream_info( context ) < 0; + + // Continue if no error + if ( error == 0 ) + { + // We will default to the first audio and video streams found + int audio_index = -1; + int video_index = -1; + + // Now set properties where we can (use default unknowns if required) + if ( context->duration != AV_NOPTS_VALUE ) + { + // This isn't going to be accurate for all formats + mlt_position frames = ( mlt_position )( ( ( double )context->duration / ( double )AV_TIME_BASE ) * fps ); + mlt_properties_set_position( properties, "out", frames - 1 ); + mlt_properties_set_position( properties, "length", frames ); + } + + // Find default audio and video streams + find_default_streams( context, &audio_index, &video_index ); + + // Store selected audio and video indexes on properties + mlt_properties_set_int( properties, "audio_index", audio_index ); + mlt_properties_set_int( properties, "video_index", video_index ); + + // We're going to cheat here - for a/v files, we will have two contexts (reasoning will be clear later) + if ( audio_index != -1 && video_index != -1 ) + { + // We'll use the open one as our video_context + mlt_properties_set_data( properties, "video_context", context, 0, ( mlt_destructor )av_close_input_file, NULL ); + + // And open again for our audio context + av_open_input_file( &context, file, NULL, 0, NULL ); + av_find_stream_info( context ); + + // Audio context + mlt_properties_set_data( properties, "audio_context", context, 0, ( mlt_destructor )av_close_input_file, NULL ); + } + else if ( video_index != -1 ) + { + // We only have a video context + mlt_properties_set_data( properties, "video_context", context, 0, ( mlt_destructor )av_close_input_file, NULL ); + } + else if ( audio_index != -1 ) + { + // We only have an audio context + mlt_properties_set_data( properties, "audio_context", context, 0, ( mlt_destructor )av_close_input_file, NULL ); + } + else + { + // Something has gone wrong + error = -1; + } + } + } + + return error; +} + +/** Convert a frame position to a time code. +*/ + +static double producer_time_of_frame( mlt_producer this, mlt_position position ) +{ + // Get the properties + mlt_properties properties = mlt_producer_properties( this ); + + // Obtain the fps + double fps = mlt_properties_get_double( properties, "fps" ); + + // Do the calc + return ( double )position / fps; +} + +/** Get an image from a frame. +*/ + +static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) +{ + // Get the properties from the frame + mlt_properties frame_properties = mlt_frame_properties( frame ); + + // Obtain the frame number of this frame + mlt_position position = mlt_properties_get_position( frame_properties, "avformat_position" ); + + // Get the producer + mlt_producer this = mlt_properties_get_data( frame_properties, "avformat_producer", NULL ); + + // Get the producer properties + mlt_properties properties = mlt_producer_properties( this ); + + // Fetch the video_context + AVFormatContext *context = mlt_properties_get_data( properties, "video_context", NULL ); + + // Get the video_index + int index = mlt_properties_get_int( properties, "video_index" ); + + // Obtain the expected frame numer + mlt_position expected = mlt_properties_get_position( properties, "video_expected" ); + + // Calculate the real time code + double real_timecode = producer_time_of_frame( this, position ); + + // Get the video stream + AVStream *stream = context->streams[ index ]; + + // Get codec context + AVCodecContext *codec_context = &stream->codec; + + // Packet + AVPacket pkt; + + // Get the conversion frame + AVPicture *output = mlt_properties_get_data( properties, "video_output_frame", NULL ); + + // Special case pause handling flag + int paused = 0; + + // Special case ffwd handling + int ignore = 0; + + // Current time calcs + double current_time = 0; + + // Set the result arguments that we know here (only *buffer is now required) + *format = mlt_image_yuv422; + *width = codec_context->width; + *height = codec_context->height; + + // Set this on the frame properties + mlt_properties_set_int( frame_properties, "width", *width ); + mlt_properties_set_int( frame_properties, "height", *height ); + + // Construct an AVFrame for YUV422 conversion + if ( output == NULL ) + { + int size = avpicture_get_size( PIX_FMT_YUV422, *width, *height ); + uint8_t *buf = malloc( size ); + output = malloc( sizeof( AVPicture ) ); + avpicture_fill( output, buf, PIX_FMT_YUV422, *width, *height ); + mlt_properties_set_data( properties, "video_output_frame", output, 0, av_free, NULL ); + mlt_properties_set_data( properties, "video_output_buffer", buf, 0, free, NULL ); + } + + // Seek if necessary + if ( position != expected ) + { + if ( position + 1 == expected ) + { + // We're paused - use last image + paused = 1; + } + else if ( position > expected && ( position - expected ) < 250 ) + { + // Fast forward - seeking is inefficient for small distances - just ignore following frames + ignore = position - expected; + } + else + { + // Set to the real timecode + av_seek_frame( context, -1, real_timecode * 1000000.0 ); + + // Remove the cached info relating to the previous position + mlt_properties_set_double( properties, "current_time", 0 ); + mlt_properties_set_data( properties, "current_image", NULL, 0, NULL, NULL ); + } + } + + // Duplicate the last image if necessary + if ( mlt_properties_get_data( properties, "current_image", NULL ) != NULL && + ( paused || mlt_properties_get_double( properties, "current_time" ) > real_timecode ) ) + { + // Get current image and size + int size = 0; + uint8_t *image = mlt_properties_get_data( properties, "current_image", &size ); + + // Duplicate it + *buffer = malloc( size ); + memcpy( *buffer, image, size ); + + // Set this on the frame properties + mlt_properties_set_data( frame_properties, "image", *buffer, size, free, NULL ); + } + else + { + int ret = 0; + int got_picture = 0; + AVFrame frame; + + memset( &pkt, 0, sizeof( pkt ) ); + memset( &frame, 0, sizeof( frame ) ); + + while( ret >= 0 && !got_picture ) + { + // Read a packet + ret = av_read_frame( context, &pkt ); + + // We only deal with video from the selected video_index + if ( ret >= 0 && pkt.stream_index == index && pkt.size > 0 ) + { + // Decode the image + // Wouldn't it be great if I could use this... + //if ( (float)pkt.pts / 1000000.0 >= real_timecode ) + ret = avcodec_decode_video( codec_context, &frame, &got_picture, pkt.data, pkt.size ); + + // Handle ignore + if ( (float)pkt.pts / 1000000.0 < real_timecode ) + { + ignore = 0; + got_picture = 0; + } + else if ( (float)pkt.pts / 1000000.0 >= real_timecode ) + { + ignore = 0; + } + else if ( got_picture && ignore -- ) + { + got_picture = 0; + } + + current_time = ( double )pkt.pts / 1000000.0; + } + + // We're finished with this packet regardless + av_free_packet( &pkt ); + } + + // Now handle the picture if we have one + if ( got_picture ) + { + // Get current image and size + int size = 0; + uint8_t *image = mlt_properties_get_data( properties, "current_image", &size ); + + if ( image == NULL || size != *width * *height * 2 ) + { + size = *width * *height * 2; + image = malloc( size ); + mlt_properties_set_data( properties, "current_image", image, size, free, NULL ); + } + + *buffer = malloc( size ); + img_convert( output, PIX_FMT_YUV422, (AVPicture *)&frame, codec_context->pix_fmt, *width, *height ); + memcpy( image, output->data[ 0 ], size ); + memcpy( *buffer, output->data[ 0 ], size ); + mlt_properties_set_data( frame_properties, "image", *buffer, size, free, NULL ); + mlt_properties_set_double( properties, "current_time", current_time ); + } + } + + // Regardless of speed, we expect to get the next frame (cos we ain't too bright) + mlt_properties_set_position( properties, "video_expected", position + 1 ); + + return 0; +} + +/** Set up video handling. +*/ + +static void producer_set_up_video( mlt_producer this, mlt_frame frame ) +{ + // Get the properties + mlt_properties properties = mlt_producer_properties( this ); + + // Fetch the video_context + AVFormatContext *context = mlt_properties_get_data( properties, "video_context", NULL ); + + // Get the video_index + int index = mlt_properties_get_int( properties, "video_index" ); + + if ( context != NULL && index != -1 ) + { + // Get the frame properties + mlt_properties frame_properties = mlt_frame_properties( frame ); + + // Get the video stream + AVStream *stream = context->streams[ index ]; + + // Get codec context + AVCodecContext *codec_context = &stream->codec; + + // Get the codec + AVCodec *codec = mlt_properties_get_data( properties, "video_codec", NULL ); + + // Initialise the codec if necessary + if ( codec == NULL ) + { + // Find the codec + codec = avcodec_find_decoder( codec_context->codec_id ); + + // If we don't have a codec and we can't initialise it, we can't do much more... + if ( codec != NULL && avcodec_open( codec_context, codec ) >= 0 ) + { + double aspect_ratio = 0; + + // Set aspect ratio + if ( codec_context->sample_aspect_ratio.num == 0) + aspect_ratio = 0; + else + aspect_ratio = av_q2d( codec_context->sample_aspect_ratio ) * codec_context->width / codec_context->height; + + if (aspect_ratio <= 0.0) + aspect_ratio = ( double )codec_context->width / ( double )codec_context->height; + + mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio ); + + // Now store the codec with its destructor + mlt_properties_set_data( properties, "video_codec", codec, 0, ( mlt_destructor )avcodec_close, NULL ); + + // Set to the real timecode + av_seek_frame( context, -1, 0 ); + } + else + { + // Remember that we can't use this later + mlt_properties_set_int( properties, "video_index", -1 ); + } + } + + // No codec, no show... + if ( codec != NULL ) + { + mlt_frame_push_get_image( frame, producer_get_image ); + mlt_properties_set_data( frame_properties, "avformat_producer", this, 0, NULL, NULL ); + } + } +} + +/** Get the audio from a frame. +*/ + +static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +{ + // Get the properties from the frame + mlt_properties frame_properties = mlt_frame_properties( frame ); + + // Obtain the frame number of this frame + mlt_position position = mlt_properties_get_position( frame_properties, "avformat_position" ); + + // Get the producer + mlt_producer this = mlt_properties_get_data( frame_properties, "avformat_producer", NULL ); + + // Get the producer properties + mlt_properties properties = mlt_producer_properties( this ); + + // Fetch the audio_context + AVFormatContext *context = mlt_properties_get_data( properties, "audio_context", NULL ); + + // Get the audio_index + int index = mlt_properties_get_int( properties, "audio_index" ); + + // Obtain the expected frame numer + mlt_position expected = mlt_properties_get_position( properties, "audio_expected" ); + + // Obtain the resample context if it exists (not always needed) + ReSampleContext *resample = mlt_properties_get_data( properties, "audio_resample", NULL ); + + // Obtain the audio buffer + int16_t *audio_buffer = mlt_properties_get_data( properties, "audio_buffer", NULL ); + + // Get amount of audio used + int audio_used = mlt_properties_get_int( properties, "audio_used" ); + + // Calculate the real time code + double real_timecode = producer_time_of_frame( this, position ); + + // Get the audio stream + AVStream *stream = context->streams[ index ]; + + // Get codec context + AVCodecContext *codec_context = &stream->codec; + + // Packet + AVPacket pkt; + + // Number of frames to ignore (for ffwd) + int ignore = 0; + + // Flag for paused (silence) + int paused = 0; + + // Check for resample and create if necessary + if ( resample == NULL ) + { + // Create the resampler + resample = audio_resample_init( *channels, codec_context->channels, *frequency, codec_context->sample_rate ); + + // And store it on properties + mlt_properties_set_data( properties, "audio_resample", resample, 0, ( mlt_destructor ) audio_resample_close, NULL ); + } + + // Check for audio buffer and create if necessary + if ( audio_buffer == NULL ) + { + // Allocate the audio buffer + audio_buffer = malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) ); + + // And store it on properties for reuse + mlt_properties_set_data( properties, "audio_buffer", audio_buffer, 0, free, NULL ); + } + + // Seek if necessary + if ( position != expected ) + { + if ( position + 1 == expected ) + { + // We're paused - silence required + paused = 1; + } + else if ( position > expected && ( position - expected ) < 250 ) + { + // Fast forward - seeking is inefficient for small distances - just ignore following frames + ignore = position - expected; + } + else + { + // Set to the real timecode + av_seek_frame( context, -1, real_timecode * 1000000.0 ); + + // Clear the usage in the audio buffer + audio_used = 0; + } + } + + // Get the audio if required + if ( !paused ) + { + int ret = 0; + int got_audio = 0; + int16_t temp[ AVCODEC_MAX_AUDIO_FRAME_SIZE / 2 ]; + + memset( &pkt, 0, sizeof( pkt ) ); + + while( ret >= 0 && !got_audio ) + { + // Check if the buffer already contains the samples required + if ( audio_used >= *samples && ignore == 0 ) + { + got_audio = 1; + break; + } + + // Read a packet + ret = av_read_frame( context, &pkt ); + + int len = pkt.size; + uint8_t *ptr = pkt.data; + int data_size; + + if ( ptr == NULL || len == 0 ) + break; + + // We only deal with video from the selected video_index + while ( ret >= 0 && pkt.stream_index == index && len > 0 ) + { + // Decode the audio + ret = avcodec_decode_audio( codec_context, temp, &data_size, ptr, len ); + + if ( ret < 0 ) + break; + + len -= ret; + ptr += ret; + + if ( data_size > 0 ) + { + int size_out = audio_resample( resample, &audio_buffer[ audio_used * *channels ], temp, data_size / ( codec_context->channels * sizeof( int16_t ) ) ); + + audio_used += size_out; + + // Handle ignore + while ( ignore && audio_used > *samples ) + { + ignore --; + audio_used -= *samples; + memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * sizeof( int16_t ) ); + } + } + + // If we're behind, ignore this packet + float current_pts = (float)pkt.pts / 1000000.0; + double discrepancy = mlt_properties_get_double( properties, "discrepancy" ); + + fprintf( stderr, "%f < %f\n", discrepancy * current_pts, (float) real_timecode ); + if ( discrepancy * current_pts < real_timecode ) + ignore = 1; + } + + // We're finished with this packet regardless + av_free_packet( &pkt ); + } + + // Now handle the audio if we have enough + if ( audio_used >= *samples ) + { + *buffer = malloc( *samples * *channels * sizeof( int16_t ) ); + memcpy( *buffer, audio_buffer, *samples * *channels * sizeof( int16_t ) ); + audio_used -= *samples; + memmove( audio_buffer, &audio_buffer[ *samples * *channels ], audio_used * *channels * sizeof( int16_t ) ); + mlt_properties_set_data( frame_properties, "audio", *buffer, 0, free, NULL ); + } + else + { + frame->get_audio = NULL; + mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); + audio_used = 0; + } + + // Store the number of audio samples still available + mlt_properties_set_int( properties, "audio_used", audio_used ); + } + else + { + // Get silence and don't touch the context + frame->get_audio = NULL; + mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples ); + } + + // Regardless of speed, we expect to get the next frame (cos we ain't too bright) + mlt_properties_set_position( properties, "audio_expected", position + 1 ); + + return 0; +} + +/** Set up audio handling. +*/ + +static void producer_set_up_audio( mlt_producer this, mlt_frame frame ) +{ + // Get the properties + mlt_properties properties = mlt_producer_properties( this ); + + // Fetch the audio_context + AVFormatContext *context = mlt_properties_get_data( properties, "audio_context", NULL ); + + // Get the audio_index + int index = mlt_properties_get_int( properties, "audio_index" ); + + // Deal with audio context + if ( context != NULL && index != -1 ) + { + // Get the frame properties + mlt_properties frame_properties = mlt_frame_properties( frame ); + + // Get the audio stream + AVStream *stream = context->streams[ index ]; + + // Get codec context + AVCodecContext *codec_context = &stream->codec; + + // Get the codec + AVCodec *codec = mlt_properties_get_data( properties, "audio_codec", NULL ); + + // Initialise the codec if necessary + if ( codec == NULL ) + { + // Find the codec + codec = avcodec_find_decoder( codec_context->codec_id ); + + // If we don't have a codec and we can't initialise it, we can't do much more... + if ( codec != NULL && avcodec_open( codec_context, codec ) >= 0 ) + { + // Now store the codec with its destructor + mlt_properties_set_data( properties, "audio_codec", codec, 0, ( mlt_destructor )avcodec_close, NULL ); + } + else + { + // Remember that we can't use this later + mlt_properties_set_int( properties, "audio_index", -1 ); + } + } + + // No codec, no show... + if ( codec != NULL ) + { + frame->get_audio = producer_get_audio; + mlt_properties_set_data( frame_properties, "avformat_producer", this, 0, NULL, NULL ); + } + } +} + +/** Our get frame implementation. +*/ + +static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index ) +{ + // Create an empty frame + *frame = mlt_frame_init( ); + + // Update timecode on the frame we're creating + mlt_frame_set_position( *frame, mlt_producer_position( this ) ); + + // Set the position of this producer + mlt_properties_set_position( mlt_frame_properties( *frame ), "avformat_position", mlt_producer_get_in( this ) + mlt_producer_position( this ) ); + + // Set up the video + producer_set_up_video( this, *frame ); + + // Set up the audio + producer_set_up_audio( this, *frame ); + + // Set the aspect_ratio + mlt_properties_set_double( mlt_frame_properties( *frame ), "aspect_ratio", mlt_properties_get_double( mlt_producer_properties( this ), "aspect_ratio" ) ); + + // Calculate the next timecode + mlt_producer_prepare_next( this ); + + return 0; +} diff --git a/src/modules/avformat/producer_avformat.h b/src/modules/avformat/producer_avformat.h new file mode 100644 index 00000000..28af65d2 --- /dev/null +++ b/src/modules/avformat/producer_avformat.h @@ -0,0 +1,28 @@ +/* + * producer_avformat.h -- avformat producer + * 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. + */ + +#ifndef _PRODUCER_AVFORMAT_H_ +#define _PRODUCER_AVFORMAT_H_ + +#include + +extern mlt_producer producer_avformat_init( char *file ); + +#endif diff --git a/src/modules/inigo/producer_inigo.c b/src/modules/inigo/producer_inigo.c index a266af7e..ed20ea3b 100644 --- a/src/modules/inigo/producer_inigo.c +++ b/src/modules/inigo/producer_inigo.c @@ -94,7 +94,7 @@ static mlt_producer create_producer( char *file ) // 3rd line fallbacks if ( result == NULL ) - result = mlt_factory_producer( "ffmpeg", file ); + result = mlt_factory_producer( "avformat", file ); return result; } diff --git a/src/modules/sdl/consumer_sdl.c b/src/modules/sdl/consumer_sdl.c index dbd4ff36..4fd2068d 100644 --- a/src/modules/sdl/consumer_sdl.c +++ b/src/modules/sdl/consumer_sdl.c @@ -251,6 +251,9 @@ static int consumer_play_audio( consumer_sdl this, mlt_frame frame, int init_aud int channels = 2; int frequency = 48000; static int counter = 0; + if ( mlt_properties_get_int( properties, "frequency" ) != 0 ) + frequency = mlt_properties_get_int( properties, "frequency" ); + int samples = mlt_sample_calculator( mlt_properties_get_double( this->properties, "fps" ), frequency, counter++ ); int16_t *pcm; -- 2.39.2