X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fframework%2Fmlt_frame.c;h=612592932a7df6d7834780178e59b114a3c9759f;hb=3d7654dbf63ed73cde8c58989fecedce1d155531;hp=56efe98b932970258c392ae02751c6b155a4c891;hpb=8e8264ac20e3fed14e480a589ef65cb410d0a64c;p=mlt diff --git a/src/framework/mlt_frame.c b/src/framework/mlt_frame.c index 56efe98b..61259293 100644 --- a/src/framework/mlt_frame.c +++ b/src/framework/mlt_frame.c @@ -41,15 +41,15 @@ mlt_frame mlt_frame_init( mlt_service service ) { // Allocate a frame - mlt_frame this = calloc( sizeof( struct mlt_frame_s ), 1 ); + mlt_frame self = calloc( sizeof( struct mlt_frame_s ), 1 ); - if ( this != NULL ) + if ( self != NULL ) { mlt_profile profile = mlt_service_profile( service ); // Initialise the properties - mlt_properties properties = &this->parent; - mlt_properties_init( properties, this ); + mlt_properties properties = &self->parent; + mlt_properties_init( properties, self ); // Set default properties on the frame mlt_properties_set_position( properties, "_position", 0.0 ); @@ -63,236 +63,266 @@ mlt_frame mlt_frame_init( mlt_service service ) mlt_properties_set_data( properties, "alpha", NULL, 0, NULL, NULL ); // Construct stacks for frames and methods - this->stack_image = mlt_deque_init( ); - this->stack_audio = mlt_deque_init( ); - this->stack_service = mlt_deque_init( ); + self->stack_image = mlt_deque_init( ); + self->stack_audio = mlt_deque_init( ); + self->stack_service = mlt_deque_init( ); } - return this; + return self; } /** Get a frame's properties. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return the frame's properties or NULL if an invalid frame is supplied */ -mlt_properties mlt_frame_properties( mlt_frame this ) +mlt_properties mlt_frame_properties( mlt_frame self ) { - return this != NULL ? &this->parent : NULL; + return self != NULL ? &self->parent : NULL; } /** Determine if the frame will produce a test card image. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return true (non-zero) if this will produce from a test card */ -int mlt_frame_is_test_card( mlt_frame this ) +int mlt_frame_is_test_card( mlt_frame self ) { - return mlt_deque_count( this->stack_image ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_image" ); + return mlt_deque_count( self->stack_image ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( self ), "test_image" ); } /** Determine if the frame will produce audio from a test card. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return true (non-zero) if this will produce from a test card */ -int mlt_frame_is_test_audio( mlt_frame this ) +int mlt_frame_is_test_audio( mlt_frame self ) { - return mlt_deque_count( this->stack_audio ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "test_audio" ); + return mlt_deque_count( self->stack_audio ) == 0 || mlt_properties_get_int( MLT_FRAME_PROPERTIES( self ), "test_audio" ); } /** Get the sample aspect ratio of the frame. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return the aspect ratio */ -double mlt_frame_get_aspect_ratio( mlt_frame this ) +double mlt_frame_get_aspect_ratio( mlt_frame self ) { - return mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "aspect_ratio" ); + return mlt_properties_get_double( MLT_FRAME_PROPERTIES( self ), "aspect_ratio" ); } /** Set the sample aspect ratio of the frame. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param value the new image sample aspect ratio * \return true if error */ -int mlt_frame_set_aspect_ratio( mlt_frame this, double value ) +int mlt_frame_set_aspect_ratio( mlt_frame self, double value ) { - return mlt_properties_set_double( MLT_FRAME_PROPERTIES( this ), "aspect_ratio", value ); + return mlt_properties_set_double( MLT_FRAME_PROPERTIES( self ), "aspect_ratio", value ); } /** Get the time position of this frame. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return the position */ -mlt_position mlt_frame_get_position( mlt_frame this ) +mlt_position mlt_frame_get_position( mlt_frame self ) { - int pos = mlt_properties_get_position( MLT_FRAME_PROPERTIES( this ), "_position" ); + int pos = mlt_properties_get_position( MLT_FRAME_PROPERTIES( self ), "_position" ); return pos < 0 ? 0 : pos; } /** Set the time position of this frame. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param value the position * \return true if error */ -int mlt_frame_set_position( mlt_frame this, mlt_position value ) +int mlt_frame_set_position( mlt_frame self, mlt_position value ) { - return mlt_properties_set_position( MLT_FRAME_PROPERTIES( this ), "_position", value ); + return mlt_properties_set_position( MLT_FRAME_PROPERTIES( self ), "_position", value ); } /** Stack a get_image callback. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param the get_image callback * \return true if error */ -int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image ) +int mlt_frame_push_get_image( mlt_frame self, mlt_get_image get_image ) { - return mlt_deque_push_back( this->stack_image, get_image ); + return mlt_deque_push_back( self->stack_image, get_image ); } /** Pop a get_image callback. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return the get_image callback */ -mlt_get_image mlt_frame_pop_get_image( mlt_frame this ) +mlt_get_image mlt_frame_pop_get_image( mlt_frame self ) { - return mlt_deque_pop_back( this->stack_image ); + return mlt_deque_pop_back( self->stack_image ); } /** Push a frame. * * \public \memberof mlt_frame_s - * \param this a frame - * \param that the frame to push onto \p this + * \param self a frame + * \param that the frame to push onto \p self * \return true if error */ -int mlt_frame_push_frame( mlt_frame this, mlt_frame that ) +int mlt_frame_push_frame( mlt_frame self, mlt_frame that ) { - return mlt_deque_push_back( this->stack_image, that ); + return mlt_deque_push_back( self->stack_image, that ); } /** Pop a frame. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return a frame that was previously pushed */ -mlt_frame mlt_frame_pop_frame( mlt_frame this ) +mlt_frame mlt_frame_pop_frame( mlt_frame self ) { - return mlt_deque_pop_back( this->stack_image ); + return mlt_deque_pop_back( self->stack_image ); } /** Push a service. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param that an opaque pointer * \return true if error */ -int mlt_frame_push_service( mlt_frame this, void *that ) +int mlt_frame_push_service( mlt_frame self, void *that ) { - return mlt_deque_push_back( this->stack_image, that ); + return mlt_deque_push_back( self->stack_image, that ); } /** Pop a service. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return an opaque pointer to something previously pushed */ -void *mlt_frame_pop_service( mlt_frame this ) +void *mlt_frame_pop_service( mlt_frame self ) { - return mlt_deque_pop_back( this->stack_image ); + return mlt_deque_pop_back( self->stack_image ); } /** Push a number. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param that an integer * \return true if error */ -int mlt_frame_push_service_int( mlt_frame this, int that ) +int mlt_frame_push_service_int( mlt_frame self, int that ) { - return mlt_deque_push_back_int( this->stack_image, that ); + return mlt_deque_push_back_int( self->stack_image, that ); } /** Pop a number. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return an integer that was previously pushed */ -int mlt_frame_pop_service_int( mlt_frame this ) +int mlt_frame_pop_service_int( mlt_frame self ) { - return mlt_deque_pop_back_int( this->stack_image ); + return mlt_deque_pop_back_int( self->stack_image ); } /** Push an audio item on the stack. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param that an opaque pointer * \return true if error */ -int mlt_frame_push_audio( mlt_frame this, void *that ) +int mlt_frame_push_audio( mlt_frame self, void *that ) { - return mlt_deque_push_back( this->stack_audio, that ); + return mlt_deque_push_back( self->stack_audio, that ); } /** Pop an audio item from the stack * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return an opaque pointer to something that was pushed onto the frame's audio stack */ -void *mlt_frame_pop_audio( mlt_frame this ) +void *mlt_frame_pop_audio( mlt_frame self ) { - return mlt_deque_pop_back( this->stack_audio ); + return mlt_deque_pop_back( self->stack_audio ); } /** Return the service stack * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return the service stack */ -mlt_deque mlt_frame_service_stack( mlt_frame this ) +mlt_deque mlt_frame_service_stack( mlt_frame self ) { - return this->stack_service; + return self->stack_service; +} + +/** Set a new image on the frame. + * + * \public \memberof mlt_frame_s + * \param self a frame + * \param image a pointer to the raw image data + * \param size the size of the image data in bytes (optional) + * \param destroy a function to deallocate \p image when the frame is closed (optional) + * \return true if error + */ + +int mlt_frame_set_image( mlt_frame self, uint8_t *image, int size, mlt_destructor destroy ) +{ + return mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "image", image, size, destroy, NULL ); +} + +/** Set a new alpha channel on the frame. + * + * \public \memberof mlt_frame_s + * \param self a frame + * \param alpha a pointer to the alpha channel + * \param size the size of the alpha channel in bytes (optional) + * \param destroy a function to deallocate \p alpha when the frame is closed (optional) + * \return true if error + */ + +int mlt_frame_set_alpha( mlt_frame self, uint8_t *alpha, int size, mlt_destructor destroy ) +{ + return mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "alpha", alpha, size, destroy, NULL ); } /** Replace image stack with the information provided. @@ -314,24 +344,24 @@ mlt_deque mlt_frame_service_stack( mlt_frame this ) * and the upper tracks should simply not be invited to stack... * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param image a new image * \param format the image format * \param width the width of the new image * \param height the height of the new image */ -void mlt_frame_replace_image( mlt_frame this, uint8_t *image, mlt_image_format format, int width, int height ) +void mlt_frame_replace_image( mlt_frame self, uint8_t *image, mlt_image_format format, int width, int height ) { // Remove all items from the stack - while( mlt_deque_pop_back( this->stack_image ) ) ; + while( mlt_deque_pop_back( self->stack_image ) ) ; // Update the information - mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "image", image, 0, NULL, NULL ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "width", width ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "height", height ); - mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "format", format ); - this->get_alpha_mask = NULL; + mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "image", image, 0, NULL, NULL ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( self ), "width", width ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( self ), "height", height ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( self ), "format", format ); + self->get_alpha_mask = NULL; } /** Get the short name for an image format. @@ -355,6 +385,40 @@ const char * mlt_image_format_name( mlt_image_format format ) return "invalid"; } +/** Get the number of bytes needed for an image. + * + * \public \memberof mlt_frame_s + * \param format the image format + * \param width width of the image in pixels + * \param height height of the image in pixels + * \param[out] bpp the number of bytes per pixel (optional) + * \return the number of bytes + */ +int mlt_image_format_size( mlt_image_format format, int width, int height, int *bpp ) +{ + height += 1; + switch ( format ) + { + case mlt_image_none: + if ( bpp ) *bpp = 0; + return 0; + case mlt_image_rgb24: + if ( bpp ) *bpp = 3; + return width * height * 3; + case mlt_image_opengl: + case mlt_image_rgb24a: + if ( bpp ) *bpp = 4; + return width * height * 4; + case mlt_image_yuv422: + if ( bpp ) *bpp = 2; + return width * height * 2; + case mlt_image_yuv420p: + if ( bpp ) *bpp = 3 / 2; + return width * height * 3 / 2; + } + return 0; +} + /** Get the image associated to the frame. * * You should express the desired format, width, and height as inputs. As long @@ -365,7 +429,7 @@ const char * mlt_image_format_name( mlt_image_format format ) * buffer, but you should always supply the desired image format. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param[out] buffer an image buffer * \param[in,out] format the image format * \param[in,out] width the horizontal size in pixels @@ -375,10 +439,10 @@ const char * mlt_image_format_name( mlt_image_format format ) * \todo Better describe the width and height as inputs. */ -int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) +int mlt_frame_get_image( mlt_frame self, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ) { - mlt_properties properties = MLT_FRAME_PROPERTIES( this ); - mlt_get_image get_image = mlt_frame_pop_get_image( this ); + mlt_properties properties = MLT_FRAME_PROPERTIES( self ); + mlt_get_image get_image = mlt_frame_pop_get_image( self ); mlt_producer producer = mlt_properties_get_data( properties, "test_card_producer", NULL ); mlt_image_format requested_format = *format; int error = 0; @@ -386,12 +450,20 @@ int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *for if ( get_image ) { mlt_properties_set_int( properties, "image_count", mlt_properties_get_int( properties, "image_count" ) - 1 ); - error = get_image( this, buffer, format, width, height, writable ); - mlt_properties_set_int( properties, "width", *width ); - mlt_properties_set_int( properties, "height", *height ); - mlt_properties_set_int( properties, "format", *format ); - if ( this->convert_image ) - this->convert_image( this, buffer, format, requested_format ); + error = get_image( self, buffer, format, width, height, writable ); + if ( !error && *buffer ) + { + mlt_properties_set_int( properties, "width", *width ); + mlt_properties_set_int( properties, "height", *height ); + if ( self->convert_image && *buffer && requested_format != mlt_image_none ) + self->convert_image( self, buffer, format, requested_format ); + mlt_properties_set_int( properties, "format", *format ); + } + else + { + // Cause the image to be loaded from test card or fallback (white) below. + mlt_frame_get_image( self, buffer, format, width, height, writable ); + } } else if ( mlt_properties_get_data( properties, "image", NULL ) ) { @@ -399,8 +471,11 @@ int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *for *buffer = mlt_properties_get_data( properties, "image", NULL ); *width = mlt_properties_get_int( properties, "width" ); *height = mlt_properties_get_int( properties, "height" ); - if ( this->convert_image ) - this->convert_image( this, buffer, format, requested_format ); + if ( self->convert_image && *buffer && requested_format != mlt_image_none ) + { + self->convert_image( self, buffer, format, requested_format ); + mlt_properties_set_int( properties, "format", *format ); + } } else if ( producer ) { @@ -422,7 +497,7 @@ int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *for else { mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); - mlt_frame_get_image( this, buffer, format, width, height, writable ); + mlt_frame_get_image( self, buffer, format, width, height, writable ); } } else @@ -485,34 +560,31 @@ int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *for mlt_properties_set_int( properties, "test_image", 1 ); } - mlt_properties_set_int( properties, "scaled_width", *width ); - mlt_properties_set_int( properties, "scaled_height", *height ); - return error; } /** Get the alpha channel associated to the frame. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return the alpha channel */ -uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ) +uint8_t *mlt_frame_get_alpha_mask( mlt_frame self ) { uint8_t *alpha = NULL; - if ( this != NULL ) + if ( self != NULL ) { - if ( this->get_alpha_mask != NULL ) - alpha = this->get_alpha_mask( this ); + if ( self->get_alpha_mask != NULL ) + alpha = self->get_alpha_mask( self ); if ( alpha == NULL ) - alpha = mlt_properties_get_data( &this->parent, "alpha", NULL ); + alpha = mlt_properties_get_data( &self->parent, "alpha", NULL ); if ( alpha == NULL ) { - int size = mlt_properties_get_int( &this->parent, "scaled_width" ) * mlt_properties_get_int( &this->parent, "scaled_height" ); + int size = mlt_properties_get_int( &self->parent, "width" ) * mlt_properties_get_int( &self->parent, "height" ); alpha = mlt_pool_alloc( size ); memset( alpha, 255, size ); - mlt_properties_set_data( &this->parent, "alpha", alpha, size, mlt_pool_release, NULL ); + mlt_properties_set_data( &self->parent, "alpha", alpha, size, mlt_pool_release, NULL ); } } return alpha; @@ -522,8 +594,7 @@ uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ) * * You do not need to deallocate the returned string. * \public \memberof mlt_frame_s - * \param this a frame - * \param format an image format enum + * \param format an audio format enum * \return a string for the name of the image format */ @@ -534,11 +605,36 @@ const char * mlt_audio_format_name( mlt_audio_format format ) case mlt_audio_none: return "none"; case mlt_audio_s16: return "s16"; case mlt_audio_s32: return "s32"; + case mlt_audio_s32le: return "s32le"; case mlt_audio_float: return "float"; + case mlt_audio_f32le: return "f32le"; } return "invalid"; } +/** Get the amount of bytes needed for a block of audio. + * + * \public \memberof mlt_frame_s + * \param format an audio format enum + * \param samples the number of samples per channel + * \param channels the number of channels + * \return the number of bytes + */ + +int mlt_audio_format_size( mlt_audio_format format, int samples, int channels ) +{ + switch ( format ) + { + case mlt_audio_none: return 0; + case mlt_audio_s16: return samples * channels * sizeof( int16_t ); + case mlt_audio_s32le: + case mlt_audio_s32: return samples * channels * sizeof( int32_t ); + case mlt_audio_f32le: + case mlt_audio_float: return samples * channels * sizeof( float ); + } + return 0; +} + /** Get the audio associated to the frame. * * You should express the desired format, frequency, channels, and samples as inputs. As long @@ -551,7 +647,7 @@ const char * mlt_audio_format_name( mlt_audio_format format ) * You should use the \p mlt_sample_calculator to determine the number of samples you want. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param[out] buffer an audio buffer * \param[in,out] format the audio format * \param[in,out] frequency the sample rate @@ -560,22 +656,22 @@ const char * mlt_audio_format_name( mlt_audio_format format ) * \return true if error */ -int mlt_frame_get_audio( mlt_frame this, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) +int mlt_frame_get_audio( mlt_frame self, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { - mlt_get_audio get_audio = mlt_frame_pop_audio( this ); - mlt_properties properties = MLT_FRAME_PROPERTIES( this ); + mlt_get_audio get_audio = mlt_frame_pop_audio( self ); + mlt_properties properties = MLT_FRAME_PROPERTIES( self ); int hide = mlt_properties_get_int( properties, "test_audio" ); mlt_audio_format requested_format = *format; if ( hide == 0 && get_audio != NULL ) { - get_audio( this, buffer, format, frequency, channels, samples ); + get_audio( self, buffer, format, frequency, channels, samples ); mlt_properties_set_int( properties, "audio_frequency", *frequency ); mlt_properties_set_int( properties, "audio_channels", *channels ); mlt_properties_set_int( properties, "audio_samples", *samples ); mlt_properties_set_int( properties, "audio_format", *format ); - if ( this->convert_audio ) - this->convert_audio( this, buffer, format, requested_format ); + if ( self->convert_audio && *buffer && requested_format != mlt_audio_none ) + self->convert_audio( self, buffer, format, requested_format ); } else if ( mlt_properties_get_data( properties, "audio", NULL ) ) { @@ -584,8 +680,8 @@ int mlt_frame_get_audio( mlt_frame this, void **buffer, mlt_audio_format *format *frequency = mlt_properties_get_int( properties, "audio_frequency" ); *channels = mlt_properties_get_int( properties, "audio_channels" ); *samples = mlt_properties_get_int( properties, "audio_samples" ); - if ( this->convert_audio ) - this->convert_audio( this, buffer, format, requested_format ); + if ( self->convert_audio && *buffer && requested_format != mlt_audio_none ) + self->convert_audio( self, buffer, format, requested_format ); } else { @@ -613,6 +709,8 @@ int mlt_frame_get_audio( mlt_frame this, void **buffer, mlt_audio_format *format case mlt_audio_float: size = *samples * *channels * sizeof( float ); break; + default: + break; } if ( size ) *buffer = mlt_pool_alloc( size ); @@ -651,7 +749,7 @@ int mlt_frame_get_audio( mlt_frame this, void **buffer, mlt_audio_format *format /** Set the audio on a frame. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param buffer an buffer containing audio samples * \param format the format of the audio in the \p buffer * \param size the total size of the buffer (optional) @@ -659,10 +757,10 @@ int mlt_frame_get_audio( mlt_frame this, void **buffer, mlt_audio_format *format * \return true if error */ -int mlt_frame_set_audio( mlt_frame this, void *buffer, mlt_audio_format format, int size, mlt_destructor destructor ) +int mlt_frame_set_audio( mlt_frame self, void *buffer, mlt_audio_format format, int size, mlt_destructor destructor ) { - mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "audio_format", format ); - return mlt_properties_set_data( MLT_FRAME_PROPERTIES( this ), "audio", buffer, size, destructor, NULL ); + mlt_properties_set_int( MLT_FRAME_PROPERTIES( self ), "audio_format", format ); + return mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "audio", buffer, size, destructor, NULL ); } /** Get audio on a frame as a waveform image. @@ -673,24 +771,32 @@ int mlt_frame_set_audio( mlt_frame this, void *buffer, mlt_audio_format format, * value with \p mlt_pool_release. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \param w the width of the image * \param h the height of the image to create * \return a pointer to a new bitmap */ -unsigned char *mlt_frame_get_waveform( mlt_frame this, int w, int h ) +unsigned char *mlt_frame_get_waveform( mlt_frame self, int w, int h ) { int16_t *pcm = NULL; - mlt_properties properties = MLT_FRAME_PROPERTIES( this ); + mlt_properties properties = MLT_FRAME_PROPERTIES( self ); mlt_audio_format format = mlt_audio_s16; - int frequency = 32000; // lower frequency available? + int frequency = 16000; int channels = 2; - double fps = mlt_profile_fps( NULL ); - int samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( this ) ); + mlt_producer producer = mlt_frame_get_original_producer( self ); + double fps = mlt_producer_get_fps( mlt_producer_cut_parent( producer ) ); + int samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( self ) ); + + // Increase audio resolution proportional to requested image size + while ( samples < w ) + { + frequency += 16000; + samples = mlt_sample_calculator( fps, frequency, mlt_frame_get_position( self ) ); + } // Get the pcm data - mlt_frame_get_audio( this, (void**)&pcm, &format, &frequency, &channels, &samples ); + mlt_frame_get_audio( self, (void**)&pcm, &format, &frequency, &channels, &samples ); // Make an 8-bit buffer large enough to hold rendering int size = w * h; @@ -701,72 +807,73 @@ unsigned char *mlt_frame_get_waveform( mlt_frame this, int w, int h ) // Render vertical lines int16_t *ubound = pcm + samples * channels; - int skip = samples / w - 1; + int skip = samples / w; + skip = !skip ? 1 : skip; + unsigned char gray = 0xFF / skip; int i, j, k; // Iterate sample stream and along x coordinate - for ( i = 0; i < w && pcm < ubound; i++ ) + for ( i = 0; pcm < ubound; i++ ) { // pcm data has channels interleaved - for ( j = 0; j < channels; j++ ) + for ( j = 0; j < channels; j++, pcm++ ) { // Determine sample's magnitude from 2s complement; int pcm_magnitude = *pcm < 0 ? ~(*pcm) + 1 : *pcm; // The height of a line is the ratio of the magnitude multiplied by - // half the vertical resolution - int height = ( int )( ( double )( pcm_magnitude ) / 32768 * h / 2 ); - // Determine the starting y coordinate - left channel above center, - // right channel below - currently assumes 2 channels - int displacement = ( h / 2 ) - ( 1 - j ) * height; + // the vertical resolution of a single channel + int height = h * pcm_magnitude / channels / 2 / 32768; + // Determine the starting y coordinate - left top, right bottom + int displacement = h * (j * 2 + 1) / channels / 2 - ( *pcm < 0 ? 0 : height ); // Position buffer pointer using y coordinate, stride, and x coordinate - unsigned char *p = &bitmap[ i + displacement * w ]; + unsigned char *p = bitmap + i / skip + displacement * w; // Draw vertical line - for ( k = 0; k < height; k++ ) - p[ w * k ] = 0xFF; - - pcm++; + for ( k = 0; k < height + 1; k++ ) + if ( *pcm < 0 ) + p[ w * k ] = ( k == 0 ) ? 0xFF : p[ w * k ] + gray; + else + p[ w * k ] = ( k == height ) ? 0xFF : p[ w * k ] + gray; } - pcm += skip * channels; } return bitmap; } -/** Get the end service that produced this frame. +/** Get the end service that produced self frame. * * This fetches the first producer of the frame and not any producers that * encapsulate it. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame * \return a producer */ -mlt_producer mlt_frame_get_original_producer( mlt_frame this ) +mlt_producer mlt_frame_get_original_producer( mlt_frame self ) { - if ( this != NULL ) - return mlt_properties_get_data( MLT_FRAME_PROPERTIES( this ), "_producer", NULL ); + if ( self != NULL ) + return mlt_properties_get_data( MLT_FRAME_PROPERTIES( self ), "_producer", NULL ); return NULL; } /** Destroy the frame. * * \public \memberof mlt_frame_s - * \param this a frame + * \param self a frame */ -void mlt_frame_close( mlt_frame this ) +void mlt_frame_close( mlt_frame self ) { - if ( this != NULL && mlt_properties_dec_ref( MLT_FRAME_PROPERTIES( this ) ) <= 0 ) + if ( self != NULL && mlt_properties_dec_ref( MLT_FRAME_PROPERTIES( self ) ) <= 0 ) { - mlt_deque_close( this->stack_image ); - mlt_deque_close( this->stack_audio ); - while( mlt_deque_peek_back( this->stack_service ) ) - mlt_service_close( mlt_deque_pop_back( this->stack_service ) ); - mlt_deque_close( this->stack_service ); - mlt_properties_close( &this->parent ); - free( this ); + mlt_deque_close( self->stack_image ); + mlt_deque_close( self->stack_audio ); + while( mlt_deque_peek_back( self->stack_service ) ) + mlt_service_close( mlt_deque_pop_back( self->stack_service ) ); + mlt_deque_close( self->stack_service ); + mlt_properties_close( &self->parent ); + free( self ); } } @@ -790,8 +897,8 @@ int mlt_sample_calculator( float fps, int frequency, int64_t position ) This approach should prevent rounding errors that can accumulate over a large number of frames causing A/V sync problems. */ - return mlt_sample_calculator_to_now( fps, frequency, position ) - - mlt_sample_calculator_to_now( fps, frequency, position + 1 ); + return mlt_sample_calculator_to_now( fps, frequency, position + 1 ) + - mlt_sample_calculator_to_now( fps, frequency, position ); } /** Determine the number of samples that belong before a time position. @@ -804,7 +911,7 @@ int mlt_sample_calculator( float fps, int frequency, int64_t position ) * \bug Will this break when mlt_position is converted to double? */ -inline int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t position ) +int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t position ) { int64_t samples = 0; @@ -816,3 +923,121 @@ inline int64_t mlt_sample_calculator_to_now( float fps, int frequency, int64_t p return samples; } + +void mlt_frame_write_ppm( mlt_frame frame ) +{ + int width; + int height; + mlt_image_format format = mlt_image_rgb24; + uint8_t *image; + + if ( mlt_frame_get_image( frame, &image, &format, &width, &height, 0 ) == 0 ) + { + FILE *file; + char filename[16]; + + sprintf( filename, "frame-%05d.ppm", mlt_frame_get_position( frame ) ); + file = fopen( filename, "wb" ); + if ( !file ) + return; + fprintf( file, "P6\n%d %d\n255\n", width, height); + fwrite( image, width * height * 3, 1, file ); + fclose( file ); + } +} + +/** Get or create a properties object unique to this service instance. + * + * Use this function to hold a service's processing parameters for this + * particular frame. Set the parameters in the service's process function. + * Then, get the parameters in the function it pushes to the frame's audio + * or image stack. This makes the service more parallel by reducing race + * conditions and less sensitive to multiple instances (by not setting a + * non-unique property on the frame). Creation and destruction of the + * properties object is handled automatically. + * + * \public \memberof mlt_frame_s + * \param self a frame + * \param service a service + * \return a properties object + */ + +mlt_properties mlt_frame_unique_properties( mlt_frame self, mlt_service service ) +{ + mlt_properties frame_props = MLT_FRAME_PROPERTIES( self ); + mlt_properties service_props = MLT_SERVICE_PROPERTIES( service ); + char *unique = mlt_properties_get( service_props, "_unique_id" ); + mlt_properties instance_props = mlt_properties_get_data( frame_props, unique, NULL ); + + if ( !instance_props ) + { + instance_props = mlt_properties_new(); + mlt_properties_set_data( frame_props, unique, instance_props, 0, (mlt_destructor) mlt_properties_close, NULL ); + } + + return instance_props; +} + +/** Make a copy of a frame. + * + * This does not copy the get_image/get_audio processing stacks or any + * data properties other than the audio and image. + * + * \public \memberof mlt_frame_s + * \param self the frame to clone + * \param is_deep a boolean to indicate whether to make a deep copy of the audio + * and video data chunks or to make a shallow copy by pointing to the supplied frame + * \return a almost-complete copy of the frame + * \todo copy the processing deques + */ + +mlt_frame mlt_frame_clone( mlt_frame self, int is_deep ) +{ + mlt_frame new_frame = mlt_frame_init( NULL ); + mlt_properties properties = MLT_FRAME_PROPERTIES( self ); + mlt_properties new_props = MLT_FRAME_PROPERTIES( new_frame ); + void *data, *copy; + int size; + + mlt_properties_inherit( new_props, properties ); + if ( is_deep ) + { + data = mlt_properties_get_data( properties, "audio", &size ); + if ( data ) + { + if ( !size ) + size = mlt_audio_format_size( mlt_properties_get_int( properties, "audio_format" ), + mlt_properties_get_int( properties, "audio_samples" ), + mlt_properties_get_int( properties, "audio_channels" ) ); + copy = mlt_pool_alloc( size ); + memcpy( copy, data, size ); + mlt_properties_set_data( new_props, "audio", copy, size, mlt_pool_release, NULL ); + } + data = mlt_properties_get_data( properties, "image", &size ); + if ( data ) + { + if ( ! size ) + size = mlt_image_format_size( mlt_properties_get_int( properties, "format" ), + mlt_properties_get_int( properties, "width" ), + mlt_properties_get_int( properties, "height" ), NULL ); + copy = mlt_pool_alloc( size ); + memcpy( copy, data, size ); + mlt_properties_set_data( new_props, "image", copy, size, mlt_pool_release, NULL ); + } + } + else + { + // This frame takes a reference on the original frame since the data is a shallow copy. + mlt_properties_inc_ref( properties ); + mlt_properties_set_data( new_props, "_cloned_frame", self, 0, + (mlt_destructor) mlt_frame_close, NULL ); + + // Copy properties + data = mlt_properties_get_data( properties, "audio", &size ); + mlt_properties_set_data( new_props, "audio", data, size, NULL, NULL ); + data = mlt_properties_get_data( properties, "image", &size ); + mlt_properties_set_data( new_props, "image", data, size, NULL, NULL ); + } + + return new_frame; +}