+
+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;
+}