+ return mlt_deque_push_back( self->stack_audio, that );
+}
+
+/** Pop an audio item from the stack
+ *
+ * \public \memberof mlt_frame_s
+ * \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 self )
+{
+ return mlt_deque_pop_back( self->stack_audio );
+}
+
+/** Return the service stack
+ *
+ * \public \memberof mlt_frame_s
+ * \param self a frame
+ * \return the service stack
+ */
+
+mlt_deque mlt_frame_service_stack( mlt_frame self )
+{
+ 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 )
+{
+ self->get_alpha_mask = NULL;
+ return mlt_properties_set_data( MLT_FRAME_PROPERTIES( self ), "alpha", alpha, size, destroy, NULL );
+}
+
+/** Replace image stack with the information provided.
+ *
+ * This might prove to be unreliable and restrictive - the idea is that a transition
+ * which normally uses two images may decide to only use the b frame (ie: in the case
+ * of a composite where the b frame completely obscures the a frame).
+ *
+ * The image must be writable and the destructor for the image itself must be taken
+ * care of on another frame and that frame cannot have a replace applied to it...
+ * Further it assumes that no alpha mask is in use.
+ *
+ * For these reasons, it can only be used in a specific situation - when you have
+ * multiple tracks each with their own transition and these transitions are applied
+ * in a strictly reversed order (ie: highest numbered [lowest track] is processed
+ * first).
+ *
+ * More reliable approach - the cases should be detected during the process phase
+ * and the upper tracks should simply not be invited to stack...
+ *
+ * \public \memberof mlt_frame_s
+ * \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 self, uint8_t *image, mlt_image_format format, int width, int height )
+{
+ // Remove all items from the stack
+ while( mlt_deque_pop_back( self->stack_image ) ) ;
+
+ // Update the information
+ 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.
+ *
+ * \public \memberof mlt_frame_s
+ * \param format the image format
+ * \return a string
+ */
+
+const char * mlt_image_format_name( mlt_image_format format )
+{
+ switch ( format )
+ {
+ case mlt_image_none: return "none";
+ case mlt_image_rgb24: return "rgb24";
+ case mlt_image_rgb24a: return "rgb24a";
+ case mlt_image_yuv422: return "yuv422";
+ case mlt_image_yuv420p: return "yuv420p";
+ case mlt_image_opengl: return "opengl";
+ }
+ 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
+ * as the loader producer was used to generate this or the imageconvert filter
+ * was attached, then you will get the image back in the format you desire.
+ * However, you do not always get the width and height you request depending
+ * on properties and filters. You do not need to supply a pre-allocated
+ * buffer, but you should always supply the desired image format.
+ *
+ * \public \memberof mlt_frame_s
+ * \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
+ * \param[in,out] height the vertical size in pixels
+ * \param writable whether or not you will need to be able to write to the memory returned in \p buffer
+ * \return true if error
+ * \todo Better describe the width and height as inputs.
+ */
+
+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( self );
+ mlt_get_image get_image = mlt_frame_pop_get_image( self );